Move InstanceBits to separate module; make Class not friend of things
[hiphop-php.git] / hphp / runtime / vm / bytecode.cpp
blob0e2284b731c639c47a976fa50bc70b2e56f64e09
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
18 #include <algorithm>
19 #include <string>
20 #include <vector>
21 #include <sstream>
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"
77 #include <iostream>
78 #include <iomanip>
79 #include <algorithm>
80 #include <boost/format.hpp>
81 #include <boost/utility/typed_in_place_factory.hpp>
83 #include <cinttypes>
85 #include <libgen.h>
86 #include <sys/mman.h>
88 namespace HPHP {
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;
101 using std::string;
103 using Transl::VMRegAnchor;
104 using Transl::EagerVMRegAnchor;
106 #if DEBUG
107 #define OPTBLD_INLINE
108 #else
109 #define OPTBLD_INLINE ALWAYS_INLINE
110 #endif
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);
123 bool
124 ActRec::skipFrame() const {
125 return m_func && m_func->skipFrame();
128 template <>
129 Class* arGetContextClassImpl<false>(const ActRec* ar) {
130 if (ar == nullptr) {
131 return nullptr;
133 return ar->m_func->cls();
136 template <>
137 Class* arGetContextClassImpl<true>(const ActRec* ar) {
138 if (ar == nullptr) {
139 return nullptr;
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);
149 if (ar == nullptr) {
150 return nullptr;
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");
170 static inline
171 Transl::Translator* tx() {
172 return Transl::Translator::Get();
175 ///////////////////////////////////////////////////////////////////////////////
177 //=============================================================================
178 // Miscellaneous macros.
180 #define NEXT() pc++
181 #define DECODE_JMP(type, var) \
182 type var __attribute__((unused)) = *(type*)pc; \
183 ONTRACE(2, \
184 Trace::trace("decode: Immediate %s %" PRIi64"\n", #type, \
185 (int64_t)var));
186 #define ITER_SKIP(offset) pc = origPc + (offset);
188 #define DECODE(type, var) \
189 DECODE_JMP(type, var); \
190 pc += sizeof(type)
191 #define DECODE_IVA(var) \
192 int32_t var UNUSED = decodeVariableSizeImm(&pc); \
193 ONTRACE(2, \
194 Trace::trace("decode: Immediate int32 %" PRIi64"\n", \
195 (int64_t)var));
196 #define DECODE_LITSTR(var) \
197 StringData* var; \
198 do { \
199 DECODE(Id, id); \
200 var = m_fp->m_func->unit()->lookupLitstrId(id); \
201 } while (false)
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) {
219 if (fp->hasThis()) {
220 return fp->getThis()->getVMClass();
221 } else if (fp->hasClass()) {
222 return fp->getClass();
223 } else {
224 return nullptr;
228 //=============================================================================
229 // VarEnv.
231 VarEnv::VarEnv()
232 : m_depth(0)
233 , m_malloced(false)
234 , m_global(false)
235 , m_cfp(0)
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)
249 : m_extraArgs(eArgs)
250 , m_depth(1)
251 , m_malloced(false)
252 , m_global(false)
253 , m_cfp(fp)
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);
271 VarEnv::~VarEnv() {
272 TRACE(3, "Destroying VarEnv %p [%s]\n",
273 this,
274 isGlobalScope() ? "global scope" : "local scope");
275 assert(m_restoreLocations.empty());
277 if (!isGlobalScope()) {
278 if (LIKELY(!m_malloced)) {
279 varenv_arena().endFrame();
280 return;
282 } else {
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.
289 m_nvTable->leak();
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();
299 va.beginFrame();
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);
303 return ret;
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;
311 return ret;
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;
321 return ret;
324 void VarEnv::destroy(VarEnv* ve) {
325 bool malloced = ve->m_malloced;
326 ve->~VarEnv();
327 if (UNLIKELY(malloced)) free(ve);
330 void VarEnv::attach(ActRec* fp) {
331 TRACE(3, "Attaching VarEnv %p [%s] %d fp @%p\n",
332 this,
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()));
337 m_cfp = fp;
338 m_depth++;
340 // Overlay fp's locals, if it has any.
342 const Func* func = fp->m_func;
343 const Id numNames = func->numNamedLocals();
344 if (!numNames) {
345 return;
347 if (!m_nvTable) {
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",
363 this,
364 isGlobalScope() ? "global scope" : "local scope",
365 fp);
366 assert(fp == m_cfp);
367 assert(m_depth > 0);
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);
393 m_depth--;
394 if (m_depth == 0) {
395 m_cfp = nullptr;
396 // don't free global varEnv
397 if (context->m_globalVarEnv != this) {
398 assert(!isGlobalScope());
399 destroy(this);
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
406 // locals.
407 void VarEnv::ensureNvt() {
408 const size_t kLazyNvtSize = 3;
409 if (!m_nvTable) {
410 m_nvTable = boost::in_place<NameValueTable>(kLazyNvtSize);
414 void VarEnv::set(const StringData* name, TypedValue* tv) {
415 ensureNvt();
416 m_nvTable->set(name, tv);
419 void VarEnv::bind(const StringData* name, TypedValue* tv) {
420 ensureNvt();
421 m_nvTable->bind(name, tv);
424 void VarEnv::setWithRef(const StringData* name, TypedValue* tv) {
425 if (tv->m_type == KindOfRef) {
426 bind(name, tv);
427 } else {
428 set(name, tv);
432 TypedValue* VarEnv::lookup(const StringData* name) {
433 if (!m_nvTable) {
434 return 0;
436 return m_nvTable->lookup(name);
439 TypedValue* VarEnv::lookupAdd(const StringData* name) {
440 ensureNvt();
441 return m_nvTable->lookupAdd(name);
444 bool VarEnv::unset(const StringData* name) {
445 if (!m_nvTable) return true;
446 m_nvTable->unset(name);
447 return true;
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));
461 } else {
462 ret.add(StrNR(sd).asString(), tvAsCVarRef(tv));
466 return ret;
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]);
491 return ea;
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) {
500 assert(nargs > 0);
502 for (unsigned i = 0; i < nargs; ++i) {
503 tvRefcountedDecRef(ea->m_extraArgs + i);
505 ea->~ExtraArgs();
506 smart_free(ea);
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 //=============================================================================
519 // Stack.
521 // Store actual stack elements array in a thread-local in order to amortize the
522 // cost of allocation.
523 class StackElms {
524 public:
525 StackElms() : m_elms(nullptr) {}
526 ~StackElms() {
527 flush();
529 TypedValue* elms() {
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());
539 return m_elms;
541 void flush() {
542 if (m_elms != nullptr) {
543 free(m_elms);
544 m_elms = nullptr;
547 private:
548 TypedValue* m_elms;
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
562 % sMinStackElms));
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));
571 Stack::Stack()
572 : m_elms(nullptr), m_top(nullptr), m_base(nullptr) {
575 Stack::~Stack() {
576 requestExit();
579 void
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
589 // overflow.
590 UNUSED size_t maxelms =
591 RuntimeOption::EvalVMStackElms - sSurprisePageSize / sizeof(TypedValue);
592 assert(!wouldOverflow(maxelms - 1));
593 assert(wouldOverflow(maxelms));
596 void
597 Stack::requestExit() {
598 if (m_elms != nullptr) {
599 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
609 // context is dead.
611 if (!t_se.isNull()) {
612 t_se->flush();
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";
623 return os.str();
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 << " ";
630 return os.str();
633 switch (tv->m_type) {
634 case KindOfRef:
635 os << "V:(";
636 os << "@" << tv->m_data.pref;
637 os << toStringElm(tv->m_data.pref->tv());
638 os << ")";
639 return os.str();
640 case KindOfClass:
641 os << "A:";
642 break;
643 default:
644 os << "C:";
645 break;
648 switch (tv->m_type) {
649 case KindOfUninit:
650 os << "Uninit";
651 break;
652 case KindOfNull:
653 os << "Null";
654 break;
655 case KindOfBoolean:
656 os << (tv->m_data.num ? "True" : "False");
657 break;
658 case KindOfInt64:
659 os << "0x" << std::hex << tv->m_data.num << std::dec;
660 break;
661 case KindOfDouble:
662 os << tv->m_data.dbl;
663 break;
664 case KindOfStaticString:
665 case KindOfString:
667 int len = tv->m_data.pstr->size();
668 bool truncated = false;
669 if (len > 128) {
670 len = 128;
671 truncated = true;
673 os << tv->m_data.pstr
674 << "c(" << tv->m_data.pstr->getCount() << ")"
675 << ":\""
676 << Util::escapeStringForCPP(tv->m_data.pstr->data(), len)
677 << "\"" << (truncated ? "..." : "");
679 break;
680 case KindOfArray:
681 assert(tv->m_data.parr->getCount() > 0);
682 os << tv->m_data.parr
683 << "c(" << tv->m_data.parr->getCount() << ")"
684 << ":Array";
685 break;
686 case KindOfObject:
687 assert(tv->m_data.pobj->getCount() > 0);
688 os << tv->m_data.pobj
689 << "c(" << tv->m_data.pobj->getCount() << ")"
690 << ":Object("
691 << tv->m_data.pobj->o_getClassName().get()->data()
692 << ")";
693 break;
694 case KindOfResource:
695 assert(tv->m_data.pres->getCount() > 0);
696 os << tv->m_data.pres
697 << "c(" << tv->m_data.pres->getCount() << ")"
698 << ":Resource("
699 << const_cast<ResourceData*>(tv->m_data.pres)
700 ->o_getClassName().get()->data()
701 << ")";
702 break;
703 case KindOfRef:
704 not_reached();
705 case KindOfClass:
706 os << tv->m_data.pcls
707 << ":" << tv->m_data.pcls->name()->data();
708 break;
709 default:
710 os << "?";
711 break;
714 return os.str();
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:
727 return "I:Array";
728 case ArrayIter::TypeIterator:
729 return "I:Iterator";
731 assert(false);
732 return "I:?";
735 void Stack::toStringFrame(std::ostream& os, const ActRec* fp,
736 int offset, const TypedValue* ftop,
737 const string& prefix) const {
738 assert(fp);
740 // Use depth-first recursion to output the most deeply nested stack frame
741 // first.
743 Offset prevPc = 0;
744 TypedValue* prevStackTop = nullptr;
745 ActRec* prevFp = g_vmContext->getPrevVMState(fp, &prevPc, &prevStackTop);
746 if (prevFp != nullptr) {
747 toStringFrame(os, prevFp, prevPc, prevStackTop, prefix);
751 os << prefix;
752 const Func* func = fp->m_func;
753 assert(func);
754 func->validate();
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)
759 << std::dec << "}";
760 TypedValue* tv = (TypedValue*)fp;
761 tv--;
763 if (func->numLocals() > 0) {
764 os << "<";
765 int n = func->numLocals();
766 for (int i = 0; i < n; i++, tv--) {
767 if (i > 0) {
768 os << " ";
770 os << toStringElm(tv);
772 os << ">";
775 assert(!func->info() || func->numIterators() == 0);
776 if (func->numIterators() > 0) {
777 os << "|";
778 Iter* it = &((Iter*)&tv[1])[-1];
779 for (int i = 0; i < func->numIterators(); i++, it--) {
780 if (i > 0) {
781 os << " ";
783 bool itRef;
784 if (func->checkIterScope(offset, i, itRef)) {
785 os << toStringIter(it, itRef);
786 } else {
787 os << "I:Undefined";
790 os << "|";
793 std::vector<std::string> stackElems;
794 visitStackElems(
795 fp, ftop, offset,
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);
808 os << '\n';
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);
833 return os.str();
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);
846 return diff < 0;
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();
861 if (sfp == fp) {
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 //=============================================================================
879 // ExecutionContext.
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;
892 return nullptr;
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());
907 return clsCns;
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
918 // class (ctx).
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
924 // will be returned.
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,
938 const Class* ctx,
939 CallType callType,
940 bool raise /* = false */) {
941 const Func* method;
942 if (callType == CallType::CtorMethod) {
943 assert(methodName == nullptr);
944 method = cls->getCtor();
945 } else {
946 assert(callType == CallType::ObjMethod || callType == CallType::ClsMethod);
947 assert(methodName != nullptr);
948 method = cls->lookupMethod(methodName);
949 while (!method) {
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;
958 if (raise) {
959 raise_error("Call to undefined method %s::%s from %s%s",
960 cls->name()->data(),
961 methodName->data(),
962 ctx ? "context " : "anonymous context",
963 ctx ? ctx->name()->data() : "");
965 return nullptr;
968 assert(method);
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();
975 assert(baseClass);
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) {
980 return method;
982 // The anonymous context cannot access protected or private methods,
983 // so we can fail fast here.
984 if (ctx == nullptr) {
985 if (raise) {
986 raise_error("Call to %s method %s::%s from anonymous context",
987 (method->attrs() & AttrPrivate) ? "private" : "protected",
988 cls->name()->data(),
989 method->name()->data());
991 return nullptr;
993 assert(ctx);
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.
999 accessible = false;
1000 } else {
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)) {
1006 return method;
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.
1017 if (raise) {
1018 raise_error("Call to protected method %s::%s from context %s",
1019 cls->name()->data(),
1020 method->name()->data(),
1021 ctx->name()->data());
1023 return nullptr;
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
1027 // with this name.
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.
1042 return ctxMethod;
1045 if (accessible) {
1046 return method;
1048 if (raise) {
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() : "");
1055 return nullptr;
1058 LookupResult VMExecutionContext::lookupObjMethod(const Func*& f,
1059 const Class* cls,
1060 const StringData* methodName,
1061 const Class* ctx,
1062 bool raise /* = false */) {
1063 f = lookupMethodCtx(cls, methodName, ctx, CallType::ObjMethod, false);
1064 if (!f) {
1065 f = cls->lookupMethod(s___call.get());
1066 if (!f) {
1067 if (raise) {
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;
1081 LookupResult
1082 VMExecutionContext::lookupClsMethod(const Func*& f,
1083 const Class* cls,
1084 const StringData* methodName,
1085 ObjectData* obj,
1086 const Class* ctx,
1087 bool raise /* = false */) {
1088 f = lookupMethodCtx(cls, methodName, ctx, CallType::ClsMethod, false);
1089 if (!f) {
1090 if (obj && obj->instanceof(cls)) {
1091 f = obj->getVMClass()->lookupMethod(s___call.get());
1093 if (!f) {
1094 f = cls->lookupMethod(s___callStatic.get());
1095 if (!f) {
1096 if (raise) {
1097 // Throw a fatal errpr
1098 lookupMethodCtx(cls, methodName, ctx, CallType::ClsMethod, true);
1100 return LookupResult::MethodNotFound;
1102 f->validate();
1103 assert(f);
1104 assert(f->attrs() & AttrStatic);
1105 return LookupResult::MagicCallStaticFound;
1107 assert(f);
1108 assert(obj);
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,
1121 const Class* cls,
1122 bool raise /* = false */) {
1123 f = cls->getCtor();
1124 if (!(f->attrs() & AttrPublic)) {
1125 Class* ctx = arGetContextClass(getFP());
1126 f = lookupMethodCtx(cls, nullptr, ctx, CallType::CtorMethod, raise);
1127 if (!f) {
1128 // If raise was true than lookupMethodCtx should have thrown,
1129 // so we should only be able to get here if raise was false
1130 assert(!raise);
1131 return LookupResult::MethodNotFound;
1134 return LookupResult::MethodFoundWithThis;
1137 ObjectData* VMExecutionContext::createObject(StringData* clsName,
1138 CArrRef params,
1139 bool init /* = true */) {
1140 Class* class_ = Unit::loadClass(clsName);
1141 if (class_ == nullptr) {
1142 throw_missing_class(clsName->data());
1144 Object o;
1145 o = newInstance(class_);
1146 if (init) {
1147 // call constructor
1148 TypedValue ret;
1149 invokeFunc(&ret, class_->getCtor(), params, o.get());
1150 tvRefcountedDecRef(&ret);
1153 ObjectData* ret = o.detach();
1154 ret->decRefCount();
1155 return ret;
1158 ObjectData* VMExecutionContext::createObjectOnly(StringData* clsName) {
1159 return createObject(clsName, null_array, false);
1162 ActRec* VMExecutionContext::getStackFrame() {
1163 VMRegAnchor _;
1164 return getFP();
1167 ObjectData* VMExecutionContext::getThis() {
1168 VMRegAnchor _;
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();
1177 return nullptr;
1180 Class* VMExecutionContext::getContextClass() {
1181 VMRegAnchor _;
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();
1195 return nullptr;
1198 CStrRef VMExecutionContext::getContainingFileName() {
1199 VMRegAnchor _;
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() {
1211 VMRegAnchor _;
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() {
1224 VMRegAnchor _;
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) {
1234 return result;
1238 Offset pc = 0;
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();
1244 int lineNumber;
1245 if ((lineNumber = unit->getLineNumber(pc)) != -1) {
1246 result.set(s_file, unit->filepath()->data(), true);
1247 result.set(s_line, lineNumber);
1248 return result;
1251 ar = getPrevVMState(ar, &pc);
1253 return result;
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() {
1270 VMRegAnchor _;
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) {
1286 VMRegAnchor _;
1287 // setVar() should only be called after getVarEnv() has been called
1288 // to create a varEnv
1289 ActRec *fp = getFP();
1290 if (!fp) return;
1291 if (fp->skipFrame()) {
1292 fp = getPrevVMState(fp);
1294 assert(!fp->hasInvName());
1295 assert(!fp->hasExtraArgs());
1296 assert(fp->m_varEnv != nullptr);
1297 if (ref) {
1298 fp->m_varEnv->bind(name, v);
1299 } else {
1300 fp->m_varEnv->set(name, v);
1304 Array VMExecutionContext::getLocalDefinedVariables(int frame) {
1305 VMRegAnchor _;
1306 ActRec *fp = getFP();
1307 for (; frame > 0; --frame) {
1308 if (!fp) break;
1309 fp = getPrevVMState(fp);
1311 if (!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) {
1324 continue;
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) {
1344 m_stack.popC();
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);
1353 ar->setNumArgs(2);
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()) {
1381 m_fp = ar;
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
1389 return true;
1390 } else {
1391 assert(ar->hasExtraArgs());
1392 assert(func->numParams() < ar->numArgs());
1394 } else {
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;
1416 } else {
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,
1421 numExtras));
1422 m_stack.ndiscard(numExtras);
1423 } else {
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++) {
1428 m_stack.popTV();
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);
1441 nlocals += nuse;
1442 func = ar->m_func;
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);
1454 m_fp = ar;
1455 if (firstDVInitializer != InvalidAbsoluteOffset) {
1456 pc = func->unit()->entry() + firstDVInitializer;
1457 } else {
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
1466 SYNC();
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();
1472 if (nparams == 1) {
1473 raise_warning(Strings::MISSING_ARGUMENT, name, i);
1474 } else {
1475 raise_warning(Strings::MISSING_ARGUMENTS, name, nparams, i);
1477 break;
1481 return true;
1484 void VMExecutionContext::syncGdbState() {
1485 if (RuntimeOption::EvalJit && !RuntimeOption::EvalJitNoGdb) {
1486 tx()->getDebugInfo()->debugSync();
1490 void VMExecutionContext::enterVMPrologue(ActRec* enterFnAr) {
1491 assert(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);
1499 } else {
1500 if (prepareFuncEntry(enterFnAr, m_pc)) {
1501 enterVMWork(enterFnAr);
1506 void VMExecutionContext::enterVMWork(ActRec* enterFnAr) {
1507 Transl::TCA start = nullptr;
1508 if (enterFnAr) {
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 */
1516 if (enterFnAr) {
1517 assert(start);
1518 tx()->enterTCAfterPrologue(start);
1519 } else {
1520 SrcKey sk(m_fp->func(), m_pc);
1521 tx()->enterTCAtSrcKey(sk);
1523 } else {
1524 dispatch();
1528 void VMExecutionContext::enterVM(TypedValue* retval, ActRec* ar) {
1529 DEBUG_ONLY int faultDepth = m_faults.size();
1530 SCOPE_EXIT { assert(m_faults.size() == faultDepth); };
1532 m_firstAR = ar;
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.
1548 bool first = true;
1549 resume:
1550 try {
1551 if (first) {
1552 first = false;
1553 if (m_fp && !ar->m_varEnv) {
1554 enterVMPrologue(ar);
1555 } else if (prepareFuncEntry(ar, m_pc)) {
1556 enterVMWork(ar);
1558 } else {
1559 enterVMWork(0);
1562 // Everything succeeded with no exception---return to the previous
1563 // VM nesting level.
1564 *retval = *m_stack.topTV();
1565 m_stack.discard();
1566 return;
1568 } catch (...) {
1569 always_assert(Transl::tl_regState == Transl::VMRegState::CLEAN);
1570 auto const action = exception_handler();
1571 if (action == UnwindAction::ResumeVM) {
1572 goto resume;
1574 always_assert(action == UnwindAction::Propagate);
1578 * Here we have to propagate an exception out of this VM's nesting
1579 * level.
1582 if (g_vmContext->m_nestedVMs.empty()) {
1583 m_fp = nullptr;
1584 m_pc = nullptr;
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();
1596 throw obj;
1598 case Fault::Type::CppException:
1599 // throwException() will take care of deleting heap-allocated
1600 // exception object for us
1601 fault.m_cppException->throwException();
1602 not_reached();
1605 not_reached();
1608 void VMExecutionContext::reenterVM(TypedValue* retval,
1609 ActRec* ar,
1610 TypedValue* savedSP) {
1611 ar->m_soff = 0;
1612 ar->m_savedRbp = 0;
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);
1617 try {
1618 enterVM(retval, ar);
1619 popVMState();
1620 } catch (...) {
1621 popVMState();
1622 throw;
1624 TRACE(1, "Reentry: exit fp %p pc %p\n", m_fp, m_pc);
1627 void VMExecutionContext::invokeFunc(TypedValue* retval,
1628 const Func* f,
1629 CArrRef params,
1630 ObjectData* this_ /* = NULL */,
1631 Class* cls /* = NULL */,
1632 VarEnv* varEnv /* = NULL */,
1633 StringData* invName /* = NULL */,
1634 InvokeFlags flags /* = InvokeNormal */) {
1635 assert(retval);
1636 assert(f);
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()) ||
1643 (!this_));
1644 // invName should only be non-NULL if we are calling __call or
1645 // __callStatic
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());
1651 VMRegAnchor _;
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();
1668 toMerge->merge();
1669 if (toMerge->isMergeOnly()) {
1670 *retval = *toMerge->getMainReturn();
1671 return;
1675 ActRec* ar = m_stack.allocA();
1676 ar->m_soff = 0;
1677 ar->m_savedRbp = 0;
1678 ar->m_func = f;
1679 if (this_) {
1680 ar->setThis(this_);
1681 } else if (cls) {
1682 ar->setClass(cls);
1683 } else {
1684 ar->setThis(nullptr);
1686 if (isMagicCall) {
1687 ar->initNumArgs(2);
1688 } else {
1689 ar->initNumArgs(params.size());
1691 ar->setVarEnv(varEnv);
1693 #ifdef HPHP_TRACE
1694 if (m_fp == nullptr) {
1695 TRACE(1, "Reentry: enter %s(%p) from top-level\n",
1696 f->name()->data(), ar);
1697 } else {
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);
1702 #endif
1704 ArrayData *arr = params.get();
1705 if (isMagicCall) {
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);
1711 } else if (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);
1719 int paramId = 0;
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);
1724 TypedValue *to;
1725 if (LIKELY(paramId < numParams)) {
1726 to = m_stack.allocTV();
1727 } else {
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);
1733 break;
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) {
1743 refDup(*from, *to);
1744 } else {
1745 if (flags & InvokeCuf) {
1746 try {
1747 raise_warning("Parameter %d to %s() expected to be "
1748 "a reference, value given",
1749 paramId + 1, f->fullName()->data());
1750 } catch (...) {
1751 // If an exception is thrown by the user error handler,
1752 // we need to clean up the stack
1753 m_stack.discard();
1754 invokeFuncCleanupHelper(retval, ar, paramId);
1755 throw;
1757 if (skipCufOnInvalidParams) {
1758 m_stack.discard();
1759 invokeFuncCleanupHelper(retval, ar, paramId);
1760 return;
1763 cellDup(*tvToCell(from), *to);
1768 if (m_fp) {
1769 reenterVM(retval, ar, savedSP);
1770 } else {
1771 assert(m_nestedVMs.size() == 0);
1772 enterVM(retval, ar);
1776 void VMExecutionContext::invokeFuncCleanupHelper(TypedValue* retval,
1777 ActRec* ar,
1778 int numArgsPushed) {
1779 assert(retval && ar);
1780 const int numFormalParams = ar->m_func->numParams();
1781 ExtraArgs* extraArgs = ar->hasExtraArgs() ? ar->getExtraArgs() : nullptr;
1783 if (extraArgs) {
1784 int n =
1785 numArgsPushed > numFormalParams ? numArgsPushed - numFormalParams : 0;
1786 ExtraArgs::deallocate(extraArgs, n);
1787 ar->m_varEnv = nullptr;
1788 numArgsPushed -= n;
1790 while (numArgsPushed > 0) {
1791 m_stack.popTV();
1792 numArgsPushed--;
1794 m_stack.popAR();
1795 tvWriteNull(retval);
1798 void VMExecutionContext::invokeFuncFew(TypedValue* retval,
1799 const Func* f,
1800 void* thisOrCls,
1801 StringData* invName,
1802 int argc,
1803 const TypedValue* argv) {
1804 assert(retval);
1805 assert(f);
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
1814 // __callStatic
1815 assert(!invName || f->name()->isame(s___call.get()) ||
1816 f->name()->isame(s___callStatic.get()));
1818 VMRegAnchor _;
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();
1829 ar->m_soff = 0;
1830 ar->m_savedRbp = 0;
1831 ar->m_func = f;
1832 ar->m_this = (ObjectData*)thisOrCls;
1833 ar->initNumArgs(argc);
1834 if (UNLIKELY(invName != nullptr)) {
1835 ar->setInvName(invName);
1836 } else {
1837 ar->m_varEnv = nullptr;
1840 #ifdef HPHP_TRACE
1841 if (m_fp == nullptr) {
1842 TRACE(1, "Reentry: enter %s(%p) from top-level\n",
1843 f->name()->data(), ar);
1844 } else {
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);
1849 #endif
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);
1856 } else {
1857 refDup(*from, *to);
1861 if (m_fp) {
1862 reenterVM(retval, ar, savedSP);
1863 } else {
1864 assert(m_nestedVMs.size() == 0);
1865 enterVM(retval, ar);
1869 void VMExecutionContext::invokeContFunc(const Func* f,
1870 ObjectData* this_,
1871 Cell* param /* = NULL */) {
1872 assert(f);
1873 assert(this_);
1875 EagerVMRegAnchor _;
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();
1885 ar->m_savedRbp = 0;
1886 ar->m_func = f;
1887 ar->m_soff = 0;
1888 ar->initNumArgs(param != nullptr ? 1 : 0);
1889 ar->setThis(this_);
1890 ar->setVarEnv(nullptr);
1892 if (param != nullptr) {
1893 cellDup(*param, *m_stack.allocC());
1896 TypedValue retval;
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) {
1921 return nullptr;
1923 ActRec* prevFp = fp->arGetSfp();
1924 if (prevFp != fp) {
1925 if (prevSp) {
1926 if (UNLIKELY(fp->m_func->isGenerator())) {
1927 *prevSp = (TypedValue*)prevFp - prevFp->m_func->numSlotsInFrame();
1928 } else {
1929 *prevSp = (TypedValue*)&fp[1];
1932 if (prevPc) *prevPc = prevFp->m_func->base() + fp->m_soff;
1933 if (fromVMEntry) *fromVMEntry = false;
1934 return prevFp;
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;
1945 assert(prevFp);
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;
1950 return prevFp;
1953 Array VMExecutionContext::debugBacktrace(bool skip /* = false */,
1954 bool withSelf /* = false */,
1955 bool withThis /* = false */,
1956 VMParserFrame*
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
1963 // the backtrace
1964 if (parserFrame) {
1965 bt.append(
1966 ArrayInit(2)
1967 .set(s_file, parserFrame->filename, true)
1968 .set(s_line, parserFrame->lineNumber, true)
1969 .toVariant()
1973 VMRegAnchor _;
1974 if (!getFP()) {
1975 // If there are no VM frames, we're done
1976 return bt;
1979 int depth = 0;
1980 ActRec* fp = nullptr;
1981 Offset pc = 0;
1983 // Get the fp and pc of the top frame (possibly skipping one frame)
1985 if (skip) {
1986 fp = getPrevVMState(getFP(), &pc);
1987 if (!fp) {
1988 // We skipped over the only VM frame, we're done
1989 return bt;
1991 } else {
1992 fp = getFP();
1993 Unit *unit = getFP()->m_func->unit();
1994 assert(unit);
1995 pc = unit->offsetOf(m_pc);
1998 // Handle the top frame
1999 if (withSelf) {
2000 // Builtins don't have a file and line number
2001 if (!fp->m_func->isBuiltin()) {
2002 Unit *unit = fp->m_func->unit();
2003 assert(unit);
2004 const char* filename = unit->filepath()->data();
2005 if (fp->m_func->originalFilename()) {
2006 filename = fp->m_func->originalFilename()->data();
2008 assert(filename);
2009 Offset off = pc;
2011 ArrayInit frame(parserFrame ? 4 : 2);
2012 frame.set(s_file, filename, true);
2013 frame.set(s_line, unit->getLineNumber(off), true);
2014 if (parserFrame) {
2015 frame.set(s_function, s_include, true);
2016 frame.set(s_args, Array::Create(parserFrame->filename), true);
2018 bt.append(frame.toVariant());
2019 depth++;
2024 // Handle the subsequent VM frames
2025 Offset prevPc = 0;
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()) {
2031 continue;
2034 ArrayInit frame(7);
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();
2047 assert(prevFile);
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) {
2062 pcAdjust = 1;
2064 frame.set(s_line,
2065 prevFp->m_func->unit()->getLineNumber(prevPc - pcAdjust),
2066 true);
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) {
2096 if (withThis) {
2097 frame.set(s_object, Object(fp->getThis()), true);
2099 frame.set(s_type, "->", true);
2100 } else {
2101 frame.set(s_type, "::", true);
2106 Array args = Array::Create();
2107 if (ignoreArgs) {
2108 // do nothing
2109 } else if (funcname.same(s_include)) {
2110 if (depth) {
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);
2117 } else {
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));
2126 } else {
2127 int i;
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());
2141 depth++;
2143 return bt;
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(
2168 CStrRef name) {
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()) {
2174 return nullptr;
2176 AtomicSmartPtr<MethodInfoVM> &m = m_functionInfos[name];
2177 m = new MethodInfoVM();
2178 func->getFuncInfo(m.get());
2179 return m.get();
2180 } else {
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
2196 return nullptr;
2198 AtomicSmartPtr<ClassInfoVM> &c = m_classInfos[name];
2199 c = new ClassInfoVM();
2200 cls->getClassInfo(c.get());
2201 return c.get();
2202 } else {
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
2217 return nullptr;
2219 AtomicSmartPtr<ClassInfoVM> &c = m_interfaceInfos[name];
2220 c = new ClassInfoVM();
2221 cls->getClassInfo(c.get());
2222 return c.get();
2223 } else {
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)) {
2238 return nullptr;
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(
2247 CStrRef name) {
2248 TypedValue* tv = Unit::lookupCns(name.get());
2249 if (tv == nullptr) {
2250 return nullptr;
2252 ConstInfoMap::const_iterator it = m_constInfo.find(name.get());
2253 if (it != m_constInfo.end()) {
2254 return it->second;
2256 StringData* key = StringData::GetStaticString(name.get());
2257 ClassInfo::ConstantInfo* ci = new ClassInfo::ConstantInfo();
2258 ci->name = *(const String*)&key;
2259 ci->valueLen = 0;
2260 ci->valueText = "";
2261 ci->setValue(tvAsCVarRef(tv));
2262 m_constInfo[key] = ci;
2263 return ci;
2266 HPHP::Eval::PhpFile* VMExecutionContext::lookupPhpFile(StringData* path,
2267 const char* currentDir,
2268 bool* initial_opt) {
2269 bool init;
2270 bool &initial = initial_opt ? *initial_opt : init;
2271 initial = true;
2273 struct stat s;
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.
2282 efile = it->second;
2283 initial = false;
2284 return efile;
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;
2291 String rpath;
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)) {
2297 hasRealpath = true;
2298 it = m_evaledFiles.find(rpath.get());
2299 if (it != m_evaledFiles.end()) {
2300 // We found it! Update the mapping for spath and
2301 // return the unit.
2302 efile = it->second;
2303 m_evaledFiles[spath.get()] = efile;
2304 spath.get()->incRefCount();
2305 initial = false;
2306 return efile;
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())) {
2318 initial = false;
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.
2325 if (hasRealpath) {
2326 m_evaledFiles[rpath.get()] = efile;
2327 rpath.get()->incRefCount();
2329 DEBUGGER_ATTACHED_ONLY(phpDebuggerFileLoadHook(efile));
2331 return efile;
2334 Unit* VMExecutionContext::evalInclude(StringData* path,
2335 const StringData* curUnitFilePath,
2336 bool* initial) {
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);
2343 } else {
2344 efile = lookupPhpFile(path, "", initial);
2346 if (efile) {
2347 return efile->unit();
2349 return nullptr;
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,
2359 InclOpFlags flags,
2360 bool* initial,
2361 Unit* unit) {
2362 String absPath;
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",
2370 path->data(),
2371 absPath->data());
2372 } else {
2373 assert(flags & InclOpDocRoot);
2374 absPath = SourceRootInfo::GetCurrentPhpRoot();
2375 TRACE(2, "lookupIncludeRoot(%s): docRoot -> %s\n",
2376 path->data(),
2377 absPath->data());
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;
2385 return it->second;
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) {
2399 m_pc = pc;
2400 unit->merge();
2401 if (unit->isMergeOnly()) {
2402 Stats::inc(Stats::PseudoMain_Skipped);
2403 *m_stack.allocTV() = *unit->getMainReturn();
2404 return false;
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();
2414 ar->setThis(this_);
2415 } else if (m_fp->hasClass()) {
2416 ar->setClass(m_fp->getClass());
2417 } else {
2418 ar->setThis(nullptr);
2420 Func* func = unit->getMain(cls);
2421 assert(!func->info());
2422 assert(!func->isGenerator());
2423 ar->m_func = func;
2424 ar->initNumArgs(0);
2425 assert(getFP());
2426 assert(!m_fp->hasInvName());
2427 arSetSfp(ar, m_fp);
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);
2439 m_fp = ar;
2440 pc = func->getEntry();
2441 SYNC();
2442 bool ret = EventHook::FunctionEnter(m_fp, funcType);
2443 pc = m_pc;
2444 return ret;
2447 StaticString
2448 s_php_namespace("<?php namespace "),
2449 s_curly_return(" { return "),
2450 s_semicolon_curly("; }"),
2451 s_php_return("<?php return "),
2452 s_semicolon(";");
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;
2462 String code;
2463 int pos = namespacedName.rfind('\\');
2464 if (pos != -1) {
2465 auto ns = namespacedName.substr(0, pos);
2466 code = s_php_namespace + ns + s_curly_return + key + s_semicolon_curly;
2467 } else {
2468 code = s_php_return + key + s_semicolon;
2470 Unit* unit = compileEvalString(code.get());
2471 assert(unit != nullptr);
2472 Variant v;
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,
2476 InvokePseudoMain);
2477 Variant &lv = m_evaledArgs.lvalAt(key, AccessFlags::Key);
2478 lv = v;
2479 return lv;
2483 * Helper for function entry, including pseudo-main entry.
2485 void
2486 VMExecutionContext::pushLocalsAndIterators(const Func* func,
2487 int nparams /*= 0*/) {
2488 // Push locals.
2489 for (int i = nparams; i < func->numLocals(); i++) {
2490 m_stack.pushUninit();
2492 // Push iterators.
2493 for (int i = 0; i < func->numIterators(); i++) {
2494 m_stack.allocI();
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
2520 // across requests.
2521 code = StringData::GetStaticString(code);
2522 if (s_evaledUnits.insert(acc, code)) {
2523 acc->second = compile_string(code->data(), code->size());
2525 return acc->second;
2528 CStrRef VMExecutionContext::createFunction(CStrRef args, CStrRef code) {
2529 VMRegAnchor _;
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);
2549 unit->merge();
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.
2559 TypedValue retval;
2560 invokeFunc(&retval, unit->getMain(), null_array,
2561 nullptr, nullptr, nullptr, nullptr,
2562 InvokePseudoMain);
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,
2571 int frame) {
2572 assert(retval);
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);
2578 return true;
2580 // Do not JIT this unit, we are using it exactly once.
2581 unit->setInterpretOnly();
2583 bool failed = true;
2584 VarEnv *varEnv = nullptr;
2585 ActRec *fp = getFP();
2586 ActRec *cfpSave = nullptr;
2587 if (fp) {
2588 for (; frame > 0; --frame) {
2589 ActRec* prevFp = getPrevVMState(fp);
2590 if (!prevFp) {
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.
2594 break;
2596 fp = prevFp;
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;
2613 if (fp) {
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");
2627 try {
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);
2633 failed = false;
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);
2652 if (ee) {
2653 g_vmContext->write("\n");
2654 g_vmContext->write(
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());
2661 } catch (...) {
2662 g_vmContext->write(s_cppException.data());
2665 if (varEnv) {
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);
2671 return failed;
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);
2685 ar->m_soff = 0;
2686 ar->m_savedRbp = 0;
2687 ar->m_savedRip = reinterpret_cast<uintptr_t>(tx()->uniqueStubs.callToExit);
2688 assert(isReturnHelper(ar->m_savedRip));
2689 m_fp = ar;
2690 m_pc = s_debuggerDummy->entry();
2691 m_firstAR = ar;
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;
2708 try {
2709 frame_free_locals_inl_no_hook<true>(m_fp, func->numLocals());
2710 } catch (...) {}
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);
2715 m_fp = nullptr;
2716 m_pc = nullptr;
2719 // Identifies the set of return helpers that we may set m_savedRip to in an
2720 // ActRec.
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();
2738 while (ar) {
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()) {
2745 ar->m_savedRip =
2746 reinterpret_cast<uintptr_t>(tx()->uniqueStubs.genRetHelper);
2747 } else {
2748 ar->m_savedRip =
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,
2763 StringData*& name,
2764 TypedValue* key,
2765 TypedValue*& val) {
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);
2771 } else {
2772 assert(!fp->hasInvName());
2773 if (fp->hasVarEnv()) {
2774 val = fp->m_varEnv->lookup(name);
2775 } else {
2776 val = nullptr;
2781 static inline void lookupd_var(ActRec* fp,
2782 StringData*& name,
2783 TypedValue* key,
2784 TypedValue*& val) {
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);
2790 } else {
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) {
2797 TypedValue tv;
2798 tvWriteNull(&tv);
2799 fp->m_varEnv->set(name, &tv);
2800 val = fp->m_varEnv->lookup(name);
2805 static inline void lookup_gbl(ActRec* fp,
2806 StringData*& name,
2807 TypedValue* key,
2808 TypedValue*& val) {
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,
2815 StringData*& name,
2816 TypedValue* key,
2817 TypedValue*& val) {
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) {
2823 TypedValue tv;
2824 tvWriteNull(&tv);
2825 varEnv->set(name, &tv);
2826 val = varEnv->lookup(name);
2830 static inline void lookup_sprop(ActRec* fp,
2831 TypedValue* clsRef,
2832 StringData*& name,
2833 TypedValue* key,
2834 TypedValue*& val,
2835 bool& visible,
2836 bool& accessible) {
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,
2844 TypedValue* output,
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();
2855 } else {
2856 output->m_type = KindOfNull;
2857 raise_error("Cls: Expected string or object");
2859 if (decRef) {
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;
2871 return -1;
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)) {
2889 tvDecRef(&tvRef2);
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);
2901 result = &tvRef2;
2905 #define DECLARE_MEMBERHELPER_ARGS \
2906 unsigned ndiscard; \
2907 TypedValue* base; \
2908 TypedValue tvScratch; \
2909 TypedValue tvLiteral; \
2910 Variant tvRef; \
2911 Variant tvRef2; \
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 \
2917 TypedValue* tvRet;
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,
2943 bool saveResult,
2944 VMExecutionContext::VectorLeaveCode mleave>
2945 inline void OPTBLD_INLINE VMExecutionContext::getHelperPre(
2946 PC& pc,
2947 unsigned& ndiscard,
2948 TypedValue*& base,
2949 TypedValue& tvScratch,
2950 TypedValue& tvLiteral,
2951 TypedValue& tvRef,
2952 TypedValue& tvRef2,
2953 MemberCode& mcode,
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);
2972 if (!ndiscard) {
2973 tvRet = m_stack.allocTV();
2974 } else {
2975 m_stack.ndiscard(ndiscard - 1);
2976 tvRet = m_stack.topTV();
2979 if (saveResult) {
2980 // If tvRef wasn't just allocated, we've already decref'd it in
2981 // the loop above.
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,
2991 unsigned& ndiscard,
2992 TypedValue*& tvRet,
2993 TypedValue*& base,
2994 TypedValue& tvScratch,
2995 TypedValue& tvLiteral,
2996 Variant& tvRef,
2997 Variant& tvRef2,
2998 MemberCode& mcode,
2999 TypedValue*& curMember) {
3000 getHelperPre<true, true, VectorLeaveCode::ConsumeAll>(MEMBERHELPERPRE_ARGS);
3001 getHelperPost<true>(GETHELPERPOST_ARGS);
3004 template <bool setMember,
3005 bool warn,
3006 bool define,
3007 bool unset,
3008 bool reffy,
3009 unsigned mdepth, // extra args on stack for set (e.g. rhs)
3010 VMExecutionContext::VectorLeaveCode mleave,
3011 bool saveResult>
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
3025 // instruction.
3026 pc += immVec.size() + sizeof(int32_t) + sizeof(int32_t);
3028 if (!setMember) {
3029 assert(mdepth == 0);
3030 assert(!define);
3031 assert(!unset);
3034 ndiscard = immVec.numStackValues();
3035 int depth = mdepth + ndiscard - 1;
3036 const LocationCode lcode = LocationCode(*vec++);
3038 TypedValue* loc = nullptr;
3039 TypedValue dummy;
3040 Class* const ctx = arGetContextClass(getFP());
3042 StringData* name;
3043 TypedValue* fr = nullptr;
3044 TypedValue* cref;
3045 TypedValue* pname;
3046 tvWriteUninit(&tvScratch);
3048 switch (lcode) {
3049 case LNL:
3050 loc = frame_local_inner(m_fp, decodeVariableSizeImm(&vec));
3051 goto lcodeName;
3052 case LNC:
3053 loc = m_stack.indTV(depth--);
3054 goto lcodeName;
3056 lcodeName:
3057 if (define) {
3058 lookupd_var(m_fp, name, loc, fr);
3059 } else {
3060 lookup_var(m_fp, name, loc, fr);
3062 if (fr == nullptr) {
3063 if (warn) {
3064 raise_notice(Strings::UNDEFINED_VARIABLE, name->data());
3066 tvWriteNull(&dummy);
3067 loc = &dummy;
3068 } else {
3069 loc = fr;
3071 decRefStr(name);
3072 break;
3074 case LGL:
3075 loc = frame_local_inner(m_fp, decodeVariableSizeImm(&vec));
3076 goto lcodeGlobal;
3077 case LGC:
3078 loc = m_stack.indTV(depth--);
3079 goto lcodeGlobal;
3081 lcodeGlobal:
3082 if (define) {
3083 lookupd_gbl(m_fp, name, loc, fr);
3084 } else {
3085 lookup_gbl(m_fp, name, loc, fr);
3087 if (fr == nullptr) {
3088 if (warn) {
3089 raise_notice(Strings::UNDEFINED_VARIABLE, name->data());
3091 tvWriteNull(&dummy);
3092 loc = &dummy;
3093 } else {
3094 loc = fr;
3096 decRefStr(name);
3097 break;
3099 case LSC:
3100 cref = m_stack.indTV(mdepth);
3101 pname = m_stack.indTV(depth--);
3102 goto lcodeSprop;
3103 case LSL:
3104 cref = m_stack.indTV(mdepth);
3105 pname = frame_local_inner(m_fp, decodeVariableSizeImm(&vec));
3106 goto lcodeSprop;
3108 lcodeSprop: {
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(),
3117 name->data());
3119 decRefStr(name);
3120 break;
3123 case LL: {
3124 int localInd = decodeVariableSizeImm(&vec);
3125 loc = frame_local_inner(m_fp, localInd);
3126 if (warn) {
3127 if (loc->m_type == KindOfUninit) {
3128 raise_notice(Strings::UNDEFINED_VARIABLE,
3129 m_fp->m_func->localVarName(localInd)->data());
3132 break;
3134 case LC:
3135 case LR:
3136 loc = m_stack.indTV(depth--);
3137 break;
3138 case LH:
3139 assert(m_fp->hasThis());
3140 tvScratch.m_type = KindOfObject;
3141 tvScratch.m_data.pobj = m_fp->getThis();
3142 loc = &tvScratch;
3143 break;
3145 default: not_reached();
3148 base = loc;
3149 tvWriteUninit(&tvLiteral);
3150 tvWriteUninit(&tvRef);
3151 tvWriteUninit(&tvRef2);
3153 // Iterate through the members.
3154 while (vec < pc) {
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;
3166 } else {
3167 assert(memberCodeImmIsLoc(mcode));
3168 curMember = frame_local_inner(m_fp, memberImm);
3170 } else {
3171 curMember = (setMember && mcode == MW) ? nullptr : m_stack.indTV(depth--);
3174 if (mleave == VectorLeaveCode::LeaveLast) {
3175 if (vec >= pc) {
3176 assert(vec == pc);
3177 break;
3181 TypedValue* result;
3182 switch (mcode) {
3183 case MEL:
3184 case MEC:
3185 case MET:
3186 case MEI:
3187 if (unset) {
3188 result = ElemU(tvScratch, tvRef, base, curMember);
3189 } else if (define) {
3190 result = ElemD<warn,reffy>(tvScratch, tvRef, base, curMember);
3191 } else {
3192 result = Elem<warn>(tvScratch, tvRef, base, curMember);
3194 break;
3195 case MPL:
3196 case MPC:
3197 case MPT:
3198 result = Prop<warn, define, unset>(tvScratch, tvRef, ctx, base,
3199 curMember);
3200 break;
3201 case MW:
3202 if (setMember) {
3203 assert(define);
3204 result = NewElem(tvScratch, tvRef, base);
3205 } else {
3206 raise_error("Cannot use [] for reading");
3207 result = nullptr;
3209 break;
3210 default:
3211 assert(false);
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) {
3218 return true;
3220 base = result;
3223 if (mleave == VectorLeaveCode::ConsumeAll) {
3224 assert(vec == pc);
3225 if (debug) {
3226 if (lcode == LSC || lcode == LSL) {
3227 assert(depth == int(mdepth));
3228 } else {
3229 assert(depth == int(mdepth) - 1);
3234 if (saveResult) {
3235 assert(!setMember);
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);
3247 return false;
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,
3261 bool define,
3262 bool unset,
3263 bool reffy,
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
3289 // the stack.
3290 if (mdepth > 0) {
3291 assert(mdepth == 1 &&
3292 "We don't really support mdepth > 1 in setHelperPost");
3294 if (ndiscard > 0) {
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");
3307 abort();
3310 inline void OPTBLD_INLINE VMExecutionContext::iopNop(PC& pc) {
3311 NEXT();
3314 inline void OPTBLD_INLINE VMExecutionContext::iopPopC(PC& pc) {
3315 NEXT();
3316 m_stack.popC();
3319 inline void OPTBLD_INLINE VMExecutionContext::iopPopV(PC& pc) {
3320 NEXT();
3321 m_stack.popV();
3324 inline void OPTBLD_INLINE VMExecutionContext::iopPopR(PC& pc) {
3325 NEXT();
3326 if (m_stack.topTV()->m_type != KindOfRef) {
3327 m_stack.popC();
3328 } else {
3329 m_stack.popV();
3333 inline void OPTBLD_INLINE VMExecutionContext::iopDup(PC& pc) {
3334 NEXT();
3335 m_stack.dup();
3338 inline void OPTBLD_INLINE VMExecutionContext::iopBox(PC& pc) {
3339 NEXT();
3340 m_stack.box();
3343 inline void OPTBLD_INLINE VMExecutionContext::iopUnbox(PC& pc) {
3344 NEXT();
3345 m_stack.unbox();
3348 inline void OPTBLD_INLINE VMExecutionContext::iopBoxR(PC& pc) {
3349 NEXT();
3350 TypedValue* tv = m_stack.topTV();
3351 if (tv->m_type != KindOfRef) {
3352 tvBox(tv);
3356 inline void OPTBLD_INLINE VMExecutionContext::iopUnboxR(PC& pc) {
3357 NEXT();
3358 if (m_stack.topTV()->m_type == KindOfRef) {
3359 m_stack.unbox();
3363 inline void OPTBLD_INLINE VMExecutionContext::iopNull(PC& pc) {
3364 NEXT();
3365 m_stack.pushNull();
3368 inline void OPTBLD_INLINE VMExecutionContext::iopNullUninit(PC& pc) {
3369 NEXT();
3370 m_stack.pushNullUninit();
3373 inline void OPTBLD_INLINE VMExecutionContext::iopTrue(PC& pc) {
3374 NEXT();
3375 m_stack.pushTrue();
3378 inline void OPTBLD_INLINE VMExecutionContext::iopFalse(PC& pc) {
3379 NEXT();
3380 m_stack.pushFalse();
3383 inline void OPTBLD_INLINE VMExecutionContext::iopFile(PC& pc) {
3384 NEXT();
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) {
3390 NEXT();
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) {
3396 NEXT();
3397 DECODE(int64_t, i);
3398 m_stack.pushInt(i);
3401 inline void OPTBLD_INLINE VMExecutionContext::iopDouble(PC& pc) {
3402 NEXT();
3403 DECODE(double, d);
3404 m_stack.pushDouble(d);
3407 inline void OPTBLD_INLINE VMExecutionContext::iopString(PC& pc) {
3408 NEXT();
3409 DECODE_LITSTR(s);
3410 m_stack.pushStaticString(s);
3413 inline void OPTBLD_INLINE VMExecutionContext::iopArray(PC& pc) {
3414 NEXT();
3415 DECODE(Id, id);
3416 ArrayData* a = m_fp->m_func->unit()->lookupArrayId(id);
3417 m_stack.pushStaticArray(a);
3420 inline void OPTBLD_INLINE VMExecutionContext::iopNewArray(PC& pc) {
3421 NEXT();
3422 auto arr = ArrayData::MakeReserve(HphpArray::SmallSize);
3423 m_stack.pushArrayNoRc(arr);
3426 inline void OPTBLD_INLINE VMExecutionContext::iopNewTuple(PC& pc) {
3427 NEXT();
3428 DECODE_IVA(n);
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) {
3436 NEXT();
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));
3445 } else {
3446 cellAsVariant(*c3).asArrRef().set(tvAsCVarRef(c2), tvAsCVarRef(c1));
3448 m_stack.popC();
3449 m_stack.popC();
3452 inline void OPTBLD_INLINE VMExecutionContext::iopAddElemV(PC& pc) {
3453 NEXT();
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)));
3462 } else {
3463 cellAsVariant(*c3).asArrRef().set(tvAsCVarRef(c2), ref(tvAsCVarRef(r1)));
3465 m_stack.popV();
3466 m_stack.popC();
3469 inline void OPTBLD_INLINE VMExecutionContext::iopAddNewElemC(PC& pc) {
3470 NEXT();
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));
3477 m_stack.popC();
3480 inline void OPTBLD_INLINE VMExecutionContext::iopAddNewElemV(PC& pc) {
3481 NEXT();
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)));
3488 m_stack.popV();
3491 inline void OPTBLD_INLINE VMExecutionContext::iopNewCol(PC& pc) {
3492 NEXT();
3493 DECODE_IVA(cType);
3494 DECODE_IVA(nElms);
3495 ObjectData* obj;
3496 switch (cType) {
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;
3502 default:
3503 obj = nullptr;
3504 raise_error("NewCol: Invalid collection type");
3505 break;
3507 // Reserve enough room for nElms elements in advance
3508 if (nElms) {
3509 collectionReserve(obj, nElms);
3511 m_stack.pushObject(obj);
3514 inline void OPTBLD_INLINE VMExecutionContext::iopColAddNewElemC(PC& pc) {
3515 NEXT();
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);
3520 } else {
3521 raise_error("ColAddNewElemC: $2 must be a collection");
3523 m_stack.popC();
3526 inline void OPTBLD_INLINE VMExecutionContext::iopColAddElemC(PC& pc) {
3527 NEXT();
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);
3533 } else {
3534 raise_error("ColAddElemC: $3 must be a collection");
3536 m_stack.popC();
3537 m_stack.popC();
3540 inline void OPTBLD_INLINE VMExecutionContext::iopCns(PC& pc) {
3541 NEXT();
3542 DECODE_LITSTR(s);
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);
3547 return;
3549 auto const c1 = m_stack.allocC();
3550 cellDup(*cns, *c1);
3553 inline void OPTBLD_INLINE VMExecutionContext::iopCnsE(PC& pc) {
3554 NEXT();
3555 DECODE_LITSTR(s);
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();
3561 cellDup(*cns, *c1);
3564 inline void OPTBLD_INLINE VMExecutionContext::iopCnsU(PC& pc) {
3565 NEXT();
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) {
3572 raise_notice(
3573 Strings::UNDEFINED_CONSTANT,
3574 fallback->data(),
3575 fallback->data()
3577 m_stack.pushStaticString(fallback);
3578 return;
3581 auto const c1 = m_stack.allocC();
3582 cellDup(*cns, *c1);
3585 inline void OPTBLD_INLINE VMExecutionContext::iopDefCns(PC& pc) {
3586 NEXT();
3587 DECODE_LITSTR(s);
3588 TypedValue* tv = m_stack.topTV();
3589 tvAsVariant(tv) = Unit::defCns(s, tv);
3592 inline void OPTBLD_INLINE VMExecutionContext::iopClsCns(PC& pc) {
3593 NEXT();
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) {
3608 NEXT();
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) {
3622 NEXT();
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());
3628 } else {
3629 cellAsVariant(*c2) = concat(cellAsVariant(*c2).toString(),
3630 cellAsCVarRef(*c1).toString());
3632 assert(c2->m_data.pstr->getCount() > 0);
3633 m_stack.popC();
3636 inline void OPTBLD_INLINE VMExecutionContext::iopNot(PC& pc) {
3637 NEXT();
3638 Cell* c1 = m_stack.topC();
3639 cellAsVariant(*c1) = !cellAsVariant(*c1).toBoolean();
3643 inline void OPTBLD_INLINE VMExecutionContext::iopAbs(PC& pc) {
3644 NEXT();
3645 auto c1 = m_stack.topC();
3647 tvAsVariant(c1) = f_abs(tvAsCVarRef(c1));
3650 template<class Op>
3651 inline void OPTBLD_INLINE VMExecutionContext::implCellBinOp(PC& pc, Op op) {
3652 NEXT();
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);
3657 *c2 = result;
3658 m_stack.popC();
3661 template<class Op>
3662 inline void OPTBLD_INLINE VMExecutionContext::implCellBinOpBool(PC& pc, Op op) {
3663 NEXT();
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);
3669 m_stack.popC();
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) {
3765 NEXT();
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(
3782 "sqrt",
3784 KindOfDouble,
3785 c1->m_type
3787 tvRefcountedDecRefCell(c1);
3788 c1->m_type = KindOfNull;
3792 inline void OPTBLD_INLINE VMExecutionContext::iopBitNot(PC& pc) {
3793 NEXT();
3794 cellBitNot(*m_stack.topC());
3797 inline void OPTBLD_INLINE VMExecutionContext::iopCastBool(PC& pc) {
3798 NEXT();
3799 Cell* c1 = m_stack.topC();
3800 tvCastToBooleanInPlace(c1);
3803 inline void OPTBLD_INLINE VMExecutionContext::iopCastInt(PC& pc) {
3804 NEXT();
3805 Cell* c1 = m_stack.topC();
3806 tvCastToInt64InPlace(c1);
3809 inline void OPTBLD_INLINE VMExecutionContext::iopCastDouble(PC& pc) {
3810 NEXT();
3811 Cell* c1 = m_stack.topC();
3812 tvCastToDoubleInPlace(c1);
3815 inline void OPTBLD_INLINE VMExecutionContext::iopCastString(PC& pc) {
3816 NEXT();
3817 Cell* c1 = m_stack.topC();
3818 tvCastToStringInPlace(c1);
3821 inline void OPTBLD_INLINE VMExecutionContext::iopCastArray(PC& pc) {
3822 NEXT();
3823 Cell* c1 = m_stack.topC();
3824 tvCastToArrayInPlace(c1);
3827 inline void OPTBLD_INLINE VMExecutionContext::iopCastObject(PC& pc) {
3828 NEXT();
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) {
3838 case KindOfObject:
3839 cls = Unit::lookupClass(ne);
3840 if (cls) return tv->m_data.pobj->instanceof(cls);
3841 break;
3842 case KindOfArray:
3843 cls = Unit::lookupClass(ne);
3844 if (cls && interface_supports_array(cls->name())) {
3845 return true;
3847 break;
3848 case KindOfString:
3849 case KindOfStaticString:
3850 cls = Unit::lookupClass(ne);
3851 if (cls && interface_supports_string(cls->name())) {
3852 return true;
3854 break;
3855 case KindOfInt64:
3856 cls = Unit::lookupClass(ne);
3857 if (cls && interface_supports_int(cls->name())) {
3858 return true;
3860 break;
3861 case KindOfDouble:
3862 cls = Unit::lookupClass(ne);
3863 if (cls && interface_supports_double(cls->name())) {
3864 return true;
3866 break;
3867 default:
3868 return false;
3870 return false;
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);
3882 if (normName) {
3883 rhs = Unit::GetNamedEntity(normName.get(), false);
3884 if (LIKELY(rhs && rhs->getCachedClass() != nullptr)) {
3885 return cellInstanceOf(c2, rhs);
3888 return false;
3891 inline void OPTBLD_INLINE VMExecutionContext::iopInstanceOf(PC& pc) {
3892 NEXT();
3893 Cell* c1 = m_stack.topC(); // c2 instanceof c1
3894 Cell* c2 = m_stack.indC(1);
3895 bool r = false;
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());
3904 } else {
3905 raise_error("Class name must be a valid object or a string");
3907 m_stack.popC();
3908 tvRefcountedDecRefCell(c2);
3909 c2->m_data.num = r;
3910 c2->m_type = KindOfBoolean;
3913 inline void OPTBLD_INLINE VMExecutionContext::iopInstanceOfD(PC& pc) {
3914 NEXT();
3915 DECODE(Id, id);
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);
3923 c1->m_data.num = r;
3924 c1->m_type = KindOfBoolean;
3927 inline void OPTBLD_INLINE VMExecutionContext::iopPrint(PC& pc) {
3928 NEXT();
3929 Cell* c1 = m_stack.topC();
3930 echo(cellAsVariant(*c1).toString());
3931 tvRefcountedDecRefCell(c1);
3932 c1->m_type = KindOfInt64;
3933 c1->m_data.num = 1;
3936 inline void OPTBLD_INLINE VMExecutionContext::iopClone(PC& pc) {
3937 NEXT();
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();
3945 m_stack.popTV();
3946 m_stack.pushNull();
3947 tv->m_type = KindOfObject;
3948 tv->m_data.pobj = newobj;
3951 inline void OPTBLD_INLINE VMExecutionContext::iopExit(PC& pc) {
3952 NEXT();
3953 int exitCode = 0;
3954 Cell* c1 = m_stack.topC();
3955 if (c1->m_type == KindOfInt64) {
3956 exitCode = c1->m_data.num;
3957 } else {
3958 echo(cellAsVariant(*c1).toString());
3960 m_stack.popC();
3961 m_stack.pushNull();
3962 throw ExitException(exitCode);
3965 inline void OPTBLD_INLINE VMExecutionContext::iopFatal(PC& pc) {
3966 NEXT();
3967 TypedValue* top = m_stack.topTV();
3968 std::string msg;
3969 DECODE_IVA(skipFrame);
3970 if (IS_STRING_TYPE(top->m_type)) {
3971 msg = top->m_data.pstr->data();
3972 } else {
3973 msg = "Fatal error message not a string";
3975 m_stack.popTV();
3976 if (skipFrame) {
3977 raise_error_without_first_frame(msg);
3978 } else {
3979 raise_error(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) {
3990 NEXT();
3991 DECODE_JMP(Offset, offset);
3992 jmpSurpriseCheck(offset);
3994 pc += offset - 1;
3997 template<Op op>
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");
4001 NEXT();
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) {
4009 pc += offset - 1;
4010 m_stack.popX();
4011 } else {
4012 pc += sizeof(Offset);
4013 m_stack.popX();
4015 } else {
4016 auto const condition = toBoolean(cellAsCVarRef(*c1));
4017 if (op == OpJmpZ ? !condition : condition) {
4018 pc += offset - 1;
4019 m_stack.popC();
4020 } else {
4021 pc += sizeof(Offset);
4022 m_stack.popC();
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 { \
4036 int iterIndex; \
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; \
4049 } while(0)
4051 inline void OPTBLD_INLINE VMExecutionContext::iopIterBreak(PC& pc) {
4052 PC savedPc = pc;
4053 NEXT();
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) {
4074 out = d;
4075 return SwitchMatch::NORMAL;
4076 } else {
4077 return SwitchMatch::DEFAULT;
4081 inline void OPTBLD_INLINE VMExecutionContext::iopSwitch(PC& pc) {
4082 PC origPC = pc;
4083 NEXT();
4084 DECODE(int32_t, veclen);
4085 assert(veclen > 0);
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();
4092 if (!bounded) {
4093 assert(val->m_type == KindOfInt64);
4094 // Continuation switch: no bounds checking needed
4095 int64_t label = val->m_data.num;
4096 m_stack.popX();
4097 assert(label >= 0 && label < veclen);
4098 pc = origPC + jmptab[label];
4099 } else {
4100 // Generic integer switch
4101 int64_t intval;
4102 SwitchMatch match = SwitchMatch::NORMAL;
4104 switch (val->m_type) {
4105 case KindOfUninit:
4106 case KindOfNull:
4107 intval = 0;
4108 break;
4110 case KindOfBoolean:
4111 // bool(true) is equal to any non-zero int, bool(false) == 0
4112 if (val->m_data.num) {
4113 match = SwitchMatch::NONZERO;
4114 } else {
4115 intval = 0;
4117 break;
4119 case KindOfInt64:
4120 intval = val->m_data.num;
4121 break;
4123 case KindOfDouble:
4124 match = doubleCheck(val->m_data.dbl, intval);
4125 break;
4127 case KindOfStaticString:
4128 case KindOfString: {
4129 double dval = 0.0;
4130 DataType t = val->m_data.pstr->isNumericWithVal(intval, dval, 1);
4131 switch (t) {
4132 case KindOfNull:
4133 intval = 0;
4134 break;
4136 case KindOfDouble:
4137 match = doubleCheck(dval, intval);
4138 break;
4140 case KindOfInt64:
4141 // do nothing
4142 break;
4144 default:
4145 not_reached();
4147 tvRefcountedDecRef(val);
4148 break;
4151 case KindOfArray:
4152 match = SwitchMatch::DEFAULT;
4153 tvDecRef(val);
4154 break;
4156 case KindOfObject:
4157 intval = val->m_data.pobj->o_toInt64();
4158 tvDecRef(val);
4159 break;
4161 case KindOfResource:
4162 intval = val->m_data.pres->o_toInt64();
4163 tvDecRef(val);
4164 break;
4166 default:
4167 not_reached();
4169 m_stack.discard();
4171 if (match != SwitchMatch::NORMAL ||
4172 intval < base || intval >= (base + veclen - 2)) {
4173 switch (match) {
4174 case SwitchMatch::NORMAL:
4175 case SwitchMatch::DEFAULT:
4176 pc = origPC + jmptab[veclen - 1];
4177 break;
4179 case SwitchMatch::NONZERO:
4180 pc = origPC + jmptab[veclen - 2];
4181 break;
4183 } else {
4184 pc = origPC + jmptab[intval - base];
4189 inline void OPTBLD_INLINE VMExecutionContext::iopSSwitch(PC& pc) {
4190 PC origPC = pc;
4191 NEXT();
4192 DECODE(int32_t, veclen);
4193 assert(veclen > 1);
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();
4200 unsigned i;
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;
4206 break;
4209 if (i == cases) {
4210 // default case
4211 pc = origPC + jmptab[veclen-1].dest;
4213 m_stack.popC();
4216 inline void OPTBLD_INLINE VMExecutionContext::iopRetC(PC& pc) {
4217 NEXT();
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));
4228 // Adjust the stack
4229 m_stack.ndiscard(m_fp->m_func->numSlotsInFrame() + 1);
4231 if (LIKELY(sfp != m_fp)) {
4232 // Restore caller's execution state.
4233 m_fp = sfp;
4234 pc = m_fp->m_func->unit()->entry() + m_fp->m_func->base() + soff;
4235 m_stack.ret();
4236 assert(m_stack.topTV() == retval_ptr);
4237 } else {
4238 // No caller; terminate.
4239 m_stack.ret();
4240 #ifdef HPHP_TRACE
4242 std::ostringstream os;
4243 os << toStringElm(m_stack.topTV());
4244 ONTRACE(1,
4245 Trace::trace("Return %s from VMExecutionContext::dispatch("
4246 "%p)\n", os.str().c_str(), m_fp));
4248 #endif
4249 pc = 0;
4253 inline void OPTBLD_INLINE VMExecutionContext::iopRetV(PC& pc) {
4254 iopRetC(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);
4272 m_stack.popC();
4273 DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionThrownHook(obj.get()));
4274 throw obj;
4277 inline void OPTBLD_INLINE VMExecutionContext::iopAGetC(PC& pc) {
4278 NEXT();
4279 TypedValue* tv = m_stack.topTV();
4280 lookupClsRef(tv, tv, true);
4283 inline void OPTBLD_INLINE VMExecutionContext::iopAGetL(PC& pc) {
4284 NEXT();
4285 DECODE_HA(local);
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,
4303 TypedValue* fr,
4304 TypedValue* to,
4305 Id pind) {
4306 if (fr->m_type == KindOfUninit) {
4307 // `to' is uninitialized here, so we need to tvWriteNull before
4308 // possibly causing stack unwinding.
4309 tvWriteNull(to);
4310 raise_undefined_local(fp, pind);
4311 } else {
4312 cgetl_inner_body(fr, to);
4316 inline void OPTBLD_INLINE VMExecutionContext::iopCGetL(PC& pc) {
4317 NEXT();
4318 DECODE_HA(local);
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) {
4325 NEXT();
4326 DECODE_HA(local);
4327 TypedValue* oldTop = m_stack.topTV();
4328 TypedValue* newTop = m_stack.allocTV();
4329 memcpy(newTop, oldTop, sizeof *newTop);
4330 Cell* to = oldTop;
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) {
4336 NEXT();
4337 DECODE_HA(local);
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) {
4348 NEXT();
4349 StringData* name;
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);
4356 tvWriteNull(to);
4357 } else {
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) {
4365 NEXT();
4366 StringData* name;
4367 TypedValue* to = m_stack.topTV();
4368 TypedValue* fr = nullptr;
4369 lookup_gbl(m_fp, name, to, fr);
4370 if (fr == nullptr) {
4371 if (MoreWarnings) {
4372 raise_notice(Strings::UNDEFINED_VARIABLE, name->data());
4374 tvRefcountedDecRefCell(to);
4375 tvWriteNull(to);
4376 } else if (fr->m_type == KindOfUninit) {
4377 raise_notice(Strings::UNDEFINED_VARIABLE, name->data());
4378 tvRefcountedDecRefCell(to);
4379 tvWriteNull(to);
4380 } else {
4381 tvRefcountedDecRefCell(to);
4382 cgetl_inner_body(fr, to);
4384 decRefStr(name); // TODO(#1146727): leaks during exceptions
4387 #define SPROP_OP_PRELUDE \
4388 NEXT(); \
4389 TypedValue* clsref = m_stack.topTV(); \
4390 TypedValue* nameCell = m_stack.indTV(1); \
4391 TypedValue* output = nameCell; \
4392 TypedValue* val; \
4393 bool visible, accessible; \
4394 lookup_sprop(m_fp, clsref, name, nameCell, val, visible, \
4395 accessible);
4397 #define SPROP_OP_POSTLUDE \
4398 decRefStr(name);
4400 #define GETS(box) do { \
4401 SPROP_OP_PRELUDE \
4402 if (!(visible && accessible)) { \
4403 raise_error("Invalid static property access: %s::%s", \
4404 clsref->m_data.pcls->name()->data(), \
4405 name->data()); \
4407 if (box) { \
4408 if (val->m_type != KindOfRef) { \
4409 tvBox(val); \
4411 refDup(*val, *output); \
4412 } else { \
4413 cellDup(*tvToCell(val), *output); \
4415 m_stack.popA(); \
4416 SPROP_OP_POSTLUDE \
4417 } while (0)
4419 inline void OPTBLD_INLINE VMExecutionContext::iopCGetS(PC& pc) {
4420 StringData* name;
4421 GETS(false);
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) {
4429 PC oldPC = pc;
4430 NEXT();
4431 DECLARE_GETHELPER_ARGS
4432 getHelper(GETHELPER_ARGS);
4433 if (tvRet->m_type == KindOfRef) {
4434 tvUnbox(tvRet);
4436 assert(hasImmVector(toOp(*oldPC)));
4437 const ImmVector& immVec = ImmVector::createFromStream(oldPC + 1);
4438 StringData* name;
4439 MemberCode mc;
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) {
4447 tvBox(fr);
4449 refDup(*fr, *to);
4452 inline void OPTBLD_INLINE VMExecutionContext::iopVGetL(PC& pc) {
4453 NEXT();
4454 DECODE_HA(local);
4455 Ref* to = m_stack.allocV();
4456 TypedValue* fr = frame_local(m_fp, local);
4457 vgetl_body(fr, to);
4460 inline void OPTBLD_INLINE VMExecutionContext::iopVGetN(PC& pc) {
4461 NEXT();
4462 StringData* name;
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);
4468 vgetl_body(fr, to);
4469 decRefStr(name);
4472 inline void OPTBLD_INLINE VMExecutionContext::iopVGetG(PC& pc) {
4473 NEXT();
4474 StringData* name;
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);
4480 vgetl_body(fr, to);
4481 decRefStr(name);
4484 inline void OPTBLD_INLINE VMExecutionContext::iopVGetS(PC& pc) {
4485 StringData* name;
4486 GETS(true);
4488 #undef GETS
4490 inline void OPTBLD_INLINE VMExecutionContext::iopVGetM(PC& pc) {
4491 NEXT();
4492 DECLARE_SETHELPER_ARGS
4493 TypedValue* tv1 = m_stack.allocTV();
4494 tvWriteUninit(tv1);
4495 if (!setHelperPre<false, true, false, true, 1,
4496 VectorLeaveCode::ConsumeAll>(MEMBERHELPERPRE_ARGS)) {
4497 if (base->m_type != KindOfRef) {
4498 tvBox(base);
4500 refDup(*base, *tv1);
4501 } else {
4502 tvWriteNull(tv1);
4503 tvBox(tv1);
4505 setHelperPost<1>(SETHELPERPOST_ARGS);
4508 inline void OPTBLD_INLINE VMExecutionContext::iopIssetN(PC& pc) {
4509 NEXT();
4510 StringData* name;
4511 TypedValue* tv1 = m_stack.topTV();
4512 TypedValue* tv = nullptr;
4513 bool e;
4514 lookup_var(m_fp, name, tv1, tv);
4515 if (tv == nullptr) {
4516 e = false;
4517 } else {
4518 e = !tvIsNull(tvToCell(tv));
4520 tvRefcountedDecRefCell(tv1);
4521 tv1->m_data.num = e;
4522 tv1->m_type = KindOfBoolean;
4523 decRefStr(name);
4526 inline void OPTBLD_INLINE VMExecutionContext::iopIssetG(PC& pc) {
4527 NEXT();
4528 StringData* name;
4529 TypedValue* tv1 = m_stack.topTV();
4530 TypedValue* tv = nullptr;
4531 bool e;
4532 lookup_gbl(m_fp, name, tv1, tv);
4533 if (tv == nullptr) {
4534 e = false;
4535 } else {
4536 e = !tvIsNull(tvToCell(tv));
4538 tvRefcountedDecRefCell(tv1);
4539 tv1->m_data.num = e;
4540 tv1->m_type = KindOfBoolean;
4541 decRefStr(name);
4544 inline void OPTBLD_INLINE VMExecutionContext::iopIssetS(PC& pc) {
4545 StringData* name;
4546 SPROP_OP_PRELUDE
4547 bool e;
4548 if (!(visible && accessible)) {
4549 e = false;
4550 } else {
4551 e = !tvIsNull(tvToCell(val));
4553 m_stack.popA();
4554 output->m_data.num = e;
4555 output->m_type = KindOfBoolean;
4556 SPROP_OP_POSTLUDE
4559 template <bool isEmpty>
4560 inline void OPTBLD_INLINE VMExecutionContext::isSetEmptyM(PC& pc) {
4561 NEXT();
4562 DECLARE_GETHELPER_ARGS
4563 getHelperPre<false, false, VectorLeaveCode::LeaveLast>(MEMBERHELPERPRE_ARGS);
4564 // Process last member specially, in order to employ the IssetElem/IssetProp
4565 // operations.
4566 bool isSetEmptyResult = false;
4567 switch (mcode) {
4568 case MEL:
4569 case MEC:
4570 case MET:
4571 case MEI: {
4572 isSetEmptyResult = IssetEmptyElem<isEmpty>(tvScratch, *tvRef.asTypedValue(),
4573 base, curMember);
4574 break;
4576 case MPL:
4577 case MPC:
4578 case MPT: {
4579 Class* ctx = arGetContextClass(m_fp);
4580 isSetEmptyResult = IssetEmptyProp<isEmpty>(ctx, base, curMember);
4581 break;
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) { \
4596 NEXT(); \
4597 DECODE_HA(local); \
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) { \
4610 NEXT(); \
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) {
4634 NEXT();
4635 DECODE_HA(local);
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) {
4644 NEXT();
4645 StringData* name;
4646 TypedValue* tv1 = m_stack.topTV();
4647 TypedValue* tv = nullptr;
4648 bool e;
4649 lookup_var(m_fp, name, tv1, tv);
4650 if (tv == nullptr) {
4651 e = true;
4652 } else {
4653 e = !cellToBool(*tvToCell(tv));
4655 tvRefcountedDecRefCell(tv1);
4656 tv1->m_data.num = e;
4657 tv1->m_type = KindOfBoolean;
4658 decRefStr(name);
4661 inline void OPTBLD_INLINE VMExecutionContext::iopEmptyG(PC& pc) {
4662 NEXT();
4663 StringData* name;
4664 TypedValue* tv1 = m_stack.topTV();
4665 TypedValue* tv = nullptr;
4666 bool e;
4667 lookup_gbl(m_fp, name, tv1, tv);
4668 if (tv == nullptr) {
4669 e = true;
4670 } else {
4671 e = !cellToBool(*tvToCell(tv));
4673 tvRefcountedDecRefCell(tv1);
4674 tv1->m_data.num = e;
4675 tv1->m_type = KindOfBoolean;
4676 decRefStr(name);
4679 inline void OPTBLD_INLINE VMExecutionContext::iopEmptyS(PC& pc) {
4680 StringData* name;
4681 SPROP_OP_PRELUDE
4682 bool e;
4683 if (!(visible && accessible)) {
4684 e = true;
4685 } else {
4686 e = !cellToBool(*tvToCell(val));
4688 m_stack.popA();
4689 output->m_data.num = e;
4690 output->m_type = KindOfBoolean;
4691 SPROP_OP_POSTLUDE
4694 inline void OPTBLD_INLINE VMExecutionContext::iopEmptyM(PC& pc) {
4695 isSetEmptyM<true>(pc);
4698 inline void OPTBLD_INLINE VMExecutionContext::iopAKExists(PC& pc) {
4699 NEXT();
4700 TypedValue* arr = m_stack.topTV();
4701 TypedValue* key = arr + 1;
4702 bool result = f_array_key_exists(tvAsCVarRef(key), tvAsCVarRef(arr));
4703 m_stack.popTV();
4704 tvRefcountedDecRef(key);
4705 key->m_data.num = result;
4706 key->m_type = KindOfBoolean;
4709 inline void OPTBLD_INLINE VMExecutionContext::iopArrayIdx(PC& pc) {
4710 NEXT();
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),
4716 tvAsCVarRef(arr),
4717 tvAsCVarRef(def));
4718 m_stack.popTV();
4719 m_stack.popTV();
4720 tvAsVariant(key) = result;
4723 inline void OPTBLD_INLINE VMExecutionContext::iopSetL(PC& pc) {
4724 NEXT();
4725 DECODE_HA(local);
4726 assert(local < m_fp->m_func->numLocals());
4727 Cell* fr = m_stack.topC();
4728 TypedValue* to = frame_local(m_fp, local);
4729 tvSet(*fr, *to);
4732 inline void OPTBLD_INLINE VMExecutionContext::iopSetN(PC& pc) {
4733 NEXT();
4734 StringData* name;
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);
4740 tvSet(*fr, *to);
4741 memcpy((void*)tv2, (void*)fr, sizeof(TypedValue));
4742 m_stack.discard();
4743 decRefStr(name);
4746 inline void OPTBLD_INLINE VMExecutionContext::iopSetG(PC& pc) {
4747 NEXT();
4748 StringData* name;
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);
4754 tvSet(*fr, *to);
4755 memcpy((void*)tv2, (void*)fr, sizeof(TypedValue));
4756 m_stack.discard();
4757 decRefStr(name);
4760 inline void OPTBLD_INLINE VMExecutionContext::iopSetS(PC& pc) {
4761 NEXT();
4762 TypedValue* tv1 = m_stack.topTV();
4763 TypedValue* classref = m_stack.indTV(1);
4764 TypedValue* propn = m_stack.indTV(2);
4765 TypedValue* output = propn;
4766 StringData* name;
4767 TypedValue* val;
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(),
4773 name->data());
4775 tvSet(*tv1, *val);
4776 tvRefcountedDecRefCell(propn);
4777 memcpy(output, tv1, sizeof(TypedValue));
4778 m_stack.ndiscard(2);
4779 decRefStr(name);
4782 inline void OPTBLD_INLINE VMExecutionContext::iopSetM(PC& pc) {
4783 NEXT();
4784 DECLARE_SETHELPER_ARGS
4785 if (!setHelperPre<false, true, false, false, 1,
4786 VectorLeaveCode::LeaveLast>(MEMBERHELPERPRE_ARGS)) {
4787 Cell* c1 = m_stack.topC();
4789 if (mcode == MW) {
4790 SetNewElem<true>(base, c1);
4791 } else {
4792 switch (mcode) {
4793 case MEL:
4794 case MEC:
4795 case MET:
4796 case MEI: {
4797 StringData* result = SetElem<true>(base, curMember, c1);
4798 if (result) {
4799 tvRefcountedDecRefCell(c1);
4800 c1->m_type = KindOfString;
4801 c1->m_data.pstr = result;
4803 break;
4805 case MPL:
4806 case MPC:
4807 case MPT: {
4808 Class* ctx = arGetContextClass(m_fp);
4809 SetProp<true>(ctx, base, curMember, c1);
4810 break;
4812 default: assert(false);
4816 setHelperPost<1>(SETHELPERPOST_ARGS);
4819 inline void OPTBLD_INLINE VMExecutionContext::iopSetWithRefLM(PC& pc) {
4820 NEXT();
4821 DECLARE_SETHELPER_ARGS
4822 bool skip = setHelperPre<false, true, false, false, 0,
4823 VectorLeaveCode::ConsumeAll>(MEMBERHELPERPRE_ARGS);
4824 DECODE_HA(local);
4825 if (!skip) {
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) {
4833 NEXT();
4834 DECLARE_SETHELPER_ARGS
4835 bool skip = setHelperPre<false, true, false, false, 1,
4836 VectorLeaveCode::ConsumeAll>(MEMBERHELPERPRE_ARGS);
4837 if (!skip) {
4838 TypedValue* from = m_stack.top();
4839 tvAsVariant(base) = withRefBind(tvAsVariant(from));
4841 setHelperPost<0>(SETHELPERPOST_ARGS);
4842 m_stack.popTV();
4845 inline void OPTBLD_INLINE VMExecutionContext::iopSetOpL(PC& pc) {
4846 NEXT();
4847 DECODE_HA(local);
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);
4853 cellDup(*to, *fr);
4856 inline void OPTBLD_INLINE VMExecutionContext::iopSetOpN(PC& pc) {
4857 NEXT();
4858 DECODE(unsigned char, op);
4859 StringData* name;
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);
4870 m_stack.discard();
4871 decRefStr(name);
4874 inline void OPTBLD_INLINE VMExecutionContext::iopSetOpG(PC& pc) {
4875 NEXT();
4876 DECODE(unsigned char, op);
4877 StringData* name;
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);
4888 m_stack.discard();
4889 decRefStr(name);
4892 inline void OPTBLD_INLINE VMExecutionContext::iopSetOpS(PC& pc) {
4893 NEXT();
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;
4899 StringData* name;
4900 TypedValue* val;
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(),
4906 name->data());
4908 SETOP_BODY(val, op, fr);
4909 tvRefcountedDecRefCell(propn);
4910 tvRefcountedDecRef(fr);
4911 cellDup(*tvToCell(val), *output);
4912 m_stack.ndiscard(2);
4913 decRefStr(name);
4916 inline void OPTBLD_INLINE VMExecutionContext::iopSetOpM(PC& pc) {
4917 NEXT();
4918 DECODE(unsigned char, op);
4919 DECLARE_SETHELPER_ARGS
4920 if (!setHelperPre<MoreWarnings, true, false, false, 1,
4921 VectorLeaveCode::LeaveLast>(MEMBERHELPERPRE_ARGS)) {
4922 TypedValue* result;
4923 Cell* rhs = m_stack.topC();
4925 if (mcode == MW) {
4926 result = SetOpNewElem(tvScratch, *tvRef.asTypedValue(), op, base, rhs);
4927 } else {
4928 switch (mcode) {
4929 case MEL:
4930 case MEC:
4931 case MET:
4932 case MEI:
4933 result = SetOpElem(tvScratch, *tvRef.asTypedValue(), op, base,
4934 curMember, rhs);
4935 break;
4936 case MPL:
4937 case MPC:
4938 case MPT: {
4939 Class *ctx = arGetContextClass(m_fp);
4940 result = SetOpProp(tvScratch, *tvRef.asTypedValue(), ctx, op, base,
4941 curMember, rhs);
4942 break;
4944 default:
4945 assert(false);
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) {
4957 NEXT();
4958 DECODE_HA(local);
4959 DECODE(unsigned char, op);
4960 TypedValue* to = m_stack.allocTV();
4961 tvWriteUninit(to);
4962 TypedValue* fr = frame_local(m_fp, local);
4963 IncDecBody<true>(op, fr, to);
4966 inline void OPTBLD_INLINE VMExecutionContext::iopIncDecN(PC& pc) {
4967 NEXT();
4968 DECODE(unsigned char, op);
4969 StringData* name;
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);
4976 decRefStr(name);
4979 inline void OPTBLD_INLINE VMExecutionContext::iopIncDecG(PC& pc) {
4980 NEXT();
4981 DECODE(unsigned char, op);
4982 StringData* name;
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);
4989 decRefStr(name);
4992 inline void OPTBLD_INLINE VMExecutionContext::iopIncDecS(PC& pc) {
4993 StringData* name;
4994 SPROP_OP_PRELUDE
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(),
4999 name->data());
5001 tvRefcountedDecRefCell(nameCell);
5002 IncDecBody<true>(op, val, output);
5003 m_stack.discard();
5004 SPROP_OP_POSTLUDE
5007 inline void OPTBLD_INLINE VMExecutionContext::iopIncDecM(PC& pc) {
5008 NEXT();
5009 DECODE(unsigned char, op);
5010 DECLARE_SETHELPER_ARGS
5011 TypedValue to;
5012 tvWriteUninit(&to);
5013 if (!setHelperPre<MoreWarnings, true, false, false, 0,
5014 VectorLeaveCode::LeaveLast>(MEMBERHELPERPRE_ARGS)) {
5015 if (mcode == MW) {
5016 IncDecNewElem<true>(tvScratch, *tvRef.asTypedValue(), op, base, to);
5017 } else {
5018 switch (mcode) {
5019 case MEL:
5020 case MEC:
5021 case MET:
5022 case MEI:
5023 IncDecElem<true>(tvScratch, *tvRef.asTypedValue(), op, base,
5024 curMember, to);
5025 break;
5026 case MPL:
5027 case MPC:
5028 case MPT: {
5029 Class* ctx = arGetContextClass(m_fp);
5030 IncDecProp<true>(tvScratch, *tvRef.asTypedValue(), ctx, op, base,
5031 curMember, to);
5032 break;
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) {
5044 NEXT();
5045 DECODE_HA(local);
5046 Ref* fr = m_stack.topV();
5047 TypedValue* to = frame_local(m_fp, local);
5048 tvBind(fr, to);
5051 inline void OPTBLD_INLINE VMExecutionContext::iopBindN(PC& pc) {
5052 NEXT();
5053 StringData* name;
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);
5059 tvBind(fr, to);
5060 memcpy((void*)nameTV, (void*)fr, sizeof(TypedValue));
5061 m_stack.discard();
5062 decRefStr(name);
5065 inline void OPTBLD_INLINE VMExecutionContext::iopBindG(PC& pc) {
5066 NEXT();
5067 StringData* name;
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);
5073 tvBind(fr, to);
5074 memcpy((void*)nameTV, (void*)fr, sizeof(TypedValue));
5075 m_stack.discard();
5076 decRefStr(name);
5079 inline void OPTBLD_INLINE VMExecutionContext::iopBindS(PC& pc) {
5080 NEXT();
5081 TypedValue* fr = m_stack.topTV();
5082 TypedValue* classref = m_stack.indTV(1);
5083 TypedValue* propn = m_stack.indTV(2);
5084 TypedValue* output = propn;
5085 StringData* name;
5086 TypedValue* val;
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(),
5092 name->data());
5094 tvBind(fr, val);
5095 tvRefcountedDecRefCell(propn);
5096 memcpy(output, fr, sizeof(TypedValue));
5097 m_stack.ndiscard(2);
5098 decRefStr(name);
5101 inline void OPTBLD_INLINE VMExecutionContext::iopBindM(PC& pc) {
5102 NEXT();
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
5108 tvBind(tv1, base);
5110 setHelperPost<1>(SETHELPERPOST_ARGS);
5113 inline void OPTBLD_INLINE VMExecutionContext::iopUnsetL(PC& pc) {
5114 NEXT();
5115 DECODE_HA(local);
5116 assert(local < m_fp->m_func->numLocals());
5117 TypedValue* tv = frame_local(m_fp, local);
5118 tvRefcountedDecRef(tv);
5119 tvWriteUninit(tv);
5122 inline void OPTBLD_INLINE VMExecutionContext::iopUnsetN(PC& pc) {
5123 NEXT();
5124 StringData* name;
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);
5131 tvWriteUninit(tv);
5133 m_stack.popC();
5134 decRefStr(name);
5137 inline void OPTBLD_INLINE VMExecutionContext::iopUnsetG(PC& pc) {
5138 NEXT();
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);
5144 m_stack.popC();
5145 decRefStr(name);
5148 inline void OPTBLD_INLINE VMExecutionContext::iopUnsetM(PC& pc) {
5149 NEXT();
5150 DECLARE_SETHELPER_ARGS
5151 if (!setHelperPre<false, false, true, false, 0,
5152 VectorLeaveCode::LeaveLast>(MEMBERHELPERPRE_ARGS)) {
5153 switch (mcode) {
5154 case MEL:
5155 case MEC:
5156 case MET:
5157 case MEI:
5158 UnsetElem(base, curMember);
5159 break;
5160 case MPL:
5161 case MPC:
5162 case MPT: {
5163 Class* ctx = arGetContextClass(m_fp);
5164 UnsetProp(ctx, base, curMember);
5165 break;
5167 default: assert(false);
5170 setHelperPost<0>(SETHELPERPOST_ARGS);
5173 inline ActRec* OPTBLD_INLINE VMExecutionContext::fPushFuncImpl(
5174 const Func* func,
5175 int numArgs) {
5176 DEBUGGER_IF(phpBreakpointEnabled(func->name()->data()));
5177 ActRec* ar = m_stack.allocA();
5178 arSetSfp(ar, m_fp);
5179 ar->m_func = func;
5180 ar->initNumArgs(numArgs);
5181 ar->setVarEnv(nullptr);
5182 return ar;
5185 inline void OPTBLD_INLINE VMExecutionContext::iopFPushFunc(PC& pc) {
5186 NEXT();
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);
5203 } else {
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.
5214 m_stack.discard();
5215 ActRec* ar = fPushFuncImpl(func, numArgs);
5216 if (origObj) {
5217 if (func->attrs() & AttrStatic && !func->isClosureBody()) {
5218 ar->setClass(origObj->getVMClass());
5219 decRefObj(origObj);
5220 } else {
5221 ar->setThis(origObj);
5222 // Teleport the reference from the destroyed stack cell to the
5223 // ActRec. Don't try this at home.
5225 } else {
5226 ar->setThis(nullptr);
5227 decRefStr(origSd);
5231 inline void OPTBLD_INLINE VMExecutionContext::iopFPushFuncD(PC& pc) {
5232 NEXT();
5233 DECODE_IVA(numArgs);
5234 DECODE(Id, id);
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) {
5246 NEXT();
5247 DECODE_IVA(numArgs);
5248 DECODE(Id, nsFunc);
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) {
5267 const Func* f;
5268 LookupResult res = lookupObjMethod(f, cls, name,
5269 arGetContextClass(getFP()), true);
5270 assert(f);
5271 ActRec* ar = m_stack.allocA();
5272 arSetSfp(ar, m_fp);
5273 ar->m_func = f;
5274 if (res == LookupResult::MethodFoundNoThis) {
5275 decRefObj(obj);
5276 ar->setClass(cls);
5277 } else {
5278 assert(res == LookupResult::MethodFoundWithThis ||
5279 res == LookupResult::MagicCallFound);
5280 /* Transfer ownership of obj to the ActRec*/
5281 ar->setThis(obj);
5283 ar->initNumArgs(numArgs);
5284 if (res == LookupResult::MagicCallFound) {
5285 ar->setInvName(name);
5286 } else {
5287 ar->setVarEnv(NULL);
5288 decRefStr(name);
5292 inline void OPTBLD_INLINE VMExecutionContext::iopFPushObjMethod(PC& pc) {
5293 NEXT();
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) {
5312 NEXT();
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
5322 m_stack.discard();
5323 fPushObjMethodImpl(cls, name, obj, numArgs);
5326 template<bool forwarding>
5327 void VMExecutionContext::pushClsMethodImpl(Class* cls,
5328 StringData* name,
5329 ObjectData* obj,
5330 int numArgs) {
5331 const Func* f;
5332 LookupResult res = lookupClsMethod(f, cls, name, obj,
5333 arGetContextClass(getFP()), true);
5334 if (res == LookupResult::MethodFoundNoThis ||
5335 res == LookupResult::MagicCallStaticFound) {
5336 obj = nullptr;
5337 } else {
5338 assert(obj);
5339 assert(res == LookupResult::MethodFoundWithThis ||
5340 res == LookupResult::MagicCallFound);
5341 obj->incRefCount();
5343 assert(f);
5344 ActRec* ar = m_stack.allocA();
5345 arSetSfp(ar, m_fp);
5346 ar->m_func = f;
5347 if (obj) {
5348 ar->setThis(obj);
5349 } else {
5350 if (!forwarding) {
5351 ar->setClass(cls);
5352 } else {
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();
5360 ar->setClass(cls);
5363 ar->initNumArgs(numArgs);
5364 if (res == LookupResult::MagicCallFound ||
5365 res == LookupResult::MagicCallStaticFound) {
5366 ar->setInvName(name);
5367 } else {
5368 ar->setVarEnv(nullptr);
5369 decRefStr(const_cast<StringData*>(name));
5373 inline void OPTBLD_INLINE VMExecutionContext::iopFPushClsMethod(PC& pc) {
5374 NEXT();
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) {
5392 NEXT();
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) {
5407 NEXT();
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;
5416 assert(cls);
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) {
5425 NEXT();
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);
5431 // Lookup the ctor
5432 const Func* f;
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();
5445 arSetSfp(ar, m_fp);
5446 ar->m_func = f;
5447 ar->setThis(this_);
5448 ar->initNumArgs(numArgs, true /* isFPushCtor */);
5449 arSetSfp(ar, m_fp);
5450 ar->setVarEnv(nullptr);
5453 inline void OPTBLD_INLINE VMExecutionContext::iopFPushCtorD(PC& pc) {
5454 NEXT();
5455 DECODE_IVA(numArgs);
5456 DECODE(Id, id);
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());
5464 // Lookup the ctor
5465 const Func* f;
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();
5476 arSetSfp(ar, m_fp);
5477 ar->m_func = f;
5478 ar->setThis(this_);
5479 ar->initNumArgs(numArgs, true /* isFPushCtor */);
5480 ar->setVarEnv(nullptr);
5483 inline void OPTBLD_INLINE VMExecutionContext::iopDecodeCufIter(PC& pc) {
5484 PC origPc = pc;
5485 NEXT();
5486 DECODE_IA(itId);
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();
5497 ActRec* ar = m_fp;
5498 if (m_fp->m_func->isBuiltin()) {
5499 ar = getOuterVMFrame(ar);
5501 const Func* f = vm_decode_function(tvAsVariant(func),
5502 ar, false,
5503 obj, cls, invName,
5504 false);
5506 if (f == nullptr) {
5507 pc = origPc + offset;
5508 } else {
5509 cit.setFunc(f);
5510 if (obj) {
5511 cit.setCtx(obj);
5512 obj->incRefCount();
5513 } else {
5514 cit.setCtx(cls);
5516 cit.setName(invName);
5518 m_stack.popC();
5521 inline void OPTBLD_INLINE VMExecutionContext::iopFPushCufIter(PC& pc) {
5522 NEXT();
5523 DECODE_IVA(numArgs);
5524 DECODE_IA(itId);
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();
5533 arSetSfp(ar, m_fp);
5534 ar->m_func = f;
5535 ar->m_this = (ObjectData*)o;
5536 if (o && !(uintptr_t(o) & 1)) ar->m_this->incRefCount();
5537 if (n) {
5538 ar->setInvName(n);
5539 n->incRefCount();
5540 } else {
5541 ar->setVarEnv(nullptr);
5543 ar->initNumArgs(numArgs, false /* isFPushCtor */);
5546 inline void OPTBLD_INLINE VMExecutionContext::doFPushCuf(PC& pc,
5547 bool forward,
5548 bool safe) {
5549 NEXT();
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(),
5559 forward,
5560 obj, cls, invName,
5561 !safe);
5563 if (safe) m_stack.topTV()[1] = m_stack.topTV()[0];
5564 m_stack.ndiscard(1);
5565 if (f == nullptr) {
5566 f = SystemLib::s_nullFunc;
5567 if (safe) {
5568 m_stack.pushFalse();
5570 } else if (safe) {
5571 m_stack.pushTrue();
5574 ActRec* ar = m_stack.allocA();
5575 arSetSfp(ar, m_fp);
5576 ar->m_func = f;
5577 if (obj) {
5578 ar->setThis(obj);
5579 obj->incRefCount();
5580 } else if (cls) {
5581 ar->setClass(cls);
5582 } else {
5583 ar->setThis(nullptr);
5585 ar->initNumArgs(numArgs, false /* isFPushCtor */);
5586 if (invName) {
5587 ar->setInvName(invName);
5588 } else {
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) {
5611 #ifdef DEBUG
5612 ActRec* ar = arFromInstr(m_stack.top(), (Op*)pc);
5613 #endif
5614 NEXT();
5615 DECODE_IVA(paramId);
5616 #ifdef DEBUG
5617 assert(paramId < ar->numArgs());
5618 #endif
5621 #define FPASSC_CHECKED_PRELUDE \
5622 ActRec* ar = arFromInstr(m_stack.top(), (Op*)pc); \
5623 NEXT(); \
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);
5654 NEXT();
5655 DECODE_IVA(paramId);
5656 assert(paramId < ar->numArgs());
5657 const Func* func = ar->m_func;
5658 if (!func->byRef(paramId)) {
5659 m_stack.unbox();
5663 inline void OPTBLD_INLINE VMExecutionContext::iopFPassR(PC& pc) {
5664 ActRec* ar = arFromInstr(m_stack.top(), (Op*)pc);
5665 NEXT();
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) {
5672 tvBox(tv);
5674 } else {
5675 if (m_stack.topTV()->m_type == KindOfRef) {
5676 m_stack.unbox();
5681 inline void OPTBLD_INLINE VMExecutionContext::iopFPassL(PC& pc) {
5682 ActRec* ar = arFromInstr(m_stack.top(), (Op*)pc);
5683 NEXT();
5684 DECODE_IVA(paramId);
5685 DECODE_HA(local);
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);
5691 } else {
5692 vgetl_body(fr, to);
5696 inline void OPTBLD_INLINE VMExecutionContext::iopFPassN(PC& pc) {
5697 ActRec* ar = arFromInstr(m_stack.top(), (Op*)pc);
5698 PC origPc = pc;
5699 NEXT();
5700 DECODE_IVA(paramId);
5701 assert(paramId < ar->numArgs());
5702 if (!ar->m_func->byRef(paramId)) {
5703 iopCGetN(origPc);
5704 } else {
5705 iopVGetN(origPc);
5709 inline void OPTBLD_INLINE VMExecutionContext::iopFPassG(PC& pc) {
5710 ActRec* ar = arFromInstr(m_stack.top(), (Op*)pc);
5711 PC origPc = pc;
5712 NEXT();
5713 DECODE_IVA(paramId);
5714 assert(paramId < ar->numArgs());
5715 if (!ar->m_func->byRef(paramId)) {
5716 iopCGetG(origPc);
5717 } else {
5718 iopVGetG(origPc);
5722 inline void OPTBLD_INLINE VMExecutionContext::iopFPassS(PC& pc) {
5723 ActRec* ar = arFromInstr(m_stack.top(), (Op*)pc);
5724 PC origPc = pc;
5725 NEXT();
5726 DECODE_IVA(paramId);
5727 assert(paramId < ar->numArgs());
5728 if (!ar->m_func->byRef(paramId)) {
5729 iopCGetS(origPc);
5730 } else {
5731 iopVGetS(origPc);
5735 void VMExecutionContext::iopFPassM(PC& pc) {
5736 ActRec* ar = arFromInstr(m_stack.top(), (Op*)pc);
5737 NEXT();
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) {
5744 tvUnbox(tvRet);
5746 } else {
5747 DECLARE_SETHELPER_ARGS
5748 TypedValue* tv1 = m_stack.allocTV();
5749 tvWriteUninit(tv1);
5750 if (!setHelperPre<false, true, false, true, 1,
5751 VectorLeaveCode::ConsumeAll>(MEMBERHELPERPRE_ARGS)) {
5752 if (base->m_type != KindOfRef) {
5753 tvBox(base);
5755 refDup(*base, *tv1);
5756 } else {
5757 tvWriteNull(tv1);
5758 tvBox(tv1);
5760 setHelperPost<1>(SETHELPERPOST_ARGS);
5764 bool VMExecutionContext::doFCall(ActRec* ar, PC& pc) {
5765 assert(getOuterVMFrame(ar) == m_fp);
5766 ar->m_savedRip =
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);
5776 SYNC();
5777 if (EventHook::FunctionEnter(ar, EventHook::NormalFunc)) return true;
5778 pc = m_pc;
5779 return false;
5782 inline void OPTBLD_INLINE VMExecutionContext::iopFCall(PC& pc) {
5783 ActRec* ar = arFromInstr(m_stack.top(), (Op*)pc);
5784 NEXT();
5785 DECODE_IVA(numArgs);
5786 assert(numArgs == ar->numArgs());
5787 checkStack(m_stack, ar->m_func);
5788 doFCall(ar, pc);
5791 inline void OPTBLD_INLINE VMExecutionContext::iopFCallBuiltin(PC& pc) {
5792 NEXT();
5793 DECODE_IVA(numArgs);
5794 DECODE_IVA(numNonDefault);
5795 DECODE(Id, id);
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);
5803 TypedValue ret;
5804 if (Native::coerceFCallArgs(args, numArgs, numNonDefault, func)) {
5805 Native::callFunc(func, nullptr, args, numArgs, ret);
5806 } else {
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());
5822 ar->setVarEnv(0);
5823 ar->initNumArgs(2);
5824 return true;
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;
5833 if (extra < 0) {
5834 extra = 0;
5835 nparams = nargs;
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)) {
5846 refDup(*from, *to);
5847 } else {
5848 try {
5849 raise_warning("Parameter %d to %s() expected to be a reference, "
5850 "value given", i + 1, f->fullName()->data());
5851 } catch (...) {
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
5855 m_stack.discard();
5856 throw;
5858 if (skipCufOnInvalidParams) {
5859 m_stack.discard();
5860 while (i--) m_stack.popTV();
5861 m_stack.popAR();
5862 m_stack.pushNull();
5863 return false;
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) {
5875 tvUnbox(to);
5877 pos = args->iter_advance(pos);
5879 ar->setExtraArgs(extraArgs);
5880 ar->initNumArgs(nargs);
5881 } else {
5882 ar->initNumArgs(nparams);
5885 return true;
5888 static void cleanupParamsAndActRec(Stack& stack,
5889 ActRec* ar,
5890 ExtraArgs* extraArgs) {
5891 assert(stack.top() + (extraArgs ?
5892 ar->m_func->numParams() :
5893 ar->numArgs()) == (void*)ar);
5894 if (extraArgs) {
5895 const int numExtra = ar->numArgs() - ar->m_func->numParams();
5896 ExtraArgs::deallocate(extraArgs, numExtra);
5898 while (stack.top() != (void*)ar) {
5899 stack.popTV();
5901 stack.popAR();
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)) {
5910 // task #1756122
5911 // this is what we /should/ do, but our code base depends
5912 // on the broken behavior of casting the second arg to an
5913 // array.
5914 cleanupParamsAndActRec(m_stack, ar, nullptr);
5915 m_stack.pushNull();
5916 raise_warning("call_user_func_array() expects parameter 2 to be array");
5917 return false;
5920 const Func* func = ar->m_func;
5922 Array args(LIKELY(c1->m_type == KindOfArray) ? c1->m_data.parr :
5923 tvAsVariant(c1).toArray().get());
5924 m_stack.popTV();
5925 checkStack(m_stack, func);
5927 assert(ar->m_savedRbp == (uint64_t)m_fp);
5928 assert(!ar->m_func->isGenerator());
5929 ar->m_savedRip =
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)))) {
5943 return false;
5945 SYNC();
5946 if (UNLIKELY(!EventHook::FunctionEnter(ar, EventHook::NormalFunc))) {
5947 pc = m_pc;
5948 return false;
5950 return true;
5953 bool VMExecutionContext::doFCallArrayTC(PC pc) {
5954 if (debug) {
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;
5963 return ret;
5966 inline void OPTBLD_INLINE VMExecutionContext::iopFCallArray(PC& pc) {
5967 NEXT();
5968 (void)doFCallArray(pc);
5971 inline void OPTBLD_INLINE VMExecutionContext::iopCufSafeArray(PC& pc) {
5972 NEXT();
5973 Array ret;
5974 ret.append(tvAsVariant(m_stack.top() + 1));
5975 ret.appendWithRef(tvAsVariant(m_stack.top() + 0));
5976 m_stack.popTV();
5977 m_stack.popTV();
5978 tvAsVariant(m_stack.top()) = ret;
5981 inline void OPTBLD_INLINE VMExecutionContext::iopCufSafeReturn(PC& pc) {
5982 NEXT();
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);
5993 if (!hasElems) {
5994 ITER_SKIP(offset);
5996 m_stack.popC();
5997 return hasElems;
6000 inline void OPTBLD_INLINE VMExecutionContext::iopIterInit(PC& pc) {
6001 PC origPc = pc;
6002 NEXT();
6003 DECODE_IA(itId);
6004 DECODE(Offset, offset);
6005 DECODE_HA(val);
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) {
6015 PC origPc = pc;
6016 NEXT();
6017 DECODE_IA(itId);
6018 DECODE(Offset, offset);
6019 DECODE_HA(val);
6020 DECODE_HA(key);
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) {
6032 PC origPc = pc;
6033 NEXT();
6034 DECODE_IA(itId);
6035 DECODE(Offset, offset);
6036 DECODE_HA(val);
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) {
6046 PC origPc = pc;
6047 NEXT();
6048 DECODE_IA(itId);
6049 DECODE(Offset, offset);
6050 DECODE_HA(val);
6051 DECODE_HA(key);
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,
6065 TypedValue *val,
6066 TypedValue *key) {
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);
6074 } else {
6075 hasElems = new_miter_other(it, r1->m_data.pref);
6078 if (!hasElems) {
6079 ITER_SKIP(offset);
6082 m_stack.popV();
6083 return hasElems;
6086 inline void OPTBLD_INLINE VMExecutionContext::iopMIterInit(PC& pc) {
6087 PC origPc = pc;
6088 NEXT();
6089 DECODE_IA(itId);
6090 DECODE(Offset, offset);
6091 DECODE_HA(val);
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) {
6100 PC origPc = pc;
6101 NEXT();
6102 DECODE_IA(itId);
6103 DECODE(Offset, offset);
6104 DECODE_HA(val);
6105 DECODE_HA(key);
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) {
6115 PC origPc = pc;
6116 NEXT();
6117 DECODE_IA(itId);
6118 DECODE(Offset, offset);
6119 DECODE_HA(val);
6120 Iter* it = frame_iter(m_fp, itId);
6121 TypedValue* tv1 = frame_local(m_fp, val);
6122 if (it->next()) {
6123 ITER_SKIP(offset);
6124 tvAsVariant(tv1) = it->arr().second();
6128 inline void OPTBLD_INLINE VMExecutionContext::iopIterNextK(PC& pc) {
6129 PC origPc = pc;
6130 NEXT();
6131 DECODE_IA(itId);
6132 DECODE(Offset, offset);
6133 DECODE_HA(val);
6134 DECODE_HA(key);
6135 Iter* it = frame_iter(m_fp, itId);
6136 TypedValue* tv1 = frame_local(m_fp, val);
6137 TypedValue* tv2 = frame_local(m_fp, key);
6138 if (it->next()) {
6139 ITER_SKIP(offset);
6140 tvAsVariant(tv1) = it->arr().second();
6141 tvAsVariant(tv2) = it->arr().first();
6145 inline void OPTBLD_INLINE VMExecutionContext::iopWIterNext(PC& pc) {
6146 PC origPc = pc;
6147 NEXT();
6148 DECODE_IA(itId);
6149 DECODE(Offset, offset);
6150 DECODE_HA(val);
6151 Iter* it = frame_iter(m_fp, itId);
6152 TypedValue* tv1 = frame_local(m_fp, val);
6153 if (it->next()) {
6154 ITER_SKIP(offset);
6155 tvAsVariant(tv1) = withRefBind(it->arr().secondRef());
6159 inline void OPTBLD_INLINE VMExecutionContext::iopWIterNextK(PC& pc) {
6160 PC origPc = pc;
6161 NEXT();
6162 DECODE_IA(itId);
6163 DECODE(Offset, offset);
6164 DECODE_HA(val);
6165 DECODE_HA(key);
6166 Iter* it = frame_iter(m_fp, itId);
6167 TypedValue* tv1 = frame_local(m_fp, val);
6168 TypedValue* tv2 = frame_local(m_fp, key);
6169 if (it->next()) {
6170 ITER_SKIP(offset);
6171 tvAsVariant(tv1) = withRefBind(it->arr().secondRef());
6172 tvAsVariant(tv2) = it->arr().first();
6176 inline void OPTBLD_INLINE VMExecutionContext::iopMIterNext(PC& pc) {
6177 PC origPc = pc;
6178 NEXT();
6179 DECODE_IA(itId);
6180 DECODE(Offset, offset);
6181 DECODE_HA(val);
6182 Iter* it = frame_iter(m_fp, itId);
6183 TypedValue* tv1 = frame_local(m_fp, val);
6184 if (miter_next_key(it, tv1, nullptr)) {
6185 ITER_SKIP(offset);
6189 inline void OPTBLD_INLINE VMExecutionContext::iopMIterNextK(PC& pc) {
6190 PC origPc = pc;
6191 NEXT();
6192 DECODE_IA(itId);
6193 DECODE(Offset, offset);
6194 DECODE_HA(val);
6195 DECODE_HA(key);
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)) {
6200 ITER_SKIP(offset);
6204 inline void OPTBLD_INLINE VMExecutionContext::iopIterFree(PC& pc) {
6205 NEXT();
6206 DECODE_IA(itId);
6207 Iter* it = frame_iter(m_fp, itId);
6208 it->free();
6211 inline void OPTBLD_INLINE VMExecutionContext::iopMIterFree(PC& pc) {
6212 NEXT();
6213 DECODE_IA(itId);
6214 Iter* it = frame_iter(m_fp, itId);
6215 it->mfree();
6218 inline void OPTBLD_INLINE VMExecutionContext::iopCIterFree(PC& pc) {
6219 NEXT();
6220 DECODE_IA(itId);
6221 Iter* it = frame_iter(m_fp, itId);
6222 it->cfree();
6225 inline void OPTBLD_INLINE inclOp(VMExecutionContext *ec, PC &pc,
6226 InclOpFlags flags) {
6227 NEXT();
6228 Cell* c1 = ec->m_stack.topC();
6229 String path(prepareKey(c1));
6230 bool initial;
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" : "",
6236 path->data());
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);
6241 ec->m_stack.popC();
6242 if (u == nullptr) {
6243 ((flags & InclOpFatal) ?
6244 (void (*)(const char *, ...))raise_error :
6245 (void (*)(const char *, ...))raise_warning)("File not found: %s",
6246 path->data());
6247 ec->m_stack.pushFalse();
6248 } else {
6249 if (!(flags & InclOpOnce) || initial) {
6250 ec->evalUnit(u, pc, EventHook::PseudoMain);
6251 } else {
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) {
6279 NEXT();
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()");
6287 m_stack.popC();
6288 evalUnit(unit, pc, EventHook::Eval);
6291 inline void OPTBLD_INLINE VMExecutionContext::iopDefFunc(PC& pc) {
6292 NEXT();
6293 DECODE_IVA(fid);
6294 Func* f = m_fp->m_func->unit()->lookupFuncId(fid);
6295 f->setCached();
6298 inline void OPTBLD_INLINE VMExecutionContext::iopDefCls(PC& pc) {
6299 NEXT();
6300 DECODE_IVA(cid);
6301 PreClass* c = m_fp->m_func->unit()->lookupPreClassId(cid);
6302 Unit::defClass(c);
6305 inline void OPTBLD_INLINE VMExecutionContext::iopDefTypedef(PC& pc) {
6306 NEXT();
6307 DECODE_IVA(tid);
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) {
6318 NEXT();
6319 checkThis(m_fp);
6320 ObjectData* this_ = m_fp->getThis();
6321 m_stack.pushObject(this_);
6324 inline void OPTBLD_INLINE VMExecutionContext::iopBareThis(PC& pc) {
6325 NEXT();
6326 DECODE(unsigned char, notice);
6327 if (m_fp->hasThis()) {
6328 ObjectData* this_ = m_fp->getThis();
6329 m_stack.pushObject(this_);
6330 } else {
6331 m_stack.pushNull();
6332 if (notice) raise_notice(Strings::WARN_NULL_THIS);
6336 inline void OPTBLD_INLINE VMExecutionContext::iopCheckThis(PC& pc) {
6337 NEXT();
6338 checkThis(m_fp);
6341 inline void OPTBLD_INLINE VMExecutionContext::iopInitThisLoc(PC& pc) {
6342 NEXT();
6343 DECODE_IVA(id);
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;
6349 tvIncRef(thisLoc);
6350 } else {
6351 tvWriteUninit(thisLoc);
6356 * Helper for StaticLoc and StaticLocInit.
6358 static inline void
6359 lookupStatic(StringData* name,
6360 const ActRec* fp,
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) {
6366 TypedValue tv;
6367 tvWriteUninit(&tv);
6368 map->set(name, tvAsCVarRef(&tv), false);
6369 val = map->nvGet(name);
6370 inited = false;
6371 } else {
6372 inited = true;
6376 inline void OPTBLD_INLINE VMExecutionContext::iopStaticLoc(PC& pc) {
6377 NEXT();
6378 DECODE_IVA(localId);
6379 DECODE_LITSTR(var);
6380 TypedValue* fr = nullptr;
6381 bool inited;
6382 lookupStatic(var, m_fp, fr, inited);
6383 assert(fr != nullptr);
6384 if (fr->m_type != KindOfRef) {
6385 assert(!inited);
6386 tvBox(fr);
6388 TypedValue* tvLocal = frame_local(m_fp, localId);
6389 tvBind(fr, tvLocal);
6390 if (inited) {
6391 m_stack.pushTrue();
6392 } else {
6393 m_stack.pushFalse();
6397 inline void OPTBLD_INLINE VMExecutionContext::iopStaticLocInit(PC& pc) {
6398 NEXT();
6399 DECODE_IVA(localId);
6400 DECODE_LITSTR(var);
6401 TypedValue* fr = nullptr;
6402 bool inited;
6403 lookupStatic(var, m_fp, fr, inited);
6404 assert(fr != nullptr);
6405 if (!inited) {
6406 Cell* initVal = m_stack.topC();
6407 cellDup(*initVal, *fr);
6409 if (fr->m_type != KindOfRef) {
6410 assert(!inited);
6411 tvBox(fr);
6413 TypedValue* tvLocal = frame_local(m_fp, localId);
6414 tvBind(fr, tvLocal);
6415 m_stack.discard();
6418 inline void OPTBLD_INLINE VMExecutionContext::iopCatch(PC& pc) {
6419 NEXT();
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) {
6428 NEXT();
6429 Class* cls = frameStaticClass(m_fp);
6430 if (!cls) {
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.
6438 NEXT();
6440 DECODE_IVA(param);
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 &&
6447 tc.isExtended())) {
6448 return;
6450 const TypedValue *tv = frame_local(m_fp, param);
6451 tc.verify(tv, func, param);
6454 inline void OPTBLD_INLINE VMExecutionContext::iopNativeImpl(PC& pc) {
6455 NEXT();
6456 uint soff = m_fp->m_soff;
6457 BuiltinFunction func = m_fp->m_func->builtinFuncPtr();
6458 assert(func);
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.
6462 func(m_fp);
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.
6469 m_fp = sfp;
6470 pc = m_fp->m_func->unit()->entry() + m_fp->m_func->base() + soff;
6471 m_stack.ret();
6472 } else {
6473 // No caller; terminate.
6474 m_stack.ret();
6475 #ifdef HPHP_TRACE
6477 std::ostringstream os;
6478 os << toStringElm(m_stack.topTV());
6479 ONTRACE(1,
6480 Trace::trace("Return %s from VMExecutionContext::dispatch("
6481 "%p)\n", os.str().c_str(), m_fp));
6483 #endif
6484 pc = 0;
6488 inline void OPTBLD_INLINE VMExecutionContext::iopHighInvalid(PC& pc) {
6489 fprintf(stderr, "invalid bytecode executed\n");
6490 abort();
6493 inline void OPTBLD_INLINE VMExecutionContext::iopSelf(PC& pc) {
6494 NEXT();
6495 Class* clss = arGetContextClass(m_fp);
6496 if (!clss) {
6497 raise_error(HPHP::Strings::CANT_ACCESS_SELF);
6499 m_stack.pushClass(clss);
6502 inline void OPTBLD_INLINE VMExecutionContext::iopParent(PC& pc) {
6503 NEXT();
6504 Class* clss = arGetContextClass(m_fp);
6505 if (!clss) {
6506 raise_error(HPHP::Strings::CANT_ACCESS_PARENT_WHEN_NO_CLASS);
6508 Class* parent = clss->parent();
6509 if (!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) {
6516 NEXT();
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;
6537 ar->initNumArgs(0);
6538 ar->setVarEnv(nullptr);
6540 return cont;
6543 c_Continuation*
6544 VMExecutionContext::createContFunc(const Func* origFunc,
6545 const Func* genFunc) {
6546 auto cont = createCont(origFunc, genFunc);
6547 cont->actRec()->setThis(nullptr);
6548 return cont;
6551 c_Continuation*
6552 VMExecutionContext::createContMeth(const Func* origFunc,
6553 const Func* genFunc,
6554 void* objOrCls) {
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();
6565 return cont;
6568 static inline void setContVar(const Func* genFunc,
6569 const StringData* name,
6570 TypedValue* src,
6571 ActRec* genFp) {
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));
6577 tvWriteUninit(src);
6578 } else {
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,
6593 ActRec* genFp,
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();
6601 bool skipThis;
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.
6605 assert(false);
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);
6614 } else {
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));
6620 tvWriteUninit(src);
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) {
6636 NEXT();
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) {
6661 NEXT();
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));
6679 m_fp = contAR;
6680 pc = contAR->m_func->getEntry();
6681 SYNC();
6683 if (UNLIKELY(!EventHook::FunctionEnter(contAR, EventHook::NormalFunc))) {
6684 pc = m_pc;
6688 inline void OPTBLD_INLINE VMExecutionContext::iopUnpackCont(PC& pc) {
6689 NEXT();
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) {
6702 NEXT();
6703 DECODE_IVA(label);
6704 c_Continuation* cont = frame_continuation(m_fp);
6706 cont->c_Continuation::t_update(label, tvAsCVarRef(m_stack.topTV()));
6707 m_stack.popTV();
6709 EventHook::FunctionExit(m_fp);
6710 ActRec* prevFp = m_fp->arGetSfp();
6711 pc = prevFp->m_func->getEntry() + m_fp->m_soff;
6712 m_fp = prevFp;
6715 inline void OPTBLD_INLINE VMExecutionContext::iopContSuspendK(PC& pc) {
6716 NEXT();
6717 DECODE_IVA(label);
6718 c_Continuation* cont = frame_continuation(m_fp);
6720 TypedValue* val = m_stack.topTV();
6721 m_stack.popTV();
6722 cont->c_Continuation::t_update_key(label, tvAsCVarRef(m_stack.topTV()),
6723 tvAsCVarRef(val));
6724 m_stack.popTV();
6726 EventHook::FunctionExit(m_fp);
6727 ActRec* prevFp = m_fp->arGetSfp();
6728 pc = prevFp->m_func->getEntry() + m_fp->m_soff;
6729 m_fp = prevFp;
6732 inline void OPTBLD_INLINE VMExecutionContext::iopContRetC(PC& pc) {
6733 NEXT();
6734 c_Continuation* cont = frame_continuation(m_fp);
6735 cont->setDone();
6736 tvSetIgnoreRef(*m_stack.topC(), *cont->m_value.asTypedValue());
6737 m_stack.popC();
6739 EventHook::FunctionExit(m_fp);
6740 ActRec* prevFp = m_fp->arGetSfp();
6741 pc = prevFp->m_func->getEntry() + m_fp->m_soff;
6742 m_fp = prevFp;
6745 inline void OPTBLD_INLINE VMExecutionContext::iopContCheck(PC& pc) {
6746 NEXT();
6747 DECODE_IVA(check_started);
6748 c_Continuation* cont = this_continuation(m_fp);
6749 if (check_started) {
6750 cont->startedCheck();
6752 cont->preNext();
6755 inline void OPTBLD_INLINE VMExecutionContext::iopContRaise(PC& pc) {
6756 NEXT();
6757 c_Continuation* cont = this_continuation(m_fp);
6758 assert(cont->m_label);
6759 --cont->m_label;
6762 inline void OPTBLD_INLINE VMExecutionContext::iopContValid(PC& pc) {
6763 NEXT();
6764 TypedValue* tv = m_stack.allocTV();
6765 tvWriteUninit(tv);
6766 tvAsVariant(tv) = !this_continuation(m_fp)->done();
6769 inline void OPTBLD_INLINE VMExecutionContext::iopContKey(PC& pc) {
6770 NEXT();
6771 c_Continuation* cont = this_continuation(m_fp);
6772 cont->startedCheck();
6774 TypedValue* tv = m_stack.allocTV();
6775 tvWriteUninit(tv);
6776 tvAsVariant(tv) = cont->m_key;
6779 inline void OPTBLD_INLINE VMExecutionContext::iopContCurrent(PC& pc) {
6780 NEXT();
6781 c_Continuation* cont = this_continuation(m_fp);
6782 cont->startedCheck();
6784 TypedValue* tv = m_stack.allocTV();
6785 tvWriteUninit(tv);
6786 tvAsVariant(tv) = cont->m_value;
6789 inline void OPTBLD_INLINE VMExecutionContext::iopContStopped(PC& pc) {
6790 NEXT();
6791 this_continuation(m_fp)->setStopped();
6794 inline void OPTBLD_INLINE VMExecutionContext::iopContHandle(PC& pc) {
6795 NEXT();
6796 c_Continuation* cont = this_continuation(m_fp);
6797 cont->setDone();
6798 cont->m_value.setNull();
6800 Variant exn = tvAsVariant(m_stack.topTV());
6801 m_stack.popC();
6802 assert(exn.asObjRef().instanceof(SystemLib::s_ExceptionClass));
6803 throw exn.asObjRef();
6806 template<class Op>
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) {
6815 NEXT();
6816 roundOpImpl(floor);
6819 inline void OPTBLD_INLINE VMExecutionContext::iopCeil(PC& pc) {
6820 NEXT();
6821 roundOpImpl(ceil);
6824 inline void OPTBLD_INLINE VMExecutionContext::iopStrlen(PC& pc) {
6825 NEXT();
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;
6832 } else {
6833 Variant ans = f_strlen(tvAsVariant(subj));
6834 tvAsVariant(subj) = ans;
6838 inline void OPTBLD_INLINE VMExecutionContext::iopIncStat(PC& pc) {
6839 NEXT();
6840 DECODE_IVA(counter);
6841 DECODE_IVA(value);
6842 Stats::inc(Stats::StatCounter(counter), value);
6845 void VMExecutionContext::classExistsImpl(PC& pc, Attr typeAttr) {
6846 NEXT();
6847 TypedValue* aloadTV = m_stack.topTV();
6848 tvCastToBooleanInPlace(aloadTV);
6849 assert(aloadTV->m_type == KindOfBoolean);
6850 bool autoload = aloadTV->m_data.num;
6851 m_stack.popX();
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);
6872 string
6873 VMExecutionContext::prettyStack(const string& prefix) const {
6874 if (!getFP()) {
6875 string s("__Halted");
6876 return s;
6878 int offset = (m_fp->m_func->unit() != nullptr)
6879 ? pcOff()
6880 : 0;
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;
6900 while (skip--) {
6901 fp = g_vmContext->getPrevVMState(fp, &pc);
6903 if (fp == nullptr) {
6904 std::cout << "Don't have a valid fp\n";
6905 return;
6908 printf("Offset = %d, in function %s\n", pc, fp->m_func->name()->data());
6909 Unit* u = fp->m_func->unit();
6910 if (u == nullptr) {
6911 std::cout << "Current unit is NULL\n";
6912 return;
6914 printf("Dumping bytecode for %s(%p)\n", u->filepath()->data(), u);
6915 std::cout << u->toString();
6918 void VMExecutionContext::PrintTCCallerInfo() {
6919 VMRegAnchor _;
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;
6928 static inline void
6929 condStackTraceSep(const char* pfx) {
6930 TRACE(3, "%s"
6931 "========================================"
6932 "========================================\n",
6933 pfx);
6936 #define COND_STACKTRACE(pfx) \
6937 ONTRACE(3, \
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: "); \
6945 PC pc = m_pc; \
6946 assert(toOp(*pc) == Op##name); \
6947 ONTRACE(1, \
6948 int offset = m_fp->m_func->unit()->offsetOf(pc); \
6949 Trace::trace("op"#name" offset: %d\n", offset)); \
6950 iop##name(pc); \
6951 SYNC(); \
6952 COND_STACKTRACE("op"#name" post: "); \
6953 condStackTraceSep("op"#name" "); \
6955 OPCODES
6956 #undef O
6957 #undef NEXT
6958 #undef DECODE_JMP
6959 #undef DECODE
6961 static inline void
6962 profileReturnValue(const DataType dt) {
6963 const Func* f = liveFunc();
6964 if (f->isPseudoMain() || f->isClosureBody() || f->isMagic() ||
6965 Func::isSpecial(f->name()))
6966 return;
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) \
6977 &&Label##name,
6978 OPCODES
6979 #undef O
6981 static const void *optabDbg[] = {
6982 #define O(name, imm, push, pop, flags) \
6983 &&LabelDbg##name,
6984 OPCODES
6985 #undef O
6987 static const void *optabCover[] = {
6988 #define O(name, imm, push, pop, flags) \
6989 &&LabelCover##name,
6990 OPCODES
6991 #undef O
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) {
6999 optab = optabCover;
7001 DEBUGGER_ATTACHED_ONLY(optab = optabDbg);
7003 * Trace-only mapping of opcodes to names.
7005 #ifdef HPHP_TRACE
7006 static const char *nametab[] = {
7007 #define O(name, imm, push, pop, flags) \
7008 #name,
7009 OPCODES
7010 #undef O
7012 #endif /* HPHP_TRACE */
7013 bool isCtlFlow = false;
7015 #define DISPATCH() do { \
7016 if ((breakOnCtlFlow && isCtlFlow) || \
7017 (limInstrs && UNLIKELY(numInstrs-- == 0))) { \
7018 ONTRACE(1, \
7019 Trace::trace("dispatch: Halt ExecutionContext::dispatch(%p)\n", \
7020 m_fp)); \
7021 return; \
7023 Op op = toOp(*pc); \
7024 COND_STACKTRACE("dispatch: "); \
7025 ONTRACE(1, \
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)]; \
7032 } while (0)
7034 ONTRACE(1, Trace::trace("dispatch: Enter ExecutionContext::dispatch(%p)\n",
7035 m_fp));
7036 PC pc = m_pc;
7037 DISPATCH();
7039 #define O(name, imm, pusph, pop, flags) \
7040 LabelDbg##name: \
7041 phpDebuggerOpcodeHook(pc); \
7042 LabelCover##name: \
7043 if (collectCoverage) { \
7044 recordCodeCoverage(pc); \
7046 Label##name: { \
7047 iop##name(pc); \
7048 SYNC(); \
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; } \
7057 DISPATCH(); \
7059 OPCODES
7060 #undef O
7061 #undef DISPATCH
7064 void VMExecutionContext::dispatch() {
7065 if (shouldProfile()) {
7066 dispatchImpl<Profile>(0);
7067 } else {
7068 dispatchImpl<0>(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) {
7097 return;
7099 int line = unit->getLineNumber(pcOff());
7100 assert(line != -1);
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(),
7128 savedVM.pc,
7129 func->unit()->offsetOf(savedVM.pc),
7130 savedVM.fp);
7132 m_nestedVMs.push_back(ReentryRecord(savedVM, reentryAR));
7133 m_nesting++;
7136 void VMExecutionContext::popVMState() {
7137 assert(m_nestedVMs.size() >= 1);
7139 VMState &savedVM = m_nestedVMs.back().m_savedState;
7140 m_pc = savedVM.pc;
7141 m_fp = savedVM.fp;
7142 m_firstAR = savedVM.firstAR;
7143 assert(m_stack.top() == savedVM.sp);
7145 if (debug) {
7146 if (savedVM.fp &&
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(),
7154 savedVM.pc,
7155 func->unit()->offsetOf(savedVM.pc),
7156 savedVM.fp);
7160 m_nestedVMs.pop_back();
7161 m_nesting--;
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();
7183 } else {
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();
7196 #ifdef DEBUG
7197 Class* cls = Unit::GetNamedEntity(s_stdclass.get())->clsList();
7198 assert(cls);
7199 assert(cls == SystemLib::s_stdclassClass);
7200 #endif
7203 void VMExecutionContext::requestExit() {
7204 MemoryProfile::finishProfiling();
7206 destructObjects();
7207 syncGdbState();
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);
7217 m_globalVarEnv = 0;
7220 varenv_arena().~VarEnvArena();
7221 request_arena().~RequestArena();
7224 ///////////////////////////////////////////////////////////////////////////////