summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Stack.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/vm/Stack.h
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/vm/Stack.h')
-rw-r--r--js/src/vm/Stack.h1017
1 files changed, 1017 insertions, 0 deletions
diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h
new file mode 100644
index 0000000000..26752fb2fa
--- /dev/null
+++ b/js/src/vm/Stack.h
@@ -0,0 +1,1017 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_Stack_h
+#define vm_Stack_h
+
+#include "mozilla/HashFunctions.h"
+#include "mozilla/MemoryReporting.h"
+
+#include <algorithm>
+#include <type_traits>
+
+#include "js/ErrorReport.h"
+#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "js/ValueArray.h"
+#include "vm/ArgumentsObject.h"
+#include "vm/JSFunction.h"
+#include "vm/JSScript.h"
+#include "wasm/WasmDebugFrame.h" // js::wasm::DebugFrame
+
+namespace js {
+
+class InterpreterRegs;
+class CallObject;
+class FrameIter;
+class ClassBodyScope;
+class EnvironmentObject;
+class BlockLexicalEnvironmentObject;
+class ExtensibleLexicalEnvironmentObject;
+class GeckoProfilerRuntime;
+class InterpreterFrame;
+class EnvironmentIter;
+class EnvironmentCoordinate;
+
+namespace jit {
+class CommonFrameLayout;
+}
+namespace wasm {
+class Instance;
+} // namespace wasm
+
+// [SMDOC] VM stack layout
+//
+// A JSRuntime's stack consists of a linked list of activations. Every
+// activation contains a number of scripted frames that are either running in
+// the interpreter (InterpreterActivation) or JIT code (JitActivation). The
+// frames inside a single activation are contiguous: whenever C++ calls back
+// into JS, a new activation is pushed.
+//
+// Every activation is tied to a single JSContext and JS::Compartment. This
+// means we can reconstruct a given context's stack by skipping activations
+// belonging to other contexts. This happens whenever an embedding enters the JS
+// engine on cx1 and then, from a native called by the JS engine, reenters the
+// VM on cx2.
+
+// Interpreter frames (InterpreterFrame)
+//
+// Each interpreter script activation (global or function code) is given a
+// fixed-size header (js::InterpreterFrame). The frame contains bookkeeping
+// information about the activation and links to the previous frame.
+//
+// The values after an InterpreterFrame in memory are its locals followed by its
+// expression stack. InterpreterFrame::argv_ points to the frame's arguments.
+// Missing formal arguments are padded with |undefined|, so the number of
+// arguments is always >= the number of formals.
+//
+// The top of an activation's current frame's expression stack is pointed to by
+// the activation's "current regs", which contains the stack pointer 'sp'. In
+// the interpreter, sp is adjusted as individual values are pushed and popped
+// from the stack and the InterpreterRegs struct (pointed to by the
+// InterpreterActivation) is a local var of js::Interpret.
+
+enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
+
+} // namespace js
+
+/*****************************************************************************/
+
+namespace js {
+
+namespace jit {
+class BaselineFrame;
+class RematerializedFrame;
+} // namespace jit
+
+/**
+ * Pointer to a live JS or WASM stack frame.
+ */
+class AbstractFramePtr {
+ friend class FrameIter;
+
+ uintptr_t ptr_;
+
+ enum {
+ Tag_InterpreterFrame = 0x0,
+ Tag_BaselineFrame = 0x1,
+ Tag_RematerializedFrame = 0x2,
+ Tag_WasmDebugFrame = 0x3,
+ TagMask = 0x3
+ };
+
+ public:
+ AbstractFramePtr() : ptr_(0) {}
+
+ MOZ_IMPLICIT AbstractFramePtr(InterpreterFrame* fp)
+ : ptr_(fp ? uintptr_t(fp) | Tag_InterpreterFrame : 0) {
+ MOZ_ASSERT_IF(fp, asInterpreterFrame() == fp);
+ }
+
+ MOZ_IMPLICIT AbstractFramePtr(jit::BaselineFrame* fp)
+ : ptr_(fp ? uintptr_t(fp) | Tag_BaselineFrame : 0) {
+ MOZ_ASSERT_IF(fp, asBaselineFrame() == fp);
+ }
+
+ MOZ_IMPLICIT AbstractFramePtr(jit::RematerializedFrame* fp)
+ : ptr_(fp ? uintptr_t(fp) | Tag_RematerializedFrame : 0) {
+ MOZ_ASSERT_IF(fp, asRematerializedFrame() == fp);
+ }
+
+ MOZ_IMPLICIT AbstractFramePtr(wasm::DebugFrame* fp)
+ : ptr_(fp ? uintptr_t(fp) | Tag_WasmDebugFrame : 0) {
+ static_assert(wasm::DebugFrame::Alignment >= TagMask, "aligned");
+ MOZ_ASSERT_IF(fp, asWasmDebugFrame() == fp);
+ }
+
+ bool isInterpreterFrame() const {
+ return (ptr_ & TagMask) == Tag_InterpreterFrame;
+ }
+ InterpreterFrame* asInterpreterFrame() const {
+ MOZ_ASSERT(isInterpreterFrame());
+ InterpreterFrame* res = (InterpreterFrame*)(ptr_ & ~TagMask);
+ MOZ_ASSERT(res);
+ return res;
+ }
+ bool isBaselineFrame() const { return (ptr_ & TagMask) == Tag_BaselineFrame; }
+ jit::BaselineFrame* asBaselineFrame() const {
+ MOZ_ASSERT(isBaselineFrame());
+ jit::BaselineFrame* res = (jit::BaselineFrame*)(ptr_ & ~TagMask);
+ MOZ_ASSERT(res);
+ return res;
+ }
+ bool isRematerializedFrame() const {
+ return (ptr_ & TagMask) == Tag_RematerializedFrame;
+ }
+ jit::RematerializedFrame* asRematerializedFrame() const {
+ MOZ_ASSERT(isRematerializedFrame());
+ jit::RematerializedFrame* res =
+ (jit::RematerializedFrame*)(ptr_ & ~TagMask);
+ MOZ_ASSERT(res);
+ return res;
+ }
+ bool isWasmDebugFrame() const {
+ return (ptr_ & TagMask) == Tag_WasmDebugFrame;
+ }
+ wasm::DebugFrame* asWasmDebugFrame() const {
+ MOZ_ASSERT(isWasmDebugFrame());
+ wasm::DebugFrame* res = (wasm::DebugFrame*)(ptr_ & ~TagMask);
+ MOZ_ASSERT(res);
+ return res;
+ }
+
+ void* raw() const { return reinterpret_cast<void*>(ptr_); }
+
+ bool operator==(const AbstractFramePtr& other) const {
+ return ptr_ == other.ptr_;
+ }
+ bool operator!=(const AbstractFramePtr& other) const {
+ return ptr_ != other.ptr_;
+ }
+
+ explicit operator bool() const { return !!ptr_; }
+
+ inline JSObject* environmentChain() const;
+ inline CallObject& callObj() const;
+ inline bool initFunctionEnvironmentObjects(JSContext* cx);
+ inline bool pushVarEnvironment(JSContext* cx, Handle<Scope*> scope);
+ template <typename SpecificEnvironment>
+ inline void pushOnEnvironmentChain(SpecificEnvironment& env);
+ template <typename SpecificEnvironment>
+ inline void popOffEnvironmentChain();
+
+ inline JS::Realm* realm() const;
+
+ inline bool hasInitialEnvironment() const;
+ inline bool isGlobalFrame() const;
+ inline bool isModuleFrame() const;
+ inline bool isEvalFrame() const;
+ inline bool isDebuggerEvalFrame() const;
+
+ inline bool hasScript() const;
+ inline JSScript* script() const;
+ inline wasm::Instance* wasmInstance() const;
+ inline GlobalObject* global() const;
+ inline bool hasGlobal(const GlobalObject* global) const;
+ inline JSFunction* callee() const;
+ inline Value calleev() const;
+ inline Value& thisArgument() const;
+
+ inline bool isConstructing() const;
+
+ inline bool debuggerNeedsCheckPrimitiveReturn() const;
+
+ inline bool isFunctionFrame() const;
+ inline bool isGeneratorFrame() const;
+
+ inline bool saveGeneratorSlots(JSContext* cx, unsigned nslots,
+ ArrayObject* dest) const;
+
+ inline bool hasCachedSavedFrame() const;
+
+ inline unsigned numActualArgs() const;
+ inline unsigned numFormalArgs() const;
+
+ inline Value* argv() const;
+
+ inline bool hasArgs() const;
+ inline bool hasArgsObj() const;
+ inline ArgumentsObject& argsObj() const;
+ inline void initArgsObj(ArgumentsObject& argsobj) const;
+
+ inline Value& unaliasedLocal(uint32_t i);
+ inline Value& unaliasedFormal(
+ unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
+ inline Value& unaliasedActual(
+ unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
+ template <class Op>
+ inline void unaliasedForEachActual(JSContext* cx, Op op);
+
+ inline bool prevUpToDate() const;
+ inline void setPrevUpToDate() const;
+ inline void unsetPrevUpToDate() const;
+
+ inline bool isDebuggee() const;
+ inline void setIsDebuggee();
+ inline void unsetIsDebuggee();
+
+ inline HandleValue returnValue() const;
+ inline void setReturnValue(const Value& rval) const;
+
+ friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, InterpreterFrame*);
+ friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&,
+ jit::BaselineFrame*);
+ friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&,
+ jit::RematerializedFrame*);
+ friend void GDBTestInitAbstractFramePtr(AbstractFramePtr& frame,
+ wasm::DebugFrame* ptr);
+};
+
+class NullFramePtr : public AbstractFramePtr {
+ public:
+ NullFramePtr() = default;
+};
+
+enum MaybeConstruct { NO_CONSTRUCT = false, CONSTRUCT = true };
+
+/*****************************************************************************/
+
+class InterpreterFrame {
+ enum Flags : uint32_t {
+ CONSTRUCTING = 0x1, /* frame is for a constructor invocation */
+
+ RESUMED_GENERATOR = 0x2, /* frame is for a resumed generator invocation */
+
+ /* Function prologue state */
+ HAS_INITIAL_ENV =
+ 0x4, /* callobj created for function or var env for eval */
+ HAS_ARGS_OBJ = 0x8, /* ArgumentsObject created for needsArgsObj script */
+
+ /* Lazy frame initialization */
+ HAS_RVAL = 0x10, /* frame has rval_ set */
+
+ /* Debugger state */
+ PREV_UP_TO_DATE = 0x20, /* see DebugScopes::updateLiveScopes */
+
+ /*
+ * See comment above 'isDebuggee' in Realm.h for explanation of
+ * invariants of debuggee compartments, scripts, and frames.
+ */
+ DEBUGGEE = 0x40, /* Execution is being observed by Debugger */
+
+ /* Used in tracking calls and profiling (see vm/GeckoProfiler.cpp) */
+ HAS_PUSHED_PROF_FRAME = 0x80, /* Gecko Profiler was notified of entry */
+
+ /*
+ * If set, we entered one of the JITs and ScriptFrameIter should skip
+ * this frame.
+ */
+ RUNNING_IN_JIT = 0x100,
+
+ /*
+ * If set, this frame has been on the stack when
+ * |js::SavedStacks::saveCurrentStack| was called, and so there is a
+ * |js::SavedFrame| object cached for this frame.
+ */
+ HAS_CACHED_SAVED_FRAME = 0x200,
+ };
+
+ mutable uint32_t flags_; /* bits described by Flags */
+ uint32_t nactual_; /* number of actual arguments, for function frames */
+ JSScript* script_; /* the script we're executing */
+ JSObject* envChain_; /* current environment chain */
+ Value rval_; /* if HAS_RVAL, return value of the frame */
+ ArgumentsObject* argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */
+
+ /*
+ * Previous frame and its pc and sp. Always nullptr for
+ * InterpreterActivation's entry frame, always non-nullptr for inline
+ * frames.
+ */
+ InterpreterFrame* prev_;
+ jsbytecode* prevpc_;
+ Value* prevsp_;
+
+ /*
+ * For an eval-in-frame DEBUGGER_EVAL frame, the frame in whose scope
+ * we're evaluating code. Iteration treats this as our previous frame.
+ */
+ AbstractFramePtr evalInFramePrev_;
+
+ Value* argv_; /* If hasArgs(), points to frame's arguments. */
+ LifoAlloc::Mark mark_; /* Used to release memory for this frame. */
+
+ static void staticAsserts() {
+ static_assert(offsetof(InterpreterFrame, rval_) % sizeof(Value) == 0);
+ static_assert(sizeof(InterpreterFrame) % sizeof(Value) == 0);
+ }
+
+ /*
+ * The utilities are private since they are not able to assert that only
+ * unaliased vars/formals are accessed. Normal code should prefer the
+ * InterpreterFrame::unaliased* members (or InterpreterRegs::stackDepth for
+ * the usual "depth is at least" assertions).
+ */
+ Value* slots() const { return (Value*)(this + 1); }
+ Value* base() const { return slots() + script()->nfixed(); }
+
+ friend class FrameIter;
+ friend class InterpreterRegs;
+ friend class InterpreterStack;
+ friend class jit::BaselineFrame;
+
+ /*
+ * Frame initialization, called by InterpreterStack operations after acquiring
+ * the raw memory for the frame:
+ */
+
+ /* Used for Invoke and Interpret. */
+ void initCallFrame(InterpreterFrame* prev, jsbytecode* prevpc, Value* prevsp,
+ JSFunction& callee, JSScript* script, Value* argv,
+ uint32_t nactual, MaybeConstruct constructing);
+
+ /* Used for eval, module or global frames. */
+ void initExecuteFrame(JSContext* cx, HandleScript script,
+ AbstractFramePtr prev, HandleObject envChain);
+
+ public:
+ /*
+ * Frame prologue/epilogue
+ *
+ * Every stack frame must have 'prologue' called before executing the
+ * first op and 'epilogue' called after executing the last op and before
+ * popping the frame (whether the exit is exceptional or not).
+ *
+ * For inline JS calls/returns, it is easy to call the prologue/epilogue
+ * exactly once. When calling JS from C++, Invoke/Execute push the stack
+ * frame but do *not* call the prologue/epilogue. That means Interpret
+ * must call the prologue/epilogue for the entry frame. This scheme
+ * simplifies jit compilation.
+ *
+ * An important corner case is what happens when an error occurs (OOM,
+ * over-recursed) after pushing the stack frame but before 'prologue' is
+ * called or completes fully. To simplify usage, 'epilogue' does not assume
+ * 'prologue' has completed and handles all the intermediate state details.
+ */
+
+ bool prologue(JSContext* cx);
+ void epilogue(JSContext* cx, jsbytecode* pc);
+
+ bool checkReturn(JSContext* cx, HandleValue thisv, MutableHandleValue result);
+
+ bool initFunctionEnvironmentObjects(JSContext* cx);
+
+ /*
+ * Initialize locals of newly-pushed frame to undefined.
+ */
+ void initLocals();
+
+ /*
+ * Stack frame type
+ *
+ * A stack frame may have one of four types, which determines which
+ * members of the frame may be accessed and other invariants:
+ *
+ * global frame: execution of global code
+ * function frame: execution of function code
+ * module frame: execution of a module
+ * eval frame: execution of eval code
+ */
+
+ bool isGlobalFrame() const { return script_->isGlobalCode(); }
+
+ bool isModuleFrame() const { return script_->isModule(); }
+
+ bool isEvalFrame() const { return script_->isForEval(); }
+
+ bool isFunctionFrame() const { return script_->isFunction(); }
+
+ /*
+ * Previous frame
+ *
+ * A frame's 'prev' frame is either null or the previous frame pointed to
+ * by cx->regs->fp when this frame was pushed. Often, given two prev-linked
+ * frames, the next-frame is a function or eval that was called by the
+ * prev-frame, but not always: the prev-frame may have called a native that
+ * reentered the VM through JS_CallFunctionValue on the same context
+ * (without calling JS_SaveFrameChain) which pushed the next-frame. Thus,
+ * 'prev' has little semantic meaning and basically just tells the VM what
+ * to set cx->regs->fp to when this frame is popped.
+ */
+
+ InterpreterFrame* prev() const { return prev_; }
+
+ AbstractFramePtr evalInFramePrev() const {
+ MOZ_ASSERT(isEvalFrame());
+ return evalInFramePrev_;
+ }
+
+ /*
+ * (Unaliased) locals and arguments
+ *
+ * Only non-eval function frames have arguments. The arguments pushed by
+ * the caller are the 'actual' arguments. The declared arguments of the
+ * callee are the 'formal' arguments. When the caller passes less actual
+ * arguments, missing formal arguments are padded with |undefined|.
+ *
+ * When a local/formal variable is aliased (accessed by nested closures,
+ * environment operations, or 'arguments'), the canonical location for
+ * that value is the slot of an environment object. Aliased locals don't
+ * have stack slots assigned to them. These functions assert that
+ * accesses to stack values are unaliased.
+ */
+
+ inline Value& unaliasedLocal(uint32_t i);
+
+ bool hasArgs() const { return isFunctionFrame(); }
+ inline Value& unaliasedFormal(unsigned i,
+ MaybeCheckAliasing = CHECK_ALIASING);
+ inline Value& unaliasedActual(unsigned i,
+ MaybeCheckAliasing = CHECK_ALIASING);
+ template <class Op>
+ inline void unaliasedForEachActual(Op op);
+
+ unsigned numFormalArgs() const {
+ MOZ_ASSERT(hasArgs());
+ return callee().nargs();
+ }
+ unsigned numActualArgs() const {
+ MOZ_ASSERT(hasArgs());
+ return nactual_;
+ }
+
+ /* Watch out, this exposes a pointer to the unaliased formal arg array. */
+ Value* argv() const {
+ MOZ_ASSERT(hasArgs());
+ return argv_;
+ }
+
+ /*
+ * Arguments object
+ *
+ * If a non-eval function has script->needsArgsObj, an arguments object is
+ * created in the prologue and stored in the local variable for the
+ * 'arguments' binding (script->argumentsLocal). Since this local is
+ * mutable, the arguments object can be overwritten and we can "lose" the
+ * arguments object. Thus, InterpreterFrame keeps an explicit argsObj_ field
+ * so that the original arguments object is always available.
+ */
+
+ ArgumentsObject& argsObj() const;
+ void initArgsObj(ArgumentsObject& argsobj);
+
+ ArrayObject* createRestParameter(JSContext* cx);
+
+ /*
+ * Environment chain
+ *
+ * In theory, the environment chain would contain an object for every
+ * lexical scope. However, only objects that are required for dynamic
+ * lookup are actually created.
+ *
+ * Given that an InterpreterFrame corresponds roughly to a ES Execution
+ * Context (ES 10.3), GetVariablesObject corresponds to the
+ * VariableEnvironment component of a Exection Context. Intuitively, the
+ * variables object is where new bindings (variables and functions) are
+ * stored. One might expect that this is either the Call object or
+ * envChain.globalObj for function or global code, respectively, however
+ * the JSAPI allows calls of Execute to specify a variables object on the
+ * environment chain other than the call/global object. This allows
+ * embeddings to run multiple scripts under the same global, each time
+ * using a new variables object to collect and discard the script's global
+ * variables.
+ */
+
+ inline HandleObject environmentChain() const;
+
+ inline EnvironmentObject& aliasedEnvironment(EnvironmentCoordinate ec) const;
+ inline EnvironmentObject& aliasedEnvironmentMaybeDebug(
+ EnvironmentCoordinate ec) const;
+ inline GlobalObject& global() const;
+ inline CallObject& callObj() const;
+ inline ExtensibleLexicalEnvironmentObject& extensibleLexicalEnvironment()
+ const;
+
+ template <typename SpecificEnvironment>
+ inline void pushOnEnvironmentChain(SpecificEnvironment& env);
+ template <typename SpecificEnvironment>
+ inline void popOffEnvironmentChain();
+ inline void replaceInnermostEnvironment(BlockLexicalEnvironmentObject& env);
+
+ // Push a VarEnvironmentObject for function frames of functions that have
+ // parameter expressions with closed over var bindings.
+ bool pushVarEnvironment(JSContext* cx, Handle<Scope*> scope);
+
+ /*
+ * For lexical envs with aliased locals, these interfaces push and pop
+ * entries on the environment chain. The "freshen" operation replaces the
+ * current lexical env with a fresh copy of it, to implement semantics
+ * providing distinct bindings per iteration of a for(;;) loop whose head
+ * has a lexical declaration. The "recreate" operation replaces the
+ * current lexical env with a copy of it containing uninitialized
+ * bindings, to implement semantics providing distinct bindings per
+ * iteration of a for-in/of loop.
+ */
+
+ bool pushLexicalEnvironment(JSContext* cx, Handle<LexicalScope*> scope);
+ bool freshenLexicalEnvironment(JSContext* cx, jsbytecode* pc);
+ bool recreateLexicalEnvironment(JSContext* cx, jsbytecode* pc);
+
+ bool pushClassBodyEnvironment(JSContext* cx, Handle<ClassBodyScope*> scope);
+
+ /*
+ * Script
+ *
+ * All frames have an associated JSScript which holds the bytecode being
+ * executed for the frame.
+ */
+
+ JSScript* script() const { return script_; }
+
+ /* Return the previous frame's pc. */
+ jsbytecode* prevpc() {
+ MOZ_ASSERT(prev_);
+ return prevpc_;
+ }
+
+ /* Return the previous frame's sp. */
+ Value* prevsp() {
+ MOZ_ASSERT(prev_);
+ return prevsp_;
+ }
+
+ /*
+ * Return the 'this' argument passed to a non-eval function frame. This is
+ * not necessarily the frame's this-binding, for instance non-strict
+ * functions will box primitive 'this' values and thisArgument() will
+ * return the original, unboxed Value.
+ */
+ Value& thisArgument() const {
+ MOZ_ASSERT(isFunctionFrame());
+ return argv()[-1];
+ }
+
+ /*
+ * Callee
+ *
+ * Only function frames have a true callee. An eval frame in a function has
+ * the same callee as its containing function frame. An async module has to
+ * create a wrapper callee to allow passing the script to generators for
+ * pausing and resuming.
+ */
+
+ JSFunction& callee() const {
+ MOZ_ASSERT(isFunctionFrame());
+ return calleev().toObject().as<JSFunction>();
+ }
+
+ const Value& calleev() const {
+ MOZ_ASSERT(isFunctionFrame());
+ return argv()[-2];
+ }
+
+ /*
+ * New Target
+ *
+ * Only non-arrow function frames have a meaningful newTarget.
+ */
+ Value newTarget() const {
+ MOZ_ASSERT(isFunctionFrame());
+ MOZ_ASSERT(!callee().isArrow());
+
+ if (isConstructing()) {
+ unsigned pushedArgs = std::max(numFormalArgs(), numActualArgs());
+ return argv()[pushedArgs];
+ }
+ return UndefinedValue();
+ }
+
+ /* Profiler flags */
+
+ bool hasPushedGeckoProfilerFrame() {
+ return !!(flags_ & HAS_PUSHED_PROF_FRAME);
+ }
+
+ void setPushedGeckoProfilerFrame() { flags_ |= HAS_PUSHED_PROF_FRAME; }
+
+ void unsetPushedGeckoProfilerFrame() { flags_ &= ~HAS_PUSHED_PROF_FRAME; }
+
+ /* Return value */
+
+ bool hasReturnValue() const { return flags_ & HAS_RVAL; }
+
+ MutableHandleValue returnValue() {
+ if (!hasReturnValue()) {
+ rval_.setUndefined();
+ }
+ return MutableHandleValue::fromMarkedLocation(&rval_);
+ }
+
+ void markReturnValue() { flags_ |= HAS_RVAL; }
+
+ void setReturnValue(const Value& v) {
+ rval_ = v;
+ markReturnValue();
+ }
+
+ // Copy values from this frame into a private Array, owned by the
+ // GeneratorObject, for suspending.
+ [[nodiscard]] inline bool saveGeneratorSlots(JSContext* cx, unsigned nslots,
+ ArrayObject* dest) const;
+
+ // Copy values from the Array into this stack frame, for resuming.
+ inline void restoreGeneratorSlots(ArrayObject* src);
+
+ void resumeGeneratorFrame(JSObject* envChain) {
+ MOZ_ASSERT(script()->isGenerator() || script()->isAsync());
+ MOZ_ASSERT_IF(!script()->isModule(), isFunctionFrame());
+ flags_ |= HAS_INITIAL_ENV;
+ envChain_ = envChain;
+ }
+
+ /*
+ * Other flags
+ */
+
+ bool isConstructing() const { return !!(flags_ & CONSTRUCTING); }
+
+ void setResumedGenerator() { flags_ |= RESUMED_GENERATOR; }
+ bool isResumedGenerator() const { return !!(flags_ & RESUMED_GENERATOR); }
+
+ /*
+ * These two queries should not be used in general: the presence/absence of
+ * the call/args object is determined by the static(ish) properties of the
+ * JSFunction/JSScript. These queries should only be performed when probing
+ * a stack frame that may be in the middle of the prologue (during which
+ * time the call/args object are created).
+ */
+
+ inline bool hasInitialEnvironment() const;
+
+ bool hasInitialEnvironmentUnchecked() const {
+ return flags_ & HAS_INITIAL_ENV;
+ }
+
+ bool hasArgsObj() const {
+ MOZ_ASSERT(script()->needsArgsObj());
+ return flags_ & HAS_ARGS_OBJ;
+ }
+
+ /*
+ * Debugger eval frames.
+ *
+ * - If evalInFramePrev_ is non-null, frame was created for an "eval in
+ * frame" call, which can push a successor to any live frame; so its
+ * logical "prev" frame is not necessarily the previous frame in memory.
+ * Iteration should treat evalInFramePrev_ as this frame's previous frame.
+ *
+ * - Don't bother to JIT it, because it's probably short-lived.
+ *
+ * - It is required to have a environment chain object outside the
+ * js::EnvironmentObject hierarchy: either a global object, or a
+ * DebugEnvironmentProxy.
+ */
+ bool isDebuggerEvalFrame() const {
+ return isEvalFrame() && !!evalInFramePrev_;
+ }
+
+ bool prevUpToDate() const { return !!(flags_ & PREV_UP_TO_DATE); }
+
+ void setPrevUpToDate() { flags_ |= PREV_UP_TO_DATE; }
+
+ void unsetPrevUpToDate() { flags_ &= ~PREV_UP_TO_DATE; }
+
+ bool isDebuggee() const { return !!(flags_ & DEBUGGEE); }
+
+ void setIsDebuggee() { flags_ |= DEBUGGEE; }
+
+ inline void unsetIsDebuggee();
+
+ bool hasCachedSavedFrame() const { return flags_ & HAS_CACHED_SAVED_FRAME; }
+ void setHasCachedSavedFrame() { flags_ |= HAS_CACHED_SAVED_FRAME; }
+ void clearHasCachedSavedFrame() { flags_ &= ~HAS_CACHED_SAVED_FRAME; }
+
+ public:
+ void trace(JSTracer* trc, Value* sp, jsbytecode* pc);
+ void traceValues(JSTracer* trc, unsigned start, unsigned end);
+
+ // Entered Baseline/Ion from the interpreter.
+ bool runningInJit() const { return !!(flags_ & RUNNING_IN_JIT); }
+ void setRunningInJit() { flags_ |= RUNNING_IN_JIT; }
+ void clearRunningInJit() { flags_ &= ~RUNNING_IN_JIT; }
+};
+
+/*****************************************************************************/
+
+class InterpreterRegs {
+ public:
+ Value* sp;
+ jsbytecode* pc;
+
+ private:
+ InterpreterFrame* fp_;
+
+ public:
+ InterpreterFrame* fp() const { return fp_; }
+
+ unsigned stackDepth() const {
+ MOZ_ASSERT(sp >= fp_->base());
+ return sp - fp_->base();
+ }
+
+ Value* spForStackDepth(unsigned depth) const {
+ MOZ_ASSERT(fp_->script()->nfixed() + depth <= fp_->script()->nslots());
+ return fp_->base() + depth;
+ }
+
+ void popInlineFrame() {
+ pc = fp_->prevpc();
+ unsigned spForNewTarget =
+ fp_->isResumedGenerator() ? 0 : fp_->isConstructing();
+ // This code is called when resuming from async and generator code.
+ // In the case of modules, we don't have arguments, so we can't use
+ // numActualArgs, which asserts 'hasArgs'.
+ unsigned nActualArgs = fp_->isModuleFrame() ? 0 : fp_->numActualArgs();
+ sp = fp_->prevsp() - nActualArgs - 1 - spForNewTarget;
+ fp_ = fp_->prev();
+ MOZ_ASSERT(fp_);
+ }
+ void prepareToRun(InterpreterFrame& fp, JSScript* script) {
+ pc = script->code();
+ sp = fp.slots() + script->nfixed();
+ fp_ = &fp;
+ }
+
+ void setToEndOfScript();
+
+ MutableHandleValue stackHandleAt(int i) {
+ return MutableHandleValue::fromMarkedLocation(&sp[i]);
+ }
+
+ HandleValue stackHandleAt(int i) const {
+ return HandleValue::fromMarkedLocation(&sp[i]);
+ }
+
+ friend void GDBTestInitInterpreterRegs(InterpreterRegs&,
+ js::InterpreterFrame*, JS::Value*,
+ uint8_t*);
+};
+
+/*****************************************************************************/
+
+class InterpreterStack {
+ friend class InterpreterActivation;
+
+ static const size_t DEFAULT_CHUNK_SIZE = 4 * 1024;
+ LifoAlloc allocator_;
+
+ // Number of interpreter frames on the stack, for over-recursion checks.
+ static const size_t MAX_FRAMES = 50 * 1000;
+ static const size_t MAX_FRAMES_TRUSTED = MAX_FRAMES + 1000;
+ size_t frameCount_;
+
+ inline uint8_t* allocateFrame(JSContext* cx, size_t size);
+
+ inline InterpreterFrame* getCallFrame(JSContext* cx, const CallArgs& args,
+ HandleScript script,
+ MaybeConstruct constructing,
+ Value** pargv);
+
+ void releaseFrame(InterpreterFrame* fp) {
+ frameCount_--;
+ allocator_.release(fp->mark_);
+ }
+
+ public:
+ InterpreterStack() : allocator_(DEFAULT_CHUNK_SIZE), frameCount_(0) {}
+
+ ~InterpreterStack() { MOZ_ASSERT(frameCount_ == 0); }
+
+ // For execution of eval, module or global code.
+ InterpreterFrame* pushExecuteFrame(JSContext* cx, HandleScript script,
+ HandleObject envChain,
+ AbstractFramePtr evalInFrame);
+
+ // Called to invoke a function.
+ InterpreterFrame* pushInvokeFrame(JSContext* cx, const CallArgs& args,
+ MaybeConstruct constructing);
+
+ // The interpreter can push light-weight, "inline" frames without entering a
+ // new InterpreterActivation or recursively calling Interpret.
+ bool pushInlineFrame(JSContext* cx, InterpreterRegs& regs,
+ const CallArgs& args, HandleScript script,
+ MaybeConstruct constructing);
+
+ void popInlineFrame(InterpreterRegs& regs);
+
+ bool resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs,
+ HandleFunction callee, HandleObject envChain);
+
+ inline void purge(JSRuntime* rt);
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return allocator_.sizeOfExcludingThis(mallocSizeOf);
+ }
+};
+
+void TraceInterpreterActivations(JSContext* cx, JSTracer* trc);
+
+/*****************************************************************************/
+
+/** Base class for all function call args. */
+class AnyInvokeArgs : public JS::CallArgs {};
+
+/** Base class for all function construction args. */
+class AnyConstructArgs : public JS::CallArgs {
+ // Only js::Construct (or internal methods that call the qualified CallArgs
+ // versions) should do these things!
+ void setCallee(const Value& v) = delete;
+ void setThis(const Value& v) = delete;
+ MutableHandleValue newTarget() const = delete;
+ MutableHandleValue rval() const = delete;
+};
+
+namespace detail {
+
+/** Function call/construct args of statically-unknown count. */
+template <MaybeConstruct Construct>
+class GenericArgsBase
+ : public std::conditional_t<Construct, AnyConstructArgs, AnyInvokeArgs> {
+ protected:
+ RootedValueVector v_;
+
+ explicit GenericArgsBase(JSContext* cx) : v_(cx) {}
+
+ public:
+ bool init(JSContext* cx, uint64_t argc) {
+ if (argc > ARGS_LENGTH_MAX) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TOO_MANY_ARGUMENTS);
+ return false;
+ }
+
+ // callee, this, arguments[, new.target iff constructing]
+ size_t len = 2 + argc + uint32_t(Construct);
+ MOZ_ASSERT(len > argc); // no overflow
+ if (!v_.resize(len)) {
+ return false;
+ }
+
+ *static_cast<JS::CallArgs*>(this) = CallArgsFromVp(argc, v_.begin());
+ this->constructing_ = Construct;
+ if (Construct) {
+ this->CallArgs::setThis(MagicValue(JS_IS_CONSTRUCTING));
+ }
+ return true;
+ }
+};
+
+/** Function call/construct args of statically-known count. */
+template <MaybeConstruct Construct, size_t N>
+class FixedArgsBase
+ : public std::conditional_t<Construct, AnyConstructArgs, AnyInvokeArgs> {
+ // Add +1 here to avoid noisy warning on gcc when N=0 (0 <= unsigned).
+ static_assert(N + 1 <= ARGS_LENGTH_MAX + 1, "o/~ too many args o/~");
+
+ protected:
+ JS::RootedValueArray<2 + N + uint32_t(Construct)> v_;
+
+ explicit FixedArgsBase(JSContext* cx) : v_(cx) {
+ *static_cast<JS::CallArgs*>(this) = CallArgsFromVp(N, v_.begin());
+ this->constructing_ = Construct;
+ if (Construct) {
+ this->CallArgs::setThis(MagicValue(JS_IS_CONSTRUCTING));
+ }
+ }
+};
+
+} // namespace detail
+
+/** Function call args of statically-unknown count. */
+class InvokeArgs : public detail::GenericArgsBase<NO_CONSTRUCT> {
+ using Base = detail::GenericArgsBase<NO_CONSTRUCT>;
+
+ public:
+ explicit InvokeArgs(JSContext* cx) : Base(cx) {}
+};
+
+/** Function call args of statically-unknown count. */
+class InvokeArgsMaybeIgnoresReturnValue
+ : public detail::GenericArgsBase<NO_CONSTRUCT> {
+ using Base = detail::GenericArgsBase<NO_CONSTRUCT>;
+
+ public:
+ explicit InvokeArgsMaybeIgnoresReturnValue(JSContext* cx) : Base(cx) {}
+
+ bool init(JSContext* cx, unsigned argc, bool ignoresReturnValue) {
+ if (!Base::init(cx, argc)) {
+ return false;
+ }
+ this->ignoresReturnValue_ = ignoresReturnValue;
+ return true;
+ }
+};
+
+/** Function call args of statically-known count. */
+template <size_t N>
+class FixedInvokeArgs : public detail::FixedArgsBase<NO_CONSTRUCT, N> {
+ using Base = detail::FixedArgsBase<NO_CONSTRUCT, N>;
+
+ public:
+ explicit FixedInvokeArgs(JSContext* cx) : Base(cx) {}
+};
+
+/** Function construct args of statically-unknown count. */
+class ConstructArgs : public detail::GenericArgsBase<CONSTRUCT> {
+ using Base = detail::GenericArgsBase<CONSTRUCT>;
+
+ public:
+ explicit ConstructArgs(JSContext* cx) : Base(cx) {}
+};
+
+/** Function call args of statically-known count. */
+template <size_t N>
+class FixedConstructArgs : public detail::FixedArgsBase<CONSTRUCT, N> {
+ using Base = detail::FixedArgsBase<CONSTRUCT, N>;
+
+ public:
+ explicit FixedConstructArgs(JSContext* cx) : Base(cx) {}
+};
+
+template <class Args, class Arraylike>
+inline bool FillArgumentsFromArraylike(JSContext* cx, Args& args,
+ const Arraylike& arraylike) {
+ uint32_t len = arraylike.length();
+ if (!args.init(cx, len)) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < len; i++) {
+ args[i].set(arraylike[i]);
+ }
+
+ return true;
+}
+
+#ifdef ENABLE_PORTABLE_BASELINE_INTERP
+struct PortableBaselineStack {
+ static const size_t DEFAULT_SIZE = 512 * 1024;
+
+ void* base;
+ void* top;
+
+ bool valid() { return base != nullptr; }
+
+ PortableBaselineStack() {
+ base = js_calloc(DEFAULT_SIZE);
+ top = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base) +
+ DEFAULT_SIZE);
+ }
+ ~PortableBaselineStack() { js_free(base); }
+};
+#endif // ENABLE_PORTABLE_BASELINE_INTERP
+
+} // namespace js
+
+namespace mozilla {
+
+template <>
+struct DefaultHasher<js::AbstractFramePtr> {
+ using Lookup = js::AbstractFramePtr;
+
+ static js::HashNumber hash(const Lookup& key) {
+ return mozilla::HashGeneric(key.raw());
+ }
+
+ static bool match(const js::AbstractFramePtr& k, const Lookup& l) {
+ return k == l;
+ }
+};
+
+} // namespace mozilla
+
+#endif // vm_Stack_h