stronger type system for rds::Link
[hiphop-php.git] / hphp / runtime / vm / jit / translator-runtime.cpp
blob5b378d758c086ede54d6b014f1117aaea586ea7b
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/translator-runtime.h"
19 #include "hphp/runtime/base/array-common.h"
20 #include "hphp/runtime/base/array-iterator.h"
21 #include "hphp/runtime/base/autoload-handler.h"
22 #include "hphp/runtime/base/builtin-functions.h"
23 #include "hphp/runtime/base/collections.h"
24 #include "hphp/runtime/base/execution-context.h"
25 #include "hphp/runtime/base/object-data.h"
26 #include "hphp/runtime/base/mixed-array.h"
27 #include "hphp/runtime/base/packed-array.h"
28 #include "hphp/runtime/base/set-array.h"
29 #include "hphp/runtime/base/stats.h"
30 #include "hphp/runtime/base/string-data.h"
31 #include "hphp/runtime/base/type-structure-helpers.h"
32 #include "hphp/runtime/base/tv-mutate.h"
33 #include "hphp/runtime/base/tv-variant.h"
34 #include "hphp/runtime/base/tv-refcount.h"
35 #include "hphp/runtime/base/tv-type.h"
36 #include "hphp/runtime/base/typed-value.h"
37 #include "hphp/runtime/base/zend-functions.h"
39 #include "hphp/runtime/ext/collections/ext_collections-map.h"
40 #include "hphp/runtime/ext/collections/ext_collections-pair.h"
41 #include "hphp/runtime/ext/collections/ext_collections-set.h"
42 #include "hphp/runtime/ext/collections/ext_collections-vector.h"
43 #include "hphp/runtime/ext/hh/ext_hh.h"
44 #include "hphp/runtime/ext/std/ext_std_function.h"
46 #include "hphp/runtime/vm/act-rec.h"
47 #include "hphp/runtime/vm/class.h"
48 #include "hphp/runtime/vm/func.h"
49 #include "hphp/runtime/vm/member-operations.h"
50 #include "hphp/runtime/vm/method-lookup.h"
51 #include "hphp/runtime/vm/type-constraint.h"
52 #include "hphp/runtime/vm/unit.h"
53 #include "hphp/runtime/vm/unit-util.h"
54 #include "hphp/runtime/vm/unwind.h"
55 #include "hphp/runtime/vm/vm-regs.h"
57 #include "hphp/runtime/vm/jit/minstr-helpers.h"
58 #include "hphp/runtime/vm/jit/target-profile.h"
59 #include "hphp/runtime/vm/jit/translator-inline.h"
60 #include "hphp/runtime/vm/jit/unwind-itanium.h"
62 #include "hphp/system/systemlib.h"
64 #include "hphp/util/portability.h"
65 #include "hphp/util/string-vsnprintf.h"
67 namespace HPHP {
69 TRACE_SET_MOD(runtime);
71 //////////////////////////////////////////////////////////////////////
73 namespace jit {
75 //////////////////////////////////////////////////////////////////////
77 ArrayData* addNewElemHelper(ArrayData* a, TypedValue value) {
78 assertx(a->isPHPArray());
80 auto r = a->append(*tvAssertCell(&value), a->hasMultipleRefs());
81 if (UNLIKELY(r != a)) {
82 decRefArr(a);
84 return r;
87 ArrayData* addElemIntKeyHelper(ArrayData* ad,
88 int64_t key,
89 TypedValue value) {
90 assertx(ad->isPHPArray());
91 assertx(cellIsPlausible(value));
92 // this does not re-enter
93 // set will decRef any old value that may have been overwritten
94 // if appropriate
95 ArrayData* retval = ad->set(key, tvAsCVarRef(&value),
96 ad->cowCheck());
97 // TODO Task #1970153: It would be great if there were set()
98 // methods that didn't bump up the refcount so that we didn't
99 // have to decrement it here
100 tvDecRefGen(&value);
101 return arrayRefShuffle<false, KindOfArray>(ad, retval, nullptr);
104 template <bool intishWarn>
105 ArrayData* addElemStringKeyHelper(ArrayData* ad,
106 StringData* key,
107 TypedValue value) {
108 assertx(ad->isPHPArray());
109 assertx(cellIsPlausible(value));
110 // this does not re-enter
111 bool copy = ad->cowCheck();
112 // set will decRef any old value that may have been overwritten
113 // if appropriate
114 int64_t intkey;
115 ArrayData* retval;
116 if (UNLIKELY(key->isStrictlyInteger(intkey))) {
117 if (intishWarn) raise_intish_index_cast();
118 retval = ad->set(intkey, *tvToCell(&value), copy);
119 } else {
120 retval = ad->set(key, *tvToCell(&value), copy);
122 // TODO Task #1970153: It would be great if there were set()
123 // methods that didn't bump up the refcount so that we didn't
124 // have to decrement it here
125 decRefStr(key);
126 tvDecRefGen(&value);
127 return arrayRefShuffle<false, KindOfArray>(ad, retval, nullptr);
130 template ArrayData*
131 addElemStringKeyHelper<true>(ArrayData*, StringData*, TypedValue);
132 template ArrayData*
133 addElemStringKeyHelper<false>(ArrayData*, StringData*, TypedValue);
135 ArrayData* dictAddElemIntKeyHelper(ArrayData* ad,
136 int64_t key,
137 TypedValue value) {
138 assertx(ad->isDict());
139 // set will decRef any old value that may have been overwritten
140 // if appropriate
141 ArrayData* retval =
142 MixedArray::SetIntDict(ad, key, *tvAssertCell(&value), ad->cowCheck());
143 // TODO Task #1970153: It would be great if there were set()
144 // methods that didn't bump up the refcount so that we didn't
145 // have to decrement it here
146 tvDecRefGen(&value);
147 return arrayRefShuffle<false, KindOfDict>(ad, retval, nullptr);
150 ArrayData* dictAddElemStringKeyHelper(ArrayData* ad,
151 StringData* key,
152 TypedValue value) {
153 assertx(ad->isDict());
154 // set will decRef any old value that may have been overwritten
155 // if appropriate
156 ArrayData* retval =
157 MixedArray::SetStrDict(ad, key, *tvAssertCell(&value), ad->cowCheck());
158 // TODO Task #1970153: It would be great if there were set()
159 // methods that didn't bump up the refcount so that we didn't
160 // have to decrement it here
161 decRefStr(key);
162 tvDecRefGen(&value);
163 return arrayRefShuffle<false, KindOfDict>(ad, retval, nullptr);
166 ArrayData* arrayAdd(ArrayData* a1, ArrayData* a2) {
167 assertx(a1->isPHPArray());
168 assertx(a2->isPHPArray());
170 if (checkHACMisc()) raiseHackArrCompatAdd();
172 if (!a2->empty()) {
173 if (a1->empty()) {
174 // We consume refs on a2 and also produce references, so there's
175 // no need to inc/dec a2.
176 decRefArr(a1);
177 return a2;
179 if (a1 != a2) {
180 auto const escalated = a1->plusEq(a2);
181 if (escalated != a1) {
182 decRefArr(a2);
183 decRefArr(a1);
184 return escalated;
188 decRefArr(a2);
189 return a1;
192 void setNewElem(TypedValue* base, Cell val) {
193 HPHP::SetNewElem<false>(base, &val);
196 void setNewElemArray(TypedValue* base, Cell val) {
197 HPHP::SetNewElemArray(base, &val);
200 void setNewElemVec(TypedValue* base, Cell val) {
201 HPHP::SetNewElemVec(base, &val);
204 RefData* boxValue(TypedValue tv) {
205 assertx(tv.m_type != KindOfRef);
206 if (tv.m_type == KindOfUninit) tv = make_tv<KindOfNull>();
207 return RefData::Make(tv);
210 inline int64_t reinterpretDblAsInt(double d) {
211 union {
212 int64_t intval;
213 double dblval;
214 } u;
215 u.dblval = d;
216 return u.intval;
219 inline double reinterpretIntAsDbl(int64_t i) {
220 union {
221 int64_t intval;
222 double dblval;
223 } u;
224 u.intval = i;
225 return u.dblval;
228 ArrayData* convCellToArrHelper(TypedValue tv) {
229 // Note: the call sites of this function all assume that
230 // no user code will run and no recoverable exceptions will
231 // occur while running this code. This seems trivially true
232 // in all cases but converting objects to arrays. It also
233 // seems true for that case as well, since the resulting array
234 // is essentially metadata for the object. If that is not true,
235 // you might end up looking at this code in a debugger and now
236 // you know why.
237 tvCastToArrayInPlace(&tv); // consumes a ref on counted values
238 return tv.m_data.parr;
241 ArrayData* convArrToNonDVArrHelper(ArrayData* adIn) {
242 assertx(adIn->isPHPArray());
243 if (adIn->isNotDVArray()) return adIn;
244 auto a = adIn->toPHPArray(adIn->cowCheck());
245 if (a != adIn) decRefArr(adIn);
246 assertx(a->isNotDVArray());
247 return a;
250 ArrayData* convVecToArrHelper(ArrayData* adIn) {
251 assertx(adIn->isVecArray());
252 auto a = PackedArray::ToPHPArrayVec(adIn, adIn->cowCheck());
253 if (a != adIn) decRefArr(adIn);
254 assertx(a->isPHPArray());
255 assertx(a->isNotDVArray());
256 return a;
259 ArrayData* convDictToArrHelper(ArrayData* adIn) {
260 assertx(adIn->isDict());
261 auto a = MixedArray::ToPHPArrayDict(adIn, adIn->cowCheck());
262 if (a != adIn) decRefArr(adIn);
263 assertx(a->isPHPArray());
264 assertx(a->isNotDVArray());
265 return a;
268 ArrayData* convKeysetToArrHelper(ArrayData* adIn) {
269 assertx(adIn->isKeyset());
270 auto a = SetArray::ToPHPArray(adIn, adIn->cowCheck());
271 if (a != adIn) decRefArr(adIn);
272 assertx(a->isPHPArray());
273 assertx(a->isNotDVArray());
274 return a;
277 ArrayData* convArrToVecHelper(ArrayData* adIn) {
278 assertx(adIn->isPHPArray());
279 auto a = adIn->toVec(adIn->cowCheck());
280 if (a != adIn) decRefArr(adIn);
281 return a;
284 ArrayData* convDictToVecHelper(ArrayData* adIn) {
285 assertx(adIn->isDict());
286 auto a = MixedArray::ToVecDict(adIn, adIn->cowCheck());
287 assertx(a != adIn);
288 decRefArr(adIn);
289 return a;
292 ArrayData* convKeysetToVecHelper(ArrayData* adIn) {
293 assertx(adIn->isKeyset());
294 auto a = SetArray::ToVec(adIn, adIn->cowCheck());
295 assertx(a != adIn);
296 decRefArr(adIn);
297 return a;
300 ArrayData* convObjToVecHelper(ObjectData* obj) {
301 auto a = castObjToVec(obj);
302 assertx(a->isVecArray());
303 decRefObj(obj);
304 return a;
307 ArrayData* convArrToDictHelper(ArrayData* adIn) {
308 assertx(adIn->isPHPArray());
309 auto a = adIn->toDict(adIn->cowCheck());
310 if (a != adIn) decRefArr(adIn);
311 return a;
314 ArrayData* convVecToDictHelper(ArrayData* adIn) {
315 assertx(adIn->isVecArray());
316 auto a = PackedArray::ToDictVec(adIn, adIn->cowCheck());
317 assertx(a != adIn);
318 decRefArr(adIn);
319 return a;
322 ArrayData* convKeysetToDictHelper(ArrayData* adIn) {
323 assertx(adIn->isKeyset());
324 auto a = SetArray::ToDict(adIn, adIn->cowCheck());
325 if (a != adIn) decRefArr(adIn);
326 return a;
329 ArrayData* convObjToDictHelper(ObjectData* obj) {
330 auto a = castObjToDict(obj);
331 assertx(a->isDict());
332 decRefObj(obj);
333 return a;
336 ArrayData* convArrToKeysetHelper(ArrayData* adIn) {
337 assertx(adIn->isPHPArray());
338 auto a = adIn->toKeyset(adIn->cowCheck());
339 if (a != adIn) decRefArr(adIn);
340 return a;
343 ArrayData* convVecToKeysetHelper(ArrayData* adIn) {
344 assertx(adIn->isVecArray());
345 auto a = PackedArray::ToKeysetVec(adIn, adIn->cowCheck());
346 assertx(a != adIn);
347 decRefArr(adIn);
348 return a;
351 ArrayData* convDictToKeysetHelper(ArrayData* adIn) {
352 assertx(adIn->isDict());
353 auto a = MixedArray::ToKeysetDict(adIn, adIn->cowCheck());
354 if (a != adIn) decRefArr(adIn);
355 return a;
358 ArrayData* convObjToKeysetHelper(ObjectData* obj) {
359 auto a = castObjToKeyset(obj);
360 assertx(a->isKeyset());
361 decRefObj(obj);
362 return a;
365 int64_t convObjToDblHelper(const ObjectData* o) {
366 return reinterpretDblAsInt(o->toDouble());
369 int64_t convArrToDblHelper(ArrayData* a) {
370 return reinterpretDblAsInt(a->empty() ? 0 : 1);
373 int64_t convStrToDblHelper(const StringData* s) {
374 return reinterpretDblAsInt(s->toDouble());
377 int64_t convResToDblHelper(const ResourceHdr* r) {
378 return reinterpretDblAsInt(r->getId());
381 int64_t convCellToDblHelper(TypedValue tv) {
382 return reinterpretDblAsInt(tvCastToDouble(tv));
385 ObjectData* convCellToObjHelper(TypedValue tv) {
386 // Note: the call sites of this function all assume that
387 // no user code will run and no recoverable exceptions will
388 // occur while running this code. This seems trivially true
389 // in all cases but converting arrays to objects. It also
390 // seems true for that case as well, since the source array
391 // is essentially metadata for the object. If that is not true,
392 // you might end up looking at this code in a debugger and now
393 // you know why.
394 tvCastToObjectInPlace(&tv); // consumes a ref on counted values
395 return tv.m_data.pobj;
398 StringData* convDblToStrHelper(int64_t i) {
399 double d = reinterpretIntAsDbl(i);
400 return buildStringData(d);
403 StringData* convIntToStrHelper(int64_t i) {
404 return buildStringData(i);
407 StringData* convObjToStrHelper(ObjectData* o) {
408 // toString() returns a counted String; detach() it to move ownership
409 // of the count to the caller
410 return o->invokeToString().detach();
413 StringData* convResToStrHelper(ResourceHdr* r) {
414 // toString() returns a counted String; detach() it to move ownership
415 // of the count to the caller
416 return r->data()->o_toString().detach();
419 inline void coerceCellFail(DataType expected, DataType actual, int64_t argNum,
420 const Func* func) {
421 raise_param_type_warning(func->displayName()->data(),
422 argNum, expected, actual);
424 throw TVCoercionException(func, argNum, actual, expected);
427 bool coerceCellToBoolHelper(TypedValue tv, int64_t argNum, const Func* func) {
428 assertx(cellIsPlausible(tv));
430 tvCoerceIfStrict(tv, argNum, func);
432 DataType type = tv.m_type;
433 if (isArrayLikeType(type) || type == KindOfObject || type == KindOfResource) {
434 coerceCellFail(KindOfBoolean, type, argNum, func);
435 not_reached();
438 return cellToBool(tv);
441 int64_t coerceStrToDblHelper(StringData* sd, int64_t argNum, const Func* func) {
442 DataType type = is_numeric_string(sd->data(), sd->size(), nullptr, nullptr);
444 if (UNLIKELY(RuntimeOption::PHP7_ScalarTypes)) {
445 auto tv = make_tv<KindOfString>(sd);
447 // In strict mode this will always fail, in weak mode it will be a noop
448 tvCoerceIfStrict(tv, argNum, func);
450 if (type != KindOfDouble && type != KindOfInt64) {
451 coerceCellFail(KindOfDouble, KindOfString, argNum, func);
452 not_reached();
455 return reinterpretDblAsInt(sd->toDouble());
458 int64_t coerceCellToDblHelper(Cell tv, int64_t argNum, const Func* func) {
459 assertx(cellIsPlausible(tv));
461 tvCoerceIfStrict(tv, argNum, func);
463 switch (tv.m_type) {
464 case KindOfNull:
465 case KindOfBoolean:
466 case KindOfInt64:
467 case KindOfDouble:
468 return convCellToDblHelper(tv);
470 case KindOfPersistentString:
471 case KindOfString:
472 return coerceStrToDblHelper(tv.m_data.pstr, argNum, func);
474 case KindOfUninit:
475 case KindOfPersistentVec:
476 case KindOfVec:
477 case KindOfPersistentDict:
478 case KindOfDict:
479 case KindOfPersistentKeyset:
480 case KindOfKeyset:
481 case KindOfPersistentArray:
482 case KindOfArray:
483 case KindOfObject:
484 case KindOfResource:
485 coerceCellFail(KindOfDouble, tv.m_type, argNum, func);
486 break;
488 case KindOfRef:
489 break;
491 not_reached();
494 int64_t coerceStrToIntHelper(StringData* sd, int64_t argNum, const Func* func) {
495 DataType type = is_numeric_string(sd->data(), sd->size(), nullptr, nullptr);
497 if (UNLIKELY(RuntimeOption::PHP7_ScalarTypes)) {
498 auto tv = make_tv<KindOfString>(sd);
500 // In strict mode this will always fail, in weak mode it will be a noop
501 tvCoerceIfStrict(tv, argNum, func);
503 if (type != KindOfDouble && type != KindOfInt64) {
504 coerceCellFail(KindOfInt64, KindOfString, argNum, func);
505 not_reached();
508 return sd->toInt64();
511 int64_t coerceCellToIntHelper(TypedValue tv, int64_t argNum, const Func* func) {
512 assertx(cellIsPlausible(tv));
514 tvCoerceIfStrict(tv, argNum, func);
516 switch (tv.m_type) {
517 case KindOfNull:
518 case KindOfBoolean:
519 case KindOfInt64:
520 case KindOfDouble:
521 return cellToInt(tv);
523 case KindOfPersistentString:
524 case KindOfString:
525 return coerceStrToIntHelper(tv.m_data.pstr, argNum, func);
527 case KindOfUninit:
528 case KindOfPersistentVec:
529 case KindOfVec:
530 case KindOfPersistentDict:
531 case KindOfDict:
532 case KindOfPersistentKeyset:
533 case KindOfKeyset:
534 case KindOfPersistentArray:
535 case KindOfArray:
536 case KindOfObject:
537 case KindOfResource:
538 coerceCellFail(KindOfInt64, tv.m_type, argNum, func);
539 break;
541 case KindOfRef:
542 break;
544 not_reached();
547 const StaticString
548 s_empty(""),
549 s_1("1");
551 StringData* convCellToStrHelper(TypedValue tv) {
552 switch (tv.m_type) {
553 case KindOfUninit:
554 case KindOfNull: return s_empty.get();
555 case KindOfBoolean: return tv.m_data.num ? s_1.get() : s_empty.get();
556 case KindOfInt64: return convIntToStrHelper(tv.m_data.num);
557 case KindOfDouble: return convDblToStrHelper(tv.m_data.num);
558 case KindOfString: tv.m_data.pstr->incRefCount();
559 /* fallthrough */
560 case KindOfPersistentString:
561 return tv.m_data.pstr;
562 case KindOfPersistentVec:
563 case KindOfVec: raise_notice("Vec to string conversion");
564 return vec_string.get();
565 case KindOfPersistentDict:
566 case KindOfDict: raise_notice("Dict to string conversion");
567 return dict_string.get();
568 case KindOfPersistentKeyset:
569 case KindOfKeyset: raise_notice("Keyset to string conversion");
570 return keyset_string.get();
571 case KindOfPersistentArray:
572 case KindOfArray: raise_notice("Array to string conversion");
573 return array_string.get();
574 case KindOfObject: return convObjToStrHelper(tv.m_data.pobj);
575 case KindOfResource: return convResToStrHelper(tv.m_data.pres);
576 case KindOfRef: break;
578 not_reached();
581 void raiseUndefProp(ObjectData* base, const StringData* name) {
582 base->raiseUndefProp(name);
585 void raiseUndefVariable(StringData* nm) {
586 raise_notice(Strings::UNDEFINED_VARIABLE, nm->data());
587 decRefStr(nm);
590 void raise_error_sd(const StringData *msg) {
591 raise_error("%s", msg->data());
594 ALWAYS_INLINE
595 static bool VerifyTypeSlowImpl(const Class* cls,
596 const Class* constraint,
597 const HPHP::TypeConstraint* expected) {
598 // This helper should only be called for the Object, This, Self, and Parent
599 // cases
600 assertx(expected->isObject() || expected->isSelf() || expected->isParent()
601 || expected->isThis());
602 // For the This, Self and Parent cases, we must always have a resolved class
603 // for the constraint
604 assertx(IMPLIES(
605 expected->isSelf() || expected->isParent() || expected->isThis(),
606 constraint != nullptr
608 // If we have a resolved class for the constraint, all we have to do is
609 // check if the value's class is compatible with it
610 if (LIKELY(constraint != nullptr)) {
611 if (expected->isThis() && RuntimeOption::EvalThisTypeHintLevel >= 2) {
612 return cls == constraint;
614 return cls->classof(constraint);
616 // The Self and Parent cases should never reach here because they were
617 // handled above
618 assertx(expected->isObject());
619 // Handle the case where the constraint is a type alias
620 return expected->checkTypeAliasObj(cls);
623 void VerifyParamTypeSlow(const Class* cls,
624 const Class* constraint,
625 const HPHP::TypeConstraint* expected,
626 int param) {
627 if (!VerifyTypeSlowImpl(cls, constraint, expected)) {
628 VerifyParamTypeFail(param);
632 void VerifyParamTypeCallable(TypedValue value, int param) {
633 if (UNLIKELY(!is_callable(tvAsCVarRef(&value)))) {
634 VerifyParamTypeFail(param);
638 void VerifyParamTypeFail(int paramNum) {
639 VMRegAnchor _;
640 const ActRec* ar = liveFrame();
641 const Func* func = ar->m_func;
642 auto const& tc = func->params()[paramNum].typeConstraint;
643 TypedValue* tv = frame_local(ar, paramNum);
644 auto unit = func->unit();
645 bool useStrictTypes =
646 unit->isHHFile() || RuntimeOption::EnableHipHopSyntax ||
647 !ar->useWeakTypes();
648 assertx(!tc.check(tv, func));
649 tc.verifyParamFail(func, tv, paramNum, useStrictTypes);
652 void VerifyRetTypeSlow(int32_t id,
653 const Class* cls,
654 const Class* constraint,
655 const HPHP::TypeConstraint* expected,
656 TypedValue tv) {
657 if (!VerifyTypeSlowImpl(cls, constraint, expected)) {
658 VerifyRetTypeFail(id, &tv);
662 void VerifyRetTypeCallable(int32_t id, TypedValue value) {
663 if (UNLIKELY(!is_callable(tvAsCVarRef(&value)))) {
664 VerifyRetTypeFail(id, &value);
668 void VerifyRetTypeFail(int32_t id, TypedValue* tv) {
669 VMRegAnchor _;
670 const ActRec* ar = liveFrame();
671 const Func* func = ar->m_func;
672 if (id == HPHP::TypeConstraint::ReturnId) {
673 auto const& tc = func->returnTypeConstraint();
674 auto unit = func->unit();
675 bool useStrictTypes =
676 RuntimeOption::EnableHipHopSyntax || func->isBuiltin() ||
677 unit->useStrictTypes();
678 assertx(!tc.check(tv, func));
679 tc.verifyReturnFail(func, tv, useStrictTypes);
680 } else {
681 auto const& tc = func->params()[id].typeConstraint;
682 assertx(!tc.check(tv, func));
683 tc.verifyOutParamFail(func, tv, id);
687 namespace {
688 ALWAYS_INLINE
689 TypedValue getDefaultIfNullCell(tv_rval rval, const TypedValue& def) {
690 return UNLIKELY(!rval) ? def : rval.tv();
693 template <bool intishWarn>
694 NEVER_INLINE
695 TypedValue arrayIdxSiSlow(ArrayData* a, StringData* key, TypedValue def) {
696 assertx(a->isPHPArray());
697 int64_t i;
698 if (UNLIKELY(key->isStrictlyInteger(i))) {
699 if (intishWarn) raise_intish_index_cast();
700 return getDefaultIfNullCell(a->rval(i), def);
701 } else {
702 return getDefaultIfNullCell(a->rval(key), def);
706 NEVER_INLINE
707 TypedValue arrayIdxSSlow(ArrayData* a, StringData* key, TypedValue def) {
708 assertx(a->isPHPArray());
709 return getDefaultIfNullCell(a->rval(key), def);
714 TypedValue arrayIdxS(ArrayData* a, StringData* key, TypedValue def) {
715 assertx(a->isPHPArray());
716 if (UNLIKELY(!a->isMixed())) return arrayIdxSSlow(a, key, def);
717 return getDefaultIfNullCell(MixedArray::RvalStr(a, key), def);
720 template <bool intishWarn>
721 TypedValue arrayIdxSi(ArrayData* a, StringData* key, TypedValue def) {
722 assertx(a->isPHPArray());
723 if (UNLIKELY(!a->isMixed())) return arrayIdxSiSlow<intishWarn>(a, key, def);
724 int64_t i;
725 if (UNLIKELY(key->isStrictlyInteger(i))) {
726 if (intishWarn) raise_intish_index_cast();
727 return getDefaultIfNullCell(MixedArray::RvalInt(a, i), def);
728 } else {
729 return getDefaultIfNullCell(MixedArray::RvalStr(a, key), def);
733 template TypedValue arrayIdxSi<false>(ArrayData*, StringData*, TypedValue);
734 template TypedValue arrayIdxSi<true>(ArrayData*, StringData*, TypedValue);
736 TypedValue arrayIdxI(ArrayData* a, int64_t key, TypedValue def) {
737 assertx(a->isPHPArray());
738 return getDefaultIfNullCell(a->rval(key), def);
741 TypedValue dictIdxI(ArrayData* a, int64_t key, TypedValue def) {
742 assertx(a->isDict());
743 return getDefaultIfNullCell(MixedArray::RvalIntDict(a, key), def);
746 TypedValue dictIdxS(ArrayData* a, StringData* key, TypedValue def) {
747 assertx(a->isDict());
748 return getDefaultIfNullCell(MixedArray::RvalStrDict(a, key), def);
751 TypedValue keysetIdxI(ArrayData* a, int64_t key, TypedValue def) {
752 assertx(a->isKeyset());
753 return getDefaultIfNullCell(SetArray::RvalInt(a, key), def);
756 TypedValue keysetIdxS(ArrayData* a, StringData* key, TypedValue def) {
757 assertx(a->isKeyset());
758 return getDefaultIfNullCell(SetArray::RvalStr(a, key), def);
761 TypedValue* getSPropOrNull(const Class* cls,
762 const StringData* name,
763 Class* ctx) {
764 auto const lookup = cls->getSProp(ctx, name);
766 if (UNLIKELY(!lookup.prop || !lookup.accessible)) return nullptr;
768 return lookup.prop;
771 TypedValue* getSPropOrRaise(const Class* cls,
772 const StringData* name,
773 Class* ctx) {
774 auto sprop = getSPropOrNull(cls, name, ctx);
775 if (UNLIKELY(!sprop)) {
776 raise_error("Invalid static property access: %s::%s",
777 cls->name()->data(), name->data());
779 return sprop;
782 TypedValue* ldGblAddrDefHelper(StringData* name) {
783 return g_context->m_globalVarEnv->lookupAdd(name);
786 template <typename T>
787 static int64_t switchBoundsCheck(T v, int64_t base, int64_t nTargets) {
788 // I'm relying on gcc to be smart enough to optimize away the next
789 // two lines when T is int64.
790 if (int64_t(v) == v) {
791 int64_t ival = v;
792 if (ival >= base && ival < (base + nTargets)) {
793 return ival - base;
796 return nTargets + 1;
799 int64_t switchDoubleHelper(int64_t val, int64_t base, int64_t nTargets) {
800 union {
801 int64_t intbits;
802 double dblval;
803 } u;
804 u.intbits = val;
805 return switchBoundsCheck(u.dblval, base, nTargets);
808 int64_t switchStringHelper(StringData* s, int64_t base, int64_t nTargets) {
809 int64_t ival;
810 double dval;
812 [&] {
813 switch (s->isNumericWithVal(ival, dval, 1)) {
814 case KindOfNull:
815 ival = switchBoundsCheck(0, base, nTargets);
816 return;
817 case KindOfInt64:
818 ival = switchBoundsCheck(ival, base, nTargets);
819 return;
820 case KindOfDouble:
821 ival = switchBoundsCheck(dval, base, nTargets);
822 return;
824 case KindOfUninit:
825 case KindOfBoolean:
826 case KindOfPersistentString:
827 case KindOfString:
828 case KindOfPersistentVec:
829 case KindOfVec:
830 case KindOfPersistentDict:
831 case KindOfDict:
832 case KindOfPersistentKeyset:
833 case KindOfKeyset:
834 case KindOfPersistentArray:
835 case KindOfArray:
836 case KindOfObject:
837 case KindOfResource:
838 case KindOfRef:
839 break;
841 not_reached();
842 }();
844 decRefStr(s);
845 return ival;
848 int64_t switchObjHelper(ObjectData* o, int64_t base, int64_t nTargets) {
849 auto const ival = o->toInt64();
850 decRefObj(o);
851 return switchBoundsCheck(ival, base, nTargets);
854 //////////////////////////////////////////////////////////////////////
856 void checkFrame(ActRec* fp, Cell* sp, bool fullCheck, Offset bcOff) {
857 const Func* func = fp->m_func;
858 func->validate();
859 if (func->cls()) {
860 assertx(!func->cls()->isZombie());
862 if ((func->attrs() & AttrMayUseVV) && fp->hasVarEnv()) {
863 assertx(fp->getVarEnv()->getFP() == fp);
865 int numLocals = func->numLocals();
866 assertx(sp <= (Cell*)fp - func->numSlotsInFrame() || fp->resumed());
868 if (!fullCheck) return;
870 int numParams = func->numParams();
871 for (int i = 0; i < numLocals; i++) {
872 if (i >= numParams && fp->resumed() && i < func->numNamedLocals()) {
873 continue;
875 assertx(tvIsPlausible(*frame_local(fp, i)));
878 visitStackElems(
879 fp, sp, bcOff,
880 [](const ActRec* ar, Offset) {
881 ar->func()->validate();
883 [](const TypedValue* tv) {
884 assertx(tvIsPlausible(*tv));
889 const Func* loadClassCtor(Class* cls, ActRec* fp) {
890 const Func* f = cls->getCtor();
891 if (UNLIKELY(!(f->attrs() & AttrPublic))) {
892 auto const ctx = arGetContextClass(fp);
893 UNUSED auto func =
894 lookupMethodCtx(cls, nullptr, ctx, CallType::CtorMethod, true);
895 assertx(func == f);
897 return f;
900 //////////////////////////////////////////////////////////////////////
902 [[noreturn]] void throwMissingArgument(const char* fmt, ...) {
903 va_list ap;
904 va_start(ap, fmt);
905 std::string msg;
906 string_vsnprintf(msg, fmt, ap);
907 va_end(ap);
909 SystemLib::throwArgumentCountErrorObject(Variant(msg));
912 void raiseMissingArgument(const Func* func, int got) {
913 const auto total = func->numNonVariadicParams();
914 const auto variadic = func->hasVariadicCaptureParam();
915 const Func::ParamInfoVec& params = func->params();
916 int expected = 0;
917 // We subtract the number of parameters with default value at the end
918 for (size_t i = total; i--; ) {
919 if (!params[i].hasDefaultValue()) {
920 expected = i + 1;
921 break;
924 bool lessNeeded = (variadic || expected < total);
926 if (RuntimeOption::PHP7_EngineExceptions) {
927 throwMissingArgument(
928 Strings::MISSING_ARGUMENT_EXCEPTION,
929 func->displayName()->data(),
930 got,
931 lessNeeded ? "at least" : "exactly",
932 expected
935 if (expected == 1) {
936 raise_warning(Strings::MISSING_ARGUMENT, func->displayName()->data(),
937 lessNeeded ? "at least" : "exactly", got);
938 } else {
939 raise_warning(Strings::MISSING_ARGUMENTS, func->displayName()->data(),
940 lessNeeded ? "at least" : "exactly", expected, got);
944 //////////////////////////////////////////////////////////////////////
946 Class* lookupClsRDS(const StringData* name) {
947 auto const handle = NamedEntity::get(name)->getClassHandle();
948 assertx(rds::isHandleBound(handle));
949 return rds::isHandleInit(handle)
950 ? &*rds::handleToRef<LowPtr<Class>, rds::Mode::NonLocal>(handle)
951 : nullptr;
954 void registerLiveObj(ObjectData* obj) {
955 assertx(RuntimeOption::EnableObjDestructCall && obj->getVMClass()->getDtor());
956 g_context->m_liveBCObjs.insert(obj);
959 void throwSwitchMode() {
960 // This is only called right after dispatchBB, so the VM regs really are
961 // clean.
962 tl_regState = VMRegState::CLEAN;
963 throw VMSwitchMode();
966 bool methodExistsHelper(Class* cls, StringData* meth) {
967 assertx(isNormalClass(cls) && !isAbstract(cls));
968 return cls->lookupMethod(meth) != nullptr;
971 ArrayData* resolveTypeStructHelper(
972 const ArrayData* a,
973 const Class* declaringCls,
974 const Class* calledCls
976 auto const ts = ArrNR(a);
977 auto resolved = resolveAndVerifyTypeStructure(ts, declaringCls, calledCls);
978 return resolved.detach();
981 bool isTypeStructHelper(ArrayData* a, Cell c) {
982 auto const ts = ArrNR(a);
983 return checkTypeStructureMatchesCell(ts, c);
986 void asTypeStructHelper(ArrayData* a, Cell c) {
987 std::string givenType, expectedType, errorKey;
988 auto const ts = ArrNR(a);
989 if (!checkTypeStructureMatchesCell(
990 ts, c, givenType, expectedType, errorKey)) {
991 throwTypeStructureDoesNotMatchCellException(
992 givenType, expectedType, errorKey);
996 void throwOOBException(TypedValue base, TypedValue key) {
997 if (isArrayLikeType(base.m_type)) {
998 throwOOBArrayKeyException(key, base.m_data.parr);
999 } else if (base.m_type == KindOfObject) {
1000 assertx(isIntType(key.m_type));
1001 collections::throwOOB(key.m_data.num);
1003 not_reached();
1006 void invalidArrayKeyHelper(const ArrayData* ad, TypedValue key) {
1007 throwInvalidArrayKeyException(&key, ad);
1010 namespace MInstrHelpers {
1012 template <bool intishWarn>
1013 TypedValue setOpElem(TypedValue* base, TypedValue key,
1014 Cell val, SetOpOp op) {
1015 TypedValue localTvRef;
1016 auto result = HPHP::SetOpElem<intishWarn>(localTvRef, op, base, key, &val);
1018 return cGetRefShuffle(localTvRef, result);
1021 template TypedValue setOpElem<true>(TypedValue*, TypedValue, Cell, SetOpOp);
1022 template TypedValue setOpElem<false>(TypedValue*, TypedValue, Cell, SetOpOp);
1024 StringData* stringGetI(StringData* base, uint64_t x) {
1025 if (LIKELY(x < base->size())) {
1026 return base->getChar(x);
1028 raise_notice("Uninitialized string offset: %" PRId64,
1029 static_cast<int64_t>(x));
1030 return staticEmptyString();
1033 uint64_t pairIsset(c_Pair* pair, int64_t index) {
1034 auto result = pair->get(index);
1035 return result ? !cellIsNull(result) : false;
1038 uint64_t vectorIsset(c_Vector* vec, int64_t index) {
1039 auto result = vec->get(index);
1040 return result ? !cellIsNull(result) : false;
1043 template <bool intishWarn>
1044 void bindElemC(TypedValue* base, TypedValue key, RefData* val) {
1045 TypedValue localTvRef;
1046 auto elem =
1047 HPHP::ElemD<MOpMode::Define, true, intishWarn>(localTvRef, base, key);
1049 if (UNLIKELY(elem == &localTvRef)) {
1050 // Skip binding a TypedValue that's about to be destroyed and just destroy
1051 // it now.
1052 tvDecRefGen(localTvRef);
1053 return;
1056 tvBindRef(val, *elem);
1059 template void bindElemC<true>(TypedValue*, TypedValue, RefData*);
1060 template void bindElemC<false>(TypedValue*, TypedValue, RefData*);
1062 template <bool intishWarn>
1063 void setWithRefElem(TypedValue* base, TypedValue keyTV, TypedValue val) {
1064 TypedValue localTvRef;
1065 auto const keyC = tvToCell(keyTV);
1067 if (UNLIKELY(val.m_type == KindOfRef)) {
1068 HPHP::SetWithRefMLElem<MOpMode::Define, true, intishWarn>(
1069 localTvRef, base, keyC, val);
1070 } else {
1071 HPHP::SetWithRefMLElem<MOpMode::Define, false, intishWarn>(
1072 localTvRef, base, keyC, val);
1076 template void setWithRefElem<true>(TypedValue*, TypedValue, TypedValue);
1077 template void setWithRefElem<false>(TypedValue*, TypedValue, TypedValue);
1079 template <bool intishWarn>
1080 TypedValue incDecElem(TypedValue* base, TypedValue key, IncDecOp op) {
1081 auto const result = HPHP::IncDecElem<intishWarn>(op, base, key);
1082 assertx(result.m_type != KindOfRef);
1083 return result;
1086 template TypedValue incDecElem<true>(TypedValue*, TypedValue, IncDecOp);
1087 template TypedValue incDecElem<false>(TypedValue*, TypedValue, IncDecOp);
1089 void bindNewElem(TypedValue* base, RefData* val) {
1090 if (UNLIKELY(isHackArrayType(base->m_type))) {
1091 throwRefInvalidArrayValueException(base->m_data.parr);
1094 TypedValue localTvRef;
1095 auto elem = HPHP::NewElem<true>(localTvRef, base);
1097 if (UNLIKELY(elem == &localTvRef)) {
1098 // Skip binding a TypedValue that's about to be destroyed and just destroy
1099 // it now.
1100 tvDecRefGen(localTvRef);
1101 return;
1104 tvBindRef(val, *elem);
1107 TypedValue* elemVecID(TypedValue* base, int64_t key) {
1108 auto cbase = tvToCell(base);
1109 assertx(isVecType(cbase->m_type));
1110 return ElemDVec<false, KeyType::Int>(cbase, key);
1113 TypedValue* elemVecIU(TypedValue* base, int64_t key) {
1114 auto cbase = tvToCell(base);
1115 assertx(isVecType(cbase->m_type));
1116 return ElemUVec<KeyType::Int>(cbase, key);
1121 //////////////////////////////////////////////////////////////////////
1123 uintptr_t tlsBaseNoInline() {
1124 return tlsBase();
1127 //////////////////////////////////////////////////////////////////////
1130 * Sometimes calls to builtin functions are inlined so that the call itself can
1131 * occur via CallBuiltin rather than NativeImpl. In these instances it's
1132 * possible that no ActRec was pushed for the builtin call, in which case the
1133 * liveFunc() will be the caller rather than the callee.
1135 * If no ActRec was pushed for the builtin function, inspect the caller to
1136 * determine if the call used strict types.
1138 bool useStrictTypesHelper(const Func* callee) {
1139 if (liveFunc() == callee) {
1140 return !liveFrame()->useWeakTypes();
1142 return liveUnit()->useStrictTypes() && !liveUnit()->isHHFile();
1145 void tvCoerceIfStrict(TypedValue& tv, int64_t argNum, const Func* func) {
1146 if (LIKELY(!RuntimeOption::PHP7_ScalarTypes ||
1147 RuntimeOption::EnableHipHopSyntax)) {
1148 return;
1151 VMRegAnchor _;
1152 if (!useStrictTypesHelper(func)) return;
1154 auto const& tc = func->params()[argNum - 1].typeConstraint;
1155 tc.verifyParam(&tv, func, argNum - 1, true);
1158 TVCoercionException::TVCoercionException(const Func* func,
1159 int arg_num,
1160 DataType actual,
1161 DataType expected)
1162 : std::runtime_error(
1163 folly::format("Unable to coerce param {} to {}() "
1164 "from {} to {}",
1165 arg_num,
1166 func->name(),
1167 actual,
1168 expected).str())
1170 if (func->attrs() & AttrParamCoerceModeFalse) {
1171 m_tv = make_tv<KindOfBoolean>(false);
1172 } else {
1173 m_tv = make_tv<KindOfNull>();
1177 //////////////////////////////////////////////////////////////////////