2 +----------------------------------------------------------------------+
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"
69 TRACE_SET_MOD(runtime
);
71 //////////////////////////////////////////////////////////////////////
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
)) {
87 ArrayData
* addElemIntKeyHelper(ArrayData
* ad
,
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
95 ArrayData
* retval
= ad
->set(key
, tvAsCVarRef(&value
),
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
101 return arrayRefShuffle
<false, KindOfArray
>(ad
, retval
, nullptr);
104 template <bool intishWarn
>
105 ArrayData
* addElemStringKeyHelper(ArrayData
* ad
,
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
116 if (UNLIKELY(key
->isStrictlyInteger(intkey
))) {
117 if (intishWarn
) raise_intish_index_cast();
118 retval
= ad
->set(intkey
, *tvToCell(&value
), copy
);
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
127 return arrayRefShuffle
<false, KindOfArray
>(ad
, retval
, nullptr);
131 addElemStringKeyHelper
<true>(ArrayData
*, StringData
*, TypedValue
);
133 addElemStringKeyHelper
<false>(ArrayData
*, StringData
*, TypedValue
);
135 ArrayData
* dictAddElemIntKeyHelper(ArrayData
* ad
,
138 assertx(ad
->isDict());
139 // set will decRef any old value that may have been overwritten
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
147 return arrayRefShuffle
<false, KindOfDict
>(ad
, retval
, nullptr);
150 ArrayData
* dictAddElemStringKeyHelper(ArrayData
* ad
,
153 assertx(ad
->isDict());
154 // set will decRef any old value that may have been overwritten
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
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();
174 // We consume refs on a2 and also produce references, so there's
175 // no need to inc/dec a2.
180 auto const escalated
= a1
->plusEq(a2
);
181 if (escalated
!= 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
) {
219 inline double reinterpretIntAsDbl(int64_t i
) {
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
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());
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());
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());
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());
277 ArrayData
* convArrToVecHelper(ArrayData
* adIn
) {
278 assertx(adIn
->isPHPArray());
279 auto a
= adIn
->toVec(adIn
->cowCheck());
280 if (a
!= adIn
) decRefArr(adIn
);
284 ArrayData
* convDictToVecHelper(ArrayData
* adIn
) {
285 assertx(adIn
->isDict());
286 auto a
= MixedArray::ToVecDict(adIn
, adIn
->cowCheck());
292 ArrayData
* convKeysetToVecHelper(ArrayData
* adIn
) {
293 assertx(adIn
->isKeyset());
294 auto a
= SetArray::ToVec(adIn
, adIn
->cowCheck());
300 ArrayData
* convObjToVecHelper(ObjectData
* obj
) {
301 auto a
= castObjToVec(obj
);
302 assertx(a
->isVecArray());
307 ArrayData
* convArrToDictHelper(ArrayData
* adIn
) {
308 assertx(adIn
->isPHPArray());
309 auto a
= adIn
->toDict(adIn
->cowCheck());
310 if (a
!= adIn
) decRefArr(adIn
);
314 ArrayData
* convVecToDictHelper(ArrayData
* adIn
) {
315 assertx(adIn
->isVecArray());
316 auto a
= PackedArray::ToDictVec(adIn
, adIn
->cowCheck());
322 ArrayData
* convKeysetToDictHelper(ArrayData
* adIn
) {
323 assertx(adIn
->isKeyset());
324 auto a
= SetArray::ToDict(adIn
, adIn
->cowCheck());
325 if (a
!= adIn
) decRefArr(adIn
);
329 ArrayData
* convObjToDictHelper(ObjectData
* obj
) {
330 auto a
= castObjToDict(obj
);
331 assertx(a
->isDict());
336 ArrayData
* convArrToKeysetHelper(ArrayData
* adIn
) {
337 assertx(adIn
->isPHPArray());
338 auto a
= adIn
->toKeyset(adIn
->cowCheck());
339 if (a
!= adIn
) decRefArr(adIn
);
343 ArrayData
* convVecToKeysetHelper(ArrayData
* adIn
) {
344 assertx(adIn
->isVecArray());
345 auto a
= PackedArray::ToKeysetVec(adIn
, adIn
->cowCheck());
351 ArrayData
* convDictToKeysetHelper(ArrayData
* adIn
) {
352 assertx(adIn
->isDict());
353 auto a
= MixedArray::ToKeysetDict(adIn
, adIn
->cowCheck());
354 if (a
!= adIn
) decRefArr(adIn
);
358 ArrayData
* convObjToKeysetHelper(ObjectData
* obj
) {
359 auto a
= castObjToKeyset(obj
);
360 assertx(a
->isKeyset());
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
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
,
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
);
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
);
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
);
468 return convCellToDblHelper(tv
);
470 case KindOfPersistentString
:
472 return coerceStrToDblHelper(tv
.m_data
.pstr
, argNum
, func
);
475 case KindOfPersistentVec
:
477 case KindOfPersistentDict
:
479 case KindOfPersistentKeyset
:
481 case KindOfPersistentArray
:
485 coerceCellFail(KindOfDouble
, tv
.m_type
, argNum
, func
);
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
);
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
);
521 return cellToInt(tv
);
523 case KindOfPersistentString
:
525 return coerceStrToIntHelper(tv
.m_data
.pstr
, argNum
, func
);
528 case KindOfPersistentVec
:
530 case KindOfPersistentDict
:
532 case KindOfPersistentKeyset
:
534 case KindOfPersistentArray
:
538 coerceCellFail(KindOfInt64
, tv
.m_type
, argNum
, func
);
551 StringData
* convCellToStrHelper(TypedValue tv
) {
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();
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;
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());
590 void raise_error_sd(const StringData
*msg
) {
591 raise_error("%s", msg
->data());
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
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
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
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
,
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
) {
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
||
648 assertx(!tc
.check(tv
, func
));
649 tc
.verifyParamFail(func
, tv
, paramNum
, useStrictTypes
);
652 void VerifyRetTypeSlow(int32_t id
,
654 const Class
* constraint
,
655 const HPHP::TypeConstraint
* expected
,
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
) {
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
);
681 auto const& tc
= func
->params()[id
].typeConstraint
;
682 assertx(!tc
.check(tv
, func
));
683 tc
.verifyOutParamFail(func
, tv
, id
);
689 TypedValue
getDefaultIfNullCell(tv_rval rval
, const TypedValue
& def
) {
690 return UNLIKELY(!rval
) ? def
: rval
.tv();
693 template <bool intishWarn
>
695 TypedValue
arrayIdxSiSlow(ArrayData
* a
, StringData
* key
, TypedValue def
) {
696 assertx(a
->isPHPArray());
698 if (UNLIKELY(key
->isStrictlyInteger(i
))) {
699 if (intishWarn
) raise_intish_index_cast();
700 return getDefaultIfNullCell(a
->rval(i
), def
);
702 return getDefaultIfNullCell(a
->rval(key
), def
);
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
);
725 if (UNLIKELY(key
->isStrictlyInteger(i
))) {
726 if (intishWarn
) raise_intish_index_cast();
727 return getDefaultIfNullCell(MixedArray::RvalInt(a
, i
), def
);
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
,
764 auto const lookup
= cls
->getSProp(ctx
, name
);
766 if (UNLIKELY(!lookup
.prop
|| !lookup
.accessible
)) return nullptr;
771 TypedValue
* getSPropOrRaise(const Class
* cls
,
772 const StringData
* name
,
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());
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
) {
792 if (ival
>= base
&& ival
< (base
+ nTargets
)) {
799 int64_t switchDoubleHelper(int64_t val
, int64_t base
, int64_t nTargets
) {
805 return switchBoundsCheck(u
.dblval
, base
, nTargets
);
808 int64_t switchStringHelper(StringData
* s
, int64_t base
, int64_t nTargets
) {
813 switch (s
->isNumericWithVal(ival
, dval
, 1)) {
815 ival
= switchBoundsCheck(0, base
, nTargets
);
818 ival
= switchBoundsCheck(ival
, base
, nTargets
);
821 ival
= switchBoundsCheck(dval
, base
, nTargets
);
826 case KindOfPersistentString
:
828 case KindOfPersistentVec
:
830 case KindOfPersistentDict
:
832 case KindOfPersistentKeyset
:
834 case KindOfPersistentArray
:
848 int64_t switchObjHelper(ObjectData
* o
, int64_t base
, int64_t nTargets
) {
849 auto const ival
= o
->toInt64();
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
;
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()) {
875 assertx(tvIsPlausible(*frame_local(fp
, i
)));
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
);
894 lookupMethodCtx(cls
, nullptr, ctx
, CallType::CtorMethod
, true);
900 //////////////////////////////////////////////////////////////////////
902 [[noreturn
]] void throwMissingArgument(const char* fmt
, ...) {
906 string_vsnprintf(msg
, fmt
, 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();
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()) {
924 bool lessNeeded
= (variadic
|| expected
< total
);
926 if (RuntimeOption::PHP7_EngineExceptions
) {
927 throwMissingArgument(
928 Strings::MISSING_ARGUMENT_EXCEPTION
,
929 func
->displayName()->data(),
931 lessNeeded
? "at least" : "exactly",
936 raise_warning(Strings::MISSING_ARGUMENT
, func
->displayName()->data(),
937 lessNeeded
? "at least" : "exactly", got
);
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
)
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
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(
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
);
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
;
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
1052 tvDecRefGen(localTvRef
);
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
);
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
);
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
1100 tvDecRefGen(localTvRef
);
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() {
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
)) {
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
,
1162 : std::runtime_error(
1163 folly::format("Unable to coerce param {} to {}() "
1170 if (func
->attrs() & AttrParamCoerceModeFalse
) {
1171 m_tv
= make_tv
<KindOfBoolean
>(false);
1173 m_tv
= make_tv
<KindOfNull
>();
1177 //////////////////////////////////////////////////////////////////////