summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Interpreter.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/Interpreter.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 '')
-rw-r--r--js/src/vm/Interpreter.h723
1 files changed, 723 insertions, 0 deletions
diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h
new file mode 100644
index 0000000000..d962e7f344
--- /dev/null
+++ b/js/src/vm/Interpreter.h
@@ -0,0 +1,723 @@
+/* -*- 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_Interpreter_h
+#define vm_Interpreter_h
+
+/*
+ * JS interpreter interface.
+ */
+
+#include "jspubtd.h"
+
+#include "vm/BuiltinObjectKind.h"
+#include "vm/CheckIsObjectKind.h" // CheckIsObjectKind
+#include "vm/Stack.h"
+
+namespace js {
+
+class WithScope;
+class EnvironmentIter;
+class PlainObject;
+
+/*
+ * Convert null/undefined |thisv| into the global lexical's |this| object, and
+ * replace other primitives with boxed versions.
+ */
+extern JSObject* BoxNonStrictThis(JSContext* cx, HandleValue thisv);
+
+extern bool GetFunctionThis(JSContext* cx, AbstractFramePtr frame,
+ MutableHandleValue res);
+
+extern void GetNonSyntacticGlobalThis(JSContext* cx, HandleObject envChain,
+ MutableHandleValue res);
+
+/*
+ * numToSkip is the number of stack values the expression decompiler should skip
+ * before it reaches |v|. If it's -1, the decompiler will search the stack.
+ */
+extern bool ReportIsNotFunction(JSContext* cx, HandleValue v, int numToSkip,
+ MaybeConstruct construct = NO_CONSTRUCT);
+
+/* See ReportIsNotFunction comment for the meaning of numToSkip. */
+extern JSObject* ValueToCallable(JSContext* cx, HandleValue v,
+ int numToSkip = -1,
+ MaybeConstruct construct = NO_CONSTRUCT);
+
+// Reasons why a call could be performed, for passing onto the debugger's
+// `onNativeCall` hook.
+// `onNativeCall` hook disabled all JITs, and this needs to be handled only in
+// the interpreter.
+enum class CallReason {
+ Call,
+ // callContentFunction or constructContentFunction in self-hosted JS.
+ CallContent,
+ // Function.prototype.call or Function.prototype.apply.
+ FunCall,
+ Getter,
+ Setter,
+};
+
+/*
+ * Call or construct arguments that are stored in rooted memory.
+ *
+ * NOTE: Any necessary |GetThisValue| computation must have been performed on
+ * |args.thisv()|, likely by the interpreter when pushing |this| onto the
+ * stack. If you're not sure whether |GetThisValue| processing has been
+ * performed, use |Invoke|.
+ */
+extern bool InternalCallOrConstruct(JSContext* cx, const CallArgs& args,
+ MaybeConstruct construct,
+ CallReason reason = CallReason::Call);
+
+/*
+ * These helpers take care of the infinite-recursion check necessary for
+ * getter/setter calls.
+ */
+extern bool CallGetter(JSContext* cx, HandleValue thisv, HandleValue getter,
+ MutableHandleValue rval);
+
+extern bool CallSetter(JSContext* cx, HandleValue thisv, HandleValue setter,
+ HandleValue rval);
+
+// ES7 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
+// 7.3.12 Call(F, V, argumentsList).
+// All parameters are required, hopefully forcing callers to be careful not to
+// (say) blindly pass callee as |newTarget| when a different value should have
+// been passed. Behavior is unspecified if any element of |args| isn't
+// initialized.
+//
+// |rval| is written to *only* after |fval| and |thisv| have been consumed, so
+// |rval| *may* alias either argument.
+extern bool Call(JSContext* cx, HandleValue fval, HandleValue thisv,
+ const AnyInvokeArgs& args, MutableHandleValue rval,
+ CallReason reason = CallReason::Call);
+
+inline bool Call(JSContext* cx, HandleValue fval, HandleValue thisv,
+ MutableHandleValue rval) {
+ FixedInvokeArgs<0> args(cx);
+ return Call(cx, fval, thisv, args, rval);
+}
+
+inline bool Call(JSContext* cx, HandleValue fval, JSObject* thisObj,
+ MutableHandleValue rval) {
+ RootedValue thisv(cx, ObjectOrNullValue(thisObj));
+ FixedInvokeArgs<0> args(cx);
+ return Call(cx, fval, thisv, args, rval);
+}
+
+inline bool Call(JSContext* cx, HandleValue fval, HandleValue thisv,
+ HandleValue arg0, MutableHandleValue rval) {
+ FixedInvokeArgs<1> args(cx);
+ args[0].set(arg0);
+ return Call(cx, fval, thisv, args, rval);
+}
+
+inline bool Call(JSContext* cx, HandleValue fval, JSObject* thisObj,
+ HandleValue arg0, MutableHandleValue rval) {
+ RootedValue thisv(cx, ObjectOrNullValue(thisObj));
+ FixedInvokeArgs<1> args(cx);
+ args[0].set(arg0);
+ return Call(cx, fval, thisv, args, rval);
+}
+
+inline bool Call(JSContext* cx, HandleValue fval, HandleValue thisv,
+ HandleValue arg0, HandleValue arg1, MutableHandleValue rval) {
+ FixedInvokeArgs<2> args(cx);
+ args[0].set(arg0);
+ args[1].set(arg1);
+ return Call(cx, fval, thisv, args, rval);
+}
+
+inline bool Call(JSContext* cx, HandleValue fval, JSObject* thisObj,
+ HandleValue arg0, HandleValue arg1, MutableHandleValue rval) {
+ RootedValue thisv(cx, ObjectOrNullValue(thisObj));
+ FixedInvokeArgs<2> args(cx);
+ args[0].set(arg0);
+ args[1].set(arg1);
+ return Call(cx, fval, thisv, args, rval);
+}
+
+inline bool Call(JSContext* cx, HandleValue fval, JSObject* thisObj,
+ HandleValue arg0, HandleValue arg1, HandleValue arg2,
+ MutableHandleValue rval) {
+ RootedValue thisv(cx, ObjectOrNullValue(thisObj));
+ FixedInvokeArgs<3> args(cx);
+ args[0].set(arg0);
+ args[1].set(arg1);
+ args[2].set(arg2);
+ return Call(cx, fval, thisv, args, rval);
+}
+
+// Perform the above Call() operation using the given arguments. Similar to
+// ConstructFromStack() below, this handles |!IsCallable(args.calleev())|.
+//
+// This internal operation is intended only for use with arguments known to be
+// on the JS stack, or at least in carefully-rooted memory. The vast majority of
+// potential users should instead use InvokeArgs in concert with Call().
+extern bool CallFromStack(JSContext* cx, const CallArgs& args,
+ CallReason reason = CallReason::Call);
+
+// ES6 7.3.13 Construct(F, argumentsList, newTarget). All parameters are
+// required, hopefully forcing callers to be careful not to (say) blindly pass
+// callee as |newTarget| when a different value should have been passed.
+// Behavior is unspecified if any element of |args| isn't initialized.
+//
+// |rval| is written to *only* after |fval| and |newTarget| have been consumed,
+// so |rval| *may* alias either argument.
+//
+// NOTE: As with the ES6 spec operation, it's the caller's responsibility to
+// ensure |fval| and |newTarget| are both |IsConstructor|.
+extern bool Construct(JSContext* cx, HandleValue fval,
+ const AnyConstructArgs& args, HandleValue newTarget,
+ MutableHandleObject objp);
+
+// Check that in the given |args|, which must be |args.isConstructing()|, that
+// |IsConstructor(args.callee())|. If this is not the case, throw a TypeError.
+// Otherwise, the user must ensure that, additionally,
+// |IsConstructor(args.newTarget())|. (If |args| comes directly from the
+// interpreter stack, as set up by JSOp::New, this comes for free.) Then perform
+// a Construct() operation using |args|.
+//
+// This internal operation is intended only for use with arguments known to be
+// on the JS stack, or at least in carefully-rooted memory. The vast majority of
+// potential users should instead use ConstructArgs in concert with Construct().
+extern bool ConstructFromStack(JSContext* cx, const CallArgs& args,
+ CallReason reason = CallReason::Call);
+
+// Call Construct(fval, args, newTarget), but use the given |thisv| as |this|
+// during construction of |fval|.
+//
+// |rval| is written to *only* after |fval|, |thisv|, and |newTarget| have been
+// consumed, so |rval| *may* alias any of these arguments.
+//
+// This method exists only for very rare cases where a |this| was created
+// caller-side for construction of |fval|: basically only for JITs using
+// |CreateThis|. If that's not you, use Construct()!
+extern bool InternalConstructWithProvidedThis(JSContext* cx, HandleValue fval,
+ HandleValue thisv,
+ const AnyConstructArgs& args,
+ HandleValue newTarget,
+ MutableHandleValue rval);
+
+/*
+ * Executes a script with the given envChain. To support debugging, the
+ * evalInFrame parameter can point to an arbitrary frame in the context's call
+ * stack to simulate executing an eval in that frame.
+ */
+extern bool ExecuteKernel(JSContext* cx, HandleScript script,
+ HandleObject envChainArg,
+ AbstractFramePtr evalInFrame,
+ MutableHandleValue result);
+
+/* Execute a script with the given envChain as global code. */
+extern bool Execute(JSContext* cx, HandleScript script, HandleObject envChain,
+ MutableHandleValue rval);
+
+class ExecuteState;
+class InvokeState;
+
+// RunState is passed to RunScript and RunScript then either passes it to the
+// interpreter or to the JITs. RunState contains all information we need to
+// construct an interpreter or JIT frame.
+class MOZ_RAII RunState {
+ protected:
+ enum Kind { Execute, Invoke };
+ Kind kind_;
+
+ RootedScript script_;
+
+ explicit RunState(JSContext* cx, Kind kind, JSScript* script)
+ : kind_(kind), script_(cx, script) {}
+
+ public:
+ bool isExecute() const { return kind_ == Execute; }
+ bool isInvoke() const { return kind_ == Invoke; }
+
+ ExecuteState* asExecute() const {
+ MOZ_ASSERT(isExecute());
+ return (ExecuteState*)this;
+ }
+ InvokeState* asInvoke() const {
+ MOZ_ASSERT(isInvoke());
+ return (InvokeState*)this;
+ }
+
+ JS::HandleScript script() const { return script_; }
+
+ InterpreterFrame* pushInterpreterFrame(JSContext* cx);
+ inline void setReturnValue(const Value& v);
+
+ private:
+ RunState(const RunState& other) = delete;
+ RunState(const ExecuteState& other) = delete;
+ RunState(const InvokeState& other) = delete;
+ void operator=(const RunState& other) = delete;
+};
+
+// Eval or global script.
+class MOZ_RAII ExecuteState : public RunState {
+ HandleObject envChain_;
+
+ AbstractFramePtr evalInFrame_;
+ MutableHandleValue result_;
+
+ public:
+ ExecuteState(JSContext* cx, JSScript* script, HandleObject envChain,
+ AbstractFramePtr evalInFrame, MutableHandleValue result)
+ : RunState(cx, Execute, script),
+ envChain_(envChain),
+ evalInFrame_(evalInFrame),
+ result_(result) {}
+
+ JSObject* environmentChain() const { return envChain_; }
+ bool isDebuggerEval() const { return !!evalInFrame_; }
+
+ InterpreterFrame* pushInterpreterFrame(JSContext* cx);
+
+ void setReturnValue(const Value& v) { result_.set(v); }
+};
+
+// Data to invoke a function.
+class MOZ_RAII InvokeState final : public RunState {
+ const CallArgs& args_;
+ MaybeConstruct construct_;
+
+ public:
+ InvokeState(JSContext* cx, const CallArgs& args, MaybeConstruct construct)
+ : RunState(cx, Invoke, args.callee().as<JSFunction>().nonLazyScript()),
+ args_(args),
+ construct_(construct) {}
+
+ bool constructing() const { return construct_; }
+ const CallArgs& args() const { return args_; }
+
+ InterpreterFrame* pushInterpreterFrame(JSContext* cx);
+
+ void setReturnValue(const Value& v) { args_.rval().set(v); }
+};
+
+inline void RunState::setReturnValue(const Value& v) {
+ if (isInvoke()) {
+ asInvoke()->setReturnValue(v);
+ } else {
+ asExecute()->setReturnValue(v);
+ }
+}
+
+extern bool RunScript(JSContext* cx, RunState& state);
+extern bool Interpret(JSContext* cx, RunState& state);
+
+extern JSType TypeOfObject(JSObject* obj);
+
+extern JSType TypeOfValue(const Value& v);
+
+// Implementation of
+// https://www.ecma-international.org/ecma-262/6.0/#sec-instanceofoperator
+extern bool InstanceofOperator(JSContext* cx, HandleObject obj, HandleValue v,
+ bool* bp);
+
+// Unwind environment chain and iterator to match the scope corresponding to
+// the given bytecode position.
+extern void UnwindEnvironment(JSContext* cx, EnvironmentIter& ei,
+ jsbytecode* pc);
+
+// Unwind all environments.
+extern void UnwindAllEnvironmentsInFrame(JSContext* cx, EnvironmentIter& ei);
+
+// Compute the pc needed to unwind the scope to the beginning of the block
+// pointed to by the try note.
+extern jsbytecode* UnwindEnvironmentToTryPc(JSScript* script,
+ const TryNote* tn);
+
+namespace detail {
+
+template <class TryNoteFilter>
+class MOZ_STACK_CLASS BaseTryNoteIter {
+ uint32_t pcOffset_;
+ TryNoteFilter isTryNoteValid_;
+
+ const TryNote* tn_;
+ const TryNote* tnEnd_;
+
+ void settle() {
+ for (; tn_ != tnEnd_; ++tn_) {
+ if (!pcInRange()) {
+ continue;
+ }
+
+ /* Try notes cannot be disjoint. That is, we can't have
+ * multiple notes with disjoint pc ranges jumping to the same
+ * catch block. This interacts awkwardly with for-of loops, in
+ * which calls to IteratorClose emitted due to abnormal
+ * completion (break, throw, return) are emitted inline, at the
+ * source location of the break, throw, or return
+ * statement. For example:
+ *
+ * for (x of iter) {
+ * try { return; } catch (e) { }
+ * }
+ *
+ * From the try-note nesting's perspective, the IteratorClose
+ * resulting from |return| is covered by the inner try, when it
+ * should not be. If IteratorClose throws, we don't want to
+ * catch it here.
+ *
+ * To make this work, we use TryNoteKind::ForOfIterClose try-notes,
+ * which cover the range of the abnormal completion. When
+ * looking up trynotes, a for-of iterclose note indicates that
+ * the enclosing for-of has just been terminated. As a result,
+ * trynotes within that for-of are no longer active. When we
+ * see a for-of-iterclose, we skip ahead in the trynotes list
+ * until we see the matching for-of.
+ *
+ * Breaking out of multiple levels of for-of at once is handled
+ * using nested TryNoteKind::ForOfIterClose try-notes. Consider this
+ * code:
+ *
+ * try {
+ * loop: for (i of first) {
+ * <A>
+ * for (j of second) {
+ * <B>
+ * break loop; // <C1/2>
+ * }
+ * }
+ * } catch {...}
+ *
+ * Here is the mapping from various PCs to try-notes that we
+ * want to return:
+ *
+ * A B C1 C2
+ * | | | |
+ * | | | [---|---] ForOfIterClose (outer)
+ * | | [---|------|---] ForOfIterClose (inner)
+ * | [--X-----|------|----] ForOf (inner)
+ * [---X-----------X------|-----] ForOf (outer)
+ * [------------------------X------] TryCatch
+ *
+ * - At A, we find the outer for-of.
+ * - At B, we find the inner for-of.
+ * - At C1, we find one TryNoteKind::ForOfIterClose, skip past one
+ * TryNoteKind::ForOf, and find the outer for-of. (This occurs if an
+ * exception is thrown while closing the inner iterator.)
+ * - At C2, we find two TryNoteKind::ForOfIterClose, skip past two
+ * TryNoteKind::ForOf, and reach the outer try-catch. (This occurs if
+ * an exception is thrown while closing the outer iterator.)
+ */
+ if (tn_->kind() == TryNoteKind::ForOfIterClose) {
+ uint32_t iterCloseDepth = 1;
+ do {
+ ++tn_;
+ MOZ_ASSERT(tn_ != tnEnd_);
+ if (pcInRange()) {
+ if (tn_->kind() == TryNoteKind::ForOfIterClose) {
+ iterCloseDepth++;
+ } else if (tn_->kind() == TryNoteKind::ForOf) {
+ iterCloseDepth--;
+ }
+ }
+ } while (iterCloseDepth > 0);
+
+ // Advance to trynote following the enclosing for-of.
+ continue;
+ }
+
+ /*
+ * We have a note that covers the exception pc but we must check
+ * whether the interpreter has already executed the corresponding
+ * handler. This is possible when the executed bytecode implements
+ * break or return from inside a for-in loop.
+ *
+ * In this case the emitter generates additional [enditer] and [goto]
+ * opcodes to close all outstanding iterators and execute the finally
+ * blocks. If such an [enditer] throws an exception, its pc can still
+ * be inside several nested for-in loops and try-finally statements
+ * even if we have already closed the corresponding iterators and
+ * invoked the finally blocks.
+ *
+ * To address this, we make [enditer] always decrease the stack even
+ * when its implementation throws an exception. Thus already executed
+ * [enditer] and [goto] opcodes will have try notes with the stack
+ * depth exceeding the current one and this condition is what we use to
+ * filter them out.
+ */
+ if (tn_ == tnEnd_ || isTryNoteValid_(tn_)) {
+ return;
+ }
+ }
+ }
+
+ public:
+ BaseTryNoteIter(JSScript* script, jsbytecode* pc,
+ TryNoteFilter isTryNoteValid)
+ : pcOffset_(script->pcToOffset(pc)), isTryNoteValid_(isTryNoteValid) {
+ // NOTE: The Span is a temporary so we can't use begin()/end()
+ // here or the iterator will outlive the span.
+ auto trynotes = script->trynotes();
+ tn_ = trynotes.data();
+ tnEnd_ = tn_ + trynotes.size();
+
+ settle();
+ }
+
+ void operator++() {
+ ++tn_;
+ settle();
+ }
+
+ bool pcInRange() const {
+ // This checks both ends of the range at once
+ // because unsigned integers wrap on underflow.
+ uint32_t offset = pcOffset_;
+ uint32_t start = tn_->start;
+ uint32_t length = tn_->length;
+ return offset - start < length;
+ }
+ bool done() const { return tn_ == tnEnd_; }
+ const TryNote* operator*() const { return tn_; }
+};
+
+} // namespace detail
+
+template <class TryNoteFilter>
+class MOZ_STACK_CLASS TryNoteIter
+ : public detail::BaseTryNoteIter<TryNoteFilter> {
+ using Base = detail::BaseTryNoteIter<TryNoteFilter>;
+
+ // Keep the script alive as long as the iterator is live.
+ RootedScript script_;
+
+ public:
+ TryNoteIter(JSContext* cx, JSScript* script, jsbytecode* pc,
+ TryNoteFilter isTryNoteValid)
+ : Base(script, pc, isTryNoteValid), script_(cx, script) {}
+};
+
+class NoOpTryNoteFilter {
+ public:
+ explicit NoOpTryNoteFilter() = default;
+ bool operator()(const TryNote*) { return true; }
+};
+
+// Iterator over all try notes. Code using this iterator is not allowed to
+// trigger GC to make sure the script stays alive. See TryNoteIter above for the
+// can-GC version.
+class MOZ_STACK_CLASS TryNoteIterAllNoGC
+ : public detail::BaseTryNoteIter<NoOpTryNoteFilter> {
+ using Base = detail::BaseTryNoteIter<NoOpTryNoteFilter>;
+ JS::AutoCheckCannotGC nogc;
+
+ public:
+ TryNoteIterAllNoGC(JSScript* script, jsbytecode* pc)
+ : Base(script, pc, NoOpTryNoteFilter()) {}
+};
+
+bool HandleClosingGeneratorReturn(JSContext* cx, AbstractFramePtr frame,
+ bool ok);
+
+/************************************************************************/
+
+bool ThrowOperation(JSContext* cx, HandleValue v);
+
+bool ThrowWithStackOperation(JSContext* cx, HandleValue v, HandleValue stack);
+
+bool GetPendingExceptionStack(JSContext* cx, MutableHandleValue vp);
+
+bool GetProperty(JSContext* cx, HandleValue value, Handle<PropertyName*> name,
+ MutableHandleValue vp);
+
+JSObject* Lambda(JSContext* cx, HandleFunction fun, HandleObject parent);
+
+bool SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index,
+ HandleValue value, bool strict);
+
+bool SetObjectElementWithReceiver(JSContext* cx, HandleObject obj,
+ HandleValue index, HandleValue value,
+ HandleValue receiver, bool strict);
+
+bool AddValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool SubValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool MulValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool DivValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool ModValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool PowValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool BitNot(JSContext* cx, MutableHandleValue in, MutableHandleValue res);
+
+bool BitXor(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool BitOr(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool BitAnd(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool BitLsh(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool BitRsh(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool UrshValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ MutableHandleValue res);
+
+bool LessThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ bool* res);
+
+bool LessThanOrEqual(JSContext* cx, MutableHandleValue lhs,
+ MutableHandleValue rhs, bool* res);
+
+bool GreaterThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
+ bool* res);
+
+bool GreaterThanOrEqual(JSContext* cx, MutableHandleValue lhs,
+ MutableHandleValue rhs, bool* res);
+
+bool AtomicIsLockFree(JSContext* cx, HandleValue in, int* out);
+
+template <bool strict>
+bool DelPropOperation(JSContext* cx, HandleValue val,
+ Handle<PropertyName*> name, bool* res);
+
+template <bool strict>
+bool DelElemOperation(JSContext* cx, HandleValue val, HandleValue index,
+ bool* res);
+
+JSObject* BindVarOperation(JSContext* cx, JSObject* envChain);
+
+JSObject* ImportMetaOperation(JSContext* cx, HandleScript script);
+
+JSObject* BuiltinObjectOperation(JSContext* cx, BuiltinObjectKind kind);
+
+bool ThrowMsgOperation(JSContext* cx, const unsigned throwMsgKind);
+
+bool GetAndClearException(JSContext* cx, MutableHandleValue res);
+
+bool GetAndClearExceptionAndStack(JSContext* cx, MutableHandleValue res,
+ MutableHandle<SavedFrame*> stack);
+
+bool DeleteNameOperation(JSContext* cx, Handle<PropertyName*> name,
+ HandleObject scopeObj, MutableHandleValue res);
+
+bool ImplicitThisOperation(JSContext* cx, HandleObject scopeObj,
+ Handle<PropertyName*> name, MutableHandleValue res);
+
+bool InitPropGetterSetterOperation(JSContext* cx, jsbytecode* pc,
+ HandleObject obj, Handle<PropertyName*> name,
+ HandleObject val);
+
+unsigned GetInitDataPropAttrs(JSOp op);
+
+bool EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val,
+ Handle<WithScope*> scope);
+
+bool InitElemGetterSetterOperation(JSContext* cx, jsbytecode* pc,
+ HandleObject obj, HandleValue idval,
+ HandleObject val);
+
+bool SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc,
+ HandleValue thisv, HandleValue callee, HandleValue arr,
+ HandleValue newTarget, MutableHandleValue res);
+
+bool OptimizeSpreadCall(JSContext* cx, HandleValue arg,
+ MutableHandleValue result);
+
+bool OptimizeGetIterator(JSContext* cx, HandleValue arg, bool* result);
+
+ArrayObject* ArrayFromArgumentsObject(JSContext* cx,
+ Handle<ArgumentsObject*> args);
+
+JSObject* NewObjectOperation(JSContext* cx, HandleScript script,
+ const jsbytecode* pc);
+
+JSObject* NewPlainObjectBaselineFallback(JSContext* cx,
+ Handle<SharedShape*> shape,
+ gc::AllocKind allocKind,
+ gc::AllocSite* site);
+
+JSObject* NewPlainObjectOptimizedFallback(JSContext* cx,
+ Handle<SharedShape*> shape,
+ gc::AllocKind allocKind,
+ gc::Heap initialHeap);
+
+ArrayObject* NewArrayOperation(JSContext* cx, uint32_t length,
+ NewObjectKind newKind = GenericObject);
+
+// Called from JIT code when inline array allocation fails.
+ArrayObject* NewArrayObjectBaselineFallback(JSContext* cx, uint32_t length,
+ gc::AllocKind allocKind,
+ gc::AllocSite* site);
+ArrayObject* NewArrayObjectOptimizedFallback(JSContext* cx, uint32_t length,
+ gc::AllocKind allocKind,
+ NewObjectKind newKind);
+
+[[nodiscard]] bool GetImportOperation(JSContext* cx, HandleObject envChain,
+ HandleScript script, jsbytecode* pc,
+ MutableHandleValue vp);
+
+void ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber,
+ HandleId id);
+
+void ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber,
+ Handle<PropertyName*> name);
+
+void ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber,
+ HandleScript script, jsbytecode* pc);
+
+void ReportInNotObjectError(JSContext* cx, HandleValue lref, HandleValue rref);
+
+// The parser only reports redeclarations that occurs within a single
+// script. Due to the extensibility of the global lexical scope, we also check
+// for redeclarations during runtime in JSOp::GlobalOrEvalDeclInstantation.
+void ReportRuntimeRedeclaration(JSContext* cx, Handle<PropertyName*> name,
+ const char* redeclKind);
+
+bool ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind);
+
+bool ThrowUninitializedThis(JSContext* cx);
+
+bool ThrowInitializedThis(JSContext* cx);
+
+bool ThrowObjectCoercible(JSContext* cx, HandleValue value);
+
+bool DefaultClassConstructor(JSContext* cx, unsigned argc, Value* vp);
+
+bool Debug_CheckSelfHosted(JSContext* cx, HandleValue funVal);
+
+bool CheckClassHeritageOperation(JSContext* cx, HandleValue heritage);
+
+PlainObject* ObjectWithProtoOperation(JSContext* cx, HandleValue proto);
+
+JSObject* FunWithProtoOperation(JSContext* cx, HandleFunction fun,
+ HandleObject parent, HandleObject proto);
+
+bool SetPropertySuper(JSContext* cx, HandleValue lval, HandleValue receiver,
+ Handle<PropertyName*> name, HandleValue rval,
+ bool strict);
+
+bool SetElementSuper(JSContext* cx, HandleValue lval, HandleValue receiver,
+ HandleValue index, HandleValue rval, bool strict);
+
+bool LoadAliasedDebugVar(JSContext* cx, JSObject* env, jsbytecode* pc,
+ MutableHandleValue result);
+
+bool CloseIterOperation(JSContext* cx, HandleObject iter, CompletionKind kind);
+} /* namespace js */
+
+#endif /* vm_Interpreter_h */