2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_VM_BYTECODE_H_
18 #define incl_HPHP_VM_BYTECODE_H_
20 #include "hphp/runtime/base/array-iterator.h"
21 #include "hphp/runtime/base/rds.h"
22 #include "hphp/runtime/base/rds-util.h"
23 #include "hphp/runtime/base/record-data.h"
24 #include "hphp/runtime/base/tv-arith.h"
25 #include "hphp/runtime/base/tv-conversions.h"
26 #include "hphp/runtime/base/tv-mutate.h"
27 #include "hphp/runtime/base/tv-variant.h"
28 #include "hphp/runtime/base/tv-refcount.h"
30 #include "hphp/runtime/vm/act-rec.h"
31 #include "hphp/runtime/vm/class.h"
32 #include "hphp/runtime/vm/class-meth-data-ref.h"
33 #include "hphp/runtime/vm/cls-ref.h"
34 #include "hphp/runtime/vm/func.h"
35 #include "hphp/runtime/vm/name-value-table.h"
36 #include "hphp/runtime/vm/unit.h"
38 #include "hphp/runtime/vm/jit/types.h"
40 #include "hphp/util/arena.h"
41 #include "hphp/util/type-traits.h"
43 #include <type_traits>
46 ///////////////////////////////////////////////////////////////////////////////
52 ///////////////////////////////////////////////////////////////////////////////
54 #define EVAL_FILENAME_SUFFIX ") : eval()'d code"
56 // perform the set(op) operation on lhs & rhs, leaving the result in lhs.
57 // The old value of lhs is decrefed. Caller must call tvToCell() if lhs or
58 // rhs might be a ref.
60 void setopBody(tv_lval lhs
, SetOpOp op
, Cell
* rhs
) {
61 assertx(cellIsPlausible(*lhs
));
62 assertx(cellIsPlausible(*rhs
));
65 case SetOpOp::PlusEqual
: cellAddEq(lhs
, *rhs
); return;
66 case SetOpOp::MinusEqual
: cellSubEq(lhs
, *rhs
); return;
67 case SetOpOp::MulEqual
: cellMulEq(lhs
, *rhs
); return;
68 case SetOpOp::DivEqual
: cellDivEq(lhs
, *rhs
); return;
69 case SetOpOp::PowEqual
: cellPowEq(lhs
, *rhs
); return;
70 case SetOpOp::ModEqual
: cellModEq(lhs
, *rhs
); return;
71 case SetOpOp::ConcatEqual
: cellConcatEq(lhs
, *rhs
); return;
72 case SetOpOp::AndEqual
: cellBitAndEq(lhs
, *rhs
); return;
73 case SetOpOp::OrEqual
: cellBitOrEq(lhs
, *rhs
); return;
74 case SetOpOp::XorEqual
: cellBitXorEq(lhs
, *rhs
); return;
75 case SetOpOp::SlEqual
: cellShlEq(lhs
, *rhs
); return;
76 case SetOpOp::SrEqual
: cellShrEq(lhs
, *rhs
); return;
77 case SetOpOp::PlusEqualO
: cellAddEqO(lhs
, *rhs
); return;
78 case SetOpOp::MinusEqualO
: cellSubEqO(lhs
, *rhs
); return;
79 case SetOpOp::MulEqualO
: cellMulEqO(lhs
, *rhs
); return;
84 ///////////////////////////////////////////////////////////////////////////////
87 ExtraArgs(const ExtraArgs
&) = delete;
88 ExtraArgs
& operator=(const ExtraArgs
&) = delete;
91 * Allocate an ExtraArgs structure, with arguments copied from the
92 * evaluation stack. This takes ownership of the args without
93 * adjusting reference counts, so they must be discarded from the
96 static ExtraArgs
* allocateCopy(TypedValue
* args
, unsigned nargs
);
99 * Allocate an ExtraArgs, without initializing any of the arguments.
100 * All arguments must be initialized via getExtraArg before
101 * deallocate() is called for the returned pointer.
103 static ExtraArgs
* allocateUninit(unsigned nargs
);
106 * Deallocate an extraArgs structure. Either use the one that
107 * exists in a ActRec, or do it explicitly.
109 static void deallocate(ActRec
*);
110 static void deallocate(ExtraArgs
*, unsigned numArgs
);
112 // Just free the memory, don't dec-ref anything.
113 static void deallocateRaw(ExtraArgs
*);
116 * Make a copy of ExtraArgs.
118 ExtraArgs
* clone(ActRec
* fp
) const;
121 * Get the slot for extra arg i, where i = argNum - func->numParams.
123 TypedValue
* getExtraArg(unsigned argInd
) const;
129 static void* allocMem(unsigned nargs
);
132 TypedValue m_extraArgs
[0];
133 TYPE_SCAN_FLEXIBLE_ARRAY_FIELD(m_extraArgs
);
137 * Variable environment.
139 * A variable environment consists of the locals for the current function
140 * (either pseudo-main, global function, or method), plus any variables that
141 * are dynamically defined.
143 * Logically, a global function or method starts off with a variable
144 * environment that contains only its locals, but a pseudo-main is handed
145 * its caller's existing variable environment. Generally, however, we don't
146 * create a variable environment for global functions or methods until it
147 * actually needs one (i.e. if it is about to include a pseudo-main, or if
148 * it uses dynamic variable lookups).
150 * Named locals always appear in the expected place on the stack, even after
151 * a VarEnv is attached. Internally uses a NameValueTable to hook up names to
152 * the local locations.
156 NameValueTable m_nvTable
;
157 ExtraArgs
* m_extraArgs
;
163 explicit VarEnv(ActRec
* fp
, ExtraArgs
* eArgs
);
164 explicit VarEnv(const VarEnv
* varEnv
, ActRec
* fp
);
167 // Free the VarEnv and locals for the given frame
168 // which must have a VarEnv
169 static void deallocate(ActRec
* fp
);
171 // Allocates a local VarEnv and attaches it to the existing FP.
172 static VarEnv
* createLocal(ActRec
* fp
);
174 // Allocate a global VarEnv. Initially not attached to any frame.
175 static void createGlobal();
177 VarEnv
* clone(ActRec
* fp
) const;
179 void suspend(const ActRec
* oldFP
, ActRec
* newFP
);
180 void enterFP(ActRec
* oldFP
, ActRec
* newFP
);
181 void exitFP(ActRec
* fp
);
183 void set(const StringData
* name
, tv_rval tv
);
184 TypedValue
* lookup(const StringData
* name
);
185 TypedValue
* lookupAdd(const StringData
* name
);
186 bool unset(const StringData
* name
);
188 Array
getDefinedVariables() const;
190 // Used for save/store m_cfp for debugger
191 ActRec
* getFP() const { return m_nvTable
.getFP(); }
192 bool isGlobalScope() const { return m_global
; }
194 // Access to wrapped ExtraArgs, if we have one.
195 TypedValue
* getExtraArg(unsigned argInd
) const;
199 * Action taken to handle any extra arguments passed for a function call.
201 enum class ExtraArgsAction
{
202 None
, // no extra arguments; zero out m_extraArgs
203 Discard
, // discard extra arguments
204 Variadic
, // populate `...$args' parameter
205 MayUseVV
, // create ExtraArgs
206 VarAndVV
, // both of the above
209 inline ExtraArgsAction
extra_args_action(const Func
* func
, uint32_t argc
) {
210 using Action
= ExtraArgsAction
;
212 auto const nparams
= func
->numNonVariadicParams();
213 if (argc
<= nparams
) return Action::None
;
215 if (LIKELY(func
->discardExtraArgs())) {
216 return Action::Discard
;
218 if (func
->attrs() & AttrMayUseVV
) {
219 return func
->hasVariadicCaptureParam() ? Action::VarAndVV
222 assertx(func
->hasVariadicCaptureParam());
223 return Action::Variadic
;
226 ///////////////////////////////////////////////////////////////////////////////
229 * Returns true iff ar represents a frame on the VM eval stack or a Resumable
230 * object on the PHP heap.
232 bool isVMFrame(const ActRec
* ar
);
235 * Returns true iff the given address is one of the special debugger return
238 bool isDebuggerReturnHelper(void* addr
);
241 * If ar->m_savedRip points somewhere in the TC that is not a return helper,
242 * change it to point to an appropriate return helper. The two different
243 * versions are for the different needs of the C++ unwinder and debugger hooks,
246 void unwindPreventReturnToTC(ActRec
* ar
);
247 void debuggerPreventReturnToTC(ActRec
* ar
);
250 * Call debuggerPreventReturnToTC() on all live VM frames in this thread.
252 void debuggerPreventReturnsToTC();
254 ///////////////////////////////////////////////////////////////////////////////
256 inline ActRec
* arAtOffset(const ActRec
* ar
, int32_t offset
) {
257 return (ActRec
*)(intptr_t(ar
) + intptr_t(offset
* sizeof(TypedValue
)));
260 void frame_free_locals_no_hook(ActRec
* fp
);
262 #define arReturn(a, x) \
266 new (&val_) Variant(x); \
267 frame_free_locals_no_hook(ar_); \
268 tvCopy(val_, *ar_->retSlot()); \
269 return ar_->retSlot(); \
272 #define tvReturn(x) \
275 new (&val_) Variant(x); \
276 assertx(!isRefType(val_.m_type) && val_.m_type != KindOfUninit); \
280 template <bool crossBuiltin
> Class
* arGetContextClassImpl(const ActRec
* ar
);
281 template <> Class
* arGetContextClassImpl
<true>(const ActRec
* ar
);
282 template <> Class
* arGetContextClassImpl
<false>(const ActRec
* ar
);
283 inline Class
* arGetContextClass(const ActRec
* ar
) {
284 return arGetContextClassImpl
<false>(ar
);
286 inline Class
* arGetContextClassFromBuiltin(const ActRec
* ar
) {
287 return arGetContextClassImpl
<true>(ar
);
290 ///////////////////////////////////////////////////////////////////////////////
292 // Used by extension functions that take a PHP "callback", since they need to
293 // figure out the callback context once and call it multiple times. (e.g.
294 // array_map, array_filter, ...)
303 constexpr size_t kNumIterCells
= sizeof(Iter
) / sizeof(Cell
);
304 constexpr size_t kNumActRecCells
= sizeof(ActRec
) / sizeof(Cell
);
306 constexpr size_t clsRefCountToCells(size_t n
) {
307 return (n
* sizeof(cls_ref
) + sizeof(Cell
) - 1) / sizeof(Cell
);
310 ///////////////////////////////////////////////////////////////////////////////
313 * We pad all stack overflow checks by a small amount to allow for three
316 * - inlining functions without having to either do another stack
317 * check (or chase down prologues to smash checks to be bigger).
319 * - omitting stack overflow checks on leaf functions
321 * - delaying stack overflow checks on reentry
323 constexpr int kStackCheckLeafPadding
= 20;
324 constexpr int kStackCheckReenterPadding
= 9;
325 constexpr int kStackCheckPadding
= kStackCheckLeafPadding
+
326 kStackCheckReenterPadding
;
328 // Interpreter evaluation stack.
333 TypedValue
* m_base
; // Stack grows down, so m_base is beyond the end of
337 bool isAllocated() { return m_elms
!= nullptr; }
338 void* getStackLowAddress() const { return m_elms
; }
339 void* getStackHighAddress() const { return m_base
; }
340 bool isValidAddress(uintptr_t v
) {
341 return v
>= uintptr_t(m_elms
) && v
< uintptr_t(m_base
);
346 static const int sSurprisePageSize
;
347 static const unsigned sMinStackElms
;
348 static void ValidateStackSize();
352 std::string
toString(const ActRec
* fp
, int offset
,
353 std::string prefix
="") const;
355 bool wouldOverflow(int numCells
) const;
359 * topOfStackOffset --
361 * Accessors for the x64 translator. Do not play on or around.
367 static constexpr size_t topOfStackOffset() {
368 return offsetof(Stack
, m_top
);
371 static TypedValue
* anyFrameStackBase(const ActRec
* fp
);
372 static TypedValue
* frameStackBase(const ActRec
* fp
);
373 static TypedValue
* resumableStackBase(const ActRec
* fp
);
376 size_t count() const {
377 return ((uintptr_t)m_base
- (uintptr_t)m_top
) / sizeof(TypedValue
);
380 // Same as discard(), but meant to replace popC() iff the interpreter knows
381 // for certain that decrementing a refcount is unnecessary.
384 assertx(m_top
!= m_base
);
385 assertx(!isRefcountedType(m_top
->m_type
));
392 assertx(m_top
!= m_base
);
393 assertx(cellIsPlausible(*m_top
));
401 assertx(m_top
!= m_base
);
402 assertx(refIsPlausible(*m_top
));
410 assertx(m_top
!= m_base
);
411 assertx(m_top
->m_type
== KindOfUninit
);
418 assertx(m_top
!= m_base
);
419 assertx(tvIsPlausible(*m_top
));
425 // popAR() should only be used to tear down a pre-live ActRec. Once
426 // an ActRec is live, it should be torn down using frame_free_locals()
427 // followed by discardAR() or ret().
430 assertx(m_top
!= m_base
);
431 ActRec
* ar
= (ActRec
*)m_top
;
432 if (ar
->func()->cls() && ar
->hasThis()) decRefObj(ar
->getThis());
433 if (ar
->magicDispatch()) {
434 decRefStr(ar
->getInvName());
441 assertx(m_top
!= m_base
);
443 for (int i
= 0; i
< kNumActRecCells
; ++i
) {
444 tvDebugTrash(m_top
+ i
);
447 m_top
+= kNumActRecCells
;
448 assertx((uintptr_t)m_top
<= (uintptr_t)m_base
);
453 // Leave part of the activation on the stack, since the return value now
456 for (int i
= 0; i
< kNumActRecCells
- 1; ++i
) {
457 tvDebugTrash(m_top
+ i
);
460 m_top
+= kNumActRecCells
- 1;
461 assertx((uintptr_t)m_top
<= (uintptr_t)m_base
);
466 assertx(m_top
!= m_base
);
472 void ndiscard(size_t n
) {
473 assertx((uintptr_t)&m_top
[n
] <= (uintptr_t)m_base
);
475 for (int i
= 0; i
< n
; ++i
) {
476 tvDebugTrash(m_top
+ i
);
484 assertx(c
<= m_base
);
487 while (m_top
< c
) tvDebugTrash(m_top
++);
495 assertx(m_top
!= m_base
);
496 assertx(m_top
!= m_elms
);
497 assertx(!isRefType(m_top
->m_type
));
506 assertx(m_top
!= m_elms
);
508 tvWriteUninit(*m_top
);
513 assertx(m_top
!= m_elms
);
519 void pushNullUninit() {
520 assertx(m_top
!= m_elms
);
522 m_top
->m_data
.num
= 0;
523 m_top
->m_type
= KindOfUninit
;
526 template<DataType t
, class T
> void pushVal(T v
) {
527 assertx(m_top
!= m_elms
);
529 *m_top
= make_tv
<t
>(v
);
531 ALWAYS_INLINE
void pushBool(bool v
) { pushVal
<KindOfBoolean
>(v
); }
532 ALWAYS_INLINE
void pushInt(int64_t v
) { pushVal
<KindOfInt64
>(v
); }
533 ALWAYS_INLINE
void pushDouble(double v
) { pushVal
<KindOfDouble
>(v
); }
535 // This should only be called directly when the caller has
536 // already adjusted the refcount appropriately
538 void pushStringNoRc(StringData
* s
) {
539 assertx(m_top
!= m_elms
);
541 *m_top
= make_tv
<KindOfString
>(s
);
545 void pushStaticString(const StringData
* s
) {
546 assertx(s
->isStatic()); // No need to call s->incRefCount().
547 assertx(m_top
!= m_elms
);
549 *m_top
= make_tv
<KindOfPersistentString
>(s
);
552 // These should only be called directly when the caller has
553 // already adjusted the refcount appropriately
555 void pushArrayNoRc(ArrayData
* a
) {
556 assertx(a
->isPHPArray());
557 assertx(m_top
!= m_elms
);
559 *m_top
= make_tv
<KindOfArray
>(a
);
563 void pushVecNoRc(ArrayData
* a
) {
564 assertx(a
->isVecArray());
565 assertx(m_top
!= m_elms
);
567 *m_top
= make_tv
<KindOfVec
>(a
);
571 void pushDictNoRc(ArrayData
* a
) {
572 assertx(a
->isDict());
573 assertx(m_top
!= m_elms
);
575 *m_top
= make_tv
<KindOfDict
>(a
);
579 void pushKeysetNoRc(ArrayData
* a
) {
580 assertx(a
->isKeyset());
581 assertx(m_top
!= m_elms
);
583 *m_top
= make_tv
<KindOfKeyset
>(a
);
587 void pushArray(ArrayData
* a
) {
594 void pushVec(ArrayData
* a
) {
601 void pushDict(ArrayData
* a
) {
608 void pushKeyset(ArrayData
* a
) {
615 void pushStaticArray(const ArrayData
* a
) {
616 assertx(a
->isStatic()); // No need to call a->incRefCount().
617 assertx(a
->isPHPArray());
618 assertx(m_top
!= m_elms
);
620 *m_top
= make_tv
<KindOfPersistentArray
>(a
);
624 void pushStaticVec(const ArrayData
* a
) {
625 assertx(a
->isStatic()); // No need to call a->incRefCount().
626 assertx(a
->isVecArray());
627 assertx(m_top
!= m_elms
);
629 *m_top
= make_tv
<KindOfPersistentVec
>(a
);
633 void pushStaticDict(const ArrayData
* a
) {
634 assertx(a
->isStatic()); // No need to call a->incRefCount().
635 assertx(a
->isDict());
636 assertx(m_top
!= m_elms
);
638 *m_top
= make_tv
<KindOfPersistentDict
>(a
);
642 void pushStaticKeyset(const ArrayData
* a
) {
643 assertx(a
->isStatic()); // No need to call a->incRefCount().
644 assertx(a
->isKeyset());
645 assertx(m_top
!= m_elms
);
647 *m_top
= make_tv
<KindOfPersistentKeyset
>(a
);
650 // This should only be called directly when the caller has
651 // already adjusted the refcount appropriately
653 void pushObjectNoRc(ObjectData
* o
) {
654 assertx(m_top
!= m_elms
);
656 *m_top
= make_tv
<KindOfObject
>(o
);
660 void pushObject(ObjectData
* o
) {
666 void pushRecordNoRc(RecordData
* r
) {
667 assertx(m_top
!= m_elms
);
669 *m_top
= make_tv
<KindOfRecord
>(r
);
673 void pushFunc(Func
* f
) {
675 *m_top
= make_tv
<KindOfFunc
>(f
);
679 void pushClsMethNoRc(ClsMethDataRef clsMeth
) {
681 *m_top
= make_tv
<KindOfClsMeth
>(clsMeth
);
685 void nalloc(size_t n
) {
686 assertx((uintptr_t)(m_top
- n
) <= (uintptr_t)m_base
);
692 assertx(m_top
!= m_elms
);
699 assertx(m_top
!= m_elms
);
705 TypedValue
* allocTV() {
706 assertx(m_top
!= m_elms
);
713 assertx((uintptr_t)(m_top
- kNumActRecCells
) >= (uintptr_t)m_elms
);
714 assertx(kNumActRecCells
* sizeof(Cell
) == sizeof(ActRec
));
715 m_top
-= kNumActRecCells
;
716 return (ActRec
*)m_top
;
721 assertx(kNumIterCells
* sizeof(Cell
) == sizeof(Iter
));
722 assertx((uintptr_t)(m_top
- kNumIterCells
) >= (uintptr_t)m_elms
);
723 m_top
-= kNumIterCells
;
727 void allocClsRefSlots(size_t n
) {
728 assertx((uintptr_t)(m_top
- clsRefCountToCells(n
)) >= (uintptr_t)m_elms
);
729 m_top
-= clsRefCountToCells(n
);
731 memset(m_top
, kTrashClsRef
, clsRefCountToCells(n
) * sizeof(Cell
));
736 void replaceC(const Cell c
) {
737 assertx(m_top
!= m_base
);
738 assertx(!isRefType(m_top
->m_type
));
743 template <DataType DT
>
746 assertx(m_top
!= m_base
);
747 assertx(!isRefType(m_top
->m_type
));
749 *m_top
= make_tv
<DT
>();
752 template <DataType DT
, typename T
>
754 void replaceC(T value
) {
755 assertx(m_top
!= m_base
);
756 assertx(!isRefType(m_top
->m_type
));
758 *m_top
= make_tv
<DT
>(value
);
762 void replaceTV(const TypedValue
& tv
) {
763 assertx(m_top
!= m_base
);
768 template <DataType DT
>
771 assertx(m_top
!= m_base
);
773 *m_top
= make_tv
<DT
>();
776 template <DataType DT
, typename T
>
778 void replaceTV(T value
) {
779 assertx(m_top
!= m_base
);
781 *m_top
= make_tv
<DT
>(value
);
786 assertx(m_top
!= m_base
);
787 return tvAssertCell(m_top
);
792 assertx(m_top
!= m_base
);
793 assertx(isRefType(m_top
->m_type
));
798 TypedValue
* topTV() {
799 assertx(m_top
!= m_base
);
804 ActRec
* indA(size_t ind
) {
805 assertx(m_top
!= m_base
);
806 assertx(count() > ind
+ kNumActRecCells
);
807 return (ActRec
*)&m_top
[ind
];
811 Cell
* indC(size_t ind
) {
812 assertx(m_top
!= m_base
);
813 assertx(!isRefType(m_top
[ind
].m_type
));
814 return tvAssertCell(&m_top
[ind
]);
818 TypedValue
* indTV(size_t ind
) {
819 assertx(m_top
!= m_base
);
824 //////////////////////////////////////////////////////////////////////
827 * Visit all the slots and pre-live ActRecs on a live eval stack,
828 * handling FPI regions and resumables correctly, and stopping when we
829 * reach the supplied activation record.
831 * The stack elements are visited from lower address to higher, with
832 * ActRecs visited after the stack slots below them.
834 * This will not read the VM registers (pc, fp, sp), so it will
835 * perform the requested visitation independent of modifications to
836 * the VM stack or frame pointer.
838 template<class TV
, class ARFun
, class TVFun
>
839 typename maybe_const
<TV
, TypedValue
>::type
840 visitStackElems(const ActRec
* const fp
,
842 Offset
const bcOffset
,
845 const TypedValue
* const base
= Stack::anyFrameStackBase(fp
);
846 auto cursor
= stackTop
;
847 assertx(cursor
<= base
);
849 if (auto fe
= fp
->m_func
->findFPI(bcOffset
)) {
852 if (!fp
->resumed()) {
853 ar
= arAtOffset(fp
, -fe
->m_fpOff
);
855 // fp is pointing into the Resumable struct. Since fpOff is
856 // given as an offset from the frame pointer as if it were in
857 // the normal place on the main stack, we have to reconstruct
858 // that "normal place".
859 auto const fakePrevFP
= reinterpret_cast<const ActRec
*>(
860 base
+ fp
->m_func
->numSlotsInFrame()
862 ar
= arAtOffset(fakePrevFP
, -fe
->m_fpOff
);
865 while (cursor
< reinterpret_cast<TypedValue
*>(ar
)) {
869 if (cursor
== reinterpret_cast<TypedValue
*>(ar
)) {
870 arFun(ar
, fe
->m_fpushOff
);
871 cursor
+= kNumActRecCells
;
874 assertx(cursor
>= reinterpret_cast<TypedValue
*>(ar
) + kNumActRecCells
);
875 if (fe
->m_parentIndex
== -1) break;
876 fe
= &fp
->m_func
->fpitab()[fe
->m_parentIndex
];
880 while (cursor
< base
) {
885 void resetCoverageCounters();
887 // The interpOne*() methods implement individual opcode handlers.
888 using InterpOneFunc
= jit::TCA (*) (ActRec
*, TypedValue
*, Offset
);
889 extern InterpOneFunc interpOneEntryPoints
[];
891 bool doFCallUnpackTC(PC origpc
, int32_t numArgs
, void*);
892 bool doFCall(ActRec
* ar
, uint32_t numArgs
, bool unpack
);
893 jit::TCA
dispatchBB();
894 void pushFrameSlots(const Func
* func
, int nparams
= 0);
895 Array
getDefinedVariables(const ActRec
*);
897 enum class StackArgsState
{ // tells prepareFuncEntry how much work to do
898 // the stack may contain more arguments than the function expects
900 // the stack has already been trimmed of any extra arguments, which
901 // have been teleported away into ExtraArgs and/or a variadic param
904 void enterVMAtFunc(ActRec
* enterFnAr
, StackArgsState stk
, VarEnv
* varEnv
);
905 void enterVMAtCurPC();
906 bool prepareArrayArgs(ActRec
* ar
, const Cell args
, Stack
& stack
,
907 int nregular
, TypedValue
* retval
, bool checkRefAnnot
);
909 ///////////////////////////////////////////////////////////////////////////////