2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 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/autoload-handler.h"
20 #include "hphp/runtime/base/collections.h"
21 #include "hphp/runtime/base/packed-array.h"
22 #include "hphp/runtime/base/stats.h"
23 #include "hphp/runtime/base/zend-functions.h"
24 #include "hphp/runtime/ext/ext_closure.h"
25 #include "hphp/runtime/ext/collections/ext_collections-idl.h"
26 #include "hphp/runtime/ext/hh/ext_hh.h"
27 #include "hphp/runtime/ext/std/ext_std_function.h"
28 #include "hphp/runtime/vm/jit/mc-generator-internal.h"
29 #include "hphp/runtime/vm/jit/mc-generator.h"
30 #include "hphp/runtime/vm/jit/target-profile.h"
31 #include "hphp/runtime/vm/jit/translator-inline.h"
32 #include "hphp/runtime/vm/jit/unwind-x64.h"
33 #include "hphp/runtime/vm/member-operations.h"
34 #include "hphp/runtime/vm/minstr-state.h"
35 #include "hphp/runtime/vm/type-constraint.h"
36 #include "hphp/runtime/vm/unit-util.h"
37 #include "hphp/runtime/vm/unwind.h"
41 TRACE_SET_MOD(runtime
);
43 //////////////////////////////////////////////////////////////////////
45 const StaticString
s_staticPrefix("86static_");
47 // Defined here so it can be inlined below.
48 RefData
* lookupStaticFromClosure(ObjectData
* closure
,
51 assertx(closure
->instanceof(c_Closure::classof()));
52 auto str
= String::attach(
53 StringData::Make(s_staticPrefix
.slice(), name
->slice())
55 auto const cls
= closure
->getVMClass();
56 auto const slot
= cls
->lookupDeclProp(str
.get());
57 assertx(slot
!= kInvalidSlot
);
58 auto const val
= static_cast<c_Closure
*>(closure
)->getStaticVar(slot
);
60 if (val
->m_type
== KindOfUninit
) {
62 val
->m_type
= KindOfNull
;
64 return val
->m_data
.pref
;
67 assertx(val
->m_type
== KindOfRef
);
68 return val
->m_data
.pref
;
73 //////////////////////////////////////////////////////////////////////
75 ArrayData
* addNewElemHelper(ArrayData
* a
, TypedValue value
) {
76 ArrayData
* r
= a
->append(tvAsCVarRef(&value
), a
->getCount() != 1);
77 if (UNLIKELY(r
!= a
)) {
80 tvRefcountedDecRef(value
);
84 ArrayData
* addElemIntKeyHelper(ArrayData
* ad
,
87 // this does not re-enter
88 // set will decRef any old value that may have been overwritten
90 ArrayData
* retval
= ad
->set(key
, tvAsCVarRef(&value
),
91 ad
->hasMultipleRefs());
92 // TODO Task #1970153: It would be great if there were set()
93 // methods that didn't bump up the refcount so that we didn't
94 // have to decrement it here
95 tvRefcountedDecRef(&value
);
96 return arrayRefShuffle
<false>(ad
, retval
, nullptr);
99 ArrayData
* addElemStringKeyHelper(ArrayData
* ad
,
102 // this does not re-enter
103 bool copy
= ad
->hasMultipleRefs();
104 // set will decRef any old value that may have been overwritten
107 ArrayData
* retval
= UNLIKELY(key
->isStrictlyInteger(intkey
)) ?
108 ad
->set(intkey
, tvAsCVarRef(&value
), copy
) :
109 ad
->set(key
, tvAsCVarRef(&value
), copy
);
110 // TODO Task #1970153: It would be great if there were set()
111 // methods that didn't bump up the refcount so that we didn't
112 // have to decrement it here
114 tvRefcountedDecRef(&value
);
115 return arrayRefShuffle
<false>(ad
, retval
, nullptr);
118 ArrayData
* arrayAdd(ArrayData
* a1
, ArrayData
* a2
) {
121 // We consume refs on a2 and also produce references, so there's
122 // no need to inc/dec a2.
127 auto const escalated
= a1
->plusEq(a2
);
128 if (escalated
!= a1
) {
139 void setNewElem(TypedValue
* base
, Cell val
) {
140 HPHP::SetNewElem
<false>(base
, &val
);
143 void setNewElemArray(TypedValue
* base
, Cell val
) {
144 HPHP::SetNewElemArray(base
, &val
);
147 TypedValue
setOpElem(TypedValue
* base
, TypedValue key
,
148 Cell val
, MInstrState
* mis
, SetOpOp op
) {
149 auto result
= HPHP::SetOpElem(mis
->tvRef
, op
, base
, key
, &val
);
152 cellDup(*tvToCell(result
), ret
);
156 TypedValue
incDecElem(
163 HPHP::IncDecElem
<true>(mis
->tvRef
, op
, base
, key
, result
);
164 assertx(result
.m_type
!= KindOfRef
);
168 void bindNewElemIR(TypedValue
* base
, RefData
* val
, MInstrState
* mis
) {
169 auto elem
= HPHP::NewElem
<true>(mis
->tvRef
, base
);
170 tvBindRef(val
, elem
);
173 RefData
* boxValue(TypedValue tv
) {
174 assertx(tv
.m_type
!= KindOfRef
);
175 if (tv
.m_type
== KindOfUninit
) tv
= make_tv
<KindOfNull
>();
176 return RefData::Make(tv
);
179 inline int64_t reinterpretDblAsInt(double d
) {
188 inline double reinterpretIntAsDbl(int64_t i
) {
197 ArrayData
* convCellToArrHelper(TypedValue tv
) {
198 // Note: the call sites of this function all assume that
199 // no user code will run and no recoverable exceptions will
200 // occur while running this code. This seems trivially true
201 // in all cases but converting objects to arrays. It also
202 // seems true for that case as well, since the resulting array
203 // is essentially metadata for the object. If that is not true,
204 // you might end up looking at this code in a debugger and now
206 tvCastToArrayInPlace(&tv
); // consumes a ref on counted values
207 return tv
.m_data
.parr
;
210 int64_t convObjToDblHelper(const ObjectData
* o
) {
211 return reinterpretDblAsInt(o
->toDouble());
214 int64_t convArrToDblHelper(ArrayData
* a
) {
215 return reinterpretDblAsInt(a
->empty() ? 0 : 1);
218 int64_t convStrToDblHelper(const StringData
* s
) {
219 return reinterpretDblAsInt(s
->toDouble());
222 int64_t convResToDblHelper(const ResourceHdr
* r
) {
223 return reinterpretDblAsInt(r
->getId());
226 int64_t convCellToDblHelper(TypedValue tv
) {
227 return reinterpretDblAsInt(tvCastToDouble(&tv
));
230 int64_t convArrToIntHelper(ArrayData
* a
) {
231 return a
->empty() ? 0 : 1;
234 ObjectData
* convCellToObjHelper(TypedValue tv
) {
235 // Note: the call sites of this function all assume that
236 // no user code will run and no recoverable exceptions will
237 // occur while running this code. This seems trivially true
238 // in all cases but converting arrays to objects. It also
239 // seems true for that case as well, since the source array
240 // is essentially metadata for the object. If that is not true,
241 // you might end up looking at this code in a debugger and now
243 tvCastToObjectInPlace(&tv
); // consumes a ref on counted values
244 return tv
.m_data
.pobj
;
247 StringData
* convDblToStrHelper(int64_t i
) {
248 double d
= reinterpretIntAsDbl(i
);
249 return buildStringData(d
);
252 StringData
* convIntToStrHelper(int64_t i
) {
253 return buildStringData(i
);
256 StringData
* convObjToStrHelper(ObjectData
* o
) {
257 // toString() returns a counted String; detach() it to move ownership
258 // of the count to the caller
259 return o
->invokeToString().detach();
262 StringData
* convResToStrHelper(ResourceHdr
* r
) {
263 // toString() returns a counted String; detach() it to move ownership
264 // of the count to the caller
265 return r
->data()->o_toString().detach();
268 TypedValue
getMemoKeyHelper(TypedValue tv
) {
269 auto var
= HHVM_FN(serialize_memoize_param
)(tvAsCVarRef(&tv
));
270 auto res
= var
.asTypedValue();
271 tvRefcountedIncRef(res
);
275 inline void coerceCellFail(DataType expected
, DataType actual
, int64_t argNum
,
277 raise_param_type_warning(func
->name()->data(), argNum
, expected
, actual
);
279 throw TVCoercionException(func
, argNum
, actual
, expected
);
282 bool coerceCellToBoolHelper(TypedValue tv
, int64_t argNum
, const Func
* func
) {
283 assertx(cellIsPlausible(tv
));
285 DataType type
= tv
.m_type
;
286 if (type
== KindOfArray
|| type
== KindOfObject
|| type
== KindOfResource
) {
287 coerceCellFail(KindOfBoolean
, type
, argNum
, func
);
291 return cellToBool(tv
);
294 int64_t coerceStrToDblHelper(StringData
* sd
, int64_t argNum
, const Func
* func
) {
295 DataType type
= is_numeric_string(sd
->data(), sd
->size(), nullptr, nullptr);
297 if (type
!= KindOfDouble
&& type
!= KindOfInt64
) {
298 coerceCellFail(KindOfDouble
, KindOfString
, argNum
, func
);
302 return reinterpretDblAsInt(sd
->toDouble());
305 int64_t coerceCellToDblHelper(Cell tv
, int64_t argNum
, const Func
* func
) {
306 assertx(cellIsPlausible(tv
));
313 return convCellToDblHelper(tv
);
315 case KindOfStaticString
:
317 return coerceStrToDblHelper(tv
.m_data
.pstr
, argNum
, func
);
323 coerceCellFail(KindOfDouble
, tv
.m_type
, argNum
, func
);
333 int64_t coerceStrToIntHelper(StringData
* sd
, int64_t argNum
, const Func
* func
) {
334 DataType type
= is_numeric_string(sd
->data(), sd
->size(), nullptr, nullptr);
336 if (type
!= KindOfDouble
&& type
!= KindOfInt64
) {
337 coerceCellFail(KindOfInt64
, KindOfString
, argNum
, func
);
341 return sd
->toInt64();
344 int64_t coerceCellToIntHelper(TypedValue tv
, int64_t argNum
, const Func
* func
) {
345 assertx(cellIsPlausible(tv
));
352 return cellToInt(tv
);
354 case KindOfStaticString
:
356 return coerceStrToIntHelper(tv
.m_data
.pstr
, argNum
, func
);
362 coerceCellFail(KindOfInt64
, tv
.m_type
, argNum
, func
);
376 StringData
* convCellToStrHelper(TypedValue tv
) {
379 case KindOfNull
: return s_empty
.get();
380 case KindOfBoolean
: return tv
.m_data
.num
? s_1
.get() : s_empty
.get();
381 case KindOfInt64
: return convIntToStrHelper(tv
.m_data
.num
);
382 case KindOfDouble
: return convDblToStrHelper(tv
.m_data
.num
);
383 case KindOfString
: tv
.m_data
.pstr
->incRefCount();
385 case KindOfStaticString
:
386 return tv
.m_data
.pstr
;
387 case KindOfArray
: raise_notice("Array to string conversion");
388 return array_string
.get();
389 case KindOfObject
: return convObjToStrHelper(tv
.m_data
.pobj
);
390 case KindOfResource
: return convResToStrHelper(tv
.m_data
.pres
);
392 case KindOfClass
: break;
397 void raiseUndefProp(ObjectData
* base
, const StringData
* name
) {
398 base
->raiseUndefProp(name
);
401 void raiseUndefVariable(StringData
* nm
) {
402 raise_notice(Strings::UNDEFINED_VARIABLE
, nm
->data());
406 void raise_error_sd(const StringData
*msg
) {
407 raise_error("%s", msg
->data());
411 static bool VerifyTypeSlowImpl(const Class
* cls
,
412 const Class
* constraint
,
413 const HPHP::TypeConstraint
* expected
) {
414 // This helper should only be called for the Object, Self, and Parent cases
415 assertx(expected
->isObject() || expected
->isSelf() || expected
->isParent());
416 // For the Self and Parent cases, we must always have a resolved class for
419 expected
->isSelf() || expected
->isParent(), constraint
!= nullptr));
420 // If we have a resolved class for the constraint, all we have to do is
421 // check if the value's class is compatible with it
422 if (LIKELY(constraint
!= nullptr)) {
423 return cls
->classof(constraint
);
425 // The Self and Parent cases should never reach here because they were
427 assertx(expected
->isObject());
428 // Handle the case where the constraint is a type alias
429 return expected
->checkTypeAliasObj(cls
);
432 void VerifyParamTypeSlow(const Class
* cls
,
433 const Class
* constraint
,
434 const HPHP::TypeConstraint
* expected
,
436 if (!VerifyTypeSlowImpl(cls
, constraint
, expected
)) {
437 VerifyParamTypeFail(param
);
441 void VerifyParamTypeCallable(TypedValue value
, int param
) {
442 if (UNLIKELY(!is_callable(tvAsCVarRef(&value
)))) {
443 VerifyParamTypeFail(param
);
447 void VerifyParamTypeFail(int paramNum
) {
449 const ActRec
* ar
= liveFrame();
450 const Func
* func
= ar
->m_func
;
451 auto const& tc
= func
->params()[paramNum
].typeConstraint
;
452 TypedValue
* tv
= frame_local(ar
, paramNum
);
453 assertx(!tc
.check(tv
, func
));
454 tc
.verifyParamFail(func
, tv
, paramNum
);
457 void VerifyRetTypeSlow(const Class
* cls
,
458 const Class
* constraint
,
459 const HPHP::TypeConstraint
* expected
,
460 const TypedValue tv
) {
461 if (!VerifyTypeSlowImpl(cls
, constraint
, expected
)) {
462 VerifyRetTypeFail(tv
);
466 void VerifyRetTypeCallable(TypedValue value
) {
467 if (UNLIKELY(!is_callable(tvAsCVarRef(&value
)))) {
468 VerifyRetTypeFail(value
);
472 void VerifyRetTypeFail(TypedValue tv
) {
474 const ActRec
* ar
= liveFrame();
475 const Func
* func
= ar
->m_func
;
476 const HPHP::TypeConstraint
& tc
= func
->returnTypeConstraint();
477 assertx(!tc
.check(&tv
, func
));
478 tc
.verifyReturnFail(func
, &tv
);
481 RefData
* closureStaticLocInit(StringData
* name
, ActRec
* fp
, TypedValue val
) {
482 auto const func
= fp
->m_func
;
483 assertx(func
->isClosureBody());
484 auto const closureLoc
= frame_local(fp
, func
->numParams());
487 auto const refData
= lookupStaticFromClosure(
488 closureLoc
->m_data
.pobj
, name
, inited
);
490 cellCopy(val
, *refData
->tv());
496 static bool ak_exist_string_impl(ArrayData
* arr
, StringData
* key
) {
498 if (key
->isStrictlyInteger(n
)) {
499 return arr
->exists(n
);
501 return arr
->exists(key
);
504 bool ak_exist_string(ArrayData
* arr
, StringData
* key
) {
505 return ak_exist_string_impl(arr
, key
);
508 bool ak_exist_string_obj(ObjectData
* obj
, StringData
* key
) {
509 if (obj
->isCollection()) {
510 return collections::contains(obj
, Variant
{key
});
512 auto arr
= obj
->toArray();
513 return ak_exist_string_impl(arr
.get(), key
);
516 bool ak_exist_int_obj(ObjectData
* obj
, int64_t key
) {
517 if (obj
->isCollection()) {
518 return collections::contains(obj
, key
);
520 auto arr
= obj
->toArray();
521 return arr
.get()->exists(key
);
526 TypedValue
getDefaultIfNullCell(const TypedValue
* tv
, TypedValue
& def
) {
527 if (UNLIKELY(nullptr == tv
)) {
528 // DecRef of def is done unconditionally by the IR, since there's
529 // a good chance it will be paired with an IncRef and optimized
530 // away. So we need to IncRef here if it is being returned.
531 tvRefcountedIncRef(&def
);
534 auto const ret
= tvToCell(tv
);
535 tvRefcountedIncRef(ret
);
540 TypedValue
arrayIdxS(ArrayData
* a
, StringData
* key
, TypedValue def
) {
541 return getDefaultIfNullCell(a
->nvGet(key
), def
);
544 TypedValue
arrayIdxSi(ArrayData
* a
, StringData
* key
, TypedValue def
) {
546 return UNLIKELY(key
->isStrictlyInteger(i
)) ?
547 getDefaultIfNullCell(a
->nvGet(i
), def
) :
548 getDefaultIfNullCell(a
->nvGet(key
), def
);
551 TypedValue
arrayIdxI(ArrayData
* a
, int64_t key
, TypedValue def
) {
552 return getDefaultIfNullCell(a
->nvGet(key
), def
);
555 TypedValue
arrayIdxIc(ArrayData
* a
, int64_t key
, TypedValue def
) {
556 return arrayIdxI(a
, key
, def
);
559 const StaticString
s_idx("hh\\idx");
561 TypedValue
genericIdx(TypedValue obj
, TypedValue key
, TypedValue def
) {
562 static auto func
= Unit::loadFunc(s_idx
.get());
563 assertx(func
!= nullptr);
564 TypedValue args
[] = {
570 g_context
->invokeFuncFew(&ret
, func
, nullptr, nullptr, 3, &args
[0]);
574 TypedValue
mapIdx(ObjectData
* mapOD
, StringData
* key
, TypedValue def
) {
575 assert(collections::isType(mapOD
->getVMClass(), CollectionType::Map
) ||
576 collections::isType(mapOD
->getVMClass(), CollectionType::ImmMap
));
577 return getDefaultIfNullCell(static_cast<BaseMap
*>(mapOD
)->get(key
), def
);
580 int32_t arrayVsize(ArrayData
* ad
) {
584 TypedValue
* getSPropOrNull(const Class
* cls
,
585 const StringData
* name
,
587 auto const lookup
= cls
->getSProp(ctx
, name
);
589 if (UNLIKELY(!lookup
.prop
|| !lookup
.accessible
)) return nullptr;
594 TypedValue
* getSPropOrRaise(const Class
* cls
,
595 const StringData
* name
,
597 auto sprop
= getSPropOrNull(cls
, name
, ctx
);
598 if (UNLIKELY(!sprop
)) {
599 raise_error("Invalid static property access: %s::%s",
600 cls
->name()->data(), name
->data());
605 TypedValue
* ldGblAddrHelper(StringData
* name
) {
606 return g_context
->m_globalVarEnv
->lookup(name
);
609 TypedValue
* ldGblAddrDefHelper(StringData
* name
) {
610 return g_context
->m_globalVarEnv
->lookupAdd(name
);
613 template <typename T
>
614 static int64_t switchBoundsCheck(T v
, int64_t base
, int64_t nTargets
) {
615 // I'm relying on gcc to be smart enough to optimize away the next
616 // two lines when T is int64.
617 if (int64_t(v
) == v
) {
619 if (ival
>= base
&& ival
< (base
+ nTargets
)) {
626 int64_t switchDoubleHelper(int64_t val
, int64_t base
, int64_t nTargets
) {
632 return switchBoundsCheck(u
.dblval
, base
, nTargets
);
635 int64_t switchStringHelper(StringData
* s
, int64_t base
, int64_t nTargets
) {
640 switch (s
->isNumericWithVal(ival
, dval
, 1)) {
642 ival
= switchBoundsCheck(0, base
, nTargets
);
645 ival
= switchBoundsCheck(ival
, base
, nTargets
);
648 ival
= switchBoundsCheck(dval
, base
, nTargets
);
653 case KindOfStaticString
:
669 int64_t switchObjHelper(ObjectData
* o
, int64_t base
, int64_t nTargets
) {
670 auto const ival
= o
->toInt64();
672 return switchBoundsCheck(ival
, base
, nTargets
);
675 TCA
sswitchHelperFast(const StringData
* val
,
676 const SSwitchMap
* table
,
678 TCA
* dest
= table
->find(val
);
679 return dest
? *dest
: *def
;
682 // TODO(#2031980): clear these out
683 void tv_release_generic(TypedValue
* tv
) {
684 assertx(vmRegStateIsDirty());
685 assertx(tv
->m_type
== KindOfString
|| tv
->m_type
== KindOfArray
||
686 tv
->m_type
== KindOfObject
|| tv
->m_type
== KindOfResource
||
687 tv
->m_type
== KindOfRef
);
688 g_destructors
[typeToDestrIdx(tv
->m_type
)](tv
->m_data
.pref
);
691 Cell
lookupCnsHelper(const TypedValue
* tv
,
694 assertx(tv
->m_type
== KindOfUninit
);
696 // Deferred constants such as SID
697 if (UNLIKELY(tv
->m_data
.pref
!= nullptr)) {
698 auto callback
= (Unit::SystemConstantCallback
)(tv
->m_data
.pref
);
699 const Cell
* cns
= callback().asTypedValue();
700 if (LIKELY(cns
->m_type
!= KindOfUninit
)) {
707 const Cell
* cns
= nullptr;
708 if (UNLIKELY(rds::s_constants().get() != nullptr)) {
709 cns
= rds::s_constants()->nvGet(nm
);
712 cns
= Unit::loadCns(const_cast<StringData
*>(nm
));
714 if (LIKELY(cns
!= nullptr)) {
720 // Undefined constants
722 raise_error("Undefined constant '%s'", nm
->data());
724 raise_notice(Strings::UNDEFINED_CONSTANT
, nm
->data(), nm
->data());
726 c1
.m_data
.pstr
= const_cast<StringData
*>(nm
);
727 c1
.m_type
= KindOfStaticString
;
733 void lookupClsMethodHelper(Class
* cls
,
739 ObjectData
* obj
= fp
->hasThis() ? fp
->getThis() : nullptr;
740 Class
* ctx
= fp
->m_func
->cls();
742 g_context
->lookupClsMethod(f
, cls
, meth
, obj
, ctx
, true);
743 if (res
== LookupResult::MethodFoundNoThis
||
744 res
== LookupResult::MagicCallStaticFound
) {
748 assertx(res
== LookupResult::MethodFoundWithThis
||
749 res
== LookupResult::MagicCallFound
);
754 if (res
== LookupResult::MagicCallFound
||
755 res
== LookupResult::MagicCallStaticFound
) {
756 ar
->setMagicDispatch(meth
);
760 *arPreliveOverwriteCells(ar
) = make_tv
<KindOfString
>(meth
);
765 void profileObjClassHelper(ClassProfile
* profile
, ObjectData
* obj
) {
766 profile
->reportClass(obj
->getVMClass());
769 Cell
lookupCnsUHelper(const TypedValue
* tv
,
771 StringData
* fallback
) {
772 const Cell
* cns
= nullptr;
775 // lookup qualified name in thread-local constants
776 bool cacheConsts
= rds::s_constants().get() != nullptr;
777 if (UNLIKELY(cacheConsts
)) {
778 cns
= rds::s_constants()->nvGet(nm
);
781 cns
= Unit::loadCns(const_cast<StringData
*>(nm
));
784 // try cache handle for unqualified name
785 if (UNLIKELY(!cns
&& tv
->m_type
!= KindOfUninit
)) {
786 cns
= const_cast<Cell
*>(tv
);
789 // lookup unqualified name in thread-local constants
790 if (UNLIKELY(!cns
)) {
791 if (UNLIKELY(cacheConsts
)) {
792 cns
= rds::s_constants()->nvGet(fallback
);
795 cns
= Unit::loadCns(const_cast<StringData
*>(fallback
));
797 if (UNLIKELY(!cns
)) {
798 raise_notice(Strings::UNDEFINED_CONSTANT
,
799 fallback
->data(), fallback
->data());
800 c1
.m_data
.pstr
= const_cast<StringData
*>(fallback
);
801 c1
.m_type
= KindOfStaticString
;
809 //////////////////////////////////////////////////////////////////////
811 void checkFrame(ActRec
* fp
, Cell
* sp
, bool fullCheck
, Offset bcOff
) {
812 const Func
* func
= fp
->m_func
;
815 assertx(!func
->cls()->isZombie());
817 if ((func
->attrs() & AttrMayUseVV
) && fp
->hasVarEnv()) {
818 assertx(fp
->getVarEnv()->getFP() == fp
);
820 int numLocals
= func
->numLocals();
821 assertx(sp
<= (Cell
*)fp
- func
->numSlotsInFrame() || fp
->resumed());
823 if (!fullCheck
) return;
825 int numParams
= func
->numParams();
826 for (int i
= 0; i
< numLocals
; i
++) {
827 if (i
>= numParams
&& fp
->resumed() && i
< func
->numNamedLocals()) {
830 assertx(tvIsPlausible(*frame_local(fp
, i
)));
835 [](const ActRec
* ar
) {
836 ar
->func()->validate();
838 [](const TypedValue
* tv
) {
839 assertx(tv
->m_type
== KindOfClass
|| tvIsPlausible(*tv
));
844 void traceCallback(ActRec
* fp
, Cell
* sp
, Offset pcOff
) {
845 if (Trace::moduleEnabled(Trace::hhirTracelets
)) {
846 FTRACE(0, "{} {} {} {} {}\n",
847 fp
->m_func
->fullName()->data(), pcOff
, fp
, sp
,
848 __builtin_return_address(0));
850 checkFrame(fp
, sp
, /*fullCheck*/true, pcOff
);
853 enum class OnFail
{ Warn
, Fatal
};
855 template<OnFail FailBehavior
, class FooNR
>
856 void loadFuncContextImpl(FooNR callableNR
, ActRec
* preLiveAR
, ActRec
* fp
) {
858 std::is_same
<FooNR
,ArrNR
>::value
||
859 std::is_same
<FooNR
,StrNR
>::value
,
860 "check loadFuncContextImpl for a new FooNR"
863 ObjectData
* inst
= nullptr;
864 Class
* cls
= nullptr;
865 StringData
* invName
= nullptr;
867 auto func
= vm_decode_function(
874 FailBehavior
== OnFail::Warn
876 if (UNLIKELY(func
== nullptr)) {
877 if (FailBehavior
== OnFail::Fatal
) {
878 raise_error("Invalid callable (array)");
880 func
= SystemLib::s_nullFunc
;
883 preLiveAR
->m_func
= func
;
886 preLiveAR
->setThis(inst
);
888 preLiveAR
->setClass(cls
);
890 if (UNLIKELY(invName
!= nullptr)) {
891 preLiveAR
->setMagicDispatch(invName
);
895 void loadArrayFunctionContext(ArrayData
* arr
, ActRec
* preLiveAR
, ActRec
* fp
) {
897 loadFuncContextImpl
<OnFail::Fatal
>(ArrNR(arr
), preLiveAR
, fp
);
899 *arPreliveOverwriteCells(preLiveAR
) = make_tv
<KindOfArray
>(arr
);
905 static void fpushCufHelperArraySlowPath(ArrayData
* arr
,
908 loadFuncContextImpl
<OnFail::Warn
>(ArrNR(arr
), preLiveAR
, fp
);
912 static bool strHasColon(StringData
* sd
) {
913 auto const sl
= sd
->slice();
914 auto const e
= sl
.end();
915 for (auto p
= sl
.begin(); p
!= e
; ++p
) {
916 if (*p
== ':') return true;
921 void fpushCufHelperArray(ArrayData
* arr
, ActRec
* preLiveAR
, ActRec
* fp
) {
923 if (UNLIKELY(!arr
->isPacked() || arr
->getSize() != 2)) {
924 return fpushCufHelperArraySlowPath(arr
, preLiveAR
, fp
);
927 auto const elem0
= tvToCell(PackedArray::NvGetInt(arr
, 0));
928 auto const elem1
= tvToCell(PackedArray::NvGetInt(arr
, 1));
930 if (UNLIKELY(elem0
->m_type
!= KindOfObject
||
931 !isStringType(elem1
->m_type
))) {
932 return fpushCufHelperArraySlowPath(arr
, preLiveAR
, fp
);
935 // If the string contains a class name (e.g. Foo::bar), all kinds
936 // of weird junk happens (wrt forwarding class contexts and
937 // things). We just do a quick loop to try to bail out of this
939 if (UNLIKELY(strHasColon(elem1
->m_data
.pstr
))) {
940 return fpushCufHelperArraySlowPath(arr
, preLiveAR
, fp
);
943 auto const inst
= elem0
->m_data
.pobj
;
944 auto const func
= g_context
->lookupMethodCtx(
950 if (UNLIKELY(!func
|| (func
->attrs() & AttrStatic
))) {
951 return fpushCufHelperArraySlowPath(arr
, preLiveAR
, fp
);
954 preLiveAR
->m_func
= func
;
956 preLiveAR
->setThis(inst
);
958 *arPreliveOverwriteCells(preLiveAR
) = make_tv
<KindOfArray
>(arr
);
964 static void fpushCufHelperStringSlowPath(StringData
* sd
,
967 loadFuncContextImpl
<OnFail::Warn
>(StrNR(sd
), preLiveAR
, fp
);
971 static void fpushStringFail(const StringData
* sd
, ActRec
* preLiveAR
) {
972 throw_invalid_argument("function: method '%s' not found", sd
->data());
973 preLiveAR
->m_func
= SystemLib::s_nullFunc
;
976 void fpushCufHelperString(StringData
* sd
, ActRec
* preLiveAR
, ActRec
* fp
) {
978 if (UNLIKELY(strHasColon(sd
))) {
979 return fpushCufHelperStringSlowPath(sd
, preLiveAR
, fp
);
982 auto const func
= Unit::loadFunc(sd
);
983 preLiveAR
->m_func
= func
;
984 if (UNLIKELY(!func
)) {
985 return fpushStringFail(sd
, preLiveAR
);
988 *arPreliveOverwriteCells(preLiveAR
) = make_tv
<KindOfString
>(sd
);
993 const Func
* loadClassCtor(Class
* cls
) {
994 const Func
* f
= cls
->getCtor();
995 if (UNLIKELY(!(f
->attrs() & AttrPublic
))) {
997 UNUSED LookupResult res
=
998 g_context
->lookupCtorMethod(f
, cls
, true /*raise*/);
999 assertx(res
== LookupResult::MethodFoundWithThis
);
1004 const Func
* lookupUnknownFunc(const StringData
* name
) {
1006 auto const func
= Unit::loadFunc(name
);
1007 if (UNLIKELY(!func
)) {
1008 raise_error("Call to undefined function %s()", name
->data());
1013 const Func
* lookupFallbackFunc(const StringData
* name
,
1014 const StringData
* fallback
) {
1016 // Try to load the first function
1017 auto func
= Unit::loadFunc(name
);
1018 if (LIKELY(!func
)) {
1019 // Then try to load the fallback function
1020 func
= Unit::loadFunc(fallback
);
1021 if (UNLIKELY(!func
)) {
1022 raise_error("Call to undefined function %s()", name
->data());
1028 Class
* lookupKnownClass(Class
** cache
, const StringData
* clsName
) {
1029 Class
* cls
= *cache
;
1030 assertx(!cls
); // the caller should already have checked
1032 AutoloadHandler::s_instance
->autoloadClass(
1033 StrNR(const_cast<StringData
*>(clsName
)));
1036 if (UNLIKELY(!cls
)) raise_error(Strings::UNKNOWN_CLASS
, clsName
->data());
1040 Cell
lookupClassConstantTv(TypedValue
* cache
,
1041 const NamedEntity
* ne
,
1042 const StringData
* cls
,
1043 const StringData
* cns
) {
1044 Cell clsCns
= g_context
->lookupClsCns(ne
, cls
, cns
);
1045 cellDup(clsCns
, *cache
);
1049 //////////////////////////////////////////////////////////////////////
1051 ObjectData
* colAddNewElemCHelper(ObjectData
* coll
, TypedValue value
) {
1052 collections::initElem(coll
, &value
);
1053 // If we specialized this on Vector we could use a DecRefNZ here (since we
1054 // could assume that initElem has incref'd the value). Right now, HH\Set
1055 // goes through this code path also, though, and it might fail to add the new
1057 tvRefcountedDecRef(value
);
1061 ObjectData
* colAddElemCHelper(ObjectData
* coll
, TypedValue key
,
1063 collections::initMapElem(coll
, &key
, &value
);
1064 // consume the input value. the collection setter either threw or created a
1065 // reference to value, so we can use a cheaper decref.
1066 tvRefcountedDecRefNZ(value
);
1070 //////////////////////////////////////////////////////////////////////
1073 * The standard VMRegAnchor treatment won't work for some cases called
1074 * during function preludes.
1076 * The fp sync machinery is fundamentally based on the notion that
1077 * instruction pointers in the TC are uniquely associated with source
1078 * HHBC instructions, and that source HHBC instructions are in turn
1079 * uniquely associated with SP->FP deltas.
1081 * trimExtraArgs is called from the prologue of the callee.
1082 * The prologue is 1) still in the caller frame for now,
1083 * and 2) shared across multiple call sites. 1 means that we have the
1084 * fp from the caller's frame, and 2 means that this fp is not enough
1087 * However, the prologue passes us the callee actRec, whose predecessor
1088 * has to be the caller. So we can sync sp and fp by ourselves here.
1091 static void sync_regstate_to_caller(ActRec
* preLive
) {
1092 assertx(tl_regState
== VMRegState::DIRTY
);
1093 auto const ec
= g_context
.getNoCheck();
1094 auto& regs
= vmRegsUnsafe();
1095 regs
.stack
.top() = (TypedValue
*)preLive
- preLive
->numArgs();
1096 ActRec
* fp
= preLive
== vmFirstAR() ?
1097 ec
->m_nestedVMs
.back().fp
: preLive
->m_sfp
;
1099 regs
.pc
= fp
->m_func
->unit()->at(fp
->m_func
->base() + preLive
->m_soff
);
1100 tl_regState
= VMRegState::CLEAN
;
1103 #define SHUFFLE_EXTRA_ARGS_PRELUDE() \
1104 const Func* f = ar->m_func; \
1105 int numParams = f->numNonVariadicParams(); \
1106 int numArgs = ar->numArgs(); \
1107 assertx(numArgs > numParams); \
1108 int numExtra = numArgs - numParams; \
1109 TRACE(1, "extra args: %d args, function %s takes only %d, ar %p\n", \
1110 numArgs, f->name()->data(), numParams, ar); \
1111 auto tvArgs = reinterpret_cast<TypedValue*>(ar) - numArgs; \
1112 /* end SHUFFLE_EXTRA_ARGS_PRELUDE */
1115 static void trimExtraArgsMayReenter(ActRec
* ar
,
1119 sync_regstate_to_caller(ar
);
1121 tvRefcountedDecRef(tvArgs
); // may reenter for __destruct
1123 } while (tvArgs
!= limit
);
1124 ar
->setNumArgs(ar
->m_func
->numParams());
1126 // go back to dirty (see the comments of sync_regstate_to_caller)
1127 tl_regState
= VMRegState::DIRTY
;
1130 void trimExtraArgs(ActRec
* ar
) {
1131 SHUFFLE_EXTRA_ARGS_PRELUDE()
1132 assertx(!f
->hasVariadicCaptureParam());
1133 assertx(!(f
->attrs() & AttrMayUseVV
));
1135 TypedValue
* limit
= tvArgs
+ numExtra
;
1137 if (UNLIKELY(tvDecRefWillCallHelper(tvArgs
))) {
1138 trimExtraArgsMayReenter(ar
, tvArgs
, limit
);
1141 tvDecRefOnly(tvArgs
);
1143 } while (tvArgs
!= limit
);
1145 assertx(f
->numParams() == (numArgs
- numExtra
));
1146 assertx(f
->numParams() == numParams
);
1147 ar
->setNumArgs(numParams
);
1150 void shuffleExtraArgsMayUseVV(ActRec
* ar
) {
1151 SHUFFLE_EXTRA_ARGS_PRELUDE()
1152 assertx(!f
->hasVariadicCaptureParam());
1153 assertx(f
->attrs() & AttrMayUseVV
);
1155 ar
->setExtraArgs(ExtraArgs::allocateCopy(tvArgs
, numExtra
));
1158 void shuffleExtraArgsVariadic(ActRec
* ar
) {
1159 SHUFFLE_EXTRA_ARGS_PRELUDE()
1160 assertx(f
->hasVariadicCaptureParam());
1161 assertx(!(f
->attrs() & AttrMayUseVV
));
1164 auto varArgsArray
= Array::attach(MixedArray::MakePacked(numExtra
, tvArgs
));
1165 // write into the last (variadic) param
1166 auto tv
= reinterpret_cast<TypedValue
*>(ar
) - numParams
- 1;
1167 tv
->m_type
= KindOfArray
;
1168 tv
->m_data
.parr
= varArgsArray
.detach();
1169 assertx(tv
->m_data
.parr
->hasExactlyOneRef());
1171 // no incref is needed, since extra values are being transferred
1172 // from the stack to the last local
1173 assertx(f
->numParams() == (numArgs
- numExtra
+ 1));
1174 assertx(f
->numParams() == (numParams
+ 1));
1175 ar
->setNumArgs(numParams
+ 1);
1179 void shuffleExtraArgsVariadicAndVV(ActRec
* ar
) {
1180 SHUFFLE_EXTRA_ARGS_PRELUDE()
1181 assertx(f
->hasVariadicCaptureParam());
1182 assertx(f
->attrs() & AttrMayUseVV
);
1185 ar
->setExtraArgs(ExtraArgs::allocateCopy(tvArgs
, numExtra
));
1187 auto varArgsArray
= Array::attach(MixedArray::MakePacked(numExtra
, tvArgs
));
1188 auto tvIncr
= tvArgs
; uint32_t i
= 0;
1189 // an incref is needed to compensate for discarding from the stack
1190 for (; i
< numExtra
; ++i
, ++tvIncr
) { tvRefcountedIncRef(tvIncr
); }
1191 // write into the last (variadic) param
1192 auto tv
= reinterpret_cast<TypedValue
*>(ar
) - numParams
- 1;
1193 tv
->m_type
= KindOfArray
;
1194 tv
->m_data
.parr
= varArgsArray
.detach();
1195 assertx(tv
->m_data
.parr
->hasExactlyOneRef());
1196 // Before, for each arg: refcount = n + 1 (stack)
1197 // After, for each arg: refcount = n + 2 (ExtraArgs, varArgsArray)
1201 #undef SHUFFLE_EXTRA_ARGS_PRELUDE
1203 void raiseMissingArgument(const Func
* func
, int got
) {
1204 const auto total
= func
->numNonVariadicParams();
1205 const auto variadic
= func
->hasVariadicCaptureParam();
1206 const Func::ParamInfoVec
& params
= func
->params();
1208 // We subtract the number of parameters with default value at the end
1209 for (size_t i
= total
; i
--; ) {
1210 if (!params
[i
].hasDefaultValue()) {
1215 bool lessNeeded
= (variadic
|| expected
< total
);
1216 if (expected
== 1) {
1217 raise_warning(Strings::MISSING_ARGUMENT
, func
->name()->data(),
1218 lessNeeded
? "at least" : "exactly", got
);
1220 raise_warning(Strings::MISSING_ARGUMENTS
, func
->name()->data(),
1221 lessNeeded
? "at least" : "exactly", expected
, got
);
1225 rds::Handle
lookupClsRDSHandle(const StringData
* name
) {
1226 return NamedEntity::get(name
)->getClassHandle();
1229 void registerLiveObj(ObjectData
* obj
) {
1230 assertx(RuntimeOption::EnableObjDestructCall
&& obj
->getVMClass()->getDtor());
1231 g_context
->m_liveBCObjs
.insert(obj
);
1234 void throwSwitchMode() {
1235 // This is only called right after dispatchBB, so the VM regs really are
1237 tl_regState
= VMRegState::CLEAN
;
1238 throw VMSwitchMode();
1241 namespace MInstrHelpers
{
1243 StringData
* stringGetI(StringData
* base
, uint64_t x
) {
1244 if (LIKELY(x
< base
->size())) {
1245 return base
->getChar(x
);
1247 if (RuntimeOption::EnableHipHopSyntax
) {
1248 raise_warning("Out of bounds");
1250 return staticEmptyString();
1253 uint64_t pairIsset(c_Pair
* pair
, int64_t index
) {
1254 auto result
= pair
->get(index
);
1255 return result
? !cellIsNull(result
) : false;
1258 uint64_t vectorIsset(c_Vector
* vec
, int64_t index
) {
1259 auto result
= vec
->get(index
);
1260 return result
? !cellIsNull(result
) : false;
1263 void bindElemC(TypedValue
* base
, TypedValue key
, RefData
* val
,
1265 auto elem
= HPHP::ElemD
<false, true>(mis
->tvRef
, base
, key
);
1266 tvBindRef(val
, elem
);
1269 void setWithRefElemC(TypedValue
* base
, TypedValue keyTV
, TypedValue val
,
1271 auto const keyC
= tvToCell(&keyTV
);
1272 auto elem
= HPHP::ElemD
<false, false>(mis
->tvRef
, base
, *keyC
);
1276 void setWithRefNewElem(TypedValue
* base
, TypedValue val
, MInstrState
* mis
) {
1277 auto elem
= NewElem
<false>(mis
->tvRef
, base
);
1283 //////////////////////////////////////////////////////////////////////
1285 uintptr_t tlsBaseNoInline() {
1289 //////////////////////////////////////////////////////////////////////