2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/bytecode.h"
23 #include "folly/String.h"
25 #include "hphp/runtime/base/tv-comparisons.h"
26 #include "hphp/runtime/base/tv-conversions.h"
27 #include "hphp/runtime/base/tv-arith.h"
28 #include "hphp/compiler/builtin_symbols.h"
29 #include "hphp/runtime/vm/event-hook.h"
30 #include "hphp/runtime/vm/jit/translator.h"
31 #include "hphp/runtime/vm/srckey.h"
32 #include "hphp/runtime/vm/member-operations.h"
33 #include "hphp/runtime/base/class-info.h"
34 #include "hphp/runtime/base/code-coverage.h"
35 #include "hphp/runtime/base/file-repository.h"
36 #include "hphp/runtime/base/base-includes.h"
37 #include "hphp/runtime/base/execution-context.h"
38 #include "hphp/runtime/base/runtime-option.h"
39 #include "hphp/runtime/base/hphp-array.h"
40 #include "hphp/runtime/base/strings.h"
41 #include "hphp/util/util.h"
42 #include "hphp/util/trace.h"
43 #include "hphp/util/debug.h"
44 #include "hphp/runtime/base/stat-cache.h"
45 #include "hphp/runtime/vm/debug/debug.h"
46 #include "hphp/runtime/vm/hhbc.h"
47 #include "hphp/runtime/vm/php-debug.h"
48 #include "hphp/runtime/vm/debugger-hook.h"
49 #include "hphp/runtime/vm/runtime.h"
50 #include "hphp/runtime/vm/jit/target-cache.h"
51 #include "hphp/runtime/vm/type-constraint.h"
52 #include "hphp/runtime/vm/unwind.h"
53 #include "hphp/runtime/vm/jit/translator-inline.h"
54 #include "hphp/runtime/vm/native.h"
55 #include "hphp/runtime/ext/ext_math.h"
56 #include "hphp/runtime/ext/ext_string.h"
57 #include "hphp/runtime/ext/ext_error.h"
58 #include "hphp/runtime/ext/ext_closure.h"
59 #include "hphp/runtime/ext/ext_continuation.h"
60 #include "hphp/runtime/ext/ext_function.h"
61 #include "hphp/runtime/ext/ext_variable.h"
62 #include "hphp/runtime/ext/ext_array.h"
63 #include "hphp/runtime/base/stats.h"
64 #include "hphp/runtime/vm/type-profile.h"
65 #include "hphp/runtime/server/source-root-info.h"
66 #include "hphp/runtime/base/extended-logger.h"
67 #include "hphp/runtime/base/tracer.h"
68 #include "hphp/runtime/base/memory-profile.h"
70 #include "hphp/system/systemlib.h"
71 #include "hphp/runtime/ext/ext_collections.h"
73 #include "hphp/runtime/vm/name-value-table-wrapper.h"
74 #include "hphp/runtime/vm/request-arena.h"
75 #include "hphp/util/arena.h"
80 #include <boost/format.hpp>
81 #include <boost/utility/typed_in_place_factory.hpp>
90 // TODO: #1746957, #1756122
91 // we should skip the call in call_user_func_array, if
92 // by reference params are passed by value, or if its
93 // argument is not an array, but currently lots of tests
94 // depend on actually making the call.
95 const bool skipCufOnInvalidParams
= false;
97 // RepoAuthoritative has been raptured out of runtime_option.cpp. It needs
98 // to be closer to other bytecode.cpp data.
99 bool RuntimeOption::RepoAuthoritative
= false;
103 using Transl::VMRegAnchor
;
104 using Transl::EagerVMRegAnchor
;
107 #define OPTBLD_INLINE
109 #define OPTBLD_INLINE ALWAYS_INLINE
111 TRACE_SET_MOD(bcinterp
);
113 ActRec
* ActRec::arGetSfp() const {
114 ActRec
* prevFrame
= (ActRec
*)m_savedRbp
;
115 if (LIKELY(((uintptr_t)prevFrame
- Util::s_stackLimit
) >=
116 Util::s_stackSize
)) {
117 if (LIKELY(prevFrame
!= nullptr)) return prevFrame
;
120 return const_cast<ActRec
*>(this);
124 ActRec::skipFrame() const {
125 return m_func
&& m_func
->skipFrame();
129 Class
* arGetContextClassImpl
<false>(const ActRec
* ar
) {
133 return ar
->m_func
->cls();
137 Class
* arGetContextClassImpl
<true>(const ActRec
* ar
) {
141 if (ar
->m_func
->isPseudoMain() || ar
->m_func
->isBuiltin()) {
142 // Pseudomains inherit the context of their caller
143 VMExecutionContext
* context
= g_vmContext
;
144 ar
= context
->getPrevVMState(ar
);
145 while (ar
!= nullptr &&
146 (ar
->m_func
->isPseudoMain() || ar
->m_func
->isBuiltin())) {
147 ar
= context
->getPrevVMState(ar
);
153 return ar
->m_func
->cls();
156 const StaticString
s_call_user_func("call_user_func");
157 const StaticString
s_call_user_func_array("call_user_func_array");
158 const StaticString
s_stdclass("stdclass");
159 const StaticString
s___call("__call");
160 const StaticString
s___callStatic("__callStatic");
161 const StaticString
s_file("file");
162 const StaticString
s_line("line");
163 const StaticString
s_function("function");
164 const StaticString
s_args("args");
165 const StaticString
s_class("class");
166 const StaticString
s_object("object");
167 const StaticString
s_type("type");
168 const StaticString
s_include("include");
171 Transl::Translator
* tx() {
172 return Transl::Translator::Get();
175 ///////////////////////////////////////////////////////////////////////////////
177 //=============================================================================
178 // Miscellaneous macros.
181 #define DECODE_JMP(type, var) \
182 type var __attribute__((unused)) = *(type*)pc; \
184 Trace::trace("decode: Immediate %s %" PRIi64"\n", #type, \
186 #define ITER_SKIP(offset) pc = origPc + (offset);
188 #define DECODE(type, var) \
189 DECODE_JMP(type, var); \
191 #define DECODE_IVA(var) \
192 int32_t var UNUSED = decodeVariableSizeImm(&pc); \
194 Trace::trace("decode: Immediate int32 %" PRIi64"\n", \
196 #define DECODE_LITSTR(var) \
200 var = m_fp->m_func->unit()->lookupLitstrId(id); \
203 #define DECODE_HA(var) DECODE_IVA(var)
204 #define DECODE_IA(var) DECODE_IVA(var)
206 #define DECODE_ITER_LIST(typeList, idList, vecLen) \
207 DECODE(int32_t, vecLen); \
208 assert(vecLen > 0); \
209 Id* typeList = (Id*)pc; \
210 Id* idList = (Id*)pc + 1; \
211 pc += 2 * vecLen * sizeof(Id);
213 #define SYNC() m_pc = pc
215 //=============================================================================
216 // Miscellaneous helpers.
218 static inline Class
* frameStaticClass(ActRec
* fp
) {
220 return fp
->getThis()->getVMClass();
221 } else if (fp
->hasClass()) {
222 return fp
->getClass();
228 //=============================================================================
236 , m_nvTable(boost::in_place
<NameValueTable
>(
237 RuntimeOption::EvalVMInitialGlobalTableSize
))
239 TypedValue globalArray
;
240 globalArray
.m_type
= KindOfArray
;
241 globalArray
.m_data
.parr
=
242 new (request_arena()) GlobalNameValueTableWrapper(&*m_nvTable
);
243 globalArray
.m_data
.parr
->incRefCount();
244 m_nvTable
->set(StringData::GetStaticString("GLOBALS"), &globalArray
);
245 tvRefcountedDecRef(&globalArray
);
248 VarEnv::VarEnv(ActRec
* fp
, ExtraArgs
* eArgs
)
255 const Func
* func
= fp
->m_func
;
256 const Id numNames
= func
->numNamedLocals();
258 if (!numNames
) return;
260 m_nvTable
= boost::in_place
<NameValueTable
>(numNames
);
262 TypedValue
** origLocs
=
263 reinterpret_cast<TypedValue
**>(uintptr_t(this) + sizeof(VarEnv
));
264 TypedValue
* loc
= frame_local(fp
, 0);
265 for (Id i
= 0; i
< numNames
; ++i
, --loc
) {
266 assert(func
->lookupVarId(func
->localVarName(i
)) == (int)i
);
267 origLocs
[i
] = m_nvTable
->migrateSet(func
->localVarName(i
), loc
);
272 TRACE(3, "Destroying VarEnv %p [%s]\n",
274 isGlobalScope() ? "global scope" : "local scope");
275 assert(m_restoreLocations
.empty());
277 if (!isGlobalScope()) {
278 if (LIKELY(!m_malloced
)) {
279 varenv_arena().endFrame();
284 * When detaching the global scope, we leak any live objects (and
285 * let the smart allocator clean them up). This is because we're
286 * not supposed to run destructors for objects that are live at
287 * the end of a request.
293 size_t VarEnv::getObjectSz(ActRec
* fp
) {
294 return sizeof(VarEnv
) + sizeof(TypedValue
*) * fp
->m_func
->numNamedLocals();
297 VarEnv
* VarEnv::createLocalOnStack(ActRec
* fp
) {
298 auto& va
= varenv_arena();
300 void* mem
= va
.alloc(getObjectSz(fp
));
301 VarEnv
* ret
= new (mem
) VarEnv(fp
, fp
->getExtraArgs());
302 TRACE(3, "Creating lazily attached VarEnv %p on stack\n", mem
);
306 VarEnv
* VarEnv::createLocalOnHeap(ActRec
* fp
) {
307 void* mem
= malloc(getObjectSz(fp
));
308 VarEnv
* ret
= new (mem
) VarEnv(fp
, fp
->getExtraArgs());
309 TRACE(3, "Creating lazily attached VarEnv %p on heap\n", mem
);
310 ret
->m_malloced
= true;
314 VarEnv
* VarEnv::createGlobal() {
315 assert(!g_vmContext
->m_globalVarEnv
);
317 VarEnv
* ret
= new (request_arena()) VarEnv();
318 TRACE(3, "Creating VarEnv %p [global scope]\n", ret
);
319 ret
->m_global
= true;
320 g_vmContext
->m_globalVarEnv
= ret
;
324 void VarEnv::destroy(VarEnv
* ve
) {
325 bool malloced
= ve
->m_malloced
;
327 if (UNLIKELY(malloced
)) free(ve
);
330 void VarEnv::attach(ActRec
* fp
) {
331 TRACE(3, "Attaching VarEnv %p [%s] %d fp @%p\n",
333 isGlobalScope() ? "global scope" : "local scope",
334 int(fp
->m_func
->numNamedLocals()), fp
);
335 assert(m_depth
== 0 || fp
->arGetSfp() == m_cfp
||
336 (fp
->arGetSfp() == fp
&& g_vmContext
->isNested()));
340 // Overlay fp's locals, if it has any.
342 const Func
* func
= fp
->m_func
;
343 const Id numNames
= func
->numNamedLocals();
348 m_nvTable
= boost::in_place
<NameValueTable
>(numNames
);
351 TypedValue
** origLocs
= new (varenv_arena()) TypedValue
*[
352 func
->numNamedLocals()];
353 TypedValue
* loc
= frame_local(fp
, 0);
354 for (Id i
= 0; i
< numNames
; ++i
, --loc
) {
355 assert(func
->lookupVarId(func
->localVarName(i
)) == (int)i
);
356 origLocs
[i
] = m_nvTable
->migrate(func
->localVarName(i
), loc
);
358 m_restoreLocations
.push_back(origLocs
);
361 void VarEnv::detach(ActRec
* fp
) {
362 TRACE(3, "Detaching VarEnv %p [%s] @%p\n",
364 isGlobalScope() ? "global scope" : "local scope",
369 // Merge/remove fp's overlaid locals, if it had any.
370 const Func
* func
= fp
->m_func
;
371 if (Id
const numLocals
= func
->numNamedLocals()) {
373 * In the case of a lazily attached VarEnv, we have our locations
374 * for the first (lazy) attach stored immediately following the
375 * VarEnv in memory. In this case m_restoreLocations will be empty.
377 assert((!isGlobalScope() && m_depth
== 1) == m_restoreLocations
.empty());
378 TypedValue
** origLocs
=
379 !m_restoreLocations
.empty()
380 ? m_restoreLocations
.back()
381 : reinterpret_cast<TypedValue
**>(uintptr_t(this) + sizeof(VarEnv
));
383 for (Id i
= 0; i
< numLocals
; i
++) {
384 m_nvTable
->resettle(func
->localVarName(i
), origLocs
[i
]);
386 if (!m_restoreLocations
.empty()) {
387 m_restoreLocations
.pop_back();
391 VMExecutionContext
* context
= g_vmContext
;
392 m_cfp
= context
->getPrevVMState(fp
);
396 // don't free global varEnv
397 if (context
->m_globalVarEnv
!= this) {
398 assert(!isGlobalScope());
404 // This helper is creating a NVT because of dynamic variable accesses,
405 // even though we're already attached to a frame and it had no named
407 void VarEnv::ensureNvt() {
408 const size_t kLazyNvtSize
= 3;
410 m_nvTable
= boost::in_place
<NameValueTable
>(kLazyNvtSize
);
414 void VarEnv::set(const StringData
* name
, TypedValue
* tv
) {
416 m_nvTable
->set(name
, tv
);
419 void VarEnv::bind(const StringData
* name
, TypedValue
* tv
) {
421 m_nvTable
->bind(name
, tv
);
424 void VarEnv::setWithRef(const StringData
* name
, TypedValue
* tv
) {
425 if (tv
->m_type
== KindOfRef
) {
432 TypedValue
* VarEnv::lookup(const StringData
* name
) {
436 return m_nvTable
->lookup(name
);
439 TypedValue
* VarEnv::lookupAdd(const StringData
* name
) {
441 return m_nvTable
->lookupAdd(name
);
444 bool VarEnv::unset(const StringData
* name
) {
445 if (!m_nvTable
) return true;
446 m_nvTable
->unset(name
);
450 Array
VarEnv::getDefinedVariables() const {
451 Array ret
= Array::Create();
453 if (!m_nvTable
) return ret
;
455 NameValueTable::Iterator
iter(&*m_nvTable
);
456 for (; iter
.valid(); iter
.next()) {
457 const StringData
* sd
= iter
.curKey();
458 const TypedValue
* tv
= iter
.curVal();
459 if (tvAsCVarRef(tv
).isReferenced()) {
460 ret
.setRef(StrNR(sd
).asString(), tvAsCVarRef(tv
));
462 ret
.add(StrNR(sd
).asString(), tvAsCVarRef(tv
));
469 TypedValue
* VarEnv::getExtraArg(unsigned argInd
) const {
470 return m_extraArgs
->getExtraArg(argInd
);
473 //=============================================================================
475 ExtraArgs::ExtraArgs() {}
476 ExtraArgs::~ExtraArgs() {}
478 void* ExtraArgs::allocMem(unsigned nargs
) {
479 return smart_malloc(sizeof(TypedValue
) * nargs
+ sizeof(ExtraArgs
));
482 ExtraArgs
* ExtraArgs::allocateCopy(TypedValue
* args
, unsigned nargs
) {
483 void* mem
= allocMem(nargs
);
484 ExtraArgs
* ea
= new (mem
) ExtraArgs();
487 * The stack grows downward, so the args in memory are "backward"; i.e. the
488 * leftmost (in PHP) extra arg is highest in memory.
490 std::reverse_copy(args
, args
+ nargs
, &ea
->m_extraArgs
[0]);
494 ExtraArgs
* ExtraArgs::allocateUninit(unsigned nargs
) {
495 void* mem
= ExtraArgs::allocMem(nargs
);
496 return new (mem
) ExtraArgs();
499 void ExtraArgs::deallocate(ExtraArgs
* ea
, unsigned nargs
) {
502 for (unsigned i
= 0; i
< nargs
; ++i
) {
503 tvRefcountedDecRef(ea
->m_extraArgs
+ i
);
509 void ExtraArgs::deallocate(ActRec
* ar
) {
510 const int numExtra
= ar
->numArgs() - ar
->m_func
->numParams();
511 deallocate(ar
->getExtraArgs(), numExtra
);
514 TypedValue
* ExtraArgs::getExtraArg(unsigned argInd
) const {
515 return const_cast<TypedValue
*>(&m_extraArgs
[argInd
]);
518 //=============================================================================
521 // Store actual stack elements array in a thread-local in order to amortize the
522 // cost of allocation.
525 StackElms() : m_elms(nullptr) {}
530 if (m_elms
== nullptr) {
531 // RuntimeOption::EvalVMStackElms-sized and -aligned.
532 size_t algnSz
= RuntimeOption::EvalVMStackElms
* sizeof(TypedValue
);
533 if (posix_memalign((void**)&m_elms
, algnSz
, algnSz
) != 0) {
534 throw std::runtime_error(
535 std::string("VM stack initialization failed: ") +
536 folly::errnoStr(errno
).c_str());
542 if (m_elms
!= nullptr) {
550 IMPLEMENT_THREAD_LOCAL(StackElms
, t_se
);
552 const int Stack::sSurprisePageSize
= sysconf(_SC_PAGESIZE
);
553 // We reserve the bottom page of each stack for use as the surprise
554 // page, so the minimum useful stack size is the next power of two.
555 const uint
Stack::sMinStackElms
= 2 * sSurprisePageSize
/ sizeof(TypedValue
);
557 void Stack::ValidateStackSize() {
558 if (RuntimeOption::EvalVMStackElms
< sMinStackElms
) {
559 throw std::runtime_error(str(
560 boost::format("VM stack size of 0x%llx is below the minimum of 0x%x")
561 % RuntimeOption::EvalVMStackElms
564 if (!Util::isPowerOfTwo(RuntimeOption::EvalVMStackElms
)) {
565 throw std::runtime_error(str(
566 boost::format("VM stack size of 0x%llx is not a power of 2")
567 % RuntimeOption::EvalVMStackElms
));
572 : m_elms(nullptr), m_top(nullptr), m_base(nullptr) {
580 Stack::requestInit() {
581 m_elms
= t_se
->elms();
582 // Burn one element of the stack, to satisfy the constraint that
583 // valid m_top values always have the same high-order (>
584 // log(RuntimeOption::EvalVMStackElms)) bits.
585 m_top
= m_base
= m_elms
+ RuntimeOption::EvalVMStackElms
- 1;
587 // Because of the surprise page at the bottom of the stack we lose an
588 // additional 256 elements which must be taken into account when checking for
590 UNUSED
size_t maxelms
=
591 RuntimeOption::EvalVMStackElms
- sSurprisePageSize
/ sizeof(TypedValue
);
592 assert(!wouldOverflow(maxelms
- 1));
593 assert(wouldOverflow(maxelms
));
597 Stack::requestExit() {
598 if (m_elms
!= nullptr) {
603 void flush_evaluation_stack() {
604 if (g_context
.isNull()) {
605 // For RPCRequestHandler threads, the ExecutionContext can stay alive
606 // across requests, and hold references to the VM stack, and
607 // the TargetCache needs to keep track of which classes are live etc
608 // So only flush the VM stack and the target cache if the execution
611 if (!t_se
.isNull()) {
614 Transl::TargetCache::flush();
618 static std::string
toStringElm(const TypedValue
* tv
) {
619 std::ostringstream os
;
621 if (tv
->m_type
< MinDataType
|| tv
->m_type
> MaxNumDataTypes
) {
622 os
<< " ??? type " << tv
->m_type
<< "\n";
626 assert(tv
->m_type
>= MinDataType
&& tv
->m_type
< MaxNumDataTypes
);
627 if (IS_REFCOUNTED_TYPE(tv
->m_type
) && tv
->m_data
.pref
->m_count
<= 0) {
628 // OK in the invoking frame when running a destructor.
629 os
<< " ??? inner_count " << tv
->m_data
.pref
->m_count
<< " ";
633 switch (tv
->m_type
) {
636 os
<< "@" << tv
->m_data
.pref
;
637 os
<< toStringElm(tv
->m_data
.pref
->tv());
648 switch (tv
->m_type
) {
656 os
<< (tv
->m_data
.num
? "True" : "False");
659 os
<< "0x" << std::hex
<< tv
->m_data
.num
<< std::dec
;
662 os
<< tv
->m_data
.dbl
;
664 case KindOfStaticString
:
667 int len
= tv
->m_data
.pstr
->size();
668 bool truncated
= false;
673 os
<< tv
->m_data
.pstr
674 << "c(" << tv
->m_data
.pstr
->getCount() << ")"
676 << Util::escapeStringForCPP(tv
->m_data
.pstr
->data(), len
)
677 << "\"" << (truncated
? "..." : "");
681 assert(tv
->m_data
.parr
->getCount() > 0);
682 os
<< tv
->m_data
.parr
683 << "c(" << tv
->m_data
.parr
->getCount() << ")"
687 assert(tv
->m_data
.pobj
->getCount() > 0);
688 os
<< tv
->m_data
.pobj
689 << "c(" << tv
->m_data
.pobj
->getCount() << ")"
691 << tv
->m_data
.pobj
->o_getClassName().get()->data()
695 assert(tv
->m_data
.pres
->getCount() > 0);
696 os
<< tv
->m_data
.pres
697 << "c(" << tv
->m_data
.pres
->getCount() << ")"
699 << const_cast<ResourceData
*>(tv
->m_data
.pres
)
700 ->o_getClassName().get()->data()
706 os
<< tv
->m_data
.pcls
707 << ":" << tv
->m_data
.pcls
->name()->data();
717 static std::string
toStringIter(const Iter
* it
, bool itRef
) {
718 if (itRef
) return "I:MutableArray";
720 // TODO(#2458166): it might be a CufIter, but we're just lucky that
721 // the bit pattern for the CufIter is going to have a 0 in
722 // getIterType for now.
723 switch (it
->arr().getIterType()) {
724 case ArrayIter::TypeUndefined
:
725 return "I:Undefined";
726 case ArrayIter::TypeArray
:
728 case ArrayIter::TypeIterator
:
735 void Stack::toStringFrame(std::ostream
& os
, const ActRec
* fp
,
736 int offset
, const TypedValue
* ftop
,
737 const string
& prefix
) const {
740 // Use depth-first recursion to output the most deeply nested stack frame
744 TypedValue
* prevStackTop
= nullptr;
745 ActRec
* prevFp
= g_vmContext
->getPrevVMState(fp
, &prevPc
, &prevStackTop
);
746 if (prevFp
!= nullptr) {
747 toStringFrame(os
, prevFp
, prevPc
, prevStackTop
, prefix
);
752 const Func
* func
= fp
->m_func
;
755 string
funcName(func
->fullName()->data());
756 os
<< "{func:" << funcName
757 << ",soff:" << fp
->m_soff
758 << ",this:0x" << std::hex
<< (fp
->hasThis() ? fp
->getThis() : nullptr)
760 TypedValue
* tv
= (TypedValue
*)fp
;
763 if (func
->numLocals() > 0) {
765 int n
= func
->numLocals();
766 for (int i
= 0; i
< n
; i
++, tv
--) {
770 os
<< toStringElm(tv
);
775 assert(!func
->info() || func
->numIterators() == 0);
776 if (func
->numIterators() > 0) {
778 Iter
* it
= &((Iter
*)&tv
[1])[-1];
779 for (int i
= 0; i
< func
->numIterators(); i
++, it
--) {
784 if (func
->checkIterScope(offset
, i
, itRef
)) {
785 os
<< toStringIter(it
, itRef
);
793 std::vector
<std::string
> stackElems
;
796 [&](const ActRec
* ar
) {
797 stackElems
.push_back(
798 folly::format("{{func:{}}}", ar
->m_func
->fullName()->data()).str()
801 [&](const TypedValue
* tv
) {
802 stackElems
.push_back(toStringElm(tv
));
805 std::reverse(stackElems
.begin(), stackElems
.end());
806 os
<< ' ' << folly::join(' ', stackElems
);
811 string
Stack::toString(const ActRec
* fp
, int offset
,
812 const string prefix
/* = "" */) const {
813 // The only way to figure out which stack elements are activation records is
814 // to follow the frame chain. However, the goal for each stack frame is to
815 // print stack fragments from deepest to shallowest -- a then b in the
816 // following example:
818 // {func:foo,soff:51}<C:8> {func:bar} C:8 C:1 {func:biz} C:0
819 // aaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbb
821 // Use depth-first recursion to get the output order correct.
823 std::ostringstream os
;
824 auto unit
= fp
->unit();
825 auto func
= fp
->func();
826 os
<< prefix
<< "=== Stack at "
827 << unit
->filepath()->data() << ":"
828 << unit
->getLineNumber(unit
->offsetOf(vmpc())) << " func "
829 << func
->fullName()->data() << " ===\n";
831 toStringFrame(os
, fp
, offset
, m_top
, prefix
);
836 bool Stack::wouldOverflow(int numCells
) const {
837 // The funny approach here is to validate the translator's assembly
838 // technique. We've aligned and sized the stack so that the high order
839 // bits of valid cells are all the same. In the translator, numCells
840 // can be hardcoded, and m_top is wired into a register,
841 // so the expression requires no loads.
842 intptr_t truncatedTop
= intptr_t(m_top
) / sizeof(TypedValue
);
843 truncatedTop
&= RuntimeOption::EvalVMStackElms
- 1;
844 intptr_t diff
= truncatedTop
- numCells
-
845 sSurprisePageSize
/ sizeof(TypedValue
);
849 TypedValue
* Stack::frameStackBase(const ActRec
* fp
) {
850 const Func
* func
= fp
->m_func
;
851 assert(!func
->isGenerator());
852 return (TypedValue
*)((uintptr_t)fp
853 - (uintptr_t)(func
->numLocals()) * sizeof(TypedValue
)
854 - (uintptr_t)(func
->numIterators() * sizeof(Iter
)));
857 TypedValue
* Stack::generatorStackBase(const ActRec
* fp
) {
858 assert(fp
->m_func
->isGenerator());
859 VMExecutionContext
* context
= g_vmContext
;
860 ActRec
* sfp
= fp
->arGetSfp();
862 // In the reentrant case, we can consult the savedVM state. We simply
863 // use the top of stack of the previous VM frame (since the ActRec,
864 // locals, and iters for this frame do not reside on the VM stack).
865 return context
->m_nestedVMs
.back().m_savedState
.sp
;
867 // In the non-reentrant case, we know generators are always called from a
868 // function with an empty stack. So we find the caller's FP, compensate
869 // for its locals, and then we've found the base of the generator's stack.
870 return (TypedValue
*)sfp
- sfp
->m_func
->numSlotsInFrame();
874 __thread RequestArenaStorage s_requestArenaStorage
;
875 __thread VarEnvArenaStorage s_varEnvArenaStorage
;
878 //=============================================================================
881 using namespace HPHP
;
882 using namespace HPHP::MethodLookup
;
884 ActRec
* VMExecutionContext::getOuterVMFrame(const ActRec
* ar
) {
885 ActRec
* prevFrame
= (ActRec
*)ar
->m_savedRbp
;
886 if (LIKELY(((uintptr_t)prevFrame
- Util::s_stackLimit
) >=
887 Util::s_stackSize
)) {
888 if (LIKELY(prevFrame
!= nullptr)) return prevFrame
;
891 if (LIKELY(!m_nestedVMs
.empty())) return m_nestedVMs
.back().m_savedState
.fp
;
895 Cell
* VMExecutionContext::lookupClsCns(const NamedEntity
* ne
,
896 const StringData
* cls
,
897 const StringData
* cns
) {
898 Class
* class_
= Unit::loadClass(ne
, cls
);
899 if (class_
== nullptr) {
900 raise_error(Strings::UNKNOWN_CLASS
, cls
->data());
902 Cell
* clsCns
= class_
->clsCnsGet(cns
);
903 if (clsCns
== nullptr) {
904 raise_error("Couldn't find constant %s::%s",
905 cls
->data(), cns
->data());
910 TypedValue
* VMExecutionContext::lookupClsCns(const StringData
* cls
,
911 const StringData
* cns
) {
912 return lookupClsCns(Unit::GetNamedEntity(cls
), cls
, cns
);
915 // Look up the method specified by methodName from the class specified by cls
916 // and enforce accessibility. Accessibility checks depend on the relationship
917 // between the class that first declared the method (baseClass) and the context
920 // If there are multiple accessible methods with the specified name declared in
921 // cls and ancestors of cls, the method from the most derived class will be
922 // returned, except if we are doing an ObjMethod call ("$obj->foo()") and there
923 // is an accessible private method, in which case the accessible private method
926 // Accessibility rules:
928 // | baseClass/ctx relationship | public | protected | private |
929 // +----------------------------+--------+-----------+---------+
930 // | anon/unrelated | yes | no | no |
931 // | baseClass == ctx | yes | yes | yes |
932 // | baseClass derived from ctx | yes | yes | no |
933 // | ctx derived from baseClass | yes | yes | no |
934 // +----------------------------+--------+-----------+---------+
936 const Func
* VMExecutionContext::lookupMethodCtx(const Class
* cls
,
937 const StringData
* methodName
,
940 bool raise
/* = false */) {
942 if (callType
== CallType::CtorMethod
) {
943 assert(methodName
== nullptr);
944 method
= cls
->getCtor();
946 assert(callType
== CallType::ObjMethod
|| callType
== CallType::ClsMethod
);
947 assert(methodName
!= nullptr);
948 method
= cls
->lookupMethod(methodName
);
950 static StringData
* sd__construct
951 = StringData::GetStaticString("__construct");
952 if (UNLIKELY(methodName
== sd__construct
)) {
953 // We were looking up __construct and failed to find it. Fall back
954 // to old-style constructor: same as class name.
955 method
= cls
->getCtor();
956 if (!Func::isSpecial(method
->name())) break;
959 raise_error("Call to undefined method %s::%s from %s%s",
962 ctx
? "context " : "anonymous context",
963 ctx
? ctx
->name()->data() : "");
969 bool accessible
= true;
970 // If we found a protected or private method, we need to do some
971 // accessibility checks.
972 if ((method
->attrs() & (AttrProtected
|AttrPrivate
)) &&
973 !g_vmContext
->getDebuggerBypassCheck()) {
974 Class
* baseClass
= method
->baseCls();
976 // If the context class is the same as the class that first
977 // declared this method, then we know we have the right method
978 // and we can stop here.
979 if (ctx
== baseClass
) {
982 // The anonymous context cannot access protected or private methods,
983 // so we can fail fast here.
984 if (ctx
== nullptr) {
986 raise_error("Call to %s method %s::%s from anonymous context",
987 (method
->attrs() & AttrPrivate
) ? "private" : "protected",
989 method
->name()->data());
994 if (method
->attrs() & AttrPrivate
) {
995 // The context class is not the same as the class that declared
996 // this private method, so this private method is not accessible.
997 // We need to keep going because the context class may define a
998 // private method with this name.
1001 // If the context class is derived from the class that first
1002 // declared this protected method, then we know this method is
1003 // accessible and we know the context class cannot have a private
1004 // method with the same name, so we're done.
1005 if (ctx
->classof(baseClass
)) {
1008 if (!baseClass
->classof(ctx
)) {
1009 // The context class is not the same, an ancestor, or a descendent
1010 // of the class that first declared this protected method, so
1011 // this method is not accessible. Because the context class is
1012 // not the same or an ancestor of the class which first declared
1013 // the method, we know that the context class is not the same
1014 // or an ancestor of cls, and therefore we don't need to check
1015 // if the context class declares a private method with this name,
1016 // so we can fail fast here.
1018 raise_error("Call to protected method %s::%s from context %s",
1019 cls
->name()->data(),
1020 method
->name()->data(),
1021 ctx
->name()->data());
1025 // We now know this protected method is accessible, but we need to
1026 // keep going because the context class may define a private method
1028 assert(accessible
&& baseClass
->classof(ctx
));
1031 // If this is an ObjMethod call ("$obj->foo()") AND there is an ancestor
1032 // of cls that declares a private method with this name AND the context
1033 // class is an ancestor of cls, check if the context class declares a
1034 // private method with this name.
1035 if (method
->hasPrivateAncestor() && callType
== CallType::ObjMethod
&&
1036 ctx
&& cls
->classof(ctx
)) {
1037 const Func
* ctxMethod
= ctx
->lookupMethod(methodName
);
1038 if (ctxMethod
&& ctxMethod
->cls() == ctx
&&
1039 (ctxMethod
->attrs() & AttrPrivate
)) {
1040 // For ObjMethod calls a private method from the context class
1041 // trumps any other method we may have found.
1049 raise_error("Call to private method %s::%s from %s%s",
1050 method
->baseCls()->name()->data(),
1051 method
->name()->data(),
1052 ctx
? "context " : "anonymous context",
1053 ctx
? ctx
->name()->data() : "");
1058 LookupResult
VMExecutionContext::lookupObjMethod(const Func
*& f
,
1060 const StringData
* methodName
,
1062 bool raise
/* = false */) {
1063 f
= lookupMethodCtx(cls
, methodName
, ctx
, CallType::ObjMethod
, false);
1065 f
= cls
->lookupMethod(s___call
.get());
1068 // Throw a fatal error
1069 lookupMethodCtx(cls
, methodName
, ctx
, CallType::ObjMethod
, true);
1071 return LookupResult::MethodNotFound
;
1073 return LookupResult::MagicCallFound
;
1075 if (f
->attrs() & AttrStatic
&& !f
->isClosureBody()) {
1076 return LookupResult::MethodFoundNoThis
;
1078 return LookupResult::MethodFoundWithThis
;
1082 VMExecutionContext::lookupClsMethod(const Func
*& f
,
1084 const StringData
* methodName
,
1087 bool raise
/* = false */) {
1088 f
= lookupMethodCtx(cls
, methodName
, ctx
, CallType::ClsMethod
, false);
1090 if (obj
&& obj
->instanceof(cls
)) {
1091 f
= obj
->getVMClass()->lookupMethod(s___call
.get());
1094 f
= cls
->lookupMethod(s___callStatic
.get());
1097 // Throw a fatal errpr
1098 lookupMethodCtx(cls
, methodName
, ctx
, CallType::ClsMethod
, true);
1100 return LookupResult::MethodNotFound
;
1104 assert(f
->attrs() & AttrStatic
);
1105 return LookupResult::MagicCallStaticFound
;
1109 // __call cannot be static, this should be enforced by semantic
1110 // checks defClass time or earlier
1111 assert(!(f
->attrs() & AttrStatic
));
1112 return LookupResult::MagicCallFound
;
1114 if (obj
&& !(f
->attrs() & AttrStatic
) && obj
->instanceof(cls
)) {
1115 return LookupResult::MethodFoundWithThis
;
1117 return LookupResult::MethodFoundNoThis
;
1120 LookupResult
VMExecutionContext::lookupCtorMethod(const Func
*& f
,
1122 bool raise
/* = false */) {
1124 if (!(f
->attrs() & AttrPublic
)) {
1125 Class
* ctx
= arGetContextClass(getFP());
1126 f
= lookupMethodCtx(cls
, nullptr, ctx
, CallType::CtorMethod
, raise
);
1128 // If raise was true than lookupMethodCtx should have thrown,
1129 // so we should only be able to get here if raise was false
1131 return LookupResult::MethodNotFound
;
1134 return LookupResult::MethodFoundWithThis
;
1137 ObjectData
* VMExecutionContext::createObject(StringData
* clsName
,
1139 bool init
/* = true */) {
1140 Class
* class_
= Unit::loadClass(clsName
);
1141 if (class_
== nullptr) {
1142 throw_missing_class(clsName
->data());
1145 o
= newInstance(class_
);
1149 invokeFunc(&ret
, class_
->getCtor(), params
, o
.get());
1150 tvRefcountedDecRef(&ret
);
1153 ObjectData
* ret
= o
.detach();
1158 ObjectData
* VMExecutionContext::createObjectOnly(StringData
* clsName
) {
1159 return createObject(clsName
, null_array
, false);
1162 ActRec
* VMExecutionContext::getStackFrame() {
1167 ObjectData
* VMExecutionContext::getThis() {
1169 ActRec
* fp
= getFP();
1170 if (fp
->skipFrame()) {
1171 fp
= getPrevVMState(fp
);
1172 if (!fp
) return nullptr;
1174 if (fp
->hasThis()) {
1175 return fp
->getThis();
1180 Class
* VMExecutionContext::getContextClass() {
1182 ActRec
* ar
= getFP();
1183 assert(ar
!= nullptr);
1184 if (ar
->skipFrame()) {
1185 ar
= getPrevVMState(ar
);
1186 if (!ar
) return nullptr;
1188 return ar
->m_func
->cls();
1191 Class
* VMExecutionContext::getParentContextClass() {
1192 if (Class
* ctx
= getContextClass()) {
1193 return ctx
->parent();
1198 CStrRef
VMExecutionContext::getContainingFileName() {
1200 ActRec
* ar
= getFP();
1201 if (ar
== nullptr) return empty_string
;
1202 if (ar
->skipFrame()) {
1203 ar
= getPrevVMState(ar
);
1204 if (ar
== nullptr) return empty_string
;
1206 Unit
* unit
= ar
->m_func
->unit();
1207 return unit
->filepathRef();
1210 int VMExecutionContext::getLine() {
1212 ActRec
* ar
= getFP();
1213 Unit
* unit
= ar
? ar
->m_func
->unit() : nullptr;
1214 Offset pc
= unit
? pcOff() : 0;
1215 if (ar
== nullptr) return -1;
1216 if (ar
->skipFrame()) {
1217 ar
= getPrevVMState(ar
, &pc
);
1219 if (ar
== nullptr || (unit
= ar
->m_func
->unit()) == nullptr) return -1;
1220 return unit
->getLineNumber(pc
);
1223 Array
VMExecutionContext::getCallerInfo() {
1225 Array result
= Array::Create();
1226 ActRec
* ar
= getFP();
1227 if (ar
->skipFrame()) {
1228 ar
= getPrevVMState(ar
);
1230 while (ar
->m_func
->name()->isame(s_call_user_func
.get())
1231 || ar
->m_func
->name()->isame(s_call_user_func_array
.get())) {
1232 ar
= getPrevVMState(ar
);
1233 if (ar
== nullptr) {
1239 ar
= getPrevVMState(ar
, &pc
);
1240 while (ar
!= nullptr) {
1241 if (!ar
->m_func
->name()->isame(s_call_user_func
.get())
1242 && !ar
->m_func
->name()->isame(s_call_user_func_array
.get())) {
1243 Unit
* unit
= ar
->m_func
->unit();
1245 if ((lineNumber
= unit
->getLineNumber(pc
)) != -1) {
1246 result
.set(s_file
, unit
->filepath()->data(), true);
1247 result
.set(s_line
, lineNumber
);
1251 ar
= getPrevVMState(ar
, &pc
);
1256 bool VMExecutionContext::renameFunction(const StringData
* oldName
,
1257 const StringData
* newName
) {
1258 return m_renamedFuncs
.rename(oldName
, newName
);
1261 bool VMExecutionContext::isFunctionRenameable(const StringData
* name
) {
1262 return m_renamedFuncs
.isFunctionRenameable(name
);
1265 void VMExecutionContext::addRenameableFunctions(ArrayData
* arr
) {
1266 m_renamedFuncs
.addRenameableFunctions(arr
);
1269 VarEnv
* VMExecutionContext::getVarEnv() {
1272 ActRec
* fp
= getFP();
1273 if (UNLIKELY(!fp
)) return NULL
;
1274 if (fp
->skipFrame()) {
1275 fp
= getPrevVMState(fp
);
1277 if (!fp
) return nullptr;
1278 assert(!fp
->hasInvName());
1279 if (!fp
->hasVarEnv()) {
1280 fp
->setVarEnv(VarEnv::createLocalOnStack(fp
));
1282 return fp
->m_varEnv
;
1285 void VMExecutionContext::setVar(StringData
* name
, TypedValue
* v
, bool ref
) {
1287 // setVar() should only be called after getVarEnv() has been called
1288 // to create a varEnv
1289 ActRec
*fp
= getFP();
1291 if (fp
->skipFrame()) {
1292 fp
= getPrevVMState(fp
);
1294 assert(!fp
->hasInvName());
1295 assert(!fp
->hasExtraArgs());
1296 assert(fp
->m_varEnv
!= nullptr);
1298 fp
->m_varEnv
->bind(name
, v
);
1300 fp
->m_varEnv
->set(name
, v
);
1304 Array
VMExecutionContext::getLocalDefinedVariables(int frame
) {
1306 ActRec
*fp
= getFP();
1307 for (; frame
> 0; --frame
) {
1309 fp
= getPrevVMState(fp
);
1312 return Array::Create();
1314 assert(!fp
->hasInvName());
1315 if (fp
->hasVarEnv()) {
1316 return fp
->m_varEnv
->getDefinedVariables();
1318 const Func
*func
= fp
->m_func
;
1319 auto numLocals
= func
->numNamedLocals();
1320 ArrayInit
ret(numLocals
);
1321 for (Id id
= 0; id
< numLocals
; ++id
) {
1322 TypedValue
* ptv
= frame_local(fp
, id
);
1323 if (ptv
->m_type
== KindOfUninit
) {
1326 Variant
name(func
->localVarName(id
));
1327 ret
.add(name
, tvAsVariant(ptv
));
1329 return ret
.toArray();
1332 void VMExecutionContext::shuffleMagicArgs(ActRec
* ar
) {
1333 // We need to put this where the first argument is
1334 StringData
* invName
= ar
->getInvName();
1335 int nargs
= ar
->numArgs();
1336 ar
->setVarEnv(nullptr);
1337 assert(!ar
->hasVarEnv() && !ar
->hasInvName());
1338 // We need to make an array containing all the arguments passed by the
1339 // caller and put it where the second argument is
1340 ArrayData
* argArray
= pack_args_into_array(ar
, nargs
);
1341 argArray
->incRefCount();
1342 // Remove the arguments from the stack
1343 for (int i
= 0; i
< nargs
; ++i
) {
1346 // Move invName to where the first argument belongs, no need
1347 // to incRef/decRef since we are transferring ownership
1348 m_stack
.pushStringNoRc(invName
);
1349 // Move argArray to where the second argument belongs. We've already
1350 // incReffed the array above so we don't need to do it here.
1351 m_stack
.pushArrayNoRc(argArray
);
1356 static inline void checkStack(Stack
& stk
, const Func
* f
) {
1357 ThreadInfo
* info
= ThreadInfo::s_threadInfo
.getNoCheck();
1358 // Check whether func's maximum stack usage would overflow the stack.
1359 // Both native and VM stack overflows are independently possible.
1360 if (!stack_in_bounds(info
) ||
1361 stk
.wouldOverflow(f
->maxStackCells() + kStackCheckPadding
)) {
1362 TRACE(1, "Maximum VM stack depth exceeded.\n");
1363 raise_error("Stack overflow");
1367 bool VMExecutionContext::prepareFuncEntry(ActRec
*ar
, PC
& pc
) {
1368 const Func
* func
= ar
->m_func
;
1369 Offset firstDVInitializer
= InvalidAbsoluteOffset
;
1370 bool raiseMissingArgumentWarnings
= false;
1371 int nparams
= func
->numParams();
1372 if (UNLIKELY(ar
->m_varEnv
!= nullptr)) {
1374 * m_varEnv != nullptr => we have a varEnv, extraArgs, or an invName.
1376 if (ar
->hasInvName()) {
1377 // shuffleMagicArgs deals with everything. no need for
1378 // further argument munging
1379 shuffleMagicArgs(ar
);
1380 } else if (ar
->hasVarEnv()) {
1382 if (!func
->isGenerator()) {
1383 assert(func
->isPseudoMain());
1384 pushLocalsAndIterators(func
);
1385 ar
->m_varEnv
->attach(ar
);
1387 pc
= func
->getEntry();
1388 // Nothing more to do; get out
1391 assert(ar
->hasExtraArgs());
1392 assert(func
->numParams() < ar
->numArgs());
1395 int nargs
= ar
->numArgs();
1396 if (nargs
!= nparams
) {
1397 if (nargs
< nparams
) {
1398 // Push uninitialized nulls for missing arguments. Some of them may end
1399 // up getting default-initialized, but regardless, we need to make space
1400 // for them on the stack.
1401 const Func::ParamInfoVec
& paramInfo
= func
->params();
1402 for (int i
= nargs
; i
< nparams
; ++i
) {
1403 m_stack
.pushUninit();
1404 Offset dvInitializer
= paramInfo
[i
].funcletOff();
1405 if (dvInitializer
== InvalidAbsoluteOffset
) {
1406 // We wait to raise warnings until after all the locals have been
1407 // initialized. This is important because things need to be in a
1408 // consistent state in case the user error handler throws.
1409 raiseMissingArgumentWarnings
= true;
1410 } else if (firstDVInitializer
== InvalidAbsoluteOffset
) {
1411 // This is the first unpassed arg with a default value, so
1412 // this is where we'll need to jump to.
1413 firstDVInitializer
= dvInitializer
;
1417 if (func
->attrs() & AttrMayUseVV
) {
1418 // Extra parameters must be moved off the stack.
1419 const int numExtras
= nargs
- nparams
;
1420 ar
->setExtraArgs(ExtraArgs::allocateCopy((TypedValue
*)ar
- nargs
,
1422 m_stack
.ndiscard(numExtras
);
1424 // The function we're calling is not marked as "MayUseVV",
1425 // so just discard the extra arguments
1426 int numExtras
= nargs
- nparams
;
1427 for (int i
= 0; i
< numExtras
; i
++) {
1430 ar
->setNumArgs(nparams
);
1436 int nlocals
= nparams
;
1437 if (UNLIKELY(func
->isClosureBody())) {
1438 int nuse
= init_closure(ar
, m_stack
.top());
1439 // init_closure doesn't move m_stack
1440 m_stack
.nalloc(nuse
);
1445 if (LIKELY(!func
->isGenerator())) {
1447 * we only get here from callAndResume
1448 * if we failed to get a translation for
1449 * a generator's prologue
1451 pushLocalsAndIterators(func
, nlocals
);
1455 if (firstDVInitializer
!= InvalidAbsoluteOffset
) {
1456 pc
= func
->unit()->entry() + firstDVInitializer
;
1458 pc
= func
->getEntry();
1460 // cppext functions/methods have their own logic for raising
1461 // warnings for missing arguments, so we only need to do this work
1462 // for non-cppext functions/methods
1463 if (raiseMissingArgumentWarnings
&& !func
->info() &&
1464 !(func
->attrs() & AttrNative
)) {
1465 // need to sync m_pc to pc for backtraces/re-entry
1467 const Func::ParamInfoVec
& paramInfo
= func
->params();
1468 for (int i
= ar
->numArgs(); i
< nparams
; ++i
) {
1469 Offset dvInitializer
= paramInfo
[i
].funcletOff();
1470 if (dvInitializer
== InvalidAbsoluteOffset
) {
1471 const char* name
= func
->name()->data();
1473 raise_warning(Strings::MISSING_ARGUMENT
, name
, i
);
1475 raise_warning(Strings::MISSING_ARGUMENTS
, name
, nparams
, i
);
1484 void VMExecutionContext::syncGdbState() {
1485 if (RuntimeOption::EvalJit
&& !RuntimeOption::EvalJitNoGdb
) {
1486 tx()->getDebugInfo()->debugSync();
1490 void VMExecutionContext::enterVMPrologue(ActRec
* enterFnAr
) {
1492 Stats::inc(Stats::VMEnter
);
1493 if (ThreadInfo::s_threadInfo
->m_reqInjectionData
.getJit()) {
1494 int np
= enterFnAr
->m_func
->numParams();
1495 int na
= enterFnAr
->numArgs();
1496 if (na
> np
) na
= np
+ 1;
1497 Transl::TCA start
= enterFnAr
->m_func
->getPrologue(na
);
1498 tx()->enterTCAtPrologue(enterFnAr
, start
);
1500 if (prepareFuncEntry(enterFnAr
, m_pc
)) {
1501 enterVMWork(enterFnAr
);
1506 void VMExecutionContext::enterVMWork(ActRec
* enterFnAr
) {
1507 Transl::TCA start
= nullptr;
1509 if (!EventHook::FunctionEnter(enterFnAr
, EventHook::NormalFunc
)) return;
1510 checkStack(m_stack
, enterFnAr
->m_func
);
1511 start
= enterFnAr
->m_func
->getFuncBody();
1513 Stats::inc(Stats::VMEnter
);
1514 if (ThreadInfo::s_threadInfo
->m_reqInjectionData
.getJit()) {
1515 (void) m_fp
->unit()->offsetOf(m_pc
); /* assert */
1518 tx()->enterTCAfterPrologue(start
);
1520 SrcKey
sk(m_fp
->func(), m_pc
);
1521 tx()->enterTCAtSrcKey(sk
);
1528 void VMExecutionContext::enterVM(TypedValue
* retval
, ActRec
* ar
) {
1529 DEBUG_ONLY
int faultDepth
= m_faults
.size();
1530 SCOPE_EXIT
{ assert(m_faults
.size() == faultDepth
); };
1533 ar
->m_savedRip
= reinterpret_cast<uintptr_t>(tx()->uniqueStubs
.callToExit
);
1534 assert(isReturnHelper(ar
->m_savedRip
));
1537 * When an exception is propagating, each nesting of the VM is
1538 * responsible for unwinding its portion of the execution stack, and
1539 * finding user handlers if it is a catchable exception.
1541 * This try/catch is where all this logic is centered. The actual
1542 * unwinding happens under exception_handler in unwind.cpp, which
1543 * returns a UnwindAction here to indicate what to do next.
1545 * Either we'll enter the VM loop again at a user error/fault
1546 * handler, or propagate the exception to a less-nested VM.
1553 if (m_fp
&& !ar
->m_varEnv
) {
1554 enterVMPrologue(ar
);
1555 } else if (prepareFuncEntry(ar
, m_pc
)) {
1562 // Everything succeeded with no exception---return to the previous
1563 // VM nesting level.
1564 *retval
= *m_stack
.topTV();
1569 always_assert(Transl::tl_regState
== Transl::VMRegState::CLEAN
);
1570 auto const action
= exception_handler();
1571 if (action
== UnwindAction::ResumeVM
) {
1574 always_assert(action
== UnwindAction::Propagate
);
1578 * Here we have to propagate an exception out of this VM's nesting
1582 if (g_vmContext
->m_nestedVMs
.empty()) {
1587 assert(m_faults
.size() > 0);
1588 Fault fault
= m_faults
.back();
1589 m_faults
.pop_back();
1591 switch (fault
.m_faultType
) {
1592 case Fault::Type::UserException
:
1594 Object obj
= fault
.m_userException
;
1595 fault
.m_userException
->decRefCount();
1598 case Fault::Type::CppException
:
1599 // throwException() will take care of deleting heap-allocated
1600 // exception object for us
1601 fault
.m_cppException
->throwException();
1608 void VMExecutionContext::reenterVM(TypedValue
* retval
,
1610 TypedValue
* savedSP
) {
1613 VMState savedVM
= { getPC(), getFP(), m_firstAR
, savedSP
};
1614 TRACE(3, "savedVM: %p %p %p %p\n", m_pc
, m_fp
, m_firstAR
, savedSP
);
1615 pushVMState(savedVM
, ar
);
1616 assert(m_nestedVMs
.size() >= 1);
1618 enterVM(retval
, ar
);
1624 TRACE(1, "Reentry: exit fp %p pc %p\n", m_fp
, m_pc
);
1627 void VMExecutionContext::invokeFunc(TypedValue
* retval
,
1630 ObjectData
* this_
/* = NULL */,
1631 Class
* cls
/* = NULL */,
1632 VarEnv
* varEnv
/* = NULL */,
1633 StringData
* invName
/* = NULL */,
1634 InvokeFlags flags
/* = InvokeNormal */) {
1637 // If this is a regular function, this_ and cls must be NULL
1638 assert(f
->preClass() || f
->isPseudoMain() || (!this_
&& !cls
));
1639 // If this is a method, either this_ or cls must be non-NULL
1640 assert(!f
->preClass() || (this_
|| cls
));
1641 // If this is a static method, this_ must be NULL
1642 assert(!(f
->attrs() & AttrStatic
&& !f
->isClosureBody()) ||
1644 // invName should only be non-NULL if we are calling __call or
1646 assert(!invName
|| f
->name()->isame(s___call
.get()) ||
1647 f
->name()->isame(s___callStatic
.get()));
1648 // If a variable environment is being inherited then params must be empty
1649 assert(!varEnv
|| params
.empty());
1653 bool isMagicCall
= (invName
!= nullptr);
1655 if (this_
!= nullptr) {
1656 this_
->incRefCount();
1658 Cell
* savedSP
= m_stack
.top();
1660 if (f
->attrs() & AttrPhpLeafFn
||
1661 f
->numParams() > kStackCheckReenterPadding
- kNumActRecCells
) {
1662 checkStack(m_stack
, f
);
1665 if (flags
& InvokePseudoMain
) {
1666 assert(f
->isPseudoMain() && !params
.get());
1667 Unit
* toMerge
= f
->unit();
1669 if (toMerge
->isMergeOnly()) {
1670 *retval
= *toMerge
->getMainReturn();
1675 ActRec
* ar
= m_stack
.allocA();
1684 ar
->setThis(nullptr);
1689 ar
->initNumArgs(params
.size());
1691 ar
->setVarEnv(varEnv
);
1694 if (m_fp
== nullptr) {
1695 TRACE(1, "Reentry: enter %s(%p) from top-level\n",
1696 f
->name()->data(), ar
);
1698 TRACE(1, "Reentry: enter %s(pc %p ar %p) from %s(%p)\n",
1699 f
->name()->data(), m_pc
, ar
,
1700 m_fp
->m_func
? m_fp
->m_func
->name()->data() : "unknownBuiltin", m_fp
);
1704 ArrayData
*arr
= params
.get();
1706 // Put the method name into the location of the first parameter. We
1707 // are transferring ownership, so no need to incRef/decRef here.
1708 m_stack
.pushStringNoRc(invName
);
1709 // Put array of arguments into the location of the second parameter
1710 m_stack
.pushArray(arr
);
1712 const int numParams
= f
->numParams();
1713 const int numExtraArgs
= arr
->size() - numParams
;
1714 ExtraArgs
* extraArgs
= nullptr;
1715 if (numExtraArgs
> 0 && (f
->attrs() & AttrMayUseVV
)) {
1716 extraArgs
= ExtraArgs::allocateUninit(numExtraArgs
);
1717 ar
->setExtraArgs(extraArgs
);
1720 for (ssize_t i
= arr
->iter_begin();
1721 i
!= ArrayData::invalid_index
;
1722 i
= arr
->iter_advance(i
), ++paramId
) {
1723 TypedValue
*from
= arr
->nvGetValueRef(i
);
1725 if (LIKELY(paramId
< numParams
)) {
1726 to
= m_stack
.allocTV();
1728 if (!(f
->attrs() & AttrMayUseVV
)) {
1729 // Discard extra arguments, since the function cannot
1730 // possibly use them.
1731 assert(extraArgs
== nullptr);
1732 ar
->setNumArgs(numParams
);
1735 assert(extraArgs
!= nullptr && numExtraArgs
> 0);
1736 // VarEnv expects the extra args to be in "reverse" order
1737 // (i.e. the last extra arg has the lowest address)
1738 to
= extraArgs
->getExtraArg(paramId
- numParams
);
1740 if (LIKELY(!f
->byRef(paramId
))) {
1741 cellDup(*tvToCell(from
), *to
);
1742 } else if (from
->m_type
== KindOfRef
&& from
->m_data
.pref
->m_count
>= 2) {
1745 if (flags
& InvokeCuf
) {
1747 raise_warning("Parameter %d to %s() expected to be "
1748 "a reference, value given",
1749 paramId
+ 1, f
->fullName()->data());
1751 // If an exception is thrown by the user error handler,
1752 // we need to clean up the stack
1754 invokeFuncCleanupHelper(retval
, ar
, paramId
);
1757 if (skipCufOnInvalidParams
) {
1759 invokeFuncCleanupHelper(retval
, ar
, paramId
);
1763 cellDup(*tvToCell(from
), *to
);
1769 reenterVM(retval
, ar
, savedSP
);
1771 assert(m_nestedVMs
.size() == 0);
1772 enterVM(retval
, ar
);
1776 void VMExecutionContext::invokeFuncCleanupHelper(TypedValue
* retval
,
1778 int numArgsPushed
) {
1779 assert(retval
&& ar
);
1780 const int numFormalParams
= ar
->m_func
->numParams();
1781 ExtraArgs
* extraArgs
= ar
->hasExtraArgs() ? ar
->getExtraArgs() : nullptr;
1785 numArgsPushed
> numFormalParams
? numArgsPushed
- numFormalParams
: 0;
1786 ExtraArgs::deallocate(extraArgs
, n
);
1787 ar
->m_varEnv
= nullptr;
1790 while (numArgsPushed
> 0) {
1795 tvWriteNull(retval
);
1798 void VMExecutionContext::invokeFuncFew(TypedValue
* retval
,
1801 StringData
* invName
,
1803 const TypedValue
* argv
) {
1806 // If this is a regular function, this_ and cls must be NULL
1807 assert(f
->preClass() || !thisOrCls
);
1808 // If this is a method, either this_ or cls must be non-NULL
1809 assert(!f
->preClass() || thisOrCls
);
1810 // If this is a static method, this_ must be NULL
1811 assert(!(f
->attrs() & AttrStatic
&& !f
->isClosureBody()) ||
1812 !ActRec::decodeThis(thisOrCls
));
1813 // invName should only be non-NULL if we are calling __call or
1815 assert(!invName
|| f
->name()->isame(s___call
.get()) ||
1816 f
->name()->isame(s___callStatic
.get()));
1820 if (ObjectData
* thiz
= ActRec::decodeThis(thisOrCls
)) {
1821 thiz
->incRefCount();
1823 Cell
* savedSP
= m_stack
.top();
1824 if (f
->attrs() & AttrPhpLeafFn
||
1825 argc
> kStackCheckReenterPadding
- kNumActRecCells
) {
1826 checkStack(m_stack
, f
);
1828 ActRec
* ar
= m_stack
.allocA();
1832 ar
->m_this
= (ObjectData
*)thisOrCls
;
1833 ar
->initNumArgs(argc
);
1834 if (UNLIKELY(invName
!= nullptr)) {
1835 ar
->setInvName(invName
);
1837 ar
->m_varEnv
= nullptr;
1841 if (m_fp
== nullptr) {
1842 TRACE(1, "Reentry: enter %s(%p) from top-level\n",
1843 f
->name()->data(), ar
);
1845 TRACE(1, "Reentry: enter %s(pc %p ar %p) from %s(%p)\n",
1846 f
->name()->data(), m_pc
, ar
,
1847 m_fp
->m_func
? m_fp
->m_func
->name()->data() : "unknownBuiltin", m_fp
);
1851 for (ssize_t i
= 0; i
< argc
; ++i
) {
1852 const TypedValue
*from
= &argv
[i
];
1853 TypedValue
*to
= m_stack
.allocTV();
1854 if (LIKELY(from
->m_type
!= KindOfRef
|| !f
->byRef(i
))) {
1855 cellDup(*tvToCell(from
), *to
);
1862 reenterVM(retval
, ar
, savedSP
);
1864 assert(m_nestedVMs
.size() == 0);
1865 enterVM(retval
, ar
);
1869 void VMExecutionContext::invokeContFunc(const Func
* f
,
1871 Cell
* param
/* = NULL */) {
1877 this_
->incRefCount();
1879 Cell
* savedSP
= m_stack
.top();
1881 // no need to check stack due to ReenterPadding
1882 assert(kStackCheckReenterPadding
- kNumActRecCells
>= 1);
1884 ActRec
* ar
= m_stack
.allocA();
1888 ar
->initNumArgs(param
!= nullptr ? 1 : 0);
1890 ar
->setVarEnv(nullptr);
1892 if (param
!= nullptr) {
1893 cellDup(*param
, *m_stack
.allocC());
1897 reenterVM(&retval
, ar
, savedSP
);
1898 // Codegen for generator functions guarantees that they will return null
1899 assert(IS_NULL_TYPE(retval
.m_type
));
1902 void VMExecutionContext::invokeUnit(TypedValue
* retval
, Unit
* unit
) {
1903 Func
* func
= unit
->getMain();
1904 invokeFunc(retval
, func
, null_array
, nullptr, nullptr,
1905 m_globalVarEnv
, nullptr, InvokePseudoMain
);
1909 * Given a pointer to a VM frame, returns the previous VM frame in the call
1910 * stack. This function will also pass back by reference the previous PC (if
1911 * prevPc is non-null) and the previous SP (if prevSp is non-null).
1913 * If there is no previous VM frame, this function returns NULL and does not
1914 * set prevPc and prevSp.
1916 ActRec
* VMExecutionContext::getPrevVMState(const ActRec
* fp
,
1917 Offset
* prevPc
/* = NULL */,
1918 TypedValue
** prevSp
/* = NULL */,
1919 bool* fromVMEntry
/* = NULL */) {
1920 if (fp
== nullptr) {
1923 ActRec
* prevFp
= fp
->arGetSfp();
1926 if (UNLIKELY(fp
->m_func
->isGenerator())) {
1927 *prevSp
= (TypedValue
*)prevFp
- prevFp
->m_func
->numSlotsInFrame();
1929 *prevSp
= (TypedValue
*)&fp
[1];
1932 if (prevPc
) *prevPc
= prevFp
->m_func
->base() + fp
->m_soff
;
1933 if (fromVMEntry
) *fromVMEntry
= false;
1936 // Linear search from end of m_nestedVMs. In practice, we're probably
1937 // looking for something recently pushed.
1938 int i
= m_nestedVMs
.size() - 1;
1939 for (; i
>= 0; --i
) {
1940 if (m_nestedVMs
[i
].m_entryFP
== fp
) break;
1942 if (i
== -1) return nullptr;
1943 const VMState
& vmstate
= m_nestedVMs
[i
].m_savedState
;
1944 prevFp
= vmstate
.fp
;
1946 assert(prevFp
->m_func
->unit());
1947 if (prevSp
) *prevSp
= vmstate
.sp
;
1948 if (prevPc
) *prevPc
= prevFp
->m_func
->unit()->offsetOf(vmstate
.pc
);
1949 if (fromVMEntry
) *fromVMEntry
= true;
1953 Array
VMExecutionContext::debugBacktrace(bool skip
/* = false */,
1954 bool withSelf
/* = false */,
1955 bool withThis
/* = false */,
1957 parserFrame
/* = NULL */,
1958 bool ignoreArgs
/* = false */,
1959 int limit
/* = 0 */) {
1960 Array bt
= Array::Create();
1962 // If there is a parser frame, put it at the beginning of
1967 .set(s_file
, parserFrame
->filename
, true)
1968 .set(s_line
, parserFrame
->lineNumber
, true)
1975 // If there are no VM frames, we're done
1980 ActRec
* fp
= nullptr;
1983 // Get the fp and pc of the top frame (possibly skipping one frame)
1986 fp
= getPrevVMState(getFP(), &pc
);
1988 // We skipped over the only VM frame, we're done
1993 Unit
*unit
= getFP()->m_func
->unit();
1995 pc
= unit
->offsetOf(m_pc
);
1998 // Handle the top frame
2000 // Builtins don't have a file and line number
2001 if (!fp
->m_func
->isBuiltin()) {
2002 Unit
*unit
= fp
->m_func
->unit();
2004 const char* filename
= unit
->filepath()->data();
2005 if (fp
->m_func
->originalFilename()) {
2006 filename
= fp
->m_func
->originalFilename()->data();
2011 ArrayInit
frame(parserFrame
? 4 : 2);
2012 frame
.set(s_file
, filename
, true);
2013 frame
.set(s_line
, unit
->getLineNumber(off
), true);
2015 frame
.set(s_function
, s_include
, true);
2016 frame
.set(s_args
, Array::Create(parserFrame
->filename
), true);
2018 bt
.append(frame
.toVariant());
2024 // Handle the subsequent VM frames
2026 for (ActRec
* prevFp
= getPrevVMState(fp
, &prevPc
);
2027 fp
!= nullptr && (limit
== 0 || depth
< limit
);
2028 fp
= prevFp
, pc
= prevPc
, prevFp
= getPrevVMState(fp
, &prevPc
)) {
2029 // do not capture frame for HPHP only functions
2030 if (fp
->m_func
->isNoInjection()) {
2036 auto const curUnit
= fp
->m_func
->unit();
2037 auto const curOp
= toOp(*curUnit
->at(pc
));
2038 auto const isReturning
= curOp
== OpRetC
|| curOp
== OpRetV
;
2040 // Builtins and generators don't have a file and line number
2041 if (prevFp
&& !prevFp
->m_func
->isBuiltin() && !fp
->m_func
->isGenerator()) {
2042 auto const prevUnit
= prevFp
->m_func
->unit();
2043 auto prevFile
= prevUnit
->filepath();
2044 if (prevFp
->m_func
->originalFilename()) {
2045 prevFile
= prevFp
->m_func
->originalFilename();
2048 frame
.set(s_file
, const_cast<StringData
*>(prevFile
), true);
2050 // In the normal method case, the "saved pc" for line number printing is
2051 // pointing at the cell conversion (Unbox/Pop) instruction, not the call
2052 // itself. For multi-line calls, this instruction is associated with the
2053 // subsequent line which results in an off-by-n. We're subtracting one
2054 // in order to look up the line associated with the FCall/FCallArray
2055 // instruction. Exception handling and the other opcodes (ex. BoxR)
2056 // already do the right thing. The emitter associates object access with
2057 // the subsequent expression and this would be difficult to modify.
2058 auto const opAtPrevPc
=
2059 toOp(*reinterpret_cast<const Opcode
*>(prevUnit
->at(prevPc
)));
2060 Offset pcAdjust
= 0;
2061 if (opAtPrevPc
== OpPopR
|| opAtPrevPc
== OpUnboxR
) {
2065 prevFp
->m_func
->unit()->getLineNumber(prevPc
- pcAdjust
),
2069 // check for include
2070 String funcname
= const_cast<StringData
*>(fp
->m_func
->name());
2071 if (fp
->m_func
->isGenerator()) {
2072 // retrieve the original function name from the inner continuation
2073 funcname
= frame_continuation(fp
)->t_getorigfuncname();
2076 if (fp
->m_func
->isClosureBody()) {
2077 static StringData
* s_closure_label
=
2078 StringData::GetStaticString("{closure}");
2079 funcname
= s_closure_label
;
2082 // check for pseudomain
2083 if (funcname
->empty()) {
2084 if (!prevFp
) continue;
2085 funcname
= s_include
;
2088 frame
.set(s_function
, funcname
, true);
2090 if (!funcname
.same(s_include
)) {
2091 // Closures have an m_this but they aren't in object context
2092 Class
* ctx
= arGetContextClass(fp
);
2093 if (ctx
!= nullptr && !fp
->m_func
->isClosureBody()) {
2094 frame
.set(s_class
, ctx
->name()->data(), true);
2095 if (fp
->hasThis() && !isReturning
) {
2097 frame
.set(s_object
, Object(fp
->getThis()), true);
2099 frame
.set(s_type
, "->", true);
2101 frame
.set(s_type
, "::", true);
2106 Array args
= Array::Create();
2109 } else if (funcname
.same(s_include
)) {
2111 args
.append(const_cast<StringData
*>(curUnit
->filepath()));
2112 frame
.set(s_args
, args
, true);
2114 } else if (!RuntimeOption::EnableArgsInBacktraces
|| isReturning
) {
2115 // Provide an empty 'args' array to be consistent with hphpc
2116 frame
.set(s_args
, args
, true);
2118 int nparams
= fp
->m_func
->numParams();
2119 int nargs
= fp
->numArgs();
2120 /* builtin extra args are not stored in varenv */
2121 if (nargs
<= nparams
) {
2122 for (int i
= 0; i
< nargs
; i
++) {
2123 TypedValue
*arg
= frame_local(fp
, i
);
2124 args
.append(tvAsVariant(arg
));
2128 for (i
= 0; i
< nparams
; i
++) {
2129 TypedValue
*arg
= frame_local(fp
, i
);
2130 args
.append(tvAsVariant(arg
));
2132 for (; i
< nargs
; i
++) {
2133 TypedValue
*arg
= fp
->getExtraArg(i
- nparams
);
2134 args
.append(tvAsVariant(arg
));
2137 frame
.set(s_args
, args
, true);
2140 bt
.append(frame
.toVariant());
2146 MethodInfoVM::~MethodInfoVM() {
2147 for (std::vector
<const ClassInfo::ParameterInfo
*>::iterator it
=
2148 parameters
.begin(); it
!= parameters
.end(); ++it
) {
2149 if ((*it
)->value
!= nullptr) {
2150 free((void*)(*it
)->value
);
2155 ClassInfoVM::~ClassInfoVM() {
2156 destroyMembers(m_methodsVec
);
2157 destroyMapValues(m_properties
);
2158 destroyMapValues(m_constants
);
2161 Array
VMExecutionContext::getUserFunctionsInfo() {
2162 // Return an array of all user-defined function names. This method is used to
2163 // support get_defined_functions().
2164 return Unit::getUserFunctions();
2167 const ClassInfo::MethodInfo
* VMExecutionContext::findFunctionInfo(
2169 StringIMap
<AtomicSmartPtr
<MethodInfoVM
> >::iterator it
=
2170 m_functionInfos
.find(name
);
2171 if (it
== m_functionInfos
.end()) {
2172 Func
* func
= Unit::loadFunc(name
.get());
2173 if (func
== nullptr || func
->builtinFuncPtr()) {
2176 AtomicSmartPtr
<MethodInfoVM
> &m
= m_functionInfos
[name
];
2177 m
= new MethodInfoVM();
2178 func
->getFuncInfo(m
.get());
2181 return it
->second
.get();
2185 const ClassInfo
* VMExecutionContext::findClassInfo(CStrRef name
) {
2186 if (name
->empty()) return nullptr;
2187 StringIMap
<AtomicSmartPtr
<ClassInfoVM
> >::iterator it
=
2188 m_classInfos
.find(name
);
2189 if (it
== m_classInfos
.end()) {
2190 Class
* cls
= Unit::lookupClass(name
.get());
2191 if (cls
== nullptr) return nullptr;
2192 if (cls
->clsInfo()) return cls
->clsInfo();
2193 if (cls
->attrs() & (AttrInterface
| AttrTrait
)) {
2194 // If the specified name matches with something that is not formally
2195 // a class, return NULL
2198 AtomicSmartPtr
<ClassInfoVM
> &c
= m_classInfos
[name
];
2199 c
= new ClassInfoVM();
2200 cls
->getClassInfo(c
.get());
2203 return it
->second
.get();
2207 const ClassInfo
* VMExecutionContext::findInterfaceInfo(CStrRef name
) {
2208 StringIMap
<AtomicSmartPtr
<ClassInfoVM
> >::iterator it
=
2209 m_interfaceInfos
.find(name
);
2210 if (it
== m_interfaceInfos
.end()) {
2211 Class
* cls
= Unit::lookupClass(name
.get());
2212 if (cls
== nullptr) return nullptr;
2213 if (cls
->clsInfo()) return cls
->clsInfo();
2214 if (!(cls
->attrs() & AttrInterface
)) {
2215 // If the specified name matches with something that is not formally
2216 // an interface, return NULL
2219 AtomicSmartPtr
<ClassInfoVM
> &c
= m_interfaceInfos
[name
];
2220 c
= new ClassInfoVM();
2221 cls
->getClassInfo(c
.get());
2224 return it
->second
.get();
2228 const ClassInfo
* VMExecutionContext::findTraitInfo(CStrRef name
) {
2229 StringIMap
<AtomicSmartPtr
<ClassInfoVM
> >::iterator it
=
2230 m_traitInfos
.find(name
);
2231 if (it
!= m_traitInfos
.end()) {
2232 return it
->second
.get();
2234 Class
* cls
= Unit::lookupClass(name
.get());
2235 if (cls
== nullptr) return nullptr;
2236 if (cls
->clsInfo()) return cls
->clsInfo();
2237 if (!(cls
->attrs() & AttrTrait
)) {
2240 AtomicSmartPtr
<ClassInfoVM
> &classInfo
= m_traitInfos
[name
];
2241 classInfo
= new ClassInfoVM();
2242 cls
->getClassInfo(classInfo
.get());
2243 return classInfo
.get();
2246 const ClassInfo::ConstantInfo
* VMExecutionContext::findConstantInfo(
2248 TypedValue
* tv
= Unit::lookupCns(name
.get());
2249 if (tv
== nullptr) {
2252 ConstInfoMap::const_iterator it
= m_constInfo
.find(name
.get());
2253 if (it
!= m_constInfo
.end()) {
2256 StringData
* key
= StringData::GetStaticString(name
.get());
2257 ClassInfo::ConstantInfo
* ci
= new ClassInfo::ConstantInfo();
2258 ci
->name
= *(const String
*)&key
;
2261 ci
->setValue(tvAsCVarRef(tv
));
2262 m_constInfo
[key
] = ci
;
2266 HPHP::Eval::PhpFile
* VMExecutionContext::lookupPhpFile(StringData
* path
,
2267 const char* currentDir
,
2268 bool* initial_opt
) {
2270 bool &initial
= initial_opt
? *initial_opt
: init
;
2274 String spath
= Eval::resolveVmInclude(path
, currentDir
, &s
);
2275 if (spath
.isNull()) return nullptr;
2277 // Check if this file has already been included.
2278 EvaledFilesMap::const_iterator it
= m_evaledFiles
.find(spath
.get());
2279 HPHP::Eval::PhpFile
* efile
= nullptr;
2280 if (it
!= m_evaledFiles
.end()) {
2281 // We found it! Return the unit.
2286 // We didn't find it, so try the realpath.
2287 bool alreadyResolved
=
2288 RuntimeOption::RepoAuthoritative
||
2289 (!RuntimeOption::CheckSymLink
&& (spath
[0] == '/'));
2290 bool hasRealpath
= false;
2292 if (!alreadyResolved
) {
2293 std::string rp
= StatCache::realpath(spath
.data());
2294 if (rp
.size() != 0) {
2295 rpath
= StringData::Make(rp
.data(), rp
.size(), CopyString
);
2296 if (!rpath
.same(spath
)) {
2298 it
= m_evaledFiles
.find(rpath
.get());
2299 if (it
!= m_evaledFiles
.end()) {
2300 // We found it! Update the mapping for spath and
2303 m_evaledFiles
[spath
.get()] = efile
;
2304 spath
.get()->incRefCount();
2311 // This file hasn't been included yet, so we need to parse the file
2312 efile
= HPHP::Eval::FileRepository::checkoutFile(
2313 hasRealpath
? rpath
.get() : spath
.get(), s
);
2314 if (efile
&& initial_opt
) {
2315 // if initial_opt is not set, this shouldnt be recorded as a
2316 // per request fetch of the file.
2317 if (Transl::TargetCache::testAndSetBit(efile
->getId())) {
2320 // if parsing was successful, update the mappings for spath and
2321 // rpath (if it exists).
2322 m_evaledFiles
[spath
.get()] = efile
;
2323 spath
.get()->incRefCount();
2324 // Don't incRef efile; checkoutFile() already counted it.
2326 m_evaledFiles
[rpath
.get()] = efile
;
2327 rpath
.get()->incRefCount();
2329 DEBUGGER_ATTACHED_ONLY(phpDebuggerFileLoadHook(efile
));
2334 Unit
* VMExecutionContext::evalInclude(StringData
* path
,
2335 const StringData
* curUnitFilePath
,
2337 namespace fs
= boost::filesystem
;
2338 HPHP::Eval::PhpFile
* efile
= nullptr;
2339 if (curUnitFilePath
) {
2340 fs::path
currentUnit(curUnitFilePath
->data());
2341 fs::path
currentDir(currentUnit
.branch_path());
2342 efile
= lookupPhpFile(path
, currentDir
.string().c_str(), initial
);
2344 efile
= lookupPhpFile(path
, "", initial
);
2347 return efile
->unit();
2352 HPHP::Unit
* VMExecutionContext::evalIncludeRoot(
2353 StringData
* path
, InclOpFlags flags
, bool* initial
) {
2354 HPHP::Eval::PhpFile
* efile
= lookupIncludeRoot(path
, flags
, initial
);
2355 return efile
? efile
->unit() : 0;
2358 HPHP::Eval::PhpFile
* VMExecutionContext::lookupIncludeRoot(StringData
* path
,
2363 if ((flags
& InclOpRelative
)) {
2364 namespace fs
= boost::filesystem
;
2365 if (!unit
) unit
= getFP()->m_func
->unit();
2366 fs::path
currentUnit(unit
->filepath()->data());
2367 fs::path
currentDir(currentUnit
.branch_path());
2368 absPath
= currentDir
.string() + '/';
2369 TRACE(2, "lookupIncludeRoot(%s): relative -> %s\n",
2373 assert(flags
& InclOpDocRoot
);
2374 absPath
= SourceRootInfo::GetCurrentPhpRoot();
2375 TRACE(2, "lookupIncludeRoot(%s): docRoot -> %s\n",
2380 absPath
+= StrNR(path
);
2382 EvaledFilesMap::const_iterator it
= m_evaledFiles
.find(absPath
.get());
2383 if (it
!= m_evaledFiles
.end()) {
2384 if (initial
) *initial
= false;
2388 return lookupPhpFile(absPath
.get(), "", initial
);
2392 Instantiate hoistable classes and functions.
2393 If there is any more work left to do, setup a
2394 new frame ready to execute the pseudomain.
2396 return true iff the pseudomain needs to be executed.
2398 bool VMExecutionContext::evalUnit(Unit
* unit
, PC
& pc
, int funcType
) {
2401 if (unit
->isMergeOnly()) {
2402 Stats::inc(Stats::PseudoMain_Skipped
);
2403 *m_stack
.allocTV() = *unit
->getMainReturn();
2406 Stats::inc(Stats::PseudoMain_Executed
);
2408 ActRec
* ar
= m_stack
.allocA();
2409 assert((uintptr_t)&ar
->m_func
< (uintptr_t)&ar
->m_r
);
2410 Class
* cls
= liveClass();
2411 if (m_fp
->hasThis()) {
2412 ObjectData
*this_
= m_fp
->getThis();
2413 this_
->incRefCount();
2415 } else if (m_fp
->hasClass()) {
2416 ar
->setClass(m_fp
->getClass());
2418 ar
->setThis(nullptr);
2420 Func
* func
= unit
->getMain(cls
);
2421 assert(!func
->info());
2422 assert(!func
->isGenerator());
2426 assert(!m_fp
->hasInvName());
2428 ar
->m_soff
= uintptr_t(m_fp
->m_func
->unit()->offsetOf(pc
) -
2429 m_fp
->m_func
->base());
2430 ar
->m_savedRip
= reinterpret_cast<uintptr_t>(tx()->uniqueStubs
.retHelper
);
2431 assert(isReturnHelper(ar
->m_savedRip
));
2432 pushLocalsAndIterators(func
);
2433 if (!m_fp
->hasVarEnv()) {
2434 m_fp
->setVarEnv(VarEnv::createLocalOnStack(m_fp
));
2436 ar
->m_varEnv
= m_fp
->m_varEnv
;
2437 ar
->m_varEnv
->attach(ar
);
2440 pc
= func
->getEntry();
2442 bool ret
= EventHook::FunctionEnter(m_fp
, funcType
);
2448 s_php_namespace("<?php namespace "),
2449 s_curly_return(" { return "),
2450 s_semicolon_curly("; }"),
2451 s_php_return("<?php return "),
2453 CVarRef
VMExecutionContext::getEvaledArg(const StringData
* val
,
2454 CStrRef namespacedName
) {
2455 CStrRef key
= *(String
*)&val
;
2457 if (m_evaledArgs
.get()) {
2458 CVarRef arg
= m_evaledArgs
.get()->get(key
);
2459 if (&arg
!= &null_variant
) return arg
;
2463 int pos
= namespacedName
.rfind('\\');
2465 auto ns
= namespacedName
.substr(0, pos
);
2466 code
= s_php_namespace
+ ns
+ s_curly_return
+ key
+ s_semicolon_curly
;
2468 code
= s_php_return
+ key
+ s_semicolon
;
2470 Unit
* unit
= compileEvalString(code
.get());
2471 assert(unit
!= nullptr);
2473 // Default arg values are not currently allowed to depend on class context.
2474 g_vmContext
->invokeFunc((TypedValue
*)&v
, unit
->getMain(),
2475 null_array
, nullptr, nullptr, nullptr, nullptr,
2477 Variant
&lv
= m_evaledArgs
.lvalAt(key
, AccessFlags::Key
);
2483 * Helper for function entry, including pseudo-main entry.
2486 VMExecutionContext::pushLocalsAndIterators(const Func
* func
,
2487 int nparams
/*= 0*/) {
2489 for (int i
= nparams
; i
< func
->numLocals(); i
++) {
2490 m_stack
.pushUninit();
2493 for (int i
= 0; i
< func
->numIterators(); i
++) {
2498 void VMExecutionContext::destructObjects() {
2499 if (UNLIKELY(RuntimeOption::EnableObjDestructCall
)) {
2500 while (!m_liveBCObjs
.empty()) {
2501 ObjectData
* obj
= *m_liveBCObjs
.begin();
2502 obj
->destruct(); // Let the instance remove the node.
2504 m_liveBCObjs
.clear();
2508 // Evaled units have a footprint in the TC and translation metadata. The
2509 // applications we care about tend to have few, short, stereotyped evals,
2510 // where the same code keeps getting eval'ed over and over again; so we
2511 // keep around units for each eval'ed string, so that the TC space isn't
2512 // wasted on each eval.
2513 typedef RankedCHM
<StringData
*, HPHP::Unit
*,
2514 StringDataHashCompare
,
2515 RankEvaledUnits
> EvaledUnitsMap
;
2516 static EvaledUnitsMap s_evaledUnits
;
2517 Unit
* VMExecutionContext::compileEvalString(StringData
* code
) {
2518 EvaledUnitsMap::accessor acc
;
2519 // Promote this to a static string; otherwise it may get swept
2521 code
= StringData::GetStaticString(code
);
2522 if (s_evaledUnits
.insert(acc
, code
)) {
2523 acc
->second
= compile_string(code
->data(), code
->size());
2528 CStrRef
VMExecutionContext::createFunction(CStrRef args
, CStrRef code
) {
2530 // It doesn't matter if there's a user function named __lambda_func; we only
2531 // use this name during parsing, and then change it to an impossible name
2532 // with a NUL byte before we merge it into the request's func map. This also
2533 // has the bonus feature that the value of __FUNCTION__ inside the created
2534 // function will match Zend. (Note: Zend will actually fatal if there's a
2535 // user function named __lambda_func when you call create_function. Huzzah!)
2536 static StringData
* oldName
= StringData::GetStaticString("__lambda_func");
2537 std::ostringstream codeStr
;
2538 codeStr
<< "<?php function " << oldName
->data()
2539 << "(" << args
.data() << ") {"
2540 << code
.data() << "}\n";
2541 StringData
* evalCode
= StringData::GetStaticString(codeStr
.str());
2542 Unit
* unit
= compile_string(evalCode
->data(), evalCode
->size());
2543 // Move the function to a different name.
2544 std::ostringstream newNameStr
;
2545 newNameStr
<< '\0' << "lambda_" << ++m_lambdaCounter
;
2546 StringData
* newName
= StringData::GetStaticString(newNameStr
.str());
2547 unit
->renameFunc(oldName
, newName
);
2548 m_createdFuncs
.push_back(unit
);
2551 // Technically we shouldn't have to eval the unit right now (it'll execute
2552 // the pseudo-main, which should be empty) and could get away with just
2553 // mergeFuncs. However, Zend does it this way, as proven by the fact that you
2554 // can inject code into the evaled unit's pseudo-main:
2556 // create_function('', '} echo "hi"; if (0) {');
2558 // We have to eval now to emulate this behavior.
2560 invokeFunc(&retval
, unit
->getMain(), null_array
,
2561 nullptr, nullptr, nullptr, nullptr,
2564 // __lambda_func will be the only hoistable function.
2565 // Any functions or closures defined in it will not be hoistable.
2566 Func
* lambda
= unit
->firstHoistable();
2567 return lambda
->nameRef();
2570 bool VMExecutionContext::evalPHPDebugger(TypedValue
* retval
, StringData
*code
,
2573 // The code has "<?php" prepended already
2574 Unit
* unit
= compile_string(code
->data(), code
->size());
2575 if (unit
== nullptr) {
2576 raise_error("Syntax error");
2577 tvWriteNull(retval
);
2580 // Do not JIT this unit, we are using it exactly once.
2581 unit
->setInterpretOnly();
2584 VarEnv
*varEnv
= nullptr;
2585 ActRec
*fp
= getFP();
2586 ActRec
*cfpSave
= nullptr;
2588 for (; frame
> 0; --frame
) {
2589 ActRec
* prevFp
= getPrevVMState(fp
);
2591 // To be safe in case we failed to get prevFp. This would mean we've
2592 // been asked to eval in a frame which is beyond the top of the stack.
2593 // This suggests the debugger client has made an error.
2598 if (!fp
->hasVarEnv()) {
2599 fp
->setVarEnv(VarEnv::createLocalOnHeap(fp
));
2601 varEnv
= fp
->m_varEnv
;
2602 cfpSave
= varEnv
->getCfp();
2604 ObjectData
*this_
= nullptr;
2605 // NB: the ActRec and function within the AR may have different classes. The
2606 // class in the ActRec is the type used when invoking the function (i.e.,
2607 // Derived in Derived::Foo()) while the class obtained from the function is
2608 // the type that declared the function Foo, which may be Base. We need both
2609 // the class to match any object that this function may have been invoked on,
2610 // and we need the class from the function execution is stopped in.
2611 Class
*frameClass
= nullptr;
2612 Class
*functionClass
= nullptr;
2614 if (fp
->hasThis()) {
2615 this_
= fp
->getThis();
2616 } else if (fp
->hasClass()) {
2617 frameClass
= fp
->getClass();
2619 functionClass
= fp
->m_func
->cls();
2620 phpDebuggerEvalHook(fp
->m_func
);
2623 const static StaticString
s_cppException("Hit an exception");
2624 const static StaticString
s_phpException("Hit a php exception");
2625 const static StaticString
s_exit("Hit exit");
2626 const static StaticString
s_fatal("Hit fatal");
2628 // Invoke the given PHP, possibly specialized to match the type of the
2629 // current function on the stack, optionally passing a this pointer or
2630 // class used to execute the current function.
2631 invokeFunc(retval
, unit
->getMain(functionClass
), null_array
,
2632 this_
, frameClass
, varEnv
, nullptr, InvokePseudoMain
);
2634 } catch (FatalErrorException
&e
) {
2635 g_vmContext
->write(s_fatal
);
2636 g_vmContext
->write(" : ");
2637 g_vmContext
->write(e
.getMessage().c_str());
2638 g_vmContext
->write("\n");
2639 g_vmContext
->write(ExtendedLogger::StringOfStackTrace(e
.getBackTrace()));
2640 } catch (ExitException
&e
) {
2641 g_vmContext
->write(s_exit
.data());
2642 g_vmContext
->write(" : ");
2643 std::ostringstream os
;
2644 os
<< ExitException::ExitCode
;
2645 g_vmContext
->write(os
.str());
2646 } catch (Eval::DebuggerException
&e
) {
2647 } catch (Exception
&e
) {
2648 g_vmContext
->write(s_cppException
.data());
2649 g_vmContext
->write(" : ");
2650 g_vmContext
->write(e
.getMessage().c_str());
2651 ExtendedException
* ee
= dynamic_cast<ExtendedException
*>(&e
);
2653 g_vmContext
->write("\n");
2655 ExtendedLogger::StringOfStackTrace(ee
->getBackTrace()));
2657 } catch (Object
&e
) {
2658 g_vmContext
->write(s_phpException
.data());
2659 g_vmContext
->write(" : ");
2660 g_vmContext
->write(e
->invokeToString().data());
2662 g_vmContext
->write(s_cppException
.data());
2666 // The debugger eval frame may have attached to the VarEnv from a
2667 // frame that was not the top frame, so we need to manually set
2668 // cfp back to what it was before
2669 varEnv
->setCfp(cfpSave
);
2674 void VMExecutionContext::enterDebuggerDummyEnv() {
2675 static Unit
* s_debuggerDummy
= compile_string("<?php?>", 7);
2676 // Ensure that the VM stack is completely empty (m_fp should be null)
2677 // and that we're not in a nested VM (reentrancy)
2678 assert(getFP() == nullptr);
2679 assert(m_nestedVMs
.size() == 0);
2680 assert(m_nesting
== 0);
2681 assert(m_stack
.count() == 0);
2682 ActRec
* ar
= m_stack
.allocA();
2683 ar
->m_func
= s_debuggerDummy
->getMain();
2684 ar
->setThis(nullptr);
2687 ar
->m_savedRip
= reinterpret_cast<uintptr_t>(tx()->uniqueStubs
.callToExit
);
2688 assert(isReturnHelper(ar
->m_savedRip
));
2690 m_pc
= s_debuggerDummy
->entry();
2692 m_fp
->setVarEnv(m_globalVarEnv
);
2693 m_globalVarEnv
->attach(m_fp
);
2696 void VMExecutionContext::exitDebuggerDummyEnv() {
2697 assert(m_globalVarEnv
);
2698 // Ensure that m_fp is valid
2699 assert(getFP() != nullptr);
2700 // Ensure that m_fp points to the only frame on the call stack.
2701 // In other words, make sure there are no VM frames directly below
2702 // this one and that we are not in a nested VM (reentrancy)
2703 assert(m_fp
->arGetSfp() == m_fp
);
2704 assert(m_nestedVMs
.size() == 0);
2705 assert(m_nesting
== 0);
2706 // Teardown the frame we erected by enterDebuggerDummyEnv()
2707 const Func
* func
= m_fp
->m_func
;
2709 frame_free_locals_inl_no_hook
<true>(m_fp
, func
->numLocals());
2711 m_stack
.ndiscard(func
->numSlotsInFrame());
2712 m_stack
.discardAR();
2713 // After tearing down this frame, the VM stack should be completely empty
2714 assert(m_stack
.count() == 0);
2719 // Identifies the set of return helpers that we may set m_savedRip to in an
2721 bool VMExecutionContext::isReturnHelper(uintptr_t address
) {
2722 auto tcAddr
= reinterpret_cast<Transl::TCA
>(address
);
2723 auto& u
= tx()->uniqueStubs
;
2724 return tcAddr
== u
.retHelper
||
2725 tcAddr
== u
.genRetHelper
||
2726 tcAddr
== u
.retInlHelper
||
2727 tcAddr
== u
.callToExit
;
2730 // Walk the stack and find any return address to jitted code and bash it to
2731 // the appropriate RetFromInterpreted*Frame helper. This ensures that we don't
2732 // return into jitted code and gives the system the proper chance to interpret
2733 // blacklisted tracelets.
2734 void VMExecutionContext::preventReturnsToTC() {
2735 assert(isDebuggerAttached());
2736 if (RuntimeOption::EvalJit
) {
2737 ActRec
*ar
= getFP();
2739 if (!isReturnHelper(ar
->m_savedRip
) &&
2740 (tx()->isValidCodeAddress((Transl::TCA
)ar
->m_savedRip
))) {
2741 TRACE_RB(2, "Replace RIP in fp %p, savedRip 0x%" PRIx64
", "
2742 "func %s\n", ar
, ar
->m_savedRip
,
2743 ar
->m_func
->fullName()->data());
2744 if (ar
->m_func
->isGenerator()) {
2746 reinterpret_cast<uintptr_t>(tx()->uniqueStubs
.genRetHelper
);
2749 reinterpret_cast<uintptr_t>(tx()->uniqueStubs
.retHelper
);
2751 assert(isReturnHelper(ar
->m_savedRip
));
2753 ar
= getPrevVMState(ar
);
2758 static inline StringData
* lookup_name(TypedValue
* key
) {
2759 return prepareKey(key
);
2762 static inline void lookup_var(ActRec
* fp
,
2766 name
= lookup_name(key
);
2767 const Func
* func
= fp
->m_func
;
2768 Id id
= func
->lookupVarId(name
);
2769 if (id
!= kInvalidId
) {
2770 val
= frame_local(fp
, id
);
2772 assert(!fp
->hasInvName());
2773 if (fp
->hasVarEnv()) {
2774 val
= fp
->m_varEnv
->lookup(name
);
2781 static inline void lookupd_var(ActRec
* fp
,
2785 name
= lookup_name(key
);
2786 const Func
* func
= fp
->m_func
;
2787 Id id
= func
->lookupVarId(name
);
2788 if (id
!= kInvalidId
) {
2789 val
= frame_local(fp
, id
);
2791 assert(!fp
->hasInvName());
2792 if (!fp
->hasVarEnv()) {
2793 fp
->setVarEnv(VarEnv::createLocalOnStack(fp
));
2795 val
= fp
->m_varEnv
->lookup(name
);
2796 if (val
== nullptr) {
2799 fp
->m_varEnv
->set(name
, &tv
);
2800 val
= fp
->m_varEnv
->lookup(name
);
2805 static inline void lookup_gbl(ActRec
* fp
,
2809 name
= lookup_name(key
);
2810 assert(g_vmContext
->m_globalVarEnv
);
2811 val
= g_vmContext
->m_globalVarEnv
->lookup(name
);
2814 static inline void lookupd_gbl(ActRec
* fp
,
2818 name
= lookup_name(key
);
2819 assert(g_vmContext
->m_globalVarEnv
);
2820 VarEnv
* varEnv
= g_vmContext
->m_globalVarEnv
;
2821 val
= varEnv
->lookup(name
);
2822 if (val
== nullptr) {
2825 varEnv
->set(name
, &tv
);
2826 val
= varEnv
->lookup(name
);
2830 static inline void lookup_sprop(ActRec
* fp
,
2837 assert(clsRef
->m_type
== KindOfClass
);
2838 name
= lookup_name(key
);
2839 Class
* ctx
= arGetContextClass(fp
);
2840 val
= clsRef
->m_data
.pcls
->getSProp(ctx
, name
, visible
, accessible
);
2843 static inline void lookupClsRef(TypedValue
* input
,
2845 bool decRef
= false) {
2846 const Class
* class_
= nullptr;
2847 if (IS_STRING_TYPE(input
->m_type
)) {
2848 class_
= Unit::loadClass(input
->m_data
.pstr
);
2849 if (class_
== nullptr) {
2850 output
->m_type
= KindOfNull
;
2851 raise_error(Strings::UNKNOWN_CLASS
, input
->m_data
.pstr
->data());
2853 } else if (input
->m_type
== KindOfObject
) {
2854 class_
= input
->m_data
.pobj
->getVMClass();
2856 output
->m_type
= KindOfNull
;
2857 raise_error("Cls: Expected string or object");
2860 tvRefcountedDecRef(input
);
2862 output
->m_data
.pcls
= const_cast<Class
*>(class_
);
2863 output
->m_type
= KindOfClass
;
2866 static UNUSED
int innerCount(const TypedValue
* tv
) {
2867 if (IS_REFCOUNTED_TYPE(tv
->m_type
)) {
2868 // We're using pref here arbitrarily; any refcounted union member works.
2869 return tv
->m_data
.pref
->m_count
;
2874 static inline void ratchetRefs(TypedValue
*& result
, TypedValue
& tvRef
,
2875 TypedValue
& tvRef2
) {
2876 TRACE(5, "Ratchet: result %p(k%d c%d), ref %p(k%d c%d) ref2 %p(k%d c%d)\n",
2877 result
, result
->m_type
, innerCount(result
),
2878 &tvRef
, tvRef
.m_type
, innerCount(&tvRef
),
2879 &tvRef2
, tvRef2
.m_type
, innerCount(&tvRef2
));
2880 // Due to complications associated with ArrayAccess, it is possible to acquire
2881 // a reference as a side effect of vector operation processing. Such a
2882 // reference must be retained until after the next iteration is complete.
2883 // Therefore, move the reference from tvRef to tvRef2, so that the reference
2884 // will be released one iteration later. But only do this if tvRef was used in
2885 // this iteration, otherwise we may wipe out the last reference to something
2886 // that we need to stay alive until the next iteration.
2887 if (tvRef
.m_type
!= KindOfUninit
) {
2888 if (IS_REFCOUNTED_TYPE(tvRef2
.m_type
)) {
2890 TRACE(5, "Ratchet: decref tvref2\n");
2891 tvWriteUninit(&tvRef2
);
2894 memcpy(&tvRef2
, &tvRef
, sizeof(TypedValue
));
2895 tvWriteUninit(&tvRef
);
2896 // Update result to point to relocated reference. This can be done
2897 // unconditionally here because we maintain the invariant throughout that
2898 // either tvRef is KindOfUninit, or tvRef contains a valid object that
2899 // result points to.
2900 assert(result
== &tvRef
);
2905 #define DECLARE_MEMBERHELPER_ARGS \
2906 unsigned ndiscard; \
2908 TypedValue tvScratch; \
2909 TypedValue tvLiteral; \
2912 MemberCode mcode = MEL; \
2913 TypedValue* curMember = 0;
2914 #define DECLARE_SETHELPER_ARGS DECLARE_MEMBERHELPER_ARGS
2915 #define DECLARE_GETHELPER_ARGS \
2916 DECLARE_MEMBERHELPER_ARGS \
2919 #define MEMBERHELPERPRE_ARGS \
2920 pc, ndiscard, base, tvScratch, tvLiteral, \
2921 *tvRef.asTypedValue(), *tvRef2.asTypedValue(), mcode, curMember
2923 #define MEMBERHELPERPRE_OUT \
2924 pc, ndiscard, base, tvScratch, tvLiteral, \
2925 tvRef, tvRef2, mcode, curMember
2927 // The following arguments are outputs:
2928 // pc: bytecode instruction after the vector instruction
2929 // ndiscard: number of stack elements to discard
2930 // base: ultimate result of the vector-get
2931 // tvScratch: temporary result storage
2932 // tvRef: temporary result storage
2933 // tvRef2: temporary result storage
2934 // mcode: output MemberCode for the last member if LeaveLast
2935 // curMember: output last member value one if LeaveLast; but undefined
2936 // if the last mcode == MW
2938 // If saveResult is true, then upon completion of getHelperPre(),
2939 // tvScratch contains a reference to the result (a duplicate of what
2940 // base refers to). getHelperPost<true>(...) then saves the result
2941 // to its final location.
2942 template <bool warn
,
2944 VMExecutionContext::VectorLeaveCode mleave
>
2945 inline void OPTBLD_INLINE
VMExecutionContext::getHelperPre(
2949 TypedValue
& tvScratch
,
2950 TypedValue
& tvLiteral
,
2954 TypedValue
*& curMember
) {
2955 memberHelperPre
<false, warn
, false, false,
2956 false, 0, mleave
, saveResult
>(MEMBERHELPERPRE_OUT
);
2959 #define GETHELPERPOST_ARGS ndiscard, tvRet, tvScratch, tvRef, tvRef2
2960 template <bool saveResult
>
2961 inline void OPTBLD_INLINE
VMExecutionContext::getHelperPost(
2962 unsigned ndiscard
, TypedValue
*& tvRet
, TypedValue
& tvScratch
,
2963 Variant
& tvRef
, Variant
& tvRef2
) {
2964 // Clean up all ndiscard elements on the stack. Actually discard
2965 // only ndiscard - 1, and overwrite the last cell with the result,
2966 // or if ndiscard is zero we actually need to allocate a cell.
2967 for (unsigned depth
= 0; depth
< ndiscard
; ++depth
) {
2968 TypedValue
* tv
= m_stack
.indTV(depth
);
2969 tvRefcountedDecRef(tv
);
2973 tvRet
= m_stack
.allocTV();
2975 m_stack
.ndiscard(ndiscard
- 1);
2976 tvRet
= m_stack
.topTV();
2980 // If tvRef wasn't just allocated, we've already decref'd it in
2982 tvCopy(tvScratch
, *tvRet
);
2986 #define GETHELPER_ARGS \
2987 pc, ndiscard, tvRet, base, tvScratch, tvLiteral, \
2988 tvRef, tvRef2, mcode, curMember
2989 inline void OPTBLD_INLINE
2990 VMExecutionContext::getHelper(PC
& pc
,
2994 TypedValue
& tvScratch
,
2995 TypedValue
& tvLiteral
,
2999 TypedValue
*& curMember
) {
3000 getHelperPre
<true, true, VectorLeaveCode::ConsumeAll
>(MEMBERHELPERPRE_ARGS
);
3001 getHelperPost
<true>(GETHELPERPOST_ARGS
);
3004 template <bool setMember
,
3009 unsigned mdepth
, // extra args on stack for set (e.g. rhs)
3010 VMExecutionContext::VectorLeaveCode mleave
,
3012 inline bool OPTBLD_INLINE
VMExecutionContext::memberHelperPre(
3013 PC
& pc
, unsigned& ndiscard
, TypedValue
*& base
,
3014 TypedValue
& tvScratch
, TypedValue
& tvLiteral
,
3015 TypedValue
& tvRef
, TypedValue
& tvRef2
,
3016 MemberCode
& mcode
, TypedValue
*& curMember
) {
3017 // The caller must move pc to the vector immediate before calling
3018 // {get, set}HelperPre.
3019 const ImmVector immVec
= ImmVector::createFromStream(pc
);
3020 const uint8_t* vec
= immVec
.vec();
3021 assert(immVec
.size() > 0);
3023 // PC needs to be advanced before we do anything, otherwise if we
3024 // raise a notice in the middle of this we could resume at the wrong
3026 pc
+= immVec
.size() + sizeof(int32_t) + sizeof(int32_t);
3029 assert(mdepth
== 0);
3034 ndiscard
= immVec
.numStackValues();
3035 int depth
= mdepth
+ ndiscard
- 1;
3036 const LocationCode lcode
= LocationCode(*vec
++);
3038 TypedValue
* loc
= nullptr;
3040 Class
* const ctx
= arGetContextClass(getFP());
3043 TypedValue
* fr
= nullptr;
3046 tvWriteUninit(&tvScratch
);
3050 loc
= frame_local_inner(m_fp
, decodeVariableSizeImm(&vec
));
3053 loc
= m_stack
.indTV(depth
--);
3058 lookupd_var(m_fp
, name
, loc
, fr
);
3060 lookup_var(m_fp
, name
, loc
, fr
);
3062 if (fr
== nullptr) {
3064 raise_notice(Strings::UNDEFINED_VARIABLE
, name
->data());
3066 tvWriteNull(&dummy
);
3075 loc
= frame_local_inner(m_fp
, decodeVariableSizeImm(&vec
));
3078 loc
= m_stack
.indTV(depth
--);
3083 lookupd_gbl(m_fp
, name
, loc
, fr
);
3085 lookup_gbl(m_fp
, name
, loc
, fr
);
3087 if (fr
== nullptr) {
3089 raise_notice(Strings::UNDEFINED_VARIABLE
, name
->data());
3091 tvWriteNull(&dummy
);
3100 cref
= m_stack
.indTV(mdepth
);
3101 pname
= m_stack
.indTV(depth
--);
3104 cref
= m_stack
.indTV(mdepth
);
3105 pname
= frame_local_inner(m_fp
, decodeVariableSizeImm(&vec
));
3109 bool visible
, accessible
;
3110 assert(cref
->m_type
== KindOfClass
);
3111 const Class
* class_
= cref
->m_data
.pcls
;
3112 StringData
* name
= lookup_name(pname
);
3113 loc
= class_
->getSProp(ctx
, name
, visible
, accessible
);
3114 if (!(visible
&& accessible
)) {
3115 raise_error("Invalid static property access: %s::%s",
3116 class_
->name()->data(),
3124 int localInd
= decodeVariableSizeImm(&vec
);
3125 loc
= frame_local_inner(m_fp
, localInd
);
3127 if (loc
->m_type
== KindOfUninit
) {
3128 raise_notice(Strings::UNDEFINED_VARIABLE
,
3129 m_fp
->m_func
->localVarName(localInd
)->data());
3136 loc
= m_stack
.indTV(depth
--);
3139 assert(m_fp
->hasThis());
3140 tvScratch
.m_type
= KindOfObject
;
3141 tvScratch
.m_data
.pobj
= m_fp
->getThis();
3145 default: not_reached();
3149 tvWriteUninit(&tvLiteral
);
3150 tvWriteUninit(&tvRef
);
3151 tvWriteUninit(&tvRef2
);
3153 // Iterate through the members.
3155 mcode
= MemberCode(*vec
++);
3156 if (memberCodeHasImm(mcode
)) {
3157 int64_t memberImm
= decodeMemberCodeImm(&vec
, mcode
);
3158 if (memberCodeImmIsString(mcode
)) {
3159 tvAsVariant(&tvLiteral
) =
3160 m_fp
->m_func
->unit()->lookupLitstrId(memberImm
);
3161 assert(!IS_REFCOUNTED_TYPE(tvLiteral
.m_type
));
3162 curMember
= &tvLiteral
;
3163 } else if (mcode
== MEI
) {
3164 tvAsVariant(&tvLiteral
) = memberImm
;
3165 curMember
= &tvLiteral
;
3167 assert(memberCodeImmIsLoc(mcode
));
3168 curMember
= frame_local_inner(m_fp
, memberImm
);
3171 curMember
= (setMember
&& mcode
== MW
) ? nullptr : m_stack
.indTV(depth
--);
3174 if (mleave
== VectorLeaveCode::LeaveLast
) {
3188 result
= ElemU(tvScratch
, tvRef
, base
, curMember
);
3189 } else if (define
) {
3190 result
= ElemD
<warn
,reffy
>(tvScratch
, tvRef
, base
, curMember
);
3192 result
= Elem
<warn
>(tvScratch
, tvRef
, base
, curMember
);
3198 result
= Prop
<warn
, define
, unset
>(tvScratch
, tvRef
, ctx
, base
,
3204 result
= NewElem(tvScratch
, tvRef
, base
);
3206 raise_error("Cannot use [] for reading");
3212 result
= nullptr; // Silence compiler warning.
3214 assert(result
!= nullptr);
3215 ratchetRefs(result
, tvRef
, tvRef2
);
3216 // Check whether an error occurred (i.e. no result was set).
3217 if (setMember
&& result
== &tvScratch
&& result
->m_type
== KindOfUninit
) {
3223 if (mleave
== VectorLeaveCode::ConsumeAll
) {
3226 if (lcode
== LSC
|| lcode
== LSL
) {
3227 assert(depth
== int(mdepth
));
3229 assert(depth
== int(mdepth
) - 1);
3236 // If requested, save a copy of the result. If base already points to
3237 // tvScratch, no reference counting is necessary, because (with the
3238 // exception of the following block), tvScratch is never populated such
3239 // that it owns a reference that must be accounted for.
3240 if (base
!= &tvScratch
) {
3241 // Acquire a reference to the result via tvDup(); base points to the
3242 // result but does not own a reference.
3243 tvDup(*base
, tvScratch
);
3250 // The following arguments are outputs: (TODO put them in struct)
3251 // pc: bytecode instruction after the vector instruction
3252 // ndiscard: number of stack elements to discard
3253 // base: ultimate result of the vector-get
3254 // tvScratch: temporary result storage
3255 // tvRef: temporary result storage
3256 // tvRef2: temporary result storage
3257 // mcode: output MemberCode for the last member if LeaveLast
3258 // curMember: output last member value one if LeaveLast; but undefined
3259 // if the last mcode == MW
3260 template <bool warn
,
3264 unsigned mdepth
, // extra args on stack for set (e.g. rhs)
3265 VMExecutionContext::VectorLeaveCode mleave
>
3266 inline bool OPTBLD_INLINE
VMExecutionContext::setHelperPre(
3267 PC
& pc
, unsigned& ndiscard
, TypedValue
*& base
,
3268 TypedValue
& tvScratch
, TypedValue
& tvLiteral
,
3269 TypedValue
& tvRef
, TypedValue
& tvRef2
,
3270 MemberCode
& mcode
, TypedValue
*& curMember
) {
3271 return memberHelperPre
<true, warn
, define
, unset
,
3272 reffy
, mdepth
, mleave
, false>(MEMBERHELPERPRE_OUT
);
3275 #define SETHELPERPOST_ARGS ndiscard, tvRef, tvRef2
3276 template <unsigned mdepth
>
3277 inline void OPTBLD_INLINE
VMExecutionContext::setHelperPost(
3278 unsigned ndiscard
, Variant
& tvRef
, Variant
& tvRef2
) {
3279 // Clean up the stack. Decref all the elements for the vector, but
3280 // leave the first mdepth (they are not part of the vector data).
3281 for (unsigned depth
= mdepth
; depth
-mdepth
< ndiscard
; ++depth
) {
3282 TypedValue
* tv
= m_stack
.indTV(depth
);
3283 tvRefcountedDecRef(tv
);
3286 // NOTE: currently the only instructions using this that have return
3287 // values on the stack also have more inputs than the -vector, so
3288 // mdepth > 0. They also always return the original top value of
3291 assert(mdepth
== 1 &&
3292 "We don't really support mdepth > 1 in setHelperPost");
3295 TypedValue
* retSrc
= m_stack
.topTV();
3296 TypedValue
* dest
= m_stack
.indTV(ndiscard
+ mdepth
- 1);
3297 assert(dest
!= retSrc
);
3298 memcpy(dest
, retSrc
, sizeof *dest
);
3302 m_stack
.ndiscard(ndiscard
);
3305 inline void OPTBLD_INLINE
VMExecutionContext::iopLowInvalid(PC
& pc
) {
3306 fprintf(stderr
, "invalid bytecode executed\n");
3310 inline void OPTBLD_INLINE
VMExecutionContext::iopNop(PC
& pc
) {
3314 inline void OPTBLD_INLINE
VMExecutionContext::iopPopC(PC
& pc
) {
3319 inline void OPTBLD_INLINE
VMExecutionContext::iopPopV(PC
& pc
) {
3324 inline void OPTBLD_INLINE
VMExecutionContext::iopPopR(PC
& pc
) {
3326 if (m_stack
.topTV()->m_type
!= KindOfRef
) {
3333 inline void OPTBLD_INLINE
VMExecutionContext::iopDup(PC
& pc
) {
3338 inline void OPTBLD_INLINE
VMExecutionContext::iopBox(PC
& pc
) {
3343 inline void OPTBLD_INLINE
VMExecutionContext::iopUnbox(PC
& pc
) {
3348 inline void OPTBLD_INLINE
VMExecutionContext::iopBoxR(PC
& pc
) {
3350 TypedValue
* tv
= m_stack
.topTV();
3351 if (tv
->m_type
!= KindOfRef
) {
3356 inline void OPTBLD_INLINE
VMExecutionContext::iopUnboxR(PC
& pc
) {
3358 if (m_stack
.topTV()->m_type
== KindOfRef
) {
3363 inline void OPTBLD_INLINE
VMExecutionContext::iopNull(PC
& pc
) {
3368 inline void OPTBLD_INLINE
VMExecutionContext::iopNullUninit(PC
& pc
) {
3370 m_stack
.pushNullUninit();
3373 inline void OPTBLD_INLINE
VMExecutionContext::iopTrue(PC
& pc
) {
3378 inline void OPTBLD_INLINE
VMExecutionContext::iopFalse(PC
& pc
) {
3380 m_stack
.pushFalse();
3383 inline void OPTBLD_INLINE
VMExecutionContext::iopFile(PC
& pc
) {
3385 const StringData
* s
= m_fp
->m_func
->unit()->filepath();
3386 m_stack
.pushStaticString(const_cast<StringData
*>(s
));
3389 inline void OPTBLD_INLINE
VMExecutionContext::iopDir(PC
& pc
) {
3391 const StringData
* s
= m_fp
->m_func
->unit()->dirpath();
3392 m_stack
.pushStaticString(const_cast<StringData
*>(s
));
3395 inline void OPTBLD_INLINE
VMExecutionContext::iopInt(PC
& pc
) {
3401 inline void OPTBLD_INLINE
VMExecutionContext::iopDouble(PC
& pc
) {
3404 m_stack
.pushDouble(d
);
3407 inline void OPTBLD_INLINE
VMExecutionContext::iopString(PC
& pc
) {
3410 m_stack
.pushStaticString(s
);
3413 inline void OPTBLD_INLINE
VMExecutionContext::iopArray(PC
& pc
) {
3416 ArrayData
* a
= m_fp
->m_func
->unit()->lookupArrayId(id
);
3417 m_stack
.pushStaticArray(a
);
3420 inline void OPTBLD_INLINE
VMExecutionContext::iopNewArray(PC
& pc
) {
3422 auto arr
= ArrayData::MakeReserve(HphpArray::SmallSize
);
3423 m_stack
.pushArrayNoRc(arr
);
3426 inline void OPTBLD_INLINE
VMExecutionContext::iopNewTuple(PC
& pc
) {
3429 // This constructor moves values, no inc/decref is necessary.
3430 auto* a
= HphpArray::MakeTuple(n
, m_stack
.topC());
3431 m_stack
.ndiscard(n
);
3432 m_stack
.pushArrayNoRc(a
);
3435 inline void OPTBLD_INLINE
VMExecutionContext::iopAddElemC(PC
& pc
) {
3437 Cell
* c1
= m_stack
.topC();
3438 Cell
* c2
= m_stack
.indC(1);
3439 Cell
* c3
= m_stack
.indC(2);
3440 if (c3
->m_type
!= KindOfArray
) {
3441 raise_error("AddElemC: $3 must be an array");
3443 if (c2
->m_type
== KindOfInt64
) {
3444 cellAsVariant(*c3
).asArrRef().set(c2
->m_data
.num
, tvAsCVarRef(c1
));
3446 cellAsVariant(*c3
).asArrRef().set(tvAsCVarRef(c2
), tvAsCVarRef(c1
));
3452 inline void OPTBLD_INLINE
VMExecutionContext::iopAddElemV(PC
& pc
) {
3454 Ref
* r1
= m_stack
.topV();
3455 Cell
* c2
= m_stack
.indC(1);
3456 Cell
* c3
= m_stack
.indC(2);
3457 if (c3
->m_type
!= KindOfArray
) {
3458 raise_error("AddElemV: $3 must be an array");
3460 if (c2
->m_type
== KindOfInt64
) {
3461 cellAsVariant(*c3
).asArrRef().set(c2
->m_data
.num
, ref(tvAsCVarRef(r1
)));
3463 cellAsVariant(*c3
).asArrRef().set(tvAsCVarRef(c2
), ref(tvAsCVarRef(r1
)));
3469 inline void OPTBLD_INLINE
VMExecutionContext::iopAddNewElemC(PC
& pc
) {
3471 Cell
* c1
= m_stack
.topC();
3472 Cell
* c2
= m_stack
.indC(1);
3473 if (c2
->m_type
!= KindOfArray
) {
3474 raise_error("AddNewElemC: $2 must be an array");
3476 cellAsVariant(*c2
).asArrRef().append(tvAsCVarRef(c1
));
3480 inline void OPTBLD_INLINE
VMExecutionContext::iopAddNewElemV(PC
& pc
) {
3482 Ref
* r1
= m_stack
.topV();
3483 Cell
* c2
= m_stack
.indC(1);
3484 if (c2
->m_type
!= KindOfArray
) {
3485 raise_error("AddNewElemV: $2 must be an array");
3487 cellAsVariant(*c2
).asArrRef().append(ref(tvAsCVarRef(r1
)));
3491 inline void OPTBLD_INLINE
VMExecutionContext::iopNewCol(PC
& pc
) {
3497 case Collection::VectorType
: obj
= NEWOBJ(c_Vector
)(); break;
3498 case Collection::MapType
: obj
= NEWOBJ(c_Map
)(); break;
3499 case Collection::StableMapType
: obj
= NEWOBJ(c_StableMap
)(); break;
3500 case Collection::SetType
: obj
= NEWOBJ(c_Set
)(); break;
3501 case Collection::PairType
: obj
= NEWOBJ(c_Pair
)(); break;
3504 raise_error("NewCol: Invalid collection type");
3507 // Reserve enough room for nElms elements in advance
3509 collectionReserve(obj
, nElms
);
3511 m_stack
.pushObject(obj
);
3514 inline void OPTBLD_INLINE
VMExecutionContext::iopColAddNewElemC(PC
& pc
) {
3516 Cell
* c1
= m_stack
.topC();
3517 Cell
* c2
= m_stack
.indC(1);
3518 if (c2
->m_type
== KindOfObject
&& c2
->m_data
.pobj
->isCollection()) {
3519 collectionAppend(c2
->m_data
.pobj
, c1
);
3521 raise_error("ColAddNewElemC: $2 must be a collection");
3526 inline void OPTBLD_INLINE
VMExecutionContext::iopColAddElemC(PC
& pc
) {
3528 Cell
* c1
= m_stack
.topC();
3529 Cell
* c2
= m_stack
.indC(1);
3530 Cell
* c3
= m_stack
.indC(2);
3531 if (c3
->m_type
== KindOfObject
&& c3
->m_data
.pobj
->isCollection()) {
3532 collectionSet(c3
->m_data
.pobj
, c2
, c1
);
3534 raise_error("ColAddElemC: $3 must be a collection");
3540 inline void OPTBLD_INLINE
VMExecutionContext::iopCns(PC
& pc
) {
3543 TypedValue
* cns
= Unit::loadCns(s
);
3544 if (cns
== nullptr) {
3545 raise_notice(Strings::UNDEFINED_CONSTANT
, s
->data(), s
->data());
3546 m_stack
.pushStaticString(s
);
3549 auto const c1
= m_stack
.allocC();
3553 inline void OPTBLD_INLINE
VMExecutionContext::iopCnsE(PC
& pc
) {
3556 TypedValue
* cns
= Unit::loadCns(s
);
3557 if (cns
== nullptr) {
3558 raise_error("Undefined constant '%s'", s
->data());
3560 auto const c1
= m_stack
.allocC();
3564 inline void OPTBLD_INLINE
VMExecutionContext::iopCnsU(PC
& pc
) {
3566 DECODE_LITSTR(name
);
3567 DECODE_LITSTR(fallback
);
3568 TypedValue
* cns
= Unit::loadCns(name
);
3569 if (cns
== nullptr) {
3570 cns
= Unit::loadCns(fallback
);
3571 if (cns
== nullptr) {
3573 Strings::UNDEFINED_CONSTANT
,
3577 m_stack
.pushStaticString(fallback
);
3581 auto const c1
= m_stack
.allocC();
3585 inline void OPTBLD_INLINE
VMExecutionContext::iopDefCns(PC
& pc
) {
3588 TypedValue
* tv
= m_stack
.topTV();
3589 tvAsVariant(tv
) = Unit::defCns(s
, tv
);
3592 inline void OPTBLD_INLINE
VMExecutionContext::iopClsCns(PC
& pc
) {
3594 DECODE_LITSTR(clsCnsName
);
3595 TypedValue
* tv
= m_stack
.topTV();
3596 assert(tv
->m_type
== KindOfClass
);
3597 Class
* class_
= tv
->m_data
.pcls
;
3598 assert(class_
!= nullptr);
3599 auto const clsCns
= class_
->clsCnsGet(clsCnsName
);
3600 if (clsCns
== nullptr) {
3601 raise_error("Couldn't find constant %s::%s",
3602 class_
->name()->data(), clsCnsName
->data());
3604 cellDup(*clsCns
, *tv
);
3607 inline void OPTBLD_INLINE
VMExecutionContext::iopClsCnsD(PC
& pc
) {
3609 DECODE_LITSTR(clsCnsName
);
3610 DECODE(Id
, classId
);
3611 const NamedEntityPair
& classNamedEntity
=
3612 m_fp
->m_func
->unit()->lookupNamedEntityPairId(classId
);
3614 auto const clsCns
= lookupClsCns(classNamedEntity
.second
,
3615 classNamedEntity
.first
, clsCnsName
);
3616 assert(clsCns
!= nullptr);
3617 auto const c1
= m_stack
.allocC();
3618 cellDup(*clsCns
, *c1
);
3621 inline void OPTBLD_INLINE
VMExecutionContext::iopConcat(PC
& pc
) {
3623 Cell
* c1
= m_stack
.topC();
3624 Cell
* c2
= m_stack
.indC(1);
3625 if (IS_STRING_TYPE(c1
->m_type
) && IS_STRING_TYPE(c2
->m_type
)) {
3626 cellAsVariant(*c2
) = concat(
3627 cellAsVariant(*c2
).toString(), cellAsCVarRef(*c1
).toString());
3629 cellAsVariant(*c2
) = concat(cellAsVariant(*c2
).toString(),
3630 cellAsCVarRef(*c1
).toString());
3632 assert(c2
->m_data
.pstr
->getCount() > 0);
3636 inline void OPTBLD_INLINE
VMExecutionContext::iopNot(PC
& pc
) {
3638 Cell
* c1
= m_stack
.topC();
3639 cellAsVariant(*c1
) = !cellAsVariant(*c1
).toBoolean();
3643 inline void OPTBLD_INLINE
VMExecutionContext::iopAbs(PC
& pc
) {
3645 auto c1
= m_stack
.topC();
3647 tvAsVariant(c1
) = f_abs(tvAsCVarRef(c1
));
3651 inline void OPTBLD_INLINE
VMExecutionContext::implCellBinOp(PC
& pc
, Op op
) {
3653 auto const c1
= m_stack
.topC();
3654 auto const c2
= m_stack
.indC(1);
3655 auto const result
= op(*c2
, *c1
);
3656 tvRefcountedDecRefCell(c2
);
3662 inline void OPTBLD_INLINE
VMExecutionContext::implCellBinOpBool(PC
& pc
, Op op
) {
3664 auto const c1
= m_stack
.topC();
3665 auto const c2
= m_stack
.indC(1);
3666 bool const result
= op(*c2
, *c1
);
3667 tvRefcountedDecRefCell(c2
);
3668 *c2
= make_tv
<KindOfBoolean
>(result
);
3672 inline void OPTBLD_INLINE
VMExecutionContext::iopAdd(PC
& pc
) {
3673 implCellBinOp(pc
, cellAdd
);
3676 inline void OPTBLD_INLINE
VMExecutionContext::iopSub(PC
& pc
) {
3677 implCellBinOp(pc
, cellSub
);
3680 inline void OPTBLD_INLINE
VMExecutionContext::iopMul(PC
& pc
) {
3681 implCellBinOp(pc
, cellMul
);
3684 inline void OPTBLD_INLINE
VMExecutionContext::iopDiv(PC
& pc
) {
3685 implCellBinOp(pc
, cellDiv
);
3688 inline void OPTBLD_INLINE
VMExecutionContext::iopMod(PC
& pc
) {
3689 implCellBinOp(pc
, cellMod
);
3692 inline void OPTBLD_INLINE
VMExecutionContext::iopBitAnd(PC
& pc
) {
3693 implCellBinOp(pc
, cellBitAnd
);
3696 inline void OPTBLD_INLINE
VMExecutionContext::iopBitOr(PC
& pc
) {
3697 implCellBinOp(pc
, cellBitOr
);
3700 inline void OPTBLD_INLINE
VMExecutionContext::iopBitXor(PC
& pc
) {
3701 implCellBinOp(pc
, cellBitXor
);
3704 inline void OPTBLD_INLINE
VMExecutionContext::iopXor(PC
& pc
) {
3705 implCellBinOpBool(pc
, [&] (Cell c1
, Cell c2
) -> bool {
3706 return cellToBool(c1
) ^ cellToBool(c2
);
3710 inline void OPTBLD_INLINE
VMExecutionContext::iopSame(PC
& pc
) {
3711 implCellBinOpBool(pc
, cellSame
);
3714 inline void OPTBLD_INLINE
VMExecutionContext::iopNSame(PC
& pc
) {
3715 implCellBinOpBool(pc
, [&] (Cell c1
, Cell c2
) {
3716 return !cellSame(c1
, c2
);
3720 inline void OPTBLD_INLINE
VMExecutionContext::iopEq(PC
& pc
) {
3721 implCellBinOpBool(pc
, [&] (Cell c1
, Cell c2
) {
3722 return cellEqual(c1
, c2
);
3726 inline void OPTBLD_INLINE
VMExecutionContext::iopNeq(PC
& pc
) {
3727 implCellBinOpBool(pc
, [&] (Cell c1
, Cell c2
) {
3728 return !cellEqual(c1
, c2
);
3732 inline void OPTBLD_INLINE
VMExecutionContext::iopLt(PC
& pc
) {
3733 implCellBinOpBool(pc
, [&] (Cell c1
, Cell c2
) {
3734 return cellLess(c1
, c2
);
3738 inline void OPTBLD_INLINE
VMExecutionContext::iopLte(PC
& pc
) {
3739 implCellBinOpBool(pc
, cellLessOrEqual
);
3742 inline void OPTBLD_INLINE
VMExecutionContext::iopGt(PC
& pc
) {
3743 implCellBinOpBool(pc
, [&] (Cell c1
, Cell c2
) {
3744 return cellGreater(c1
, c2
);
3748 inline void OPTBLD_INLINE
VMExecutionContext::iopGte(PC
& pc
) {
3749 implCellBinOpBool(pc
, cellGreaterOrEqual
);
3752 inline void OPTBLD_INLINE
VMExecutionContext::iopShl(PC
& pc
) {
3753 implCellBinOp(pc
, [&] (Cell c1
, Cell c2
) {
3754 return make_tv
<KindOfInt64
>(cellToInt(c1
) << cellToInt(c2
));
3758 inline void OPTBLD_INLINE
VMExecutionContext::iopShr(PC
& pc
) {
3759 implCellBinOp(pc
, [&] (Cell c1
, Cell c2
) {
3760 return make_tv
<KindOfInt64
>(cellToInt(c1
) >> cellToInt(c2
));
3764 inline void OPTBLD_INLINE
VMExecutionContext::iopSqrt(PC
& pc
) {
3766 Cell
* c1
= m_stack
.topC();
3768 if (c1
->m_type
== KindOfNull
|| c1
->m_type
== KindOfBoolean
||
3769 (IS_STRING_TYPE(c1
->m_type
) && c1
->m_data
.pstr
->isNumeric())) {
3770 tvCastToDoubleInPlace(c1
);
3773 if (c1
->m_type
== KindOfInt64
) {
3774 c1
->m_type
= KindOfDouble
;
3775 c1
->m_data
.dbl
= f_sqrt(c1
->m_data
.num
);
3776 } else if (c1
->m_type
== KindOfDouble
) {
3777 c1
->m_data
.dbl
= f_sqrt(c1
->m_data
.dbl
);
3780 if (c1
->m_type
!= KindOfDouble
) {
3781 raise_param_type_warning(
3787 tvRefcountedDecRefCell(c1
);
3788 c1
->m_type
= KindOfNull
;
3792 inline void OPTBLD_INLINE
VMExecutionContext::iopBitNot(PC
& pc
) {
3794 cellBitNot(*m_stack
.topC());
3797 inline void OPTBLD_INLINE
VMExecutionContext::iopCastBool(PC
& pc
) {
3799 Cell
* c1
= m_stack
.topC();
3800 tvCastToBooleanInPlace(c1
);
3803 inline void OPTBLD_INLINE
VMExecutionContext::iopCastInt(PC
& pc
) {
3805 Cell
* c1
= m_stack
.topC();
3806 tvCastToInt64InPlace(c1
);
3809 inline void OPTBLD_INLINE
VMExecutionContext::iopCastDouble(PC
& pc
) {
3811 Cell
* c1
= m_stack
.topC();
3812 tvCastToDoubleInPlace(c1
);
3815 inline void OPTBLD_INLINE
VMExecutionContext::iopCastString(PC
& pc
) {
3817 Cell
* c1
= m_stack
.topC();
3818 tvCastToStringInPlace(c1
);
3821 inline void OPTBLD_INLINE
VMExecutionContext::iopCastArray(PC
& pc
) {
3823 Cell
* c1
= m_stack
.topC();
3824 tvCastToArrayInPlace(c1
);
3827 inline void OPTBLD_INLINE
VMExecutionContext::iopCastObject(PC
& pc
) {
3829 Cell
* c1
= m_stack
.topC();
3830 tvCastToObjectInPlace(c1
);
3833 inline bool OPTBLD_INLINE
VMExecutionContext::cellInstanceOf(
3834 TypedValue
* tv
, const NamedEntity
* ne
) {
3835 assert(tv
->m_type
!= KindOfRef
);
3836 Class
* cls
= nullptr;
3837 switch (tv
->m_type
) {
3839 cls
= Unit::lookupClass(ne
);
3840 if (cls
) return tv
->m_data
.pobj
->instanceof(cls
);
3843 cls
= Unit::lookupClass(ne
);
3844 if (cls
&& interface_supports_array(cls
->name())) {
3849 case KindOfStaticString
:
3850 cls
= Unit::lookupClass(ne
);
3851 if (cls
&& interface_supports_string(cls
->name())) {
3856 cls
= Unit::lookupClass(ne
);
3857 if (cls
&& interface_supports_int(cls
->name())) {
3862 cls
= Unit::lookupClass(ne
);
3863 if (cls
&& interface_supports_double(cls
->name())) {
3873 inline ALWAYS_INLINE
3874 bool VMExecutionContext::iopInstanceOfHelper(const StringData
* str1
, Cell
* c2
) {
3875 const NamedEntity
* rhs
= Unit::GetNamedEntity(str1
, false);
3876 // Because of other codepaths, an un-normalized name might enter the
3877 // table without a Class* so we need to check if it's there.
3878 if (LIKELY(rhs
&& rhs
->getCachedClass() != nullptr)) {
3879 return cellInstanceOf(c2
, rhs
);
3881 auto normName
= normalizeNS(str1
);
3883 rhs
= Unit::GetNamedEntity(normName
.get(), false);
3884 if (LIKELY(rhs
&& rhs
->getCachedClass() != nullptr)) {
3885 return cellInstanceOf(c2
, rhs
);
3891 inline void OPTBLD_INLINE
VMExecutionContext::iopInstanceOf(PC
& pc
) {
3893 Cell
* c1
= m_stack
.topC(); // c2 instanceof c1
3894 Cell
* c2
= m_stack
.indC(1);
3896 if (IS_STRING_TYPE(c1
->m_type
)) {
3897 r
= iopInstanceOfHelper(c1
->m_data
.pstr
, c2
);
3898 } else if (c1
->m_type
== KindOfObject
) {
3899 if (c2
->m_type
== KindOfObject
) {
3900 ObjectData
* lhs
= c2
->m_data
.pobj
;
3901 ObjectData
* rhs
= c1
->m_data
.pobj
;
3902 r
= lhs
->instanceof(rhs
->getVMClass());
3905 raise_error("Class name must be a valid object or a string");
3908 tvRefcountedDecRefCell(c2
);
3910 c2
->m_type
= KindOfBoolean
;
3913 inline void OPTBLD_INLINE
VMExecutionContext::iopInstanceOfD(PC
& pc
) {
3916 if (shouldProfile()) {
3917 InstanceBits::profile(m_fp
->m_func
->unit()->lookupLitstrId(id
));
3919 const NamedEntity
* ne
= m_fp
->m_func
->unit()->lookupNamedEntityId(id
);
3920 Cell
* c1
= m_stack
.topC();
3921 bool r
= cellInstanceOf(c1
, ne
);
3922 tvRefcountedDecRefCell(c1
);
3924 c1
->m_type
= KindOfBoolean
;
3927 inline void OPTBLD_INLINE
VMExecutionContext::iopPrint(PC
& pc
) {
3929 Cell
* c1
= m_stack
.topC();
3930 echo(cellAsVariant(*c1
).toString());
3931 tvRefcountedDecRefCell(c1
);
3932 c1
->m_type
= KindOfInt64
;
3936 inline void OPTBLD_INLINE
VMExecutionContext::iopClone(PC
& pc
) {
3938 TypedValue
* tv
= m_stack
.topTV();
3939 if (tv
->m_type
!= KindOfObject
) {
3940 raise_error("clone called on non-object");
3942 ObjectData
* obj
= tv
->m_data
.pobj
;
3943 const Class
* class_ UNUSED
= obj
->getVMClass();
3944 ObjectData
* newobj
= obj
->clone();
3947 tv
->m_type
= KindOfObject
;
3948 tv
->m_data
.pobj
= newobj
;
3951 inline void OPTBLD_INLINE
VMExecutionContext::iopExit(PC
& pc
) {
3954 Cell
* c1
= m_stack
.topC();
3955 if (c1
->m_type
== KindOfInt64
) {
3956 exitCode
= c1
->m_data
.num
;
3958 echo(cellAsVariant(*c1
).toString());
3962 throw ExitException(exitCode
);
3965 inline void OPTBLD_INLINE
VMExecutionContext::iopFatal(PC
& pc
) {
3967 TypedValue
* top
= m_stack
.topTV();
3969 DECODE_IVA(skipFrame
);
3970 if (IS_STRING_TYPE(top
->m_type
)) {
3971 msg
= top
->m_data
.pstr
->data();
3973 msg
= "Fatal error message not a string";
3977 raise_error_without_first_frame(msg
);
3983 inline void OPTBLD_INLINE
VMExecutionContext::jmpSurpriseCheck(Offset offset
) {
3984 if (offset
<= 0 && UNLIKELY(Transl::TargetCache::loadConditionFlags())) {
3985 EventHook::CheckSurprise();
3989 inline void OPTBLD_INLINE
VMExecutionContext::iopJmp(PC
& pc
) {
3991 DECODE_JMP(Offset
, offset
);
3992 jmpSurpriseCheck(offset
);
3998 inline void OPTBLD_INLINE
VMExecutionContext::jmpOpImpl(PC
& pc
) {
3999 static_assert(op
== OpJmpZ
|| op
== OpJmpNZ
,
4000 "jmpOpImpl should only be used by JmpZ and JmpNZ");
4002 DECODE_JMP(Offset
, offset
);
4003 jmpSurpriseCheck(offset
);
4005 Cell
* c1
= m_stack
.topC();
4006 if (c1
->m_type
== KindOfInt64
|| c1
->m_type
== KindOfBoolean
) {
4007 int64_t n
= c1
->m_data
.num
;
4008 if (op
== OpJmpZ
? n
== 0 : n
!= 0) {
4012 pc
+= sizeof(Offset
);
4016 auto const condition
= toBoolean(cellAsCVarRef(*c1
));
4017 if (op
== OpJmpZ
? !condition
: condition
) {
4021 pc
+= sizeof(Offset
);
4027 inline void OPTBLD_INLINE
VMExecutionContext::iopJmpZ(PC
& pc
) {
4028 jmpOpImpl
<OpJmpZ
>(pc
);
4031 inline void OPTBLD_INLINE
VMExecutionContext::iopJmpNZ(PC
& pc
) {
4032 jmpOpImpl
<OpJmpNZ
>(pc
);
4035 #define FREE_ITER_LIST(typeList, idList, vecLen) do { \
4037 for (iterIndex = 0; iterIndex < 2 * veclen; iterIndex += 2) { \
4038 Id iterType = typeList[iterIndex]; \
4039 Id iterId = idList[iterIndex]; \
4041 Iter *iter = frame_iter(m_fp, iterId); \
4043 switch (iterType) { \
4044 case KindOfIter: iter->free(); break; \
4045 case KindOfMIter: iter->mfree(); break; \
4046 case KindOfCIter: iter->cfree(); break; \
4051 inline void OPTBLD_INLINE
VMExecutionContext::iopIterBreak(PC
& pc
) {
4054 DECODE_ITER_LIST(iterTypeList
, iterIdList
, veclen
);
4055 DECODE_JMP(Offset
, offset
);
4057 jmpSurpriseCheck(offset
); // we do this early so iterators are still dirty if
4058 // we have an exception
4060 FREE_ITER_LIST(iterTypeList
, iterIdList
, veclen
);
4061 pc
= savedPc
+ offset
;
4064 #undef FREE_ITER_LIST
4066 enum class SwitchMatch
{
4067 NORMAL
, // value was converted to an int: match normally
4068 NONZERO
, // can't be converted to an int: match first nonzero case
4069 DEFAULT
, // can't be converted to an int: match default case
4072 static SwitchMatch
doubleCheck(double d
, int64_t& out
) {
4073 if (int64_t(d
) == d
) {
4075 return SwitchMatch::NORMAL
;
4077 return SwitchMatch::DEFAULT
;
4081 inline void OPTBLD_INLINE
VMExecutionContext::iopSwitch(PC
& pc
) {
4084 DECODE(int32_t, veclen
);
4086 Offset
* jmptab
= (Offset
*)pc
;
4087 pc
+= veclen
* sizeof(*jmptab
);
4088 DECODE(int64_t, base
);
4089 DECODE_IVA(bounded
);
4091 TypedValue
* val
= m_stack
.topTV();
4093 assert(val
->m_type
== KindOfInt64
);
4094 // Continuation switch: no bounds checking needed
4095 int64_t label
= val
->m_data
.num
;
4097 assert(label
>= 0 && label
< veclen
);
4098 pc
= origPC
+ jmptab
[label
];
4100 // Generic integer switch
4102 SwitchMatch match
= SwitchMatch::NORMAL
;
4104 switch (val
->m_type
) {
4111 // bool(true) is equal to any non-zero int, bool(false) == 0
4112 if (val
->m_data
.num
) {
4113 match
= SwitchMatch::NONZERO
;
4120 intval
= val
->m_data
.num
;
4124 match
= doubleCheck(val
->m_data
.dbl
, intval
);
4127 case KindOfStaticString
:
4128 case KindOfString
: {
4130 DataType t
= val
->m_data
.pstr
->isNumericWithVal(intval
, dval
, 1);
4137 match
= doubleCheck(dval
, intval
);
4147 tvRefcountedDecRef(val
);
4152 match
= SwitchMatch::DEFAULT
;
4157 intval
= val
->m_data
.pobj
->o_toInt64();
4161 case KindOfResource
:
4162 intval
= val
->m_data
.pres
->o_toInt64();
4171 if (match
!= SwitchMatch::NORMAL
||
4172 intval
< base
|| intval
>= (base
+ veclen
- 2)) {
4174 case SwitchMatch::NORMAL
:
4175 case SwitchMatch::DEFAULT
:
4176 pc
= origPC
+ jmptab
[veclen
- 1];
4179 case SwitchMatch::NONZERO
:
4180 pc
= origPC
+ jmptab
[veclen
- 2];
4184 pc
= origPC
+ jmptab
[intval
- base
];
4189 inline void OPTBLD_INLINE
VMExecutionContext::iopSSwitch(PC
& pc
) {
4192 DECODE(int32_t, veclen
);
4194 unsigned cases
= veclen
- 1; // the last vector item is the default case
4195 StrVecItem
* jmptab
= (StrVecItem
*)pc
;
4196 pc
+= veclen
* sizeof(*jmptab
);
4198 Cell
* val
= tvToCell(m_stack
.topTV());
4199 Unit
* u
= m_fp
->m_func
->unit();
4201 for (i
= 0; i
< cases
; ++i
) {
4202 auto& item
= jmptab
[i
];
4203 const StringData
* str
= u
->lookupLitstrId(item
.str
);
4204 if (cellEqual(*val
, str
)) {
4205 pc
= origPC
+ item
.dest
;
4211 pc
= origPC
+ jmptab
[veclen
-1].dest
;
4216 inline void OPTBLD_INLINE
VMExecutionContext::iopRetC(PC
& pc
) {
4218 uint soff
= m_fp
->m_soff
;
4219 assert(!m_fp
->m_func
->isGenerator());
4221 // Call the runtime helpers to free the local variables and iterators
4222 frame_free_locals_inl(m_fp
, m_fp
->m_func
->numLocals());
4223 ActRec
* sfp
= m_fp
->arGetSfp();
4224 // Memcpy the the return value on top of the activation record. This works
4225 // the same regardless of whether the return value is boxed or not.
4226 TypedValue
* retval_ptr
= &m_fp
->m_r
;
4227 memcpy(retval_ptr
, m_stack
.topTV(), sizeof(TypedValue
));
4229 m_stack
.ndiscard(m_fp
->m_func
->numSlotsInFrame() + 1);
4231 if (LIKELY(sfp
!= m_fp
)) {
4232 // Restore caller's execution state.
4234 pc
= m_fp
->m_func
->unit()->entry() + m_fp
->m_func
->base() + soff
;
4236 assert(m_stack
.topTV() == retval_ptr
);
4238 // No caller; terminate.
4242 std::ostringstream os
;
4243 os
<< toStringElm(m_stack
.topTV());
4245 Trace::trace("Return %s from VMExecutionContext::dispatch("
4246 "%p)\n", os
.str().c_str(), m_fp
));
4253 inline void OPTBLD_INLINE
VMExecutionContext::iopRetV(PC
& pc
) {
4257 inline void OPTBLD_INLINE
VMExecutionContext::iopUnwind(PC
& pc
) {
4258 assert(!m_faults
.empty());
4259 assert(m_faults
.back().m_savedRaiseOffset
!= kInvalidOffset
);
4260 throw VMPrepareUnwind();
4263 inline void OPTBLD_INLINE
VMExecutionContext::iopThrow(PC
& pc
) {
4264 Cell
* c1
= m_stack
.topC();
4265 if (c1
->m_type
!= KindOfObject
||
4266 !c1
->m_data
.pobj
->instanceof(SystemLib::s_ExceptionClass
)) {
4267 raise_error("Exceptions must be valid objects derived from the "
4268 "Exception base class");
4271 Object
obj(c1
->m_data
.pobj
);
4273 DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionThrownHook(obj
.get()));
4277 inline void OPTBLD_INLINE
VMExecutionContext::iopAGetC(PC
& pc
) {
4279 TypedValue
* tv
= m_stack
.topTV();
4280 lookupClsRef(tv
, tv
, true);
4283 inline void OPTBLD_INLINE
VMExecutionContext::iopAGetL(PC
& pc
) {
4286 TypedValue
* top
= m_stack
.allocTV();
4287 TypedValue
* fr
= frame_local_inner(m_fp
, local
);
4288 lookupClsRef(fr
, top
);
4291 static void raise_undefined_local(ActRec
* fp
, Id pind
) {
4292 assert(pind
< fp
->m_func
->numNamedLocals());
4293 raise_notice(Strings::UNDEFINED_VARIABLE
,
4294 fp
->m_func
->localVarName(pind
)->data());
4297 static inline void cgetl_inner_body(TypedValue
* fr
, TypedValue
* to
) {
4298 assert(fr
->m_type
!= KindOfUninit
);
4299 cellDup(*tvToCell(fr
), *to
);
4302 static inline void cgetl_body(ActRec
* fp
,
4306 if (fr
->m_type
== KindOfUninit
) {
4307 // `to' is uninitialized here, so we need to tvWriteNull before
4308 // possibly causing stack unwinding.
4310 raise_undefined_local(fp
, pind
);
4312 cgetl_inner_body(fr
, to
);
4316 inline void OPTBLD_INLINE
VMExecutionContext::iopCGetL(PC
& pc
) {
4319 Cell
* to
= m_stack
.allocC();
4320 TypedValue
* fr
= frame_local(m_fp
, local
);
4321 cgetl_body(m_fp
, fr
, to
, local
);
4324 inline void OPTBLD_INLINE
VMExecutionContext::iopCGetL2(PC
& pc
) {
4327 TypedValue
* oldTop
= m_stack
.topTV();
4328 TypedValue
* newTop
= m_stack
.allocTV();
4329 memcpy(newTop
, oldTop
, sizeof *newTop
);
4331 TypedValue
* fr
= frame_local(m_fp
, local
);
4332 cgetl_body(m_fp
, fr
, to
, local
);
4335 inline void OPTBLD_INLINE
VMExecutionContext::iopCGetL3(PC
& pc
) {
4338 TypedValue
* oldTop
= m_stack
.topTV();
4339 TypedValue
* oldSubTop
= m_stack
.indTV(1);
4340 TypedValue
* newTop
= m_stack
.allocTV();
4341 memmove(newTop
, oldTop
, sizeof *oldTop
* 2);
4342 Cell
* to
= oldSubTop
;
4343 TypedValue
* fr
= frame_local(m_fp
, local
);
4344 cgetl_body(m_fp
, fr
, to
, local
);
4347 inline void OPTBLD_INLINE
VMExecutionContext::iopCGetN(PC
& pc
) {
4350 TypedValue
* to
= m_stack
.topTV();
4351 TypedValue
* fr
= nullptr;
4352 lookup_var(m_fp
, name
, to
, fr
);
4353 if (fr
== nullptr || fr
->m_type
== KindOfUninit
) {
4354 raise_notice(Strings::UNDEFINED_VARIABLE
, name
->data());
4355 tvRefcountedDecRefCell(to
);
4358 tvRefcountedDecRefCell(to
);
4359 cgetl_inner_body(fr
, to
);
4361 decRefStr(name
); // TODO(#1146727): leaks during exceptions
4364 inline void OPTBLD_INLINE
VMExecutionContext::iopCGetG(PC
& pc
) {
4367 TypedValue
* to
= m_stack
.topTV();
4368 TypedValue
* fr
= nullptr;
4369 lookup_gbl(m_fp
, name
, to
, fr
);
4370 if (fr
== nullptr) {
4372 raise_notice(Strings::UNDEFINED_VARIABLE
, name
->data());
4374 tvRefcountedDecRefCell(to
);
4376 } else if (fr
->m_type
== KindOfUninit
) {
4377 raise_notice(Strings::UNDEFINED_VARIABLE
, name
->data());
4378 tvRefcountedDecRefCell(to
);
4381 tvRefcountedDecRefCell(to
);
4382 cgetl_inner_body(fr
, to
);
4384 decRefStr(name
); // TODO(#1146727): leaks during exceptions
4387 #define SPROP_OP_PRELUDE \
4389 TypedValue* clsref = m_stack.topTV(); \
4390 TypedValue* nameCell = m_stack.indTV(1); \
4391 TypedValue* output = nameCell; \
4393 bool visible, accessible; \
4394 lookup_sprop(m_fp, clsref, name, nameCell, val, visible, \
4397 #define SPROP_OP_POSTLUDE \
4400 #define GETS(box) do { \
4402 if (!(visible && accessible)) { \
4403 raise_error("Invalid static property access: %s::%s", \
4404 clsref->m_data.pcls->name()->data(), \
4408 if (val->m_type != KindOfRef) { \
4411 refDup(*val, *output); \
4413 cellDup(*tvToCell(val), *output); \
4419 inline void OPTBLD_INLINE
VMExecutionContext::iopCGetS(PC
& pc
) {
4422 if (shouldProfile() && name
&& name
->isStatic()) {
4423 recordType(TypeProfileKey(TypeProfileKey::StaticPropName
, name
),
4424 m_stack
.top()->m_type
);
4428 inline void OPTBLD_INLINE
VMExecutionContext::iopCGetM(PC
& pc
) {
4431 DECLARE_GETHELPER_ARGS
4432 getHelper(GETHELPER_ARGS
);
4433 if (tvRet
->m_type
== KindOfRef
) {
4436 assert(hasImmVector(toOp(*oldPC
)));
4437 const ImmVector
& immVec
= ImmVector::createFromStream(oldPC
+ 1);
4440 if (immVec
.decodeLastMember(m_fp
->unit(), name
, mc
)) {
4441 recordType(TypeProfileKey(mc
, name
), m_stack
.top()->m_type
);
4445 static inline void vgetl_body(TypedValue
* fr
, TypedValue
* to
) {
4446 if (fr
->m_type
!= KindOfRef
) {
4452 inline void OPTBLD_INLINE
VMExecutionContext::iopVGetL(PC
& pc
) {
4455 Ref
* to
= m_stack
.allocV();
4456 TypedValue
* fr
= frame_local(m_fp
, local
);
4460 inline void OPTBLD_INLINE
VMExecutionContext::iopVGetN(PC
& pc
) {
4463 TypedValue
* to
= m_stack
.topTV();
4464 TypedValue
* fr
= nullptr;
4465 lookupd_var(m_fp
, name
, to
, fr
);
4466 assert(fr
!= nullptr);
4467 tvRefcountedDecRefCell(to
);
4472 inline void OPTBLD_INLINE
VMExecutionContext::iopVGetG(PC
& pc
) {
4475 TypedValue
* to
= m_stack
.topTV();
4476 TypedValue
* fr
= nullptr;
4477 lookupd_gbl(m_fp
, name
, to
, fr
);
4478 assert(fr
!= nullptr);
4479 tvRefcountedDecRefCell(to
);
4484 inline void OPTBLD_INLINE
VMExecutionContext::iopVGetS(PC
& pc
) {
4490 inline void OPTBLD_INLINE
VMExecutionContext::iopVGetM(PC
& pc
) {
4492 DECLARE_SETHELPER_ARGS
4493 TypedValue
* tv1
= m_stack
.allocTV();
4495 if (!setHelperPre
<false, true, false, true, 1,
4496 VectorLeaveCode::ConsumeAll
>(MEMBERHELPERPRE_ARGS
)) {
4497 if (base
->m_type
!= KindOfRef
) {
4500 refDup(*base
, *tv1
);
4505 setHelperPost
<1>(SETHELPERPOST_ARGS
);
4508 inline void OPTBLD_INLINE
VMExecutionContext::iopIssetN(PC
& pc
) {
4511 TypedValue
* tv1
= m_stack
.topTV();
4512 TypedValue
* tv
= nullptr;
4514 lookup_var(m_fp
, name
, tv1
, tv
);
4515 if (tv
== nullptr) {
4518 e
= !tvIsNull(tvToCell(tv
));
4520 tvRefcountedDecRefCell(tv1
);
4521 tv1
->m_data
.num
= e
;
4522 tv1
->m_type
= KindOfBoolean
;
4526 inline void OPTBLD_INLINE
VMExecutionContext::iopIssetG(PC
& pc
) {
4529 TypedValue
* tv1
= m_stack
.topTV();
4530 TypedValue
* tv
= nullptr;
4532 lookup_gbl(m_fp
, name
, tv1
, tv
);
4533 if (tv
== nullptr) {
4536 e
= !tvIsNull(tvToCell(tv
));
4538 tvRefcountedDecRefCell(tv1
);
4539 tv1
->m_data
.num
= e
;
4540 tv1
->m_type
= KindOfBoolean
;
4544 inline void OPTBLD_INLINE
VMExecutionContext::iopIssetS(PC
& pc
) {
4548 if (!(visible
&& accessible
)) {
4551 e
= !tvIsNull(tvToCell(val
));
4554 output
->m_data
.num
= e
;
4555 output
->m_type
= KindOfBoolean
;
4559 template <bool isEmpty
>
4560 inline void OPTBLD_INLINE
VMExecutionContext::isSetEmptyM(PC
& pc
) {
4562 DECLARE_GETHELPER_ARGS
4563 getHelperPre
<false, false, VectorLeaveCode::LeaveLast
>(MEMBERHELPERPRE_ARGS
);
4564 // Process last member specially, in order to employ the IssetElem/IssetProp
4566 bool isSetEmptyResult
= false;
4572 isSetEmptyResult
= IssetEmptyElem
<isEmpty
>(tvScratch
, *tvRef
.asTypedValue(),
4579 Class
* ctx
= arGetContextClass(m_fp
);
4580 isSetEmptyResult
= IssetEmptyProp
<isEmpty
>(ctx
, base
, curMember
);
4583 default: assert(false);
4585 getHelperPost
<false>(GETHELPERPOST_ARGS
);
4586 tvRet
->m_data
.num
= isSetEmptyResult
;
4587 tvRet
->m_type
= KindOfBoolean
;
4590 inline void OPTBLD_INLINE
VMExecutionContext::iopIssetM(PC
& pc
) {
4591 isSetEmptyM
<false>(pc
);
4594 #define IOP_TYPE_CHECK_INSTR_L(checkInit, what, predicate) \
4595 inline void OPTBLD_INLINE VMExecutionContext::iopIs ## what ## L(PC& pc) { \
4598 TypedValue* tv = frame_local(m_fp, local); \
4599 if (checkInit && tv->m_type == KindOfUninit) { \
4600 raise_undefined_local(m_fp, local); \
4602 bool ret = predicate(tvAsCVarRef(tv)); \
4603 TypedValue* topTv = m_stack.allocTV(); \
4604 topTv->m_data.num = ret; \
4605 topTv->m_type = KindOfBoolean; \
4608 #define IOP_TYPE_CHECK_INSTR_C(checkInit, what, predicate) \
4609 inline void OPTBLD_INLINE VMExecutionContext::iopIs ## what ## C(PC& pc) { \
4611 TypedValue* topTv = m_stack.topTV(); \
4612 assert(topTv->m_type != KindOfRef); \
4613 bool ret = predicate(tvAsCVarRef(topTv)); \
4614 tvRefcountedDecRefCell(topTv); \
4615 topTv->m_data.num = ret; \
4616 topTv->m_type = KindOfBoolean; \
4619 #define IOP_TYPE_CHECK_INSTR(checkInit, what, predicate) \
4620 IOP_TYPE_CHECK_INSTR_L(checkInit, what, predicate) \
4621 IOP_TYPE_CHECK_INSTR_C(checkInit, what, predicate) \
4623 IOP_TYPE_CHECK_INSTR_L(false, set, is_not_null)
4624 IOP_TYPE_CHECK_INSTR(true, Null
, is_null
)
4625 IOP_TYPE_CHECK_INSTR(true, Array
, is_array
)
4626 IOP_TYPE_CHECK_INSTR(true, String
, is_string
)
4627 IOP_TYPE_CHECK_INSTR(true, Object
, is_object
)
4628 IOP_TYPE_CHECK_INSTR(true, Int
, is_int
)
4629 IOP_TYPE_CHECK_INSTR(true, Double
, is_double
)
4630 IOP_TYPE_CHECK_INSTR(true, Bool
, is_bool
)
4631 #undef IOP_TYPE_CHECK_INSTR
4633 inline void OPTBLD_INLINE
VMExecutionContext::iopEmptyL(PC
& pc
) {
4636 TypedValue
* loc
= frame_local(m_fp
, local
);
4637 bool e
= !cellToBool(*tvToCell(loc
));
4638 TypedValue
* tv1
= m_stack
.allocTV();
4639 tv1
->m_data
.num
= e
;
4640 tv1
->m_type
= KindOfBoolean
;
4643 inline void OPTBLD_INLINE
VMExecutionContext::iopEmptyN(PC
& pc
) {
4646 TypedValue
* tv1
= m_stack
.topTV();
4647 TypedValue
* tv
= nullptr;
4649 lookup_var(m_fp
, name
, tv1
, tv
);
4650 if (tv
== nullptr) {
4653 e
= !cellToBool(*tvToCell(tv
));
4655 tvRefcountedDecRefCell(tv1
);
4656 tv1
->m_data
.num
= e
;
4657 tv1
->m_type
= KindOfBoolean
;
4661 inline void OPTBLD_INLINE
VMExecutionContext::iopEmptyG(PC
& pc
) {
4664 TypedValue
* tv1
= m_stack
.topTV();
4665 TypedValue
* tv
= nullptr;
4667 lookup_gbl(m_fp
, name
, tv1
, tv
);
4668 if (tv
== nullptr) {
4671 e
= !cellToBool(*tvToCell(tv
));
4673 tvRefcountedDecRefCell(tv1
);
4674 tv1
->m_data
.num
= e
;
4675 tv1
->m_type
= KindOfBoolean
;
4679 inline void OPTBLD_INLINE
VMExecutionContext::iopEmptyS(PC
& pc
) {
4683 if (!(visible
&& accessible
)) {
4686 e
= !cellToBool(*tvToCell(val
));
4689 output
->m_data
.num
= e
;
4690 output
->m_type
= KindOfBoolean
;
4694 inline void OPTBLD_INLINE
VMExecutionContext::iopEmptyM(PC
& pc
) {
4695 isSetEmptyM
<true>(pc
);
4698 inline void OPTBLD_INLINE
VMExecutionContext::iopAKExists(PC
& pc
) {
4700 TypedValue
* arr
= m_stack
.topTV();
4701 TypedValue
* key
= arr
+ 1;
4702 bool result
= f_array_key_exists(tvAsCVarRef(key
), tvAsCVarRef(arr
));
4704 tvRefcountedDecRef(key
);
4705 key
->m_data
.num
= result
;
4706 key
->m_type
= KindOfBoolean
;
4709 inline void OPTBLD_INLINE
VMExecutionContext::iopArrayIdx(PC
& pc
) {
4711 TypedValue
* def
= m_stack
.topTV();
4712 TypedValue
* arr
= m_stack
.indTV(1);
4713 TypedValue
* key
= m_stack
.indTV(2);
4715 Variant result
= f_hphp_array_idx(tvAsCVarRef(key
),
4720 tvAsVariant(key
) = result
;
4723 inline void OPTBLD_INLINE
VMExecutionContext::iopSetL(PC
& pc
) {
4726 assert(local
< m_fp
->m_func
->numLocals());
4727 Cell
* fr
= m_stack
.topC();
4728 TypedValue
* to
= frame_local(m_fp
, local
);
4732 inline void OPTBLD_INLINE
VMExecutionContext::iopSetN(PC
& pc
) {
4735 Cell
* fr
= m_stack
.topC();
4736 TypedValue
* tv2
= m_stack
.indTV(1);
4737 TypedValue
* to
= nullptr;
4738 lookupd_var(m_fp
, name
, tv2
, to
);
4739 assert(to
!= nullptr);
4741 memcpy((void*)tv2
, (void*)fr
, sizeof(TypedValue
));
4746 inline void OPTBLD_INLINE
VMExecutionContext::iopSetG(PC
& pc
) {
4749 Cell
* fr
= m_stack
.topC();
4750 TypedValue
* tv2
= m_stack
.indTV(1);
4751 TypedValue
* to
= nullptr;
4752 lookupd_gbl(m_fp
, name
, tv2
, to
);
4753 assert(to
!= nullptr);
4755 memcpy((void*)tv2
, (void*)fr
, sizeof(TypedValue
));
4760 inline void OPTBLD_INLINE
VMExecutionContext::iopSetS(PC
& pc
) {
4762 TypedValue
* tv1
= m_stack
.topTV();
4763 TypedValue
* classref
= m_stack
.indTV(1);
4764 TypedValue
* propn
= m_stack
.indTV(2);
4765 TypedValue
* output
= propn
;
4768 bool visible
, accessible
;
4769 lookup_sprop(m_fp
, classref
, name
, propn
, val
, visible
, accessible
);
4770 if (!(visible
&& accessible
)) {
4771 raise_error("Invalid static property access: %s::%s",
4772 classref
->m_data
.pcls
->name()->data(),
4776 tvRefcountedDecRefCell(propn
);
4777 memcpy(output
, tv1
, sizeof(TypedValue
));
4778 m_stack
.ndiscard(2);
4782 inline void OPTBLD_INLINE
VMExecutionContext::iopSetM(PC
& pc
) {
4784 DECLARE_SETHELPER_ARGS
4785 if (!setHelperPre
<false, true, false, false, 1,
4786 VectorLeaveCode::LeaveLast
>(MEMBERHELPERPRE_ARGS
)) {
4787 Cell
* c1
= m_stack
.topC();
4790 SetNewElem
<true>(base
, c1
);
4797 StringData
* result
= SetElem
<true>(base
, curMember
, c1
);
4799 tvRefcountedDecRefCell(c1
);
4800 c1
->m_type
= KindOfString
;
4801 c1
->m_data
.pstr
= result
;
4808 Class
* ctx
= arGetContextClass(m_fp
);
4809 SetProp
<true>(ctx
, base
, curMember
, c1
);
4812 default: assert(false);
4816 setHelperPost
<1>(SETHELPERPOST_ARGS
);
4819 inline void OPTBLD_INLINE
VMExecutionContext::iopSetWithRefLM(PC
& pc
) {
4821 DECLARE_SETHELPER_ARGS
4822 bool skip
= setHelperPre
<false, true, false, false, 0,
4823 VectorLeaveCode::ConsumeAll
>(MEMBERHELPERPRE_ARGS
);
4826 TypedValue
* from
= frame_local(m_fp
, local
);
4827 tvAsVariant(base
) = withRefBind(tvAsVariant(from
));
4829 setHelperPost
<0>(SETHELPERPOST_ARGS
);
4832 inline void OPTBLD_INLINE
VMExecutionContext::iopSetWithRefRM(PC
& pc
) {
4834 DECLARE_SETHELPER_ARGS
4835 bool skip
= setHelperPre
<false, true, false, false, 1,
4836 VectorLeaveCode::ConsumeAll
>(MEMBERHELPERPRE_ARGS
);
4838 TypedValue
* from
= m_stack
.top();
4839 tvAsVariant(base
) = withRefBind(tvAsVariant(from
));
4841 setHelperPost
<0>(SETHELPERPOST_ARGS
);
4845 inline void OPTBLD_INLINE
VMExecutionContext::iopSetOpL(PC
& pc
) {
4848 DECODE(unsigned char, op
);
4849 Cell
* fr
= m_stack
.topC();
4850 Cell
* to
= tvToCell(frame_local(m_fp
, local
));
4851 SETOP_BODY_CELL(to
, op
, fr
);
4852 tvRefcountedDecRefCell(fr
);
4856 inline void OPTBLD_INLINE
VMExecutionContext::iopSetOpN(PC
& pc
) {
4858 DECODE(unsigned char, op
);
4860 Cell
* fr
= m_stack
.topC();
4861 TypedValue
* tv2
= m_stack
.indTV(1);
4862 TypedValue
* to
= nullptr;
4863 // XXX We're probably not getting warnings totally correct here
4864 lookupd_var(m_fp
, name
, tv2
, to
);
4865 assert(to
!= nullptr);
4866 SETOP_BODY(to
, op
, fr
);
4867 tvRefcountedDecRef(fr
);
4868 tvRefcountedDecRef(tv2
);
4869 cellDup(*tvToCell(to
), *tv2
);
4874 inline void OPTBLD_INLINE
VMExecutionContext::iopSetOpG(PC
& pc
) {
4876 DECODE(unsigned char, op
);
4878 Cell
* fr
= m_stack
.topC();
4879 TypedValue
* tv2
= m_stack
.indTV(1);
4880 TypedValue
* to
= nullptr;
4881 // XXX We're probably not getting warnings totally correct here
4882 lookupd_gbl(m_fp
, name
, tv2
, to
);
4883 assert(to
!= nullptr);
4884 SETOP_BODY(to
, op
, fr
);
4885 tvRefcountedDecRef(fr
);
4886 tvRefcountedDecRef(tv2
);
4887 cellDup(*tvToCell(to
), *tv2
);
4892 inline void OPTBLD_INLINE
VMExecutionContext::iopSetOpS(PC
& pc
) {
4894 DECODE(unsigned char, op
);
4895 Cell
* fr
= m_stack
.topC();
4896 TypedValue
* classref
= m_stack
.indTV(1);
4897 TypedValue
* propn
= m_stack
.indTV(2);
4898 TypedValue
* output
= propn
;
4901 bool visible
, accessible
;
4902 lookup_sprop(m_fp
, classref
, name
, propn
, val
, visible
, accessible
);
4903 if (!(visible
&& accessible
)) {
4904 raise_error("Invalid static property access: %s::%s",
4905 classref
->m_data
.pcls
->name()->data(),
4908 SETOP_BODY(val
, op
, fr
);
4909 tvRefcountedDecRefCell(propn
);
4910 tvRefcountedDecRef(fr
);
4911 cellDup(*tvToCell(val
), *output
);
4912 m_stack
.ndiscard(2);
4916 inline void OPTBLD_INLINE
VMExecutionContext::iopSetOpM(PC
& pc
) {
4918 DECODE(unsigned char, op
);
4919 DECLARE_SETHELPER_ARGS
4920 if (!setHelperPre
<MoreWarnings
, true, false, false, 1,
4921 VectorLeaveCode::LeaveLast
>(MEMBERHELPERPRE_ARGS
)) {
4923 Cell
* rhs
= m_stack
.topC();
4926 result
= SetOpNewElem(tvScratch
, *tvRef
.asTypedValue(), op
, base
, rhs
);
4933 result
= SetOpElem(tvScratch
, *tvRef
.asTypedValue(), op
, base
,
4939 Class
*ctx
= arGetContextClass(m_fp
);
4940 result
= SetOpProp(tvScratch
, *tvRef
.asTypedValue(), ctx
, op
, base
,
4946 result
= nullptr; // Silence compiler warning.
4950 tvRefcountedDecRef(rhs
);
4951 cellDup(*tvToCell(result
), *rhs
);
4953 setHelperPost
<1>(SETHELPERPOST_ARGS
);
4956 inline void OPTBLD_INLINE
VMExecutionContext::iopIncDecL(PC
& pc
) {
4959 DECODE(unsigned char, op
);
4960 TypedValue
* to
= m_stack
.allocTV();
4962 TypedValue
* fr
= frame_local(m_fp
, local
);
4963 IncDecBody
<true>(op
, fr
, to
);
4966 inline void OPTBLD_INLINE
VMExecutionContext::iopIncDecN(PC
& pc
) {
4968 DECODE(unsigned char, op
);
4970 TypedValue
* nameCell
= m_stack
.topTV();
4971 TypedValue
* local
= nullptr;
4972 // XXX We're probably not getting warnings totally correct here
4973 lookupd_var(m_fp
, name
, nameCell
, local
);
4974 assert(local
!= nullptr);
4975 IncDecBody
<true>(op
, local
, nameCell
);
4979 inline void OPTBLD_INLINE
VMExecutionContext::iopIncDecG(PC
& pc
) {
4981 DECODE(unsigned char, op
);
4983 TypedValue
* nameCell
= m_stack
.topTV();
4984 TypedValue
* gbl
= nullptr;
4985 // XXX We're probably not getting warnings totally correct here
4986 lookupd_gbl(m_fp
, name
, nameCell
, gbl
);
4987 assert(gbl
!= nullptr);
4988 IncDecBody
<true>(op
, gbl
, nameCell
);
4992 inline void OPTBLD_INLINE
VMExecutionContext::iopIncDecS(PC
& pc
) {
4995 DECODE(unsigned char, op
);
4996 if (!(visible
&& accessible
)) {
4997 raise_error("Invalid static property access: %s::%s",
4998 clsref
->m_data
.pcls
->name()->data(),
5001 tvRefcountedDecRefCell(nameCell
);
5002 IncDecBody
<true>(op
, val
, output
);
5007 inline void OPTBLD_INLINE
VMExecutionContext::iopIncDecM(PC
& pc
) {
5009 DECODE(unsigned char, op
);
5010 DECLARE_SETHELPER_ARGS
5013 if (!setHelperPre
<MoreWarnings
, true, false, false, 0,
5014 VectorLeaveCode::LeaveLast
>(MEMBERHELPERPRE_ARGS
)) {
5016 IncDecNewElem
<true>(tvScratch
, *tvRef
.asTypedValue(), op
, base
, to
);
5023 IncDecElem
<true>(tvScratch
, *tvRef
.asTypedValue(), op
, base
,
5029 Class
* ctx
= arGetContextClass(m_fp
);
5030 IncDecProp
<true>(tvScratch
, *tvRef
.asTypedValue(), ctx
, op
, base
,
5034 default: assert(false);
5038 setHelperPost
<0>(SETHELPERPOST_ARGS
);
5039 Cell
* c1
= m_stack
.allocC();
5040 memcpy(c1
, &to
, sizeof(TypedValue
));
5043 inline void OPTBLD_INLINE
VMExecutionContext::iopBindL(PC
& pc
) {
5046 Ref
* fr
= m_stack
.topV();
5047 TypedValue
* to
= frame_local(m_fp
, local
);
5051 inline void OPTBLD_INLINE
VMExecutionContext::iopBindN(PC
& pc
) {
5054 TypedValue
* fr
= m_stack
.topTV();
5055 TypedValue
* nameTV
= m_stack
.indTV(1);
5056 TypedValue
* to
= nullptr;
5057 lookupd_var(m_fp
, name
, nameTV
, to
);
5058 assert(to
!= nullptr);
5060 memcpy((void*)nameTV
, (void*)fr
, sizeof(TypedValue
));
5065 inline void OPTBLD_INLINE
VMExecutionContext::iopBindG(PC
& pc
) {
5068 TypedValue
* fr
= m_stack
.topTV();
5069 TypedValue
* nameTV
= m_stack
.indTV(1);
5070 TypedValue
* to
= nullptr;
5071 lookupd_gbl(m_fp
, name
, nameTV
, to
);
5072 assert(to
!= nullptr);
5074 memcpy((void*)nameTV
, (void*)fr
, sizeof(TypedValue
));
5079 inline void OPTBLD_INLINE
VMExecutionContext::iopBindS(PC
& pc
) {
5081 TypedValue
* fr
= m_stack
.topTV();
5082 TypedValue
* classref
= m_stack
.indTV(1);
5083 TypedValue
* propn
= m_stack
.indTV(2);
5084 TypedValue
* output
= propn
;
5087 bool visible
, accessible
;
5088 lookup_sprop(m_fp
, classref
, name
, propn
, val
, visible
, accessible
);
5089 if (!(visible
&& accessible
)) {
5090 raise_error("Invalid static property access: %s::%s",
5091 classref
->m_data
.pcls
->name()->data(),
5095 tvRefcountedDecRefCell(propn
);
5096 memcpy(output
, fr
, sizeof(TypedValue
));
5097 m_stack
.ndiscard(2);
5101 inline void OPTBLD_INLINE
VMExecutionContext::iopBindM(PC
& pc
) {
5103 DECLARE_SETHELPER_ARGS
5104 TypedValue
* tv1
= m_stack
.topTV();
5105 if (!setHelperPre
<false, true, false, true, 1,
5106 VectorLeaveCode::ConsumeAll
>(MEMBERHELPERPRE_ARGS
)) {
5107 // Bind the element/property with the var on the top of the stack
5110 setHelperPost
<1>(SETHELPERPOST_ARGS
);
5113 inline void OPTBLD_INLINE
VMExecutionContext::iopUnsetL(PC
& pc
) {
5116 assert(local
< m_fp
->m_func
->numLocals());
5117 TypedValue
* tv
= frame_local(m_fp
, local
);
5118 tvRefcountedDecRef(tv
);
5122 inline void OPTBLD_INLINE
VMExecutionContext::iopUnsetN(PC
& pc
) {
5125 TypedValue
* tv1
= m_stack
.topTV();
5126 TypedValue
* tv
= nullptr;
5127 lookup_var(m_fp
, name
, tv1
, tv
);
5128 assert(!m_fp
->hasInvName());
5129 if (tv
!= nullptr) {
5130 tvRefcountedDecRef(tv
);
5137 inline void OPTBLD_INLINE
VMExecutionContext::iopUnsetG(PC
& pc
) {
5139 TypedValue
* tv1
= m_stack
.topTV();
5140 StringData
* name
= lookup_name(tv1
);
5141 VarEnv
* varEnv
= m_globalVarEnv
;
5142 assert(varEnv
!= nullptr);
5143 varEnv
->unset(name
);
5148 inline void OPTBLD_INLINE
VMExecutionContext::iopUnsetM(PC
& pc
) {
5150 DECLARE_SETHELPER_ARGS
5151 if (!setHelperPre
<false, false, true, false, 0,
5152 VectorLeaveCode::LeaveLast
>(MEMBERHELPERPRE_ARGS
)) {
5158 UnsetElem(base
, curMember
);
5163 Class
* ctx
= arGetContextClass(m_fp
);
5164 UnsetProp(ctx
, base
, curMember
);
5167 default: assert(false);
5170 setHelperPost
<0>(SETHELPERPOST_ARGS
);
5173 inline ActRec
* OPTBLD_INLINE
VMExecutionContext::fPushFuncImpl(
5176 DEBUGGER_IF(phpBreakpointEnabled(func
->name()->data()));
5177 ActRec
* ar
= m_stack
.allocA();
5180 ar
->initNumArgs(numArgs
);
5181 ar
->setVarEnv(nullptr);
5185 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushFunc(PC
& pc
) {
5187 DECODE_IVA(numArgs
);
5188 Cell
* c1
= m_stack
.topC();
5189 const Func
* func
= nullptr;
5190 ObjectData
* origObj
= nullptr;
5191 StringData
* origSd
= nullptr;
5192 if (IS_STRING_TYPE(c1
->m_type
)) {
5193 origSd
= c1
->m_data
.pstr
;
5194 func
= Unit::loadFunc(origSd
);
5195 } else if (c1
->m_type
== KindOfObject
) {
5196 static StringData
* invokeName
= StringData::GetStaticString("__invoke");
5197 origObj
= c1
->m_data
.pobj
;
5198 const Class
* cls
= origObj
->getVMClass();
5199 func
= cls
->lookupMethod(invokeName
);
5200 if (func
== nullptr) {
5201 raise_error(Strings::FUNCTION_NAME_MUST_BE_STRING
);
5204 raise_error(Strings::FUNCTION_NAME_MUST_BE_STRING
);
5206 if (func
== nullptr) {
5207 raise_error("Call to undefined function %s()", c1
->m_data
.pstr
->data());
5209 assert(!origObj
|| !origSd
);
5210 assert(origObj
|| origSd
);
5211 // We've already saved origObj or origSd; we'll use them after
5212 // overwriting the pointer on the stack. Don't refcount it now; defer
5213 // till after we're done with it.
5215 ActRec
* ar
= fPushFuncImpl(func
, numArgs
);
5217 if (func
->attrs() & AttrStatic
&& !func
->isClosureBody()) {
5218 ar
->setClass(origObj
->getVMClass());
5221 ar
->setThis(origObj
);
5222 // Teleport the reference from the destroyed stack cell to the
5223 // ActRec. Don't try this at home.
5226 ar
->setThis(nullptr);
5231 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushFuncD(PC
& pc
) {
5233 DECODE_IVA(numArgs
);
5235 const NamedEntityPair nep
= m_fp
->m_func
->unit()->lookupNamedEntityPairId(id
);
5236 Func
* func
= Unit::loadFunc(nep
.second
, nep
.first
);
5237 if (func
== nullptr) {
5238 raise_error("Call to undefined function %s()",
5239 m_fp
->m_func
->unit()->lookupLitstrId(id
)->data());
5241 ActRec
* ar
= fPushFuncImpl(func
, numArgs
);
5242 ar
->setThis(nullptr);
5245 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushFuncU(PC
& pc
) {
5247 DECODE_IVA(numArgs
);
5249 DECODE(Id
, globalFunc
);
5250 Unit
* unit
= m_fp
->m_func
->unit();
5251 const NamedEntityPair nep
= unit
->lookupNamedEntityPairId(nsFunc
);
5252 Func
* func
= Unit::loadFunc(nep
.second
, nep
.first
);
5253 if (func
== nullptr) {
5254 const NamedEntityPair nep2
= unit
->lookupNamedEntityPairId(globalFunc
);
5255 func
= Unit::loadFunc(nep2
.second
, nep2
.first
);
5256 if (func
== nullptr) {
5257 const char *funcName
= unit
->lookupLitstrId(nsFunc
)->data();
5258 raise_error("Call to undefined function %s()", funcName
);
5261 ActRec
* ar
= fPushFuncImpl(func
, numArgs
);
5262 ar
->setThis(nullptr);
5265 void VMExecutionContext::fPushObjMethodImpl(
5266 Class
* cls
, StringData
* name
, ObjectData
* obj
, int numArgs
) {
5268 LookupResult res
= lookupObjMethod(f
, cls
, name
,
5269 arGetContextClass(getFP()), true);
5271 ActRec
* ar
= m_stack
.allocA();
5274 if (res
== LookupResult::MethodFoundNoThis
) {
5278 assert(res
== LookupResult::MethodFoundWithThis
||
5279 res
== LookupResult::MagicCallFound
);
5280 /* Transfer ownership of obj to the ActRec*/
5283 ar
->initNumArgs(numArgs
);
5284 if (res
== LookupResult::MagicCallFound
) {
5285 ar
->setInvName(name
);
5287 ar
->setVarEnv(NULL
);
5292 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushObjMethod(PC
& pc
) {
5294 DECODE_IVA(numArgs
);
5295 Cell
* c1
= m_stack
.topC(); // Method name.
5296 if (!IS_STRING_TYPE(c1
->m_type
)) {
5297 raise_error(Strings::METHOD_NAME_MUST_BE_STRING
);
5299 Cell
* c2
= m_stack
.indC(1); // Object.
5300 if (c2
->m_type
!= KindOfObject
) {
5301 throw_call_non_object(c1
->m_data
.pstr
->data());
5303 ObjectData
* obj
= c2
->m_data
.pobj
;
5304 Class
* cls
= obj
->getVMClass();
5305 StringData
* name
= c1
->m_data
.pstr
;
5306 // We handle decReffing obj and name in fPushObjMethodImpl
5307 m_stack
.ndiscard(2);
5308 fPushObjMethodImpl(cls
, name
, obj
, numArgs
);
5311 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushObjMethodD(PC
& pc
) {
5313 DECODE_IVA(numArgs
);
5314 DECODE_LITSTR(name
);
5315 Cell
* c1
= m_stack
.topC();
5316 if (c1
->m_type
!= KindOfObject
) {
5317 throw_call_non_object(name
->data());
5319 ObjectData
* obj
= c1
->m_data
.pobj
;
5320 Class
* cls
= obj
->getVMClass();
5321 // We handle decReffing obj in fPushObjMethodImpl
5323 fPushObjMethodImpl(cls
, name
, obj
, numArgs
);
5326 template<bool forwarding
>
5327 void VMExecutionContext::pushClsMethodImpl(Class
* cls
,
5332 LookupResult res
= lookupClsMethod(f
, cls
, name
, obj
,
5333 arGetContextClass(getFP()), true);
5334 if (res
== LookupResult::MethodFoundNoThis
||
5335 res
== LookupResult::MagicCallStaticFound
) {
5339 assert(res
== LookupResult::MethodFoundWithThis
||
5340 res
== LookupResult::MagicCallFound
);
5344 ActRec
* ar
= m_stack
.allocA();
5353 /* Propogate the current late bound class if there is one, */
5354 /* otherwise use the class given by this instruction's input */
5355 if (m_fp
->hasThis()) {
5356 cls
= m_fp
->getThis()->getVMClass();
5357 } else if (m_fp
->hasClass()) {
5358 cls
= m_fp
->getClass();
5363 ar
->initNumArgs(numArgs
);
5364 if (res
== LookupResult::MagicCallFound
||
5365 res
== LookupResult::MagicCallStaticFound
) {
5366 ar
->setInvName(name
);
5368 ar
->setVarEnv(nullptr);
5369 decRefStr(const_cast<StringData
*>(name
));
5373 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushClsMethod(PC
& pc
) {
5375 DECODE_IVA(numArgs
);
5376 Cell
* c1
= m_stack
.indC(1); // Method name.
5377 if (!IS_STRING_TYPE(c1
->m_type
)) {
5378 raise_error(Strings::FUNCTION_NAME_MUST_BE_STRING
);
5380 TypedValue
* tv
= m_stack
.top();
5381 assert(tv
->m_type
== KindOfClass
);
5382 Class
* cls
= tv
->m_data
.pcls
;
5383 StringData
* name
= c1
->m_data
.pstr
;
5384 // pushClsMethodImpl will take care of decReffing name
5385 m_stack
.ndiscard(2);
5386 assert(cls
&& name
);
5387 ObjectData
* obj
= m_fp
->hasThis() ? m_fp
->getThis() : nullptr;
5388 pushClsMethodImpl
<false>(cls
, name
, obj
, numArgs
);
5391 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushClsMethodD(PC
& pc
) {
5393 DECODE_IVA(numArgs
);
5394 DECODE_LITSTR(name
);
5395 DECODE(Id
, classId
);
5396 const NamedEntityPair
&nep
=
5397 m_fp
->m_func
->unit()->lookupNamedEntityPairId(classId
);
5398 Class
* cls
= Unit::loadClass(nep
.second
, nep
.first
);
5399 if (cls
== nullptr) {
5400 raise_error(Strings::UNKNOWN_CLASS
, nep
.first
->data());
5402 ObjectData
* obj
= m_fp
->hasThis() ? m_fp
->getThis() : nullptr;
5403 pushClsMethodImpl
<false>(cls
, name
, obj
, numArgs
);
5406 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushClsMethodF(PC
& pc
) {
5408 DECODE_IVA(numArgs
);
5409 Cell
* c1
= m_stack
.indC(1); // Method name.
5410 if (!IS_STRING_TYPE(c1
->m_type
)) {
5411 raise_error(Strings::FUNCTION_NAME_MUST_BE_STRING
);
5413 TypedValue
* tv
= m_stack
.top();
5414 assert(tv
->m_type
== KindOfClass
);
5415 Class
* cls
= tv
->m_data
.pcls
;
5417 StringData
* name
= c1
->m_data
.pstr
;
5418 // pushClsMethodImpl will take care of decReffing name
5419 m_stack
.ndiscard(2);
5420 ObjectData
* obj
= m_fp
->hasThis() ? m_fp
->getThis() : nullptr;
5421 pushClsMethodImpl
<true>(cls
, name
, obj
, numArgs
);
5424 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushCtor(PC
& pc
) {
5426 DECODE_IVA(numArgs
);
5427 TypedValue
* tv
= m_stack
.topTV();
5428 assert(tv
->m_type
== KindOfClass
);
5429 Class
* cls
= tv
->m_data
.pcls
;
5430 assert(cls
!= nullptr);
5433 LookupResult res UNUSED
= lookupCtorMethod(f
, cls
, true);
5434 assert(res
== LookupResult::MethodFoundWithThis
);
5435 // Replace input with uninitialized instance.
5436 ObjectData
* this_
= newInstance(cls
);
5437 TRACE(2, "FPushCtor: just new'ed an instance of class %s: %p\n",
5438 cls
->name()->data(), this_
);
5439 this_
->incRefCount();
5440 this_
->incRefCount();
5441 tv
->m_type
= KindOfObject
;
5442 tv
->m_data
.pobj
= this_
;
5443 // Push new activation record.
5444 ActRec
* ar
= m_stack
.allocA();
5448 ar
->initNumArgs(numArgs
, true /* isFPushCtor */);
5450 ar
->setVarEnv(nullptr);
5453 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushCtorD(PC
& pc
) {
5455 DECODE_IVA(numArgs
);
5457 const NamedEntityPair
&nep
=
5458 m_fp
->m_func
->unit()->lookupNamedEntityPairId(id
);
5459 Class
* cls
= Unit::loadClass(nep
.second
, nep
.first
);
5460 if (cls
== nullptr) {
5461 raise_error(Strings::UNKNOWN_CLASS
,
5462 m_fp
->m_func
->unit()->lookupLitstrId(id
)->data());
5466 LookupResult res UNUSED
= lookupCtorMethod(f
, cls
, true);
5467 assert(res
== LookupResult::MethodFoundWithThis
);
5468 // Push uninitialized instance.
5469 ObjectData
* this_
= newInstance(cls
);
5470 TRACE(2, "FPushCtorD: new'ed an instance of class %s: %p\n",
5471 cls
->name()->data(), this_
);
5472 this_
->incRefCount();
5473 m_stack
.pushObject(this_
);
5474 // Push new activation record.
5475 ActRec
* ar
= m_stack
.allocA();
5479 ar
->initNumArgs(numArgs
, true /* isFPushCtor */);
5480 ar
->setVarEnv(nullptr);
5483 inline void OPTBLD_INLINE
VMExecutionContext::iopDecodeCufIter(PC
& pc
) {
5487 DECODE(Offset
, offset
);
5489 Iter
* it
= frame_iter(m_fp
, itId
);
5490 CufIter
&cit
= it
->cuf();
5492 ObjectData
* obj
= nullptr;
5493 HPHP::Class
* cls
= nullptr;
5494 StringData
* invName
= nullptr;
5495 TypedValue
*func
= m_stack
.topTV();
5498 if (m_fp
->m_func
->isBuiltin()) {
5499 ar
= getOuterVMFrame(ar
);
5501 const Func
* f
= vm_decode_function(tvAsVariant(func
),
5507 pc
= origPc
+ offset
;
5516 cit
.setName(invName
);
5521 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushCufIter(PC
& pc
) {
5523 DECODE_IVA(numArgs
);
5526 Iter
* it
= frame_iter(m_fp
, itId
);
5528 auto f
= it
->cuf().func();
5529 auto o
= it
->cuf().ctx();
5530 auto n
= it
->cuf().name();
5532 ActRec
* ar
= m_stack
.allocA();
5535 ar
->m_this
= (ObjectData
*)o
;
5536 if (o
&& !(uintptr_t(o
) & 1)) ar
->m_this
->incRefCount();
5541 ar
->setVarEnv(nullptr);
5543 ar
->initNumArgs(numArgs
, false /* isFPushCtor */);
5546 inline void OPTBLD_INLINE
VMExecutionContext::doFPushCuf(PC
& pc
,
5550 DECODE_IVA(numArgs
);
5552 TypedValue func
= m_stack
.topTV()[safe
];
5554 ObjectData
* obj
= nullptr;
5555 HPHP::Class
* cls
= nullptr;
5556 StringData
* invName
= nullptr;
5558 const Func
* f
= vm_decode_function(tvAsVariant(&func
), getFP(),
5563 if (safe
) m_stack
.topTV()[1] = m_stack
.topTV()[0];
5564 m_stack
.ndiscard(1);
5566 f
= SystemLib::s_nullFunc
;
5568 m_stack
.pushFalse();
5574 ActRec
* ar
= m_stack
.allocA();
5583 ar
->setThis(nullptr);
5585 ar
->initNumArgs(numArgs
, false /* isFPushCtor */);
5587 ar
->setInvName(invName
);
5589 ar
->setVarEnv(nullptr);
5591 tvRefcountedDecRef(&func
);
5594 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushCuf(PC
& pc
) {
5595 doFPushCuf(pc
, false, false);
5598 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushCufF(PC
& pc
) {
5599 doFPushCuf(pc
, true, false);
5602 inline void OPTBLD_INLINE
VMExecutionContext::iopFPushCufSafe(PC
& pc
) {
5603 doFPushCuf(pc
, false, true);
5606 static inline ActRec
* arFromInstr(TypedValue
* sp
, const Op
* pc
) {
5607 return arFromSpOffset((ActRec
*)sp
, instrSpToArDelta(pc
));
5610 inline void OPTBLD_INLINE
VMExecutionContext::iopFPassC(PC
& pc
) {
5612 ActRec
* ar
= arFromInstr(m_stack
.top(), (Op
*)pc
);
5615 DECODE_IVA(paramId
);
5617 assert(paramId
< ar
->numArgs());
5621 #define FPASSC_CHECKED_PRELUDE \
5622 ActRec* ar = arFromInstr(m_stack.top(), (Op*)pc); \
5624 DECODE_IVA(paramId); \
5625 assert(paramId < ar->numArgs()); \
5626 const Func* func = ar->m_func;
5628 inline void OPTBLD_INLINE
VMExecutionContext::iopFPassCW(PC
& pc
) {
5629 FPASSC_CHECKED_PRELUDE
5630 if (func
->mustBeRef(paramId
)) {
5631 TRACE(1, "FPassCW: function %s(%d) param %d is by reference, "
5632 "raising a strict warning (attr:0x%x)\n",
5633 func
->name()->data(), func
->numParams(), paramId
,
5634 func
->info() ? func
->info()->attribute
: 0);
5635 raise_strict_warning("Only variables should be passed by reference");
5639 inline void OPTBLD_INLINE
VMExecutionContext::iopFPassCE(PC
& pc
) {
5640 FPASSC_CHECKED_PRELUDE
5641 if (func
->mustBeRef(paramId
)) {
5642 TRACE(1, "FPassCE: function %s(%d) param %d is by reference, "
5643 "throwing a fatal error (attr:0x%x)\n",
5644 func
->name()->data(), func
->numParams(), paramId
,
5645 func
->info() ? func
->info()->attribute
: 0);
5646 raise_error("Cannot pass parameter %d by reference", paramId
+1);
5650 #undef FPASSC_CHECKED_PRELUDE
5652 inline void OPTBLD_INLINE
VMExecutionContext::iopFPassV(PC
& pc
) {
5653 ActRec
* ar
= arFromInstr(m_stack
.top(), (Op
*)pc
);
5655 DECODE_IVA(paramId
);
5656 assert(paramId
< ar
->numArgs());
5657 const Func
* func
= ar
->m_func
;
5658 if (!func
->byRef(paramId
)) {
5663 inline void OPTBLD_INLINE
VMExecutionContext::iopFPassR(PC
& pc
) {
5664 ActRec
* ar
= arFromInstr(m_stack
.top(), (Op
*)pc
);
5666 DECODE_IVA(paramId
);
5667 assert(paramId
< ar
->numArgs());
5668 const Func
* func
= ar
->m_func
;
5669 if (func
->byRef(paramId
)) {
5670 TypedValue
* tv
= m_stack
.topTV();
5671 if (tv
->m_type
!= KindOfRef
) {
5675 if (m_stack
.topTV()->m_type
== KindOfRef
) {
5681 inline void OPTBLD_INLINE
VMExecutionContext::iopFPassL(PC
& pc
) {
5682 ActRec
* ar
= arFromInstr(m_stack
.top(), (Op
*)pc
);
5684 DECODE_IVA(paramId
);
5686 assert(paramId
< ar
->numArgs());
5687 TypedValue
* fr
= frame_local(m_fp
, local
);
5688 TypedValue
* to
= m_stack
.allocTV();
5689 if (!ar
->m_func
->byRef(paramId
)) {
5690 cgetl_body(m_fp
, fr
, to
, local
);
5696 inline void OPTBLD_INLINE
VMExecutionContext::iopFPassN(PC
& pc
) {
5697 ActRec
* ar
= arFromInstr(m_stack
.top(), (Op
*)pc
);
5700 DECODE_IVA(paramId
);
5701 assert(paramId
< ar
->numArgs());
5702 if (!ar
->m_func
->byRef(paramId
)) {
5709 inline void OPTBLD_INLINE
VMExecutionContext::iopFPassG(PC
& pc
) {
5710 ActRec
* ar
= arFromInstr(m_stack
.top(), (Op
*)pc
);
5713 DECODE_IVA(paramId
);
5714 assert(paramId
< ar
->numArgs());
5715 if (!ar
->m_func
->byRef(paramId
)) {
5722 inline void OPTBLD_INLINE
VMExecutionContext::iopFPassS(PC
& pc
) {
5723 ActRec
* ar
= arFromInstr(m_stack
.top(), (Op
*)pc
);
5726 DECODE_IVA(paramId
);
5727 assert(paramId
< ar
->numArgs());
5728 if (!ar
->m_func
->byRef(paramId
)) {
5735 void VMExecutionContext::iopFPassM(PC
& pc
) {
5736 ActRec
* ar
= arFromInstr(m_stack
.top(), (Op
*)pc
);
5738 DECODE_IVA(paramId
);
5739 assert(paramId
< ar
->numArgs());
5740 if (!ar
->m_func
->byRef(paramId
)) {
5741 DECLARE_GETHELPER_ARGS
5742 getHelper(GETHELPER_ARGS
);
5743 if (tvRet
->m_type
== KindOfRef
) {
5747 DECLARE_SETHELPER_ARGS
5748 TypedValue
* tv1
= m_stack
.allocTV();
5750 if (!setHelperPre
<false, true, false, true, 1,
5751 VectorLeaveCode::ConsumeAll
>(MEMBERHELPERPRE_ARGS
)) {
5752 if (base
->m_type
!= KindOfRef
) {
5755 refDup(*base
, *tv1
);
5760 setHelperPost
<1>(SETHELPERPOST_ARGS
);
5764 bool VMExecutionContext::doFCall(ActRec
* ar
, PC
& pc
) {
5765 assert(getOuterVMFrame(ar
) == m_fp
);
5767 reinterpret_cast<uintptr_t>(tx()->uniqueStubs
.retHelper
);
5768 assert(isReturnHelper(ar
->m_savedRip
));
5769 TRACE(3, "FCall: pc %p func %p base %d\n", m_pc
,
5770 m_fp
->m_func
->unit()->entry(),
5771 int(m_fp
->m_func
->base()));
5772 ar
->m_soff
= m_fp
->m_func
->unit()->offsetOf(pc
)
5773 - (uintptr_t)m_fp
->m_func
->base();
5774 assert(pcOff() >= m_fp
->m_func
->base());
5775 prepareFuncEntry(ar
, pc
);
5777 if (EventHook::FunctionEnter(ar
, EventHook::NormalFunc
)) return true;
5782 inline void OPTBLD_INLINE
VMExecutionContext::iopFCall(PC
& pc
) {
5783 ActRec
* ar
= arFromInstr(m_stack
.top(), (Op
*)pc
);
5785 DECODE_IVA(numArgs
);
5786 assert(numArgs
== ar
->numArgs());
5787 checkStack(m_stack
, ar
->m_func
);
5791 inline void OPTBLD_INLINE
VMExecutionContext::iopFCallBuiltin(PC
& pc
) {
5793 DECODE_IVA(numArgs
);
5794 DECODE_IVA(numNonDefault
);
5796 const NamedEntity
* ne
= m_fp
->m_func
->unit()->lookupNamedEntityId(id
);
5797 Func
* func
= Unit::lookupFunc(ne
);
5798 if (func
== nullptr) {
5799 raise_error("Undefined function: %s",
5800 m_fp
->m_func
->unit()->lookupLitstrId(id
)->data());
5802 TypedValue
* args
= m_stack
.indTV(numArgs
-1);
5804 if (Native::coerceFCallArgs(args
, numArgs
, numNonDefault
, func
)) {
5805 Native::callFunc(func
, nullptr, args
, numArgs
, ret
);
5807 ret
.m_type
= KindOfNull
;
5810 frame_free_args(args
, numNonDefault
);
5811 m_stack
.ndiscard(numArgs
);
5812 tvCopy(ret
, *m_stack
.allocTV());
5815 bool VMExecutionContext::prepareArrayArgs(ActRec
* ar
, Array
& arrayArgs
) {
5816 if (UNLIKELY(ar
->hasInvName())) {
5817 m_stack
.pushStringNoRc(ar
->getInvName());
5818 if (UNLIKELY(!arrayArgs
.get()->isVectorData())) {
5819 arrayArgs
= arrayArgs
.values();
5821 m_stack
.pushArray(arrayArgs
.get());
5827 ArrayData
* args
= arrayArgs
.get();
5829 int nargs
= args
->size();
5830 const Func
* f
= ar
->m_func
;
5831 int nparams
= f
->numParams();
5832 int extra
= nargs
- nparams
;
5837 ssize_t pos
= args
->iter_begin();
5838 for (int i
= 0; i
< nparams
; ++i
) {
5839 TypedValue
* from
= const_cast<TypedValue
*>(
5840 args
->getValueRef(pos
).asTypedValue());
5841 TypedValue
* to
= m_stack
.allocTV();
5842 if (LIKELY(!f
->byRef(i
))) {
5843 cellDup(*tvToCell(from
), *to
);
5844 } else if (LIKELY(from
->m_type
== KindOfRef
&&
5845 from
->m_data
.pref
->m_count
>= 2)) {
5849 raise_warning("Parameter %d to %s() expected to be a reference, "
5850 "value given", i
+ 1, f
->fullName()->data());
5852 // If the user error handler throws an exception, discard the
5853 // uninitialized TypedValue at the top of the eval stack so
5854 // that the unwinder doesn't choke
5858 if (skipCufOnInvalidParams
) {
5860 while (i
--) m_stack
.popTV();
5865 cellDup(*tvToCell(from
), *to
);
5867 pos
= args
->iter_advance(pos
);
5869 if (extra
&& (ar
->m_func
->attrs() & AttrMayUseVV
)) {
5870 ExtraArgs
* extraArgs
= ExtraArgs::allocateUninit(extra
);
5871 for (int i
= 0; i
< extra
; ++i
) {
5872 TypedValue
* to
= extraArgs
->getExtraArg(i
);
5873 tvDup(*args
->getValueRef(pos
).asTypedValue(), *to
);
5874 if (to
->m_type
== KindOfRef
&& to
->m_data
.pref
->m_count
== 2) {
5877 pos
= args
->iter_advance(pos
);
5879 ar
->setExtraArgs(extraArgs
);
5880 ar
->initNumArgs(nargs
);
5882 ar
->initNumArgs(nparams
);
5888 static void cleanupParamsAndActRec(Stack
& stack
,
5890 ExtraArgs
* extraArgs
) {
5891 assert(stack
.top() + (extraArgs
?
5892 ar
->m_func
->numParams() :
5893 ar
->numArgs()) == (void*)ar
);
5895 const int numExtra
= ar
->numArgs() - ar
->m_func
->numParams();
5896 ExtraArgs::deallocate(extraArgs
, numExtra
);
5898 while (stack
.top() != (void*)ar
) {
5904 bool VMExecutionContext::doFCallArray(PC
& pc
) {
5905 ActRec
* ar
= (ActRec
*)(m_stack
.top() + 1);
5906 assert(ar
->numArgs() == 1);
5908 Cell
* c1
= m_stack
.topC();
5909 if (skipCufOnInvalidParams
&& UNLIKELY(c1
->m_type
!= KindOfArray
)) {
5911 // this is what we /should/ do, but our code base depends
5912 // on the broken behavior of casting the second arg to an
5914 cleanupParamsAndActRec(m_stack
, ar
, nullptr);
5916 raise_warning("call_user_func_array() expects parameter 2 to be array");
5920 const Func
* func
= ar
->m_func
;
5922 Array
args(LIKELY(c1
->m_type
== KindOfArray
) ? c1
->m_data
.parr
:
5923 tvAsVariant(c1
).toArray().get());
5925 checkStack(m_stack
, func
);
5927 assert(ar
->m_savedRbp
== (uint64_t)m_fp
);
5928 assert(!ar
->m_func
->isGenerator());
5930 reinterpret_cast<uintptr_t>(tx()->uniqueStubs
.retHelper
);
5931 assert(isReturnHelper(ar
->m_savedRip
));
5932 TRACE(3, "FCallArray: pc %p func %p base %d\n", m_pc
,
5933 m_fp
->unit()->entry(),
5934 int(m_fp
->m_func
->base()));
5935 ar
->m_soff
= m_fp
->unit()->offsetOf(pc
)
5936 - (uintptr_t)m_fp
->m_func
->base();
5937 assert(pcOff() > m_fp
->m_func
->base());
5939 if (UNLIKELY(!prepareArrayArgs(ar
, args
))) return false;
5942 if (UNLIKELY(!(prepareFuncEntry(ar
, pc
)))) {
5946 if (UNLIKELY(!EventHook::FunctionEnter(ar
, EventHook::NormalFunc
))) {
5953 bool VMExecutionContext::doFCallArrayTC(PC pc
) {
5955 DEBUG_ONLY
DECLARE_STACK_POINTER(sp
);
5956 // native stack pointer should be 16-byte aligned.
5957 assert(uintptr_t(sp
) % 16 == 0);
5959 assert(tl_regState
== VMRegState::DIRTY
);
5960 tl_regState
= VMRegState::CLEAN
;
5961 auto const ret
= doFCallArray(pc
);
5962 tl_regState
= VMRegState::DIRTY
;
5966 inline void OPTBLD_INLINE
VMExecutionContext::iopFCallArray(PC
& pc
) {
5968 (void)doFCallArray(pc
);
5971 inline void OPTBLD_INLINE
VMExecutionContext::iopCufSafeArray(PC
& pc
) {
5974 ret
.append(tvAsVariant(m_stack
.top() + 1));
5975 ret
.appendWithRef(tvAsVariant(m_stack
.top() + 0));
5978 tvAsVariant(m_stack
.top()) = ret
;
5981 inline void OPTBLD_INLINE
VMExecutionContext::iopCufSafeReturn(PC
& pc
) {
5983 bool ok
= cellToBool(*tvToCell(m_stack
.top() + 1));
5984 tvRefcountedDecRef(m_stack
.top() + 1);
5985 tvRefcountedDecRef(m_stack
.top() + (ok
? 2 : 0));
5986 if (ok
) m_stack
.top()[2] = m_stack
.top()[0];
5987 m_stack
.ndiscard(2);
5990 inline bool VMExecutionContext::initIterator(PC
& pc
, PC
& origPc
, Iter
* it
,
5991 Offset offset
, Cell
* c1
) {
5992 bool hasElems
= it
->init(c1
);
6000 inline void OPTBLD_INLINE
VMExecutionContext::iopIterInit(PC
& pc
) {
6004 DECODE(Offset
, offset
);
6006 Cell
* c1
= m_stack
.topC();
6007 Iter
* it
= frame_iter(m_fp
, itId
);
6008 TypedValue
* tv1
= frame_local(m_fp
, val
);
6009 if (initIterator(pc
, origPc
, it
, offset
, c1
)) {
6010 tvAsVariant(tv1
) = it
->arr().second();
6014 inline void OPTBLD_INLINE
VMExecutionContext::iopIterInitK(PC
& pc
) {
6018 DECODE(Offset
, offset
);
6021 Cell
* c1
= m_stack
.topC();
6022 Iter
* it
= frame_iter(m_fp
, itId
);
6023 TypedValue
* tv1
= frame_local(m_fp
, val
);
6024 TypedValue
* tv2
= frame_local(m_fp
, key
);
6025 if (initIterator(pc
, origPc
, it
, offset
, c1
)) {
6026 tvAsVariant(tv1
) = it
->arr().second();
6027 tvAsVariant(tv2
) = it
->arr().first();
6031 inline void OPTBLD_INLINE
VMExecutionContext::iopWIterInit(PC
& pc
) {
6035 DECODE(Offset
, offset
);
6037 Cell
* c1
= m_stack
.topC();
6038 Iter
* it
= frame_iter(m_fp
, itId
);
6039 TypedValue
* tv1
= frame_local(m_fp
, val
);
6040 if (initIterator(pc
, origPc
, it
, offset
, c1
)) {
6041 tvAsVariant(tv1
) = withRefBind(it
->arr().secondRef());
6045 inline void OPTBLD_INLINE
VMExecutionContext::iopWIterInitK(PC
& pc
) {
6049 DECODE(Offset
, offset
);
6052 Cell
* c1
= m_stack
.topC();
6053 Iter
* it
= frame_iter(m_fp
, itId
);
6054 TypedValue
* tv1
= frame_local(m_fp
, val
);
6055 TypedValue
* tv2
= frame_local(m_fp
, key
);
6056 if (initIterator(pc
, origPc
, it
, offset
, c1
)) {
6057 tvAsVariant(tv1
) = withRefBind(it
->arr().secondRef());
6058 tvAsVariant(tv2
) = it
->arr().first();
6063 inline bool VMExecutionContext::initIteratorM(PC
& pc
, PC
& origPc
, Iter
* it
,
6064 Offset offset
, Ref
* r1
,
6067 bool hasElems
= false;
6068 TypedValue
* rtv
= r1
->m_data
.pref
->tv();
6069 if (rtv
->m_type
== KindOfArray
) {
6070 hasElems
= new_miter_array_key(it
, r1
->m_data
.pref
, val
, key
);
6071 } else if (rtv
->m_type
== KindOfObject
) {
6072 Class
* ctx
= arGetContextClass(g_vmContext
->getFP());
6073 hasElems
= new_miter_object(it
, r1
->m_data
.pref
, ctx
, val
, key
);
6075 hasElems
= new_miter_other(it
, r1
->m_data
.pref
);
6086 inline void OPTBLD_INLINE
VMExecutionContext::iopMIterInit(PC
& pc
) {
6090 DECODE(Offset
, offset
);
6092 Ref
* r1
= m_stack
.topV();
6093 assert(r1
->m_type
== KindOfRef
);
6094 Iter
* it
= frame_iter(m_fp
, itId
);
6095 TypedValue
* tv1
= frame_local(m_fp
, val
);
6096 initIteratorM(pc
, origPc
, it
, offset
, r1
, tv1
, nullptr);
6099 inline void OPTBLD_INLINE
VMExecutionContext::iopMIterInitK(PC
& pc
) {
6103 DECODE(Offset
, offset
);
6106 Ref
* r1
= m_stack
.topV();
6107 assert(r1
->m_type
== KindOfRef
);
6108 Iter
* it
= frame_iter(m_fp
, itId
);
6109 TypedValue
* tv1
= frame_local(m_fp
, val
);
6110 TypedValue
* tv2
= frame_local(m_fp
, key
);
6111 initIteratorM(pc
, origPc
, it
, offset
, r1
, tv1
, tv2
);
6114 inline void OPTBLD_INLINE
VMExecutionContext::iopIterNext(PC
& pc
) {
6118 DECODE(Offset
, offset
);
6120 Iter
* it
= frame_iter(m_fp
, itId
);
6121 TypedValue
* tv1
= frame_local(m_fp
, val
);
6124 tvAsVariant(tv1
) = it
->arr().second();
6128 inline void OPTBLD_INLINE
VMExecutionContext::iopIterNextK(PC
& pc
) {
6132 DECODE(Offset
, offset
);
6135 Iter
* it
= frame_iter(m_fp
, itId
);
6136 TypedValue
* tv1
= frame_local(m_fp
, val
);
6137 TypedValue
* tv2
= frame_local(m_fp
, key
);
6140 tvAsVariant(tv1
) = it
->arr().second();
6141 tvAsVariant(tv2
) = it
->arr().first();
6145 inline void OPTBLD_INLINE
VMExecutionContext::iopWIterNext(PC
& pc
) {
6149 DECODE(Offset
, offset
);
6151 Iter
* it
= frame_iter(m_fp
, itId
);
6152 TypedValue
* tv1
= frame_local(m_fp
, val
);
6155 tvAsVariant(tv1
) = withRefBind(it
->arr().secondRef());
6159 inline void OPTBLD_INLINE
VMExecutionContext::iopWIterNextK(PC
& pc
) {
6163 DECODE(Offset
, offset
);
6166 Iter
* it
= frame_iter(m_fp
, itId
);
6167 TypedValue
* tv1
= frame_local(m_fp
, val
);
6168 TypedValue
* tv2
= frame_local(m_fp
, key
);
6171 tvAsVariant(tv1
) = withRefBind(it
->arr().secondRef());
6172 tvAsVariant(tv2
) = it
->arr().first();
6176 inline void OPTBLD_INLINE
VMExecutionContext::iopMIterNext(PC
& pc
) {
6180 DECODE(Offset
, offset
);
6182 Iter
* it
= frame_iter(m_fp
, itId
);
6183 TypedValue
* tv1
= frame_local(m_fp
, val
);
6184 if (miter_next_key(it
, tv1
, nullptr)) {
6189 inline void OPTBLD_INLINE
VMExecutionContext::iopMIterNextK(PC
& pc
) {
6193 DECODE(Offset
, offset
);
6196 Iter
* it
= frame_iter(m_fp
, itId
);
6197 TypedValue
* tv1
= frame_local(m_fp
, val
);
6198 TypedValue
* tv2
= frame_local(m_fp
, key
);
6199 if (miter_next_key(it
, tv1
, tv2
)) {
6204 inline void OPTBLD_INLINE
VMExecutionContext::iopIterFree(PC
& pc
) {
6207 Iter
* it
= frame_iter(m_fp
, itId
);
6211 inline void OPTBLD_INLINE
VMExecutionContext::iopMIterFree(PC
& pc
) {
6214 Iter
* it
= frame_iter(m_fp
, itId
);
6218 inline void OPTBLD_INLINE
VMExecutionContext::iopCIterFree(PC
& pc
) {
6221 Iter
* it
= frame_iter(m_fp
, itId
);
6225 inline void OPTBLD_INLINE
inclOp(VMExecutionContext
*ec
, PC
&pc
,
6226 InclOpFlags flags
) {
6228 Cell
* c1
= ec
->m_stack
.topC();
6229 String
path(prepareKey(c1
));
6231 TRACE(2, "inclOp %s %s %s %s \"%s\"\n",
6232 flags
& InclOpOnce
? "Once" : "",
6233 flags
& InclOpDocRoot
? "DocRoot" : "",
6234 flags
& InclOpRelative
? "Relative" : "",
6235 flags
& InclOpFatal
? "Fatal" : "",
6238 Unit
* u
= flags
& (InclOpDocRoot
|InclOpRelative
) ?
6239 ec
->evalIncludeRoot(path
.get(), flags
, &initial
) :
6240 ec
->evalInclude(path
.get(), ec
->m_fp
->m_func
->unit()->filepath(), &initial
);
6243 ((flags
& InclOpFatal
) ?
6244 (void (*)(const char *, ...))raise_error
:
6245 (void (*)(const char *, ...))raise_warning
)("File not found: %s",
6247 ec
->m_stack
.pushFalse();
6249 if (!(flags
& InclOpOnce
) || initial
) {
6250 ec
->evalUnit(u
, pc
, EventHook::PseudoMain
);
6252 Stats::inc(Stats::PseudoMain_Guarded
);
6253 ec
->m_stack
.pushTrue();
6258 inline void OPTBLD_INLINE
VMExecutionContext::iopIncl(PC
& pc
) {
6259 inclOp(this, pc
, InclOpDefault
);
6262 inline void OPTBLD_INLINE
VMExecutionContext::iopInclOnce(PC
& pc
) {
6263 inclOp(this, pc
, InclOpOnce
);
6266 inline void OPTBLD_INLINE
VMExecutionContext::iopReq(PC
& pc
) {
6267 inclOp(this, pc
, InclOpFatal
);
6270 inline void OPTBLD_INLINE
VMExecutionContext::iopReqOnce(PC
& pc
) {
6271 inclOp(this, pc
, InclOpFatal
| InclOpOnce
);
6274 inline void OPTBLD_INLINE
VMExecutionContext::iopReqDoc(PC
& pc
) {
6275 inclOp(this, pc
, InclOpFatal
| InclOpOnce
| InclOpDocRoot
);
6278 inline void OPTBLD_INLINE
VMExecutionContext::iopEval(PC
& pc
) {
6280 Cell
* c1
= m_stack
.topC();
6281 String
code(prepareKey(c1
));
6282 String prefixedCode
= concat("<?php ", code
);
6283 Unit
* unit
= compileEvalString(prefixedCode
.get());
6284 if (unit
== nullptr) {
6285 raise_error("Syntax error in eval()");
6288 evalUnit(unit
, pc
, EventHook::Eval
);
6291 inline void OPTBLD_INLINE
VMExecutionContext::iopDefFunc(PC
& pc
) {
6294 Func
* f
= m_fp
->m_func
->unit()->lookupFuncId(fid
);
6298 inline void OPTBLD_INLINE
VMExecutionContext::iopDefCls(PC
& pc
) {
6301 PreClass
* c
= m_fp
->m_func
->unit()->lookupPreClassId(cid
);
6305 inline void OPTBLD_INLINE
VMExecutionContext::iopDefTypedef(PC
& pc
) {
6308 m_fp
->m_func
->unit()->defTypedef(tid
);
6311 static inline void checkThis(ActRec
* fp
) {
6312 if (!fp
->hasThis()) {
6313 raise_error(Strings::FATAL_NULL_THIS
);
6317 inline void OPTBLD_INLINE
VMExecutionContext::iopThis(PC
& pc
) {
6320 ObjectData
* this_
= m_fp
->getThis();
6321 m_stack
.pushObject(this_
);
6324 inline void OPTBLD_INLINE
VMExecutionContext::iopBareThis(PC
& pc
) {
6326 DECODE(unsigned char, notice
);
6327 if (m_fp
->hasThis()) {
6328 ObjectData
* this_
= m_fp
->getThis();
6329 m_stack
.pushObject(this_
);
6332 if (notice
) raise_notice(Strings::WARN_NULL_THIS
);
6336 inline void OPTBLD_INLINE
VMExecutionContext::iopCheckThis(PC
& pc
) {
6341 inline void OPTBLD_INLINE
VMExecutionContext::iopInitThisLoc(PC
& pc
) {
6344 TypedValue
* thisLoc
= frame_local(m_fp
, id
);
6345 tvRefcountedDecRef(thisLoc
);
6346 if (m_fp
->hasThis()) {
6347 thisLoc
->m_data
.pobj
= m_fp
->getThis();
6348 thisLoc
->m_type
= KindOfObject
;
6351 tvWriteUninit(thisLoc
);
6356 * Helper for StaticLoc and StaticLocInit.
6359 lookupStatic(StringData
* name
,
6361 TypedValue
*&val
, bool& inited
) {
6362 HphpArray
* map
= get_static_locals(fp
);
6363 assert(map
!= nullptr);
6364 val
= map
->nvGet(name
);
6365 if (val
== nullptr) {
6368 map
->set(name
, tvAsCVarRef(&tv
), false);
6369 val
= map
->nvGet(name
);
6376 inline void OPTBLD_INLINE
VMExecutionContext::iopStaticLoc(PC
& pc
) {
6378 DECODE_IVA(localId
);
6380 TypedValue
* fr
= nullptr;
6382 lookupStatic(var
, m_fp
, fr
, inited
);
6383 assert(fr
!= nullptr);
6384 if (fr
->m_type
!= KindOfRef
) {
6388 TypedValue
* tvLocal
= frame_local(m_fp
, localId
);
6389 tvBind(fr
, tvLocal
);
6393 m_stack
.pushFalse();
6397 inline void OPTBLD_INLINE
VMExecutionContext::iopStaticLocInit(PC
& pc
) {
6399 DECODE_IVA(localId
);
6401 TypedValue
* fr
= nullptr;
6403 lookupStatic(var
, m_fp
, fr
, inited
);
6404 assert(fr
!= nullptr);
6406 Cell
* initVal
= m_stack
.topC();
6407 cellDup(*initVal
, *fr
);
6409 if (fr
->m_type
!= KindOfRef
) {
6413 TypedValue
* tvLocal
= frame_local(m_fp
, localId
);
6414 tvBind(fr
, tvLocal
);
6418 inline void OPTBLD_INLINE
VMExecutionContext::iopCatch(PC
& pc
) {
6420 assert(m_faults
.size() > 0);
6421 Fault fault
= m_faults
.back();
6422 m_faults
.pop_back();
6423 assert(fault
.m_faultType
== Fault::Type::UserException
);
6424 m_stack
.pushObjectNoRc(fault
.m_userException
);
6427 inline void OPTBLD_INLINE
VMExecutionContext::iopLateBoundCls(PC
& pc
) {
6429 Class
* cls
= frameStaticClass(m_fp
);
6431 raise_error(HPHP::Strings::CANT_ACCESS_STATIC
);
6433 m_stack
.pushClass(cls
);
6436 inline void OPTBLD_INLINE
VMExecutionContext::iopVerifyParamType(PC
& pc
) {
6437 SYNC(); // We might need m_pc to be updated to throw.
6441 const Func
*func
= m_fp
->m_func
;
6442 assert(param
< func
->numParams());
6443 assert(func
->numParams() == int(func
->params().size()));
6444 const TypeConstraint
& tc
= func
->params()[param
].typeConstraint();
6445 assert(tc
.hasConstraint());
6446 if (UNLIKELY(!RuntimeOption::EvalCheckExtendedTypeHints
&&
6450 const TypedValue
*tv
= frame_local(m_fp
, param
);
6451 tc
.verify(tv
, func
, param
);
6454 inline void OPTBLD_INLINE
VMExecutionContext::iopNativeImpl(PC
& pc
) {
6456 uint soff
= m_fp
->m_soff
;
6457 BuiltinFunction func
= m_fp
->m_func
->builtinFuncPtr();
6459 // Actually call the native implementation. This will handle freeing the
6460 // locals in the normal case. In the case of an exception, the VM unwinder
6461 // will take care of it.
6463 // Adjust the stack; the native implementation put the return value in the
6464 // right place for us already
6465 m_stack
.ndiscard(m_fp
->m_func
->numSlotsInFrame());
6466 ActRec
* sfp
= m_fp
->arGetSfp();
6467 if (LIKELY(sfp
!= m_fp
)) {
6468 // Restore caller's execution state.
6470 pc
= m_fp
->m_func
->unit()->entry() + m_fp
->m_func
->base() + soff
;
6473 // No caller; terminate.
6477 std::ostringstream os
;
6478 os
<< toStringElm(m_stack
.topTV());
6480 Trace::trace("Return %s from VMExecutionContext::dispatch("
6481 "%p)\n", os
.str().c_str(), m_fp
));
6488 inline void OPTBLD_INLINE
VMExecutionContext::iopHighInvalid(PC
& pc
) {
6489 fprintf(stderr
, "invalid bytecode executed\n");
6493 inline void OPTBLD_INLINE
VMExecutionContext::iopSelf(PC
& pc
) {
6495 Class
* clss
= arGetContextClass(m_fp
);
6497 raise_error(HPHP::Strings::CANT_ACCESS_SELF
);
6499 m_stack
.pushClass(clss
);
6502 inline void OPTBLD_INLINE
VMExecutionContext::iopParent(PC
& pc
) {
6504 Class
* clss
= arGetContextClass(m_fp
);
6506 raise_error(HPHP::Strings::CANT_ACCESS_PARENT_WHEN_NO_CLASS
);
6508 Class
* parent
= clss
->parent();
6510 raise_error(HPHP::Strings::CANT_ACCESS_PARENT_WHEN_NO_PARENT
);
6512 m_stack
.pushClass(parent
);
6515 inline void OPTBLD_INLINE
VMExecutionContext::iopCreateCl(PC
& pc
) {
6517 DECODE_IVA(numArgs
);
6518 DECODE_LITSTR(clsName
);
6519 auto const cls
= Unit::loadClass(clsName
);
6520 auto const cl
= static_cast<c_Closure
*>(newInstance(cls
));
6521 cl
->init(numArgs
, m_fp
, m_stack
.top());
6522 m_stack
.ndiscard(numArgs
);
6523 m_stack
.pushObject(cl
);
6526 static inline c_Continuation
* createCont(const Func
* origFunc
,
6527 const Func
* genFunc
) {
6528 auto const cont
= c_Continuation::alloc(origFunc
, genFunc
);
6529 cont
->incRefCount();
6530 cont
->setNoDestruct();
6532 // The ActRec corresponding to the generator body lives as long as the object
6533 // does. We set it up once, here, and then just change FP to point to it when
6534 // we enter the generator body.
6535 ActRec
* ar
= cont
->actRec();
6536 ar
->m_func
= genFunc
;
6538 ar
->setVarEnv(nullptr);
6544 VMExecutionContext::createContFunc(const Func
* origFunc
,
6545 const Func
* genFunc
) {
6546 auto cont
= createCont(origFunc
, genFunc
);
6547 cont
->actRec()->setThis(nullptr);
6552 VMExecutionContext::createContMeth(const Func
* origFunc
,
6553 const Func
* genFunc
,
6555 if (origFunc
->isClosureBody()) {
6556 genFunc
= genFunc
->cloneAndSetClass(origFunc
->cls());
6559 auto cont
= createCont(origFunc
, genFunc
);
6560 auto ar
= cont
->actRec();
6561 ar
->setThisOrClass(objOrCls
);
6562 if (ar
->hasThis()) {
6563 ar
->getThis()->incRefCount();
6568 static inline void setContVar(const Func
* genFunc
,
6569 const StringData
* name
,
6572 Id destId
= genFunc
->lookupVarId(name
);
6573 if (destId
!= kInvalidId
) {
6574 // Copy the value of the local to the cont object and set the
6575 // local to uninit so that we don't need to change refcounts.
6576 tvCopy(*src
, *frame_local(genFp
, destId
));
6579 if (!genFp
->hasVarEnv()) {
6580 // We pass skipInsert to this VarEnv because it's going to exist
6581 // independent of the chain; i.e. we can't stack-allocate it. We link it
6582 // into the chain in UnpackCont, and take it out in ContSuspend.
6583 genFp
->setVarEnv(VarEnv::createLocalOnHeap(genFp
));
6585 genFp
->getVarEnv()->setWithRef(name
, src
);
6589 const StaticString
s_this("this");
6591 void VMExecutionContext::fillContinuationVars(ActRec
* origFp
,
6592 const Func
* origFunc
,
6594 const Func
* genFunc
) {
6595 // For functions that contain only named locals, the variable
6596 // environment is saved and restored by teleporting the values (and
6597 // their references) between the evaluation stack and the local
6598 // space at the end of the object using memcpy. Any variables in a
6599 // VarEnv are saved and restored from m_vars as usual.
6600 static const StringData
* thisStr
= s_this
.get();
6602 if (origFp
->hasVarEnv()) {
6603 // This is currently never executed but it will be needed for eager
6604 // execution of async functions - should be revisited later.
6606 Stats::inc(Stats::Cont_CreateVerySlow
);
6607 Array definedVariables
= origFp
->getVarEnv()->getDefinedVariables();
6608 skipThis
= definedVariables
.exists(s_this
, true);
6610 for (ArrayIter
iter(definedVariables
); !iter
.end(); iter
.next()) {
6611 setContVar(genFunc
, iter
.first().getStringData(),
6612 const_cast<TypedValue
*>(iter
.secondRef().asTypedValue()), genFp
);
6615 skipThis
= origFunc
->lookupVarId(thisStr
) != kInvalidId
;
6616 for (Id i
= 0; i
< origFunc
->numNamedLocals(); ++i
) {
6617 assert(i
== genFunc
->lookupVarId(origFunc
->localVarName(i
)));
6618 TypedValue
* src
= frame_local(origFp
, i
);
6619 tvCopy(*src
, *frame_local(genFp
, i
));
6624 // If $this is used as a local inside the body and is not provided
6625 // by our containing environment, just prefill it here instead of
6626 // using InitThisLoc inside the body
6627 if (!skipThis
&& origFp
->hasThis()) {
6628 Id id
= genFunc
->lookupVarId(thisStr
);
6629 if (id
!= kInvalidId
) {
6630 tvAsVariant(frame_local(genFp
, id
)) = origFp
->getThis();
6635 inline void OPTBLD_INLINE
VMExecutionContext::iopCreateCont(PC
& pc
) {
6637 DECODE_LITSTR(genName
);
6639 const Func
* origFunc
= m_fp
->m_func
;
6640 const Func
* genFunc
= origFunc
->getGeneratorBody(genName
);
6641 assert(genFunc
!= nullptr);
6643 c_Continuation
* cont
= origFunc
->isMethod()
6644 ? createContMeth(origFunc
, genFunc
, m_fp
->getThisOrClass())
6645 : createContFunc(origFunc
, genFunc
);
6647 fillContinuationVars(m_fp
, origFunc
, cont
->actRec(), genFunc
);
6649 TypedValue
* ret
= m_stack
.allocTV();
6650 ret
->m_type
= KindOfObject
;
6651 ret
->m_data
.pobj
= cont
;
6654 static inline c_Continuation
* this_continuation(const ActRec
* fp
) {
6655 ObjectData
* obj
= fp
->getThis();
6656 assert(obj
->instanceof(c_Continuation::s_cls
));
6657 return static_cast<c_Continuation
*>(obj
);
6660 void VMExecutionContext::iopContEnter(PC
& pc
) {
6663 // The stack must have one cell! Or else generatorStackBase() won't work!
6664 assert(m_stack
.top() + 1 ==
6665 (TypedValue
*)m_fp
- m_fp
->m_func
->numSlotsInFrame());
6667 // Do linkage of the continuation's AR.
6668 assert(m_fp
->hasThis());
6669 c_Continuation
* cont
= this_continuation(m_fp
);
6670 ActRec
* contAR
= cont
->actRec();
6671 arSetSfp(contAR
, m_fp
);
6673 contAR
->m_soff
= m_fp
->m_func
->unit()->offsetOf(pc
)
6674 - (uintptr_t)m_fp
->m_func
->base();
6675 contAR
->m_savedRip
=
6676 reinterpret_cast<uintptr_t>(tx()->uniqueStubs
.genRetHelper
);
6677 assert(isReturnHelper(contAR
->m_savedRip
));
6680 pc
= contAR
->m_func
->getEntry();
6683 if (UNLIKELY(!EventHook::FunctionEnter(contAR
, EventHook::NormalFunc
))) {
6688 inline void OPTBLD_INLINE
VMExecutionContext::iopUnpackCont(PC
& pc
) {
6690 c_Continuation
* cont
= frame_continuation(m_fp
);
6692 // check sanity of received value
6693 assert(tvIsPlausible(*m_stack
.topC()));
6695 // Return the label in a stack cell
6696 TypedValue
* label
= m_stack
.allocTV();
6697 label
->m_type
= KindOfInt64
;
6698 label
->m_data
.num
= cont
->m_label
;
6701 inline void OPTBLD_INLINE
VMExecutionContext::iopContSuspend(PC
& pc
) {
6704 c_Continuation
* cont
= frame_continuation(m_fp
);
6706 cont
->c_Continuation::t_update(label
, tvAsCVarRef(m_stack
.topTV()));
6709 EventHook::FunctionExit(m_fp
);
6710 ActRec
* prevFp
= m_fp
->arGetSfp();
6711 pc
= prevFp
->m_func
->getEntry() + m_fp
->m_soff
;
6715 inline void OPTBLD_INLINE
VMExecutionContext::iopContSuspendK(PC
& pc
) {
6718 c_Continuation
* cont
= frame_continuation(m_fp
);
6720 TypedValue
* val
= m_stack
.topTV();
6722 cont
->c_Continuation::t_update_key(label
, tvAsCVarRef(m_stack
.topTV()),
6726 EventHook::FunctionExit(m_fp
);
6727 ActRec
* prevFp
= m_fp
->arGetSfp();
6728 pc
= prevFp
->m_func
->getEntry() + m_fp
->m_soff
;
6732 inline void OPTBLD_INLINE
VMExecutionContext::iopContRetC(PC
& pc
) {
6734 c_Continuation
* cont
= frame_continuation(m_fp
);
6736 tvSetIgnoreRef(*m_stack
.topC(), *cont
->m_value
.asTypedValue());
6739 EventHook::FunctionExit(m_fp
);
6740 ActRec
* prevFp
= m_fp
->arGetSfp();
6741 pc
= prevFp
->m_func
->getEntry() + m_fp
->m_soff
;
6745 inline void OPTBLD_INLINE
VMExecutionContext::iopContCheck(PC
& pc
) {
6747 DECODE_IVA(check_started
);
6748 c_Continuation
* cont
= this_continuation(m_fp
);
6749 if (check_started
) {
6750 cont
->startedCheck();
6755 inline void OPTBLD_INLINE
VMExecutionContext::iopContRaise(PC
& pc
) {
6757 c_Continuation
* cont
= this_continuation(m_fp
);
6758 assert(cont
->m_label
);
6762 inline void OPTBLD_INLINE
VMExecutionContext::iopContValid(PC
& pc
) {
6764 TypedValue
* tv
= m_stack
.allocTV();
6766 tvAsVariant(tv
) = !this_continuation(m_fp
)->done();
6769 inline void OPTBLD_INLINE
VMExecutionContext::iopContKey(PC
& pc
) {
6771 c_Continuation
* cont
= this_continuation(m_fp
);
6772 cont
->startedCheck();
6774 TypedValue
* tv
= m_stack
.allocTV();
6776 tvAsVariant(tv
) = cont
->m_key
;
6779 inline void OPTBLD_INLINE
VMExecutionContext::iopContCurrent(PC
& pc
) {
6781 c_Continuation
* cont
= this_continuation(m_fp
);
6782 cont
->startedCheck();
6784 TypedValue
* tv
= m_stack
.allocTV();
6786 tvAsVariant(tv
) = cont
->m_value
;
6789 inline void OPTBLD_INLINE
VMExecutionContext::iopContStopped(PC
& pc
) {
6791 this_continuation(m_fp
)->setStopped();
6794 inline void OPTBLD_INLINE
VMExecutionContext::iopContHandle(PC
& pc
) {
6796 c_Continuation
* cont
= this_continuation(m_fp
);
6798 cont
->m_value
.setNull();
6800 Variant exn
= tvAsVariant(m_stack
.topTV());
6802 assert(exn
.asObjRef().instanceof(SystemLib::s_ExceptionClass
));
6803 throw exn
.asObjRef();
6807 inline void OPTBLD_INLINE
VMExecutionContext::roundOpImpl(Op op
) {
6808 TypedValue
* val
= m_stack
.topTV();
6810 tvCastToDoubleInPlace(val
);
6811 val
->m_data
.dbl
= op(val
->m_data
.dbl
);
6814 inline void OPTBLD_INLINE
VMExecutionContext::iopFloor(PC
& pc
) {
6819 inline void OPTBLD_INLINE
VMExecutionContext::iopCeil(PC
& pc
) {
6824 inline void OPTBLD_INLINE
VMExecutionContext::iopStrlen(PC
& pc
) {
6826 TypedValue
* subj
= m_stack
.topTV();
6827 if (LIKELY(IS_STRING_TYPE(subj
->m_type
))) {
6828 int64_t ans
= subj
->m_data
.pstr
->size();
6829 tvRefcountedDecRef(subj
);
6830 subj
->m_type
= KindOfInt64
;
6831 subj
->m_data
.num
= ans
;
6833 Variant ans
= f_strlen(tvAsVariant(subj
));
6834 tvAsVariant(subj
) = ans
;
6838 inline void OPTBLD_INLINE
VMExecutionContext::iopIncStat(PC
& pc
) {
6840 DECODE_IVA(counter
);
6842 Stats::inc(Stats::StatCounter(counter
), value
);
6845 void VMExecutionContext::classExistsImpl(PC
& pc
, Attr typeAttr
) {
6847 TypedValue
* aloadTV
= m_stack
.topTV();
6848 tvCastToBooleanInPlace(aloadTV
);
6849 assert(aloadTV
->m_type
== KindOfBoolean
);
6850 bool autoload
= aloadTV
->m_data
.num
;
6853 TypedValue
* name
= m_stack
.topTV();
6854 tvCastToStringInPlace(name
);
6855 assert(IS_STRING_TYPE(name
->m_type
));
6857 tvAsVariant(name
) = Unit::classExists(name
->m_data
.pstr
, autoload
, typeAttr
);
6860 inline void OPTBLD_INLINE
VMExecutionContext::iopClassExists(PC
& pc
) {
6861 classExistsImpl(pc
, AttrNone
);
6864 inline void OPTBLD_INLINE
VMExecutionContext::iopInterfaceExists(PC
& pc
) {
6865 classExistsImpl(pc
, AttrInterface
);
6868 inline void OPTBLD_INLINE
VMExecutionContext::iopTraitExists(PC
& pc
) {
6869 classExistsImpl(pc
, AttrTrait
);
6873 VMExecutionContext::prettyStack(const string
& prefix
) const {
6875 string
s("__Halted");
6878 int offset
= (m_fp
->m_func
->unit() != nullptr)
6881 string begPrefix
= prefix
+ "__";
6882 string midPrefix
= prefix
+ "|| ";
6883 string endPrefix
= prefix
+ "\\/";
6884 string stack
= m_stack
.toString(m_fp
, offset
, midPrefix
);
6885 return begPrefix
+ "\n" + stack
+ endPrefix
;
6888 void VMExecutionContext::checkRegStateWork() const {
6889 assert(Transl::tl_regState
== Transl::VMRegState::CLEAN
);
6892 void VMExecutionContext::DumpStack() {
6893 string s
= g_vmContext
->prettyStack("");
6894 fprintf(stderr
, "%s\n", s
.c_str());
6897 void VMExecutionContext::DumpCurUnit(int skip
) {
6898 ActRec
* fp
= g_vmContext
->getFP();
6899 Offset pc
= fp
->m_func
->unit() ? g_vmContext
->pcOff() : 0;
6901 fp
= g_vmContext
->getPrevVMState(fp
, &pc
);
6903 if (fp
== nullptr) {
6904 std::cout
<< "Don't have a valid fp\n";
6908 printf("Offset = %d, in function %s\n", pc
, fp
->m_func
->name()->data());
6909 Unit
* u
= fp
->m_func
->unit();
6911 std::cout
<< "Current unit is NULL\n";
6914 printf("Dumping bytecode for %s(%p)\n", u
->filepath()->data(), u
);
6915 std::cout
<< u
->toString();
6918 void VMExecutionContext::PrintTCCallerInfo() {
6920 ActRec
* fp
= g_vmContext
->getFP();
6921 Unit
* u
= fp
->m_func
->unit();
6922 fprintf(stderr
, "Called from TC address %p\n",
6923 tx()->getTranslatedCaller());
6924 std::cerr
<< u
->filepath()->data() << ':'
6925 << u
->getLineNumber(u
->offsetOf(g_vmContext
->getPC())) << std::endl
;
6929 condStackTraceSep(const char* pfx
) {
6931 "========================================"
6932 "========================================\n",
6936 #define COND_STACKTRACE(pfx) \
6938 string stack = prettyStack(pfx); \
6939 Trace::trace("%s\n", stack.c_str());)
6941 #define O(name, imm, pusph, pop, flags) \
6942 void VMExecutionContext::op##name() { \
6943 condStackTraceSep("op"#name" "); \
6944 COND_STACKTRACE("op"#name" pre: "); \
6946 assert(toOp(*pc) == Op##name); \
6948 int offset = m_fp->m_func->unit()->offsetOf(pc); \
6949 Trace::trace("op"#name" offset: %d\n", offset)); \
6952 COND_STACKTRACE("op"#name" post: "); \
6953 condStackTraceSep("op"#name" "); \
6962 profileReturnValue(const DataType dt
) {
6963 const Func
* f
= liveFunc();
6964 if (f
->isPseudoMain() || f
->isClosureBody() || f
->isMagic() ||
6965 Func::isSpecial(f
->name()))
6967 recordType(TypeProfileKey(TypeProfileKey::MethodName
, f
->name()), dt
);
6970 template <int dispatchFlags
>
6971 inline void VMExecutionContext::dispatchImpl(int numInstrs
) {
6972 static const bool limInstrs
= dispatchFlags
& LimitInstrs
;
6973 static const bool breakOnCtlFlow
= dispatchFlags
& BreakOnCtlFlow
;
6974 static const bool profile
= dispatchFlags
& Profile
;
6975 static const void *optabDirect
[] = {
6976 #define O(name, imm, push, pop, flags) \
6981 static const void *optabDbg
[] = {
6982 #define O(name, imm, push, pop, flags) \
6987 static const void *optabCover
[] = {
6988 #define O(name, imm, push, pop, flags) \
6993 assert(sizeof(optabDirect
) / sizeof(const void *) == Op_count
);
6994 assert(sizeof(optabDbg
) / sizeof(const void *) == Op_count
);
6995 const void **optab
= optabDirect
;
6996 bool collectCoverage
= ThreadInfo::s_threadInfo
->
6997 m_reqInjectionData
.getCoverage();
6998 if (collectCoverage
) {
7001 DEBUGGER_ATTACHED_ONLY(optab
= optabDbg
);
7003 * Trace-only mapping of opcodes to names.
7006 static const char *nametab
[] = {
7007 #define O(name, imm, push, pop, flags) \
7012 #endif /* HPHP_TRACE */
7013 bool isCtlFlow
= false;
7015 #define DISPATCH() do { \
7016 if ((breakOnCtlFlow && isCtlFlow) || \
7017 (limInstrs && UNLIKELY(numInstrs-- == 0))) { \
7019 Trace::trace("dispatch: Halt ExecutionContext::dispatch(%p)\n", \
7023 Op op = toOp(*pc); \
7024 COND_STACKTRACE("dispatch: "); \
7026 Trace::trace("dispatch: %d: %s\n", pcOff(), \
7027 nametab[uint8_t(op)])); \
7028 if (profile && (op == OpRetC || op == OpRetV)) { \
7029 profileReturnValue(m_stack.top()->m_type); \
7031 goto *optab[uint8_t(op)]; \
7034 ONTRACE(1, Trace::trace("dispatch: Enter ExecutionContext::dispatch(%p)\n",
7039 #define O(name, imm, pusph, pop, flags) \
7041 phpDebuggerOpcodeHook(pc); \
7043 if (collectCoverage) { \
7044 recordCodeCoverage(pc); \
7049 if (breakOnCtlFlow) { \
7050 isCtlFlow = instrIsControlFlow(Op::name); \
7051 Stats::incOp(Op::name); \
7053 const Op op = Op::name; \
7054 if (op == OpRetC || op == OpRetV || op == OpNativeImpl) { \
7055 if (UNLIKELY(!pc)) { m_fp = 0; return; } \
7064 void VMExecutionContext::dispatch() {
7065 if (shouldProfile()) {
7066 dispatchImpl
<Profile
>(0);
7072 // We are about to go back to translated code, check whether we should
7073 // stick with the interpreter. NB: if we've just executed a return
7074 // from pseudomain, then there's no PC and no more code to interpret.
7075 void VMExecutionContext::switchModeForDebugger() {
7076 if (DEBUGGER_FORCE_INTR
&& (getPC() != 0)) {
7077 throw VMSwitchMode();
7081 void VMExecutionContext::dispatchN(int numInstrs
) {
7082 dispatchImpl
<LimitInstrs
| BreakOnCtlFlow
>(numInstrs
);
7083 switchModeForDebugger();
7086 void VMExecutionContext::dispatchBB() {
7087 dispatchImpl
<BreakOnCtlFlow
>(0);
7088 switchModeForDebugger();
7091 void VMExecutionContext::recordCodeCoverage(PC pc
) {
7092 Unit
* unit
= getFP()->m_func
->unit();
7093 assert(unit
!= nullptr);
7094 if (unit
== SystemLib::s_nativeFuncUnit
||
7095 unit
== SystemLib::s_nativeClassUnit
||
7096 unit
== SystemLib::s_hhas_unit
) {
7099 int line
= unit
->getLineNumber(pcOff());
7102 if (unit
!= m_coverPrevUnit
|| line
!= m_coverPrevLine
) {
7103 ThreadInfo
* info
= ThreadInfo::s_threadInfo
.getNoCheck();
7104 m_coverPrevUnit
= unit
;
7105 m_coverPrevLine
= line
;
7106 const StringData
* filepath
= unit
->filepath();
7107 assert(filepath
->isStatic());
7108 info
->m_coverage
->Record(filepath
->data(), line
, line
);
7112 void VMExecutionContext::resetCoverageCounters() {
7113 m_coverPrevLine
= -1;
7114 m_coverPrevUnit
= nullptr;
7117 void VMExecutionContext::pushVMState(VMState
&savedVM
,
7118 const ActRec
* reentryAR
) {
7119 if (debug
&& savedVM
.fp
&&
7120 savedVM
.fp
->m_func
&&
7121 savedVM
.fp
->m_func
->unit()) {
7122 // Some asserts and tracing.
7123 const Func
* func
= savedVM
.fp
->m_func
;
7124 (void) /* bound-check asserts in offsetOf */
7125 func
->unit()->offsetOf(savedVM
.pc
);
7126 TRACE(3, "pushVMState: saving frame %s pc %p off %d fp %p\n",
7127 func
->name()->data(),
7129 func
->unit()->offsetOf(savedVM
.pc
),
7132 m_nestedVMs
.push_back(ReentryRecord(savedVM
, reentryAR
));
7136 void VMExecutionContext::popVMState() {
7137 assert(m_nestedVMs
.size() >= 1);
7139 VMState
&savedVM
= m_nestedVMs
.back().m_savedState
;
7142 m_firstAR
= savedVM
.firstAR
;
7143 assert(m_stack
.top() == savedVM
.sp
);
7147 savedVM
.fp
->m_func
&&
7148 savedVM
.fp
->m_func
->unit()) {
7149 const Func
* func
= savedVM
.fp
->m_func
;
7150 (void) /* bound-check asserts in offsetOf */
7151 func
->unit()->offsetOf(savedVM
.pc
);
7152 TRACE(3, "popVMState: restoring frame %s pc %p off %d fp %p\n",
7153 func
->name()->data(),
7155 func
->unit()->offsetOf(savedVM
.pc
),
7160 m_nestedVMs
.pop_back();
7164 void VMExecutionContext::requestInit() {
7165 assert(SystemLib::s_unit
);
7166 assert(SystemLib::s_nativeFuncUnit
);
7167 assert(SystemLib::s_nativeClassUnit
);
7169 new (&s_requestArenaStorage
) RequestArena();
7170 new (&s_varEnvArenaStorage
) VarEnvArena();
7172 EnvConstants::requestInit(new (request_arena()) EnvConstants());
7173 VarEnv::createGlobal();
7174 m_stack
.requestInit();
7175 Transl::Translator::advanceTranslator();
7176 tx()->requestInit();
7178 if (UNLIKELY(RuntimeOption::EvalJitEnableRenameFunction
)) {
7179 SystemLib::s_unit
->merge();
7180 if (SystemLib::s_hhas_unit
) SystemLib::s_hhas_unit
->merge();
7181 SystemLib::s_nativeFuncUnit
->merge();
7182 SystemLib::s_nativeClassUnit
->merge();
7184 // System units are always merge only, and
7185 // everything is persistent.
7186 assert(SystemLib::s_unit
->isEmpty());
7187 assert(!SystemLib::s_hhas_unit
|| SystemLib::s_hhas_unit
->isEmpty());
7188 assert(SystemLib::s_nativeFuncUnit
->isEmpty());
7189 assert(SystemLib::s_nativeClassUnit
->isEmpty());
7192 profileRequestStart();
7194 MemoryProfile::startProfiling();
7197 Class
* cls
= Unit::GetNamedEntity(s_stdclass
.get())->clsList();
7199 assert(cls
== SystemLib::s_stdclassClass
);
7203 void VMExecutionContext::requestExit() {
7204 MemoryProfile::finishProfiling();
7208 tx()->requestExit();
7209 Transl::Translator::clearTranslator();
7210 m_stack
.requestExit();
7211 profileRequestEnd();
7212 EventHook::Disable();
7213 EnvConstants::requestExit();
7215 if (m_globalVarEnv
) {
7216 VarEnv::destroy(m_globalVarEnv
);
7220 varenv_arena().~VarEnvArena();
7221 request_arena().~RequestArena();
7224 ///////////////////////////////////////////////////////////////////////////////