summaryrefslogtreecommitdiffstats
path: root/third_party/rust/jsparagus-stencil/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/jsparagus-stencil/src
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/jsparagus-stencil/src')
-rw-r--r--third_party/rust/jsparagus-stencil/src/bytecode_offset.rs60
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/AsyncFunctionResolveKind.h18
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/BytecodeFormatFlags.h60
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/CheckIsObjectKind.h24
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/CompletionKind.h16
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/FunctionFlags.h320
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/FunctionPrefixKind.h18
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/GeneratorAndAsyncKind.h17
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/GeneratorResumeKind.h18
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/Opcodes.h3600
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/SourceNotes.h428
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/StencilEnums.h340
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/Symbol.h110
-rw-r--r--third_party/rust/jsparagus-stencil/src/copy/ThrowMsgKind.h37
-rw-r--r--third_party/rust/jsparagus-stencil/src/env_coord.rs50
-rw-r--r--third_party/rust/jsparagus-stencil/src/frame_slot.rs21
-rw-r--r--third_party/rust/jsparagus-stencil/src/function.rs222
-rw-r--r--third_party/rust/jsparagus-stencil/src/gcthings.rs77
-rw-r--r--third_party/rust/jsparagus-stencil/src/lib.rs14
-rw-r--r--third_party/rust/jsparagus-stencil/src/opcode.rs586
-rw-r--r--third_party/rust/jsparagus-stencil/src/opcode_info.rs47
-rw-r--r--third_party/rust/jsparagus-stencil/src/regexp.rs54
-rw-r--r--third_party/rust/jsparagus-stencil/src/result.rs35
-rw-r--r--third_party/rust/jsparagus-stencil/src/scope.rs639
-rw-r--r--third_party/rust/jsparagus-stencil/src/scope_notes.rs81
-rw-r--r--third_party/rust/jsparagus-stencil/src/script.rs574
26 files changed, 7466 insertions, 0 deletions
diff --git a/third_party/rust/jsparagus-stencil/src/bytecode_offset.rs b/third_party/rust/jsparagus-stencil/src/bytecode_offset.rs
new file mode 100644
index 0000000000..3583ec6728
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/bytecode_offset.rs
@@ -0,0 +1,60 @@
+/// For tracking bytecode offsets in jumps
+#[derive(Clone, Copy, PartialEq, Debug)]
+#[must_use]
+pub struct BytecodeOffset {
+ pub offset: usize,
+}
+
+impl BytecodeOffset {
+ fn new(offset: usize) -> Self {
+ Self { offset }
+ }
+
+ /// diff_from is useful for finding the offset between two bytecodes. This is usually
+ /// used for jumps and jump_targets.
+ ///
+ /// For a forward jump, self will be a larger number, and start will be smaller
+ /// the output will be a positive number. for a backward jump, the reverse
+ /// will be true, and the number will be negative. So it is important to use this api
+ /// consistently in both cases.
+ ///
+ /// Examples:
+ /// let offset_diff: BytecodeOffsetDiff = forward_jump_target_offset.diff_from(jump)
+ /// let offset_diff: BytecodeOffsetDiff = backward_jump_target_offset.diff_from(jump)
+ pub fn diff_from(self, start: BytecodeOffset) -> BytecodeOffsetDiff {
+ BytecodeOffsetDiff::new(self, start)
+ }
+}
+
+impl From<BytecodeOffset> for usize {
+ fn from(offset: BytecodeOffset) -> usize {
+ offset.offset
+ }
+}
+
+impl From<usize> for BytecodeOffset {
+ fn from(offset: usize) -> BytecodeOffset {
+ BytecodeOffset::new(offset)
+ }
+}
+
+pub struct BytecodeOffsetDiff {
+ diff: i32,
+}
+
+impl BytecodeOffsetDiff {
+ fn new(end: BytecodeOffset, start: BytecodeOffset) -> Self {
+ let diff = (end.offset as i128 - start.offset as i128) as i32;
+ Self { diff }
+ }
+
+ pub fn uninitialized() -> Self {
+ Self { diff: 0i32 }
+ }
+}
+
+impl From<BytecodeOffsetDiff> for i32 {
+ fn from(offset: BytecodeOffsetDiff) -> i32 {
+ offset.diff
+ }
+}
diff --git a/third_party/rust/jsparagus-stencil/src/copy/AsyncFunctionResolveKind.h b/third_party/rust/jsparagus-stencil/src/copy/AsyncFunctionResolveKind.h
new file mode 100644
index 0000000000..75adfcec3c
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/AsyncFunctionResolveKind.h
@@ -0,0 +1,18 @@
+/* -*- 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_AsyncFunctionResolveKind_h
+#define vm_AsyncFunctionResolveKind_h
+
+#include <stdint.h> // uint8_t
+
+namespace js {
+
+enum class AsyncFunctionResolveKind : uint8_t { Fulfill, Reject };
+
+} // namespace js
+
+#endif /* vm_AsyncFunctionResolveKind_h */
diff --git a/third_party/rust/jsparagus-stencil/src/copy/BytecodeFormatFlags.h b/third_party/rust/jsparagus-stencil/src/copy/BytecodeFormatFlags.h
new file mode 100644
index 0000000000..0a0c0efafb
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/BytecodeFormatFlags.h
@@ -0,0 +1,60 @@
+/* -*- 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_BytecodeFormatFlags_h
+#define vm_BytecodeFormatFlags_h
+
+/*
+ * [SMDOC] Bytecode Format flags (JOF_*)
+ */
+enum {
+ JOF_BYTE = 0, /* single bytecode, no immediates */
+ JOF_UINT8 = 1, /* unspecified uint8_t argument */
+ JOF_UINT16 = 2, /* unspecified uint16_t argument */
+ JOF_UINT24 = 3, /* unspecified uint24_t argument */
+ JOF_UINT32 = 4, /* unspecified uint32_t argument */
+ JOF_INT8 = 5, /* int8_t literal */
+ JOF_INT32 = 6, /* int32_t literal */
+ JOF_JUMP = 7, /* int32_t jump offset */
+ JOF_TABLESWITCH = 8, /* table switch */
+ JOF_ENVCOORD = 9, /* embedded ScopeCoordinate immediate */
+ JOF_ARGC = 10, /* uint16_t argument count */
+ JOF_QARG = 11, /* function argument index */
+ JOF_LOCAL = 12, /* var or block-local variable */
+ JOF_RESUMEINDEX = 13, /* yield or await resume index */
+ JOF_DOUBLE = 14, /* inline DoubleValue */
+ JOF_GCTHING = 15, /* uint32_t generic gc-thing index */
+ JOF_ATOM = 16, /* uint32_t constant index */
+ JOF_OBJECT = 17, /* uint32_t object index */
+ JOF_REGEXP = 18, /* uint32_t regexp index */
+ JOF_SCOPE = 19, /* uint32_t scope index */
+ JOF_BIGINT = 20, /* uint32_t index for BigInt value */
+ JOF_ICINDEX = 21, /* uint32_t IC index */
+ JOF_LOOPHEAD = 22, /* JSOp::LoopHead, combines JOF_ICINDEX and JOF_UINT8 */
+ JOF_TWO_UINT8 = 23, /* A pair of unspecified uint8_t arguments */
+ JOF_DEBUGCOORD = 24, /* An embedded ScopeCoordinate immediate that may
+ traverse DebugEnvironmentProxies*/
+ JOF_SHAPE = 25, /* uint32_t shape index */
+ JOF_STRING = 26, /* uint32_t constant index */
+ JOF_TYPEMASK = 0xFF, /* mask for above immediate types */
+
+ JOF_NAME = 1 << 8, /* name operation */
+ JOF_PROP = 2 << 8, /* obj.prop operation */
+ JOF_ELEM = 3 << 8, /* obj[index] operation */
+ JOF_MODEMASK = 0xFF << 8, /* mask for above addressing modes */
+
+ JOF_PROPSET = 1 << 16, /* property/element/name set operation */
+ JOF_PROPINIT = 1 << 17, /* property/element/name init operation */
+ JOF_CHECKSLOPPY = 1 << 18, /* op can only be generated in sloppy mode */
+ JOF_CHECKSTRICT = 1 << 19, /* op can only be generated in strict mode */
+ JOF_INVOKE = 1 << 20, /* any call, construct, or eval instruction */
+ JOF_CONSTRUCT = 1 << 21, /* invoke instruction using [[Construct]] entry */
+ JOF_SPREAD = 1 << 22, /* invoke instruction using spread argument */
+ JOF_GNAME = 1 << 23, /* predicted global name */
+ JOF_IC = 1 << 24, /* baseline may use an IC for this op */
+};
+
+#endif /* vm_BytecodeFormatFlags_h */
diff --git a/third_party/rust/jsparagus-stencil/src/copy/CheckIsObjectKind.h b/third_party/rust/jsparagus-stencil/src/copy/CheckIsObjectKind.h
new file mode 100644
index 0000000000..321870d6ed
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/CheckIsObjectKind.h
@@ -0,0 +1,24 @@
+/* -*- 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_CheckIsObjectKind_h
+#define vm_CheckIsObjectKind_h
+
+#include <stdint.h> // uint8_t
+
+namespace js {
+
+enum class CheckIsObjectKind : uint8_t {
+ IteratorNext,
+ IteratorReturn,
+ IteratorThrow,
+ GetIterator,
+ GetAsyncIterator
+};
+
+} // namespace js
+
+#endif /* vm_CheckIsObjectKind_h */
diff --git a/third_party/rust/jsparagus-stencil/src/copy/CompletionKind.h b/third_party/rust/jsparagus-stencil/src/copy/CompletionKind.h
new file mode 100644
index 0000000000..0f9168ce83
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/CompletionKind.h
@@ -0,0 +1,16 @@
+/* -*- 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_CompletionKind_h
+#define vm_CompletionKind_h
+
+namespace js {
+
+enum class CompletionKind : uint8_t { Normal, Return, Throw };
+
+} // namespace js
+
+#endif // vm_CompletionKind_h
diff --git a/third_party/rust/jsparagus-stencil/src/copy/FunctionFlags.h b/third_party/rust/jsparagus-stencil/src/copy/FunctionFlags.h
new file mode 100644
index 0000000000..a757bf4ac5
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/FunctionFlags.h
@@ -0,0 +1,320 @@
+/* -*- 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_FunctionFlags_h
+#define vm_FunctionFlags_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF
+#include "mozilla/Attributes.h" // MOZ_IMPLICIT
+
+#include <stdint.h> // uint8_t, uint16_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+class JS_PUBLIC_API JSAtom;
+
+namespace js {
+
+class FunctionFlags {
+ public:
+ enum FunctionKind : uint8_t {
+ NormalFunction = 0,
+ Arrow, // ES6 '(args) => body' syntax
+ Method, // ES6 MethodDefinition
+ ClassConstructor,
+ Getter,
+ Setter,
+ AsmJS, // An asm.js module or exported function
+ Wasm, // An exported WebAssembly function
+ FunctionKindLimit
+ };
+
+ enum Flags : uint16_t {
+ // The general kind of a function. This is used to describe characteristics
+ // of functions that do not merit a dedicated flag bit below.
+ FUNCTION_KIND_SHIFT = 0,
+ FUNCTION_KIND_MASK = 0x0007,
+
+ // The AllocKind used was FunctionExtended and extra slots were allocated.
+ // These slots may be used by the engine or the embedding so care must be
+ // taken to avoid conflicts.
+ EXTENDED = 1 << 3,
+
+ // Set if function is a self-hosted builtin or intrinsic. An 'intrinsic'
+ // here means a native function used inside self-hosted code. In general, a
+ // self-hosted function should appear to script as though it were a native
+ // builtin.
+ SELF_HOSTED = 1 << 4,
+
+ // An interpreted function has or may-have bytecode and an environment. Only
+ // one of these flags may be used at a time. As a memory optimization, the
+ // SELFHOSTLAZY flag indicates there is no js::BaseScript at all and we must
+ // clone from the self-hosted realm in order to get bytecode.
+ BASESCRIPT = 1 << 5,
+ SELFHOSTLAZY = 1 << 6,
+
+ // Function may be called as a constructor. This corresponds in the spec as
+ // having a [[Construct]] internal method.
+ CONSTRUCTOR = 1 << 7,
+
+ // (1 << 8) is unused.
+
+ // Function comes from a FunctionExpression, ArrowFunction, or Function()
+ // call (not a FunctionDeclaration or nonstandard function-statement).
+ LAMBDA = 1 << 9,
+
+ // The WASM function has a JIT entry which emulates the
+ // js::BaseScript::jitCodeRaw mechanism.
+ WASM_JIT_ENTRY = 1 << 10,
+
+ // Function had no explicit name, but a name was set by SetFunctionName at
+ // compile time or SetFunctionName at runtime.
+ HAS_INFERRED_NAME = 1 << 11,
+
+ // Function had no explicit name, but a name was guessed for it anyway.
+ HAS_GUESSED_ATOM = 1 << 12,
+
+ // The 'length' or 'name property has been resolved. See fun_resolve.
+ RESOLVED_NAME = 1 << 13,
+ RESOLVED_LENGTH = 1 << 14,
+
+ // This function is kept only for skipping it over during delazification.
+ //
+ // This function is inside arrow function's parameter expression, and
+ // parsed twice, once before finding "=>" token, and once after finding
+ // "=>" and rewinding to the beginning of the parameters.
+ // ScriptStencil is created for both case, and the first one is kept only
+ // for delazification, to make sure delazification sees the same sequence
+ // of inner function to skip over.
+ //
+ // We call the first one "ghost".
+ // It should be kept lazy, and shouldn't be exposed to debugger.
+ GHOST_FUNCTION = 1 << 15,
+
+ // Shifted form of FunctionKinds.
+ NORMAL_KIND = NormalFunction << FUNCTION_KIND_SHIFT,
+ ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT,
+ WASM_KIND = Wasm << FUNCTION_KIND_SHIFT,
+ ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT,
+ METHOD_KIND = Method << FUNCTION_KIND_SHIFT,
+ CLASSCONSTRUCTOR_KIND = ClassConstructor << FUNCTION_KIND_SHIFT,
+ GETTER_KIND = Getter << FUNCTION_KIND_SHIFT,
+ SETTER_KIND = Setter << FUNCTION_KIND_SHIFT,
+
+ // Derived Flags combinations to use when creating functions.
+ NATIVE_FUN = NORMAL_KIND,
+ NATIVE_CTOR = CONSTRUCTOR | NORMAL_KIND,
+ ASMJS_CTOR = CONSTRUCTOR | ASMJS_KIND,
+ ASMJS_LAMBDA_CTOR = CONSTRUCTOR | LAMBDA | ASMJS_KIND,
+ WASM = WASM_KIND,
+ INTERPRETED_NORMAL = BASESCRIPT | CONSTRUCTOR | NORMAL_KIND,
+ INTERPRETED_CLASS_CTOR = BASESCRIPT | CONSTRUCTOR | CLASSCONSTRUCTOR_KIND,
+ INTERPRETED_GENERATOR_OR_ASYNC = BASESCRIPT | NORMAL_KIND,
+ INTERPRETED_LAMBDA = BASESCRIPT | LAMBDA | CONSTRUCTOR | NORMAL_KIND,
+ INTERPRETED_LAMBDA_ARROW = BASESCRIPT | LAMBDA | ARROW_KIND,
+ INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC = BASESCRIPT | LAMBDA | NORMAL_KIND,
+ INTERPRETED_GETTER = BASESCRIPT | GETTER_KIND,
+ INTERPRETED_SETTER = BASESCRIPT | SETTER_KIND,
+ INTERPRETED_METHOD = BASESCRIPT | METHOD_KIND,
+
+ // Flags that XDR ignores. See also: js::BaseScript::MutableFlags.
+ MUTABLE_FLAGS = RESOLVED_NAME | RESOLVED_LENGTH,
+
+ // Flags preserved when cloning a function.
+ STABLE_ACROSS_CLONES =
+ CONSTRUCTOR | LAMBDA | SELF_HOSTED | FUNCTION_KIND_MASK | GHOST_FUNCTION
+ };
+
+ uint16_t flags_;
+
+ public:
+ FunctionFlags() : flags_() {
+ static_assert(sizeof(FunctionFlags) == sizeof(flags_),
+ "No extra members allowed is it'll grow JSFunction");
+ static_assert(offsetof(FunctionFlags, flags_) == 0,
+ "Required for JIT flag access");
+ }
+
+ explicit FunctionFlags(uint16_t flags) : flags_(flags) {}
+ MOZ_IMPLICIT FunctionFlags(Flags f) : flags_(f) {}
+
+ static_assert(((FunctionKindLimit - 1) << FUNCTION_KIND_SHIFT) <=
+ FUNCTION_KIND_MASK,
+ "FunctionKind doesn't fit into flags_");
+
+ uint16_t toRaw() const { return flags_; }
+
+ uint16_t stableAcrossClones() const { return flags_ & STABLE_ACROSS_CLONES; }
+
+ // For flag combinations the type is int.
+ bool hasFlags(uint16_t flags) const { return flags_ & flags; }
+ FunctionFlags& setFlags(uint16_t flags) {
+ flags_ |= flags;
+ return *this;
+ }
+ FunctionFlags& clearFlags(uint16_t flags) {
+ flags_ &= ~flags;
+ return *this;
+ }
+ FunctionFlags& setFlags(uint16_t flags, bool set) {
+ if (set) {
+ setFlags(flags);
+ } else {
+ clearFlags(flags);
+ }
+ return *this;
+ }
+
+ FunctionKind kind() const {
+ return static_cast<FunctionKind>((flags_ & FUNCTION_KIND_MASK) >>
+ FUNCTION_KIND_SHIFT);
+ }
+
+ /* A function can be classified as either native (C++) or interpreted (JS): */
+ bool isInterpreted() const {
+ return hasFlags(BASESCRIPT) || hasFlags(SELFHOSTLAZY);
+ }
+ bool isNativeFun() const { return !isInterpreted(); }
+
+ bool isConstructor() const { return hasFlags(CONSTRUCTOR); }
+
+ bool isNonBuiltinConstructor() const {
+ // Note: keep this in sync with branchIfNotFunctionIsNonBuiltinCtor in
+ // MacroAssembler.cpp.
+ return hasFlags(BASESCRIPT) && hasFlags(CONSTRUCTOR) &&
+ !hasFlags(SELF_HOSTED);
+ }
+
+ /* Possible attributes of a native function: */
+ bool isAsmJSNative() const {
+ MOZ_ASSERT_IF(kind() == AsmJS, isNativeFun());
+ return kind() == AsmJS;
+ }
+ bool isWasm() const {
+ MOZ_ASSERT_IF(kind() == Wasm, isNativeFun());
+ return kind() == Wasm;
+ }
+ bool isWasmWithJitEntry() const {
+ MOZ_ASSERT_IF(hasFlags(WASM_JIT_ENTRY), isWasm());
+ return hasFlags(WASM_JIT_ENTRY);
+ }
+ bool isNativeWithoutJitEntry() const {
+ MOZ_ASSERT_IF(!hasJitEntry(), isNativeFun());
+ return !hasJitEntry();
+ }
+ bool isBuiltinNative() const {
+ return isNativeFun() && !isAsmJSNative() && !isWasm();
+ }
+ bool hasJitEntry() const {
+ return hasBaseScript() || hasSelfHostedLazyScript() || isWasmWithJitEntry();
+ }
+
+ /* Possible attributes of an interpreted function: */
+ bool hasInferredName() const { return hasFlags(HAS_INFERRED_NAME); }
+ bool hasGuessedAtom() const { return hasFlags(HAS_GUESSED_ATOM); }
+ bool isLambda() const { return hasFlags(LAMBDA); }
+
+ bool isNamedLambda(bool hasName) const {
+ return hasName && isLambda() && !hasInferredName() && !hasGuessedAtom();
+ }
+
+ // These methods determine which of the u.scripted.s union arms are active.
+ // For live JSFunctions the pointer values will always be non-null, but due
+ // to partial initialization the GC (and other features that scan the heap
+ // directly) may still return a null pointer.
+ bool hasBaseScript() const { return hasFlags(BASESCRIPT); }
+ bool hasSelfHostedLazyScript() const { return hasFlags(SELFHOSTLAZY); }
+
+ // Arrow functions store their lexical new.target in the first extended slot.
+ bool isArrow() const { return kind() == Arrow; }
+ // Every class-constructor is also a method.
+ bool isMethod() const {
+ return kind() == Method || kind() == ClassConstructor;
+ }
+ bool isClassConstructor() const { return kind() == ClassConstructor; }
+
+ bool isGetter() const { return kind() == Getter; }
+ bool isSetter() const { return kind() == Setter; }
+
+ bool allowSuperProperty() const {
+ return isMethod() || isGetter() || isSetter();
+ }
+
+ bool hasResolvedLength() const { return hasFlags(RESOLVED_LENGTH); }
+ bool hasResolvedName() const { return hasFlags(RESOLVED_NAME); }
+
+ bool isSelfHostedOrIntrinsic() const { return hasFlags(SELF_HOSTED); }
+ bool isSelfHostedBuiltin() const {
+ return isSelfHostedOrIntrinsic() && !isNativeFun();
+ }
+ bool isIntrinsic() const {
+ return isSelfHostedOrIntrinsic() && isNativeFun();
+ }
+
+ FunctionFlags& setKind(FunctionKind kind) {
+ this->flags_ &= ~FUNCTION_KIND_MASK;
+ this->flags_ |= static_cast<uint16_t>(kind) << FUNCTION_KIND_SHIFT;
+ return *this;
+ }
+
+ // Make the function constructible.
+ FunctionFlags& setIsConstructor() {
+ MOZ_ASSERT(!isConstructor());
+ MOZ_ASSERT(isSelfHostedBuiltin());
+ return setFlags(CONSTRUCTOR);
+ }
+
+ FunctionFlags& setIsSelfHostedBuiltin() {
+ MOZ_ASSERT(isInterpreted());
+ MOZ_ASSERT(!isSelfHostedBuiltin());
+ setFlags(SELF_HOSTED);
+ // Self-hosted functions should not be constructable.
+ return clearFlags(CONSTRUCTOR);
+ }
+ FunctionFlags& setIsIntrinsic() {
+ MOZ_ASSERT(isNativeFun());
+ MOZ_ASSERT(!isIntrinsic());
+ return setFlags(SELF_HOSTED);
+ }
+
+ FunctionFlags& setResolvedLength() { return setFlags(RESOLVED_LENGTH); }
+ FunctionFlags& setResolvedName() { return setFlags(RESOLVED_NAME); }
+
+ FunctionFlags& setInferredName() { return setFlags(HAS_INFERRED_NAME); }
+
+ FunctionFlags& setGuessedAtom() { return setFlags(HAS_GUESSED_ATOM); }
+
+ FunctionFlags& setSelfHostedLazy() { return setFlags(SELFHOSTLAZY); }
+ FunctionFlags& clearSelfHostedLazy() { return clearFlags(SELFHOSTLAZY); }
+ FunctionFlags& setBaseScript() { return setFlags(BASESCRIPT); }
+ FunctionFlags& clearBaseScript() { return clearFlags(BASESCRIPT); }
+
+ FunctionFlags& setWasmJitEntry() { return setFlags(WASM_JIT_ENTRY); }
+
+ bool isExtended() const { return hasFlags(EXTENDED); }
+ FunctionFlags& setIsExtended() { return setFlags(EXTENDED); }
+
+ bool isNativeConstructor() const { return hasFlags(NATIVE_CTOR); }
+
+ FunctionFlags& setIsGhost() { return setFlags(GHOST_FUNCTION); }
+ bool isGhost() const { return hasFlags(GHOST_FUNCTION); }
+
+ static uint16_t HasJitEntryFlags(bool isConstructing) {
+ uint16_t flags = BASESCRIPT | SELFHOSTLAZY;
+ if (!isConstructing) {
+ flags |= WASM_JIT_ENTRY;
+ }
+ return flags;
+ }
+
+ static FunctionFlags clearMutableflags(FunctionFlags flags) {
+ return FunctionFlags(flags.toRaw() & ~FunctionFlags::MUTABLE_FLAGS);
+ }
+};
+
+} /* namespace js */
+
+#endif /* vm_FunctionFlags_h */
diff --git a/third_party/rust/jsparagus-stencil/src/copy/FunctionPrefixKind.h b/third_party/rust/jsparagus-stencil/src/copy/FunctionPrefixKind.h
new file mode 100644
index 0000000000..d015091647
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/FunctionPrefixKind.h
@@ -0,0 +1,18 @@
+/* -*- 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_FunctionPrefixKind_h
+#define vm_FunctionPrefixKind_h
+
+#include <stdint.h> // uint8_t
+
+namespace js {
+
+enum class FunctionPrefixKind : uint8_t { None, Get, Set };
+
+} // namespace js
+
+#endif /* vm_FunctionPrefixKind_h */
diff --git a/third_party/rust/jsparagus-stencil/src/copy/GeneratorAndAsyncKind.h b/third_party/rust/jsparagus-stencil/src/copy/GeneratorAndAsyncKind.h
new file mode 100644
index 0000000000..c3761a9dec
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/GeneratorAndAsyncKind.h
@@ -0,0 +1,17 @@
+/* -*- 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_GeneratorAndAsyncKind_h
+#define vm_GeneratorAndAsyncKind_h
+
+namespace js {
+
+enum class GeneratorKind : bool { NotGenerator, Generator };
+enum class FunctionAsyncKind : bool { SyncFunction, AsyncFunction };
+
+} /* namespace js */
+
+#endif /* vm_GeneratorAndAsyncKind_h */
diff --git a/third_party/rust/jsparagus-stencil/src/copy/GeneratorResumeKind.h b/third_party/rust/jsparagus-stencil/src/copy/GeneratorResumeKind.h
new file mode 100644
index 0000000000..7ff6c0a76b
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/GeneratorResumeKind.h
@@ -0,0 +1,18 @@
+/* -*- 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_GeneratorResumeKind_h
+#define vm_GeneratorResumeKind_h
+
+#include <stdint.h> // uint8_t
+
+namespace js {
+
+enum class GeneratorResumeKind : uint8_t { Next, Throw, Return };
+
+} // namespace js
+
+#endif /* vm_GeneratorResumeKind_h */
diff --git a/third_party/rust/jsparagus-stencil/src/copy/Opcodes.h b/third_party/rust/jsparagus-stencil/src/copy/Opcodes.h
new file mode 100644
index 0000000000..abbcc98172
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/Opcodes.h
@@ -0,0 +1,3600 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sw=2 et tw=0 ft=c:
+ *
+ * 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_Opcodes_h
+#define vm_Opcodes_h
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "js/TypeDecls.h"
+
+// clang-format off
+/*
+ * [SMDOC] Bytecode Definitions
+ *
+ * SpiderMonkey bytecode instructions.
+ *
+ * To use this header, define a macro of the form:
+ *
+ * #define MACRO(op, op_snake, token, length, nuses, ndefs, format) ...
+ *
+ * Then `FOR_EACH_OPCODE(MACRO)` invokes `MACRO` for every opcode.
+ *
+ * Field Description
+ * ----- -----------
+ * op UpperCamelCase form of opcode id
+ * op_snake snake_case form of opcode id
+ * token Pretty-printer string, or null if ugly
+ * length Number of bytes including any immediate operands
+ * nuses Number of stack slots consumed by bytecode, -1 if variadic
+ * ndefs Number of stack slots produced by bytecode
+ * format JOF_ flags describing instruction operand layout, etc.
+ *
+ * For more about `format`, see the comments on the `JOF_` constants defined in
+ * BytecodeUtil.h.
+ *
+ *
+ * [SMDOC] Bytecode Invariants
+ *
+ * Creating scripts that do not follow the rules can lead to undefined
+ * behavior. Bytecode has many consumers, not just the interpreter: JITs,
+ * analyses, the debugger. That's why the rules below apply even to code that
+ * can't be reached in ordinary execution (such as code after an infinite loop
+ * or inside an `if (false)` block).
+ *
+ * The `code()` of a script must be a packed (not aligned) sequence of valid
+ * instructions from start to end. Each instruction has a single byte opcode
+ * followed by a number of operand bytes based on the opcode.
+ *
+ * ## Jump instructions
+ *
+ * Operands named `offset`, `forwardOffset`, or `defaultOffset` are jump
+ * offsets, the distance in bytes from the start of the current instruction to
+ * the start of another instruction in the same script. Operands named
+ * `forwardOffset` or `defaultOffset` must be positive.
+ *
+ * Forward jumps must jump to a `JSOp::JumpTarget` instruction. Backward jumps,
+ * indicated by negative offsets, must jump to a `JSOp::LoopHead` instruction.
+ * Jump offsets can't be zero.
+ *
+ * Needless to say, scripts must not contain overlapping instruction sequences
+ * (in the sense of <https://en.wikipedia.org/wiki/Overlapping_gene>).
+ *
+ * A script's `trynotes` and `scopeNotes` impose further constraints. Each try
+ * note and each scope note marks a region of the bytecode where some invariant
+ * holds, or some cleanup behavior is needed--that there's a for-in iterator in
+ * a particular stack slot, for instance, which must be closed on error. All
+ * paths into the span must establish that invariant. In practice, this means
+ * other code never jumps into the span: the only way in is to execute the
+ * bytecode instruction that sets up the invariant (in our example,
+ * `JSOp::Iter`).
+ *
+ * If a script's `trynotes` (see "Try Notes" in JSScript.h) contain a
+ * `JSTRY_CATCH` or `JSTRY_FINALLY` span, there must be a `JSOp::Try`
+ * instruction immediately before the span and a `JSOp::JumpTarget immediately
+ * after it. Instructions must not jump to this `JSOp::JumpTarget`. (The VM puts
+ * us there on exception.) Furthermore, the instruction sequence immediately
+ * following a `JSTRY_CATCH` span must read `JumpTarget; Exception` or, in
+ * non-function scripts, `JumpTarget; Undefined; SetRval; Exception`. (These
+ * instructions run with an exception pending; other instructions aren't
+ * designed to handle that.)
+ *
+ * Unreachable instructions are allowed, but they have to follow all the rules.
+ *
+ * Control must not reach the end of a script. (Currently, the last instruction
+ * is always JSOp::RetRval.)
+ *
+ * ## Other operands
+ *
+ * Operands named `nameIndex` or `atomIndex` (which appear on instructions that
+ * have `JOF_ATOM` in the `format` field) must be valid indexes into
+ * `script->atoms()`.
+ *
+ * Operands named `argc` (`JOF_ARGC`) are argument counts for call
+ * instructions. `argc` must be small enough that the instruction's nuses is <=
+ * the current stack depth (see "Stack depth" below).
+ *
+ * Operands named `argno` (`JOF_QARG`) refer to an argument of the current
+ * function. `argno` must be in the range `0..script->function()->nargs()`.
+ * Instructions with these operands must appear only in function scripts.
+ *
+ * Operands named `localno` (`JOF_LOCAL`) refer to a local variable stored in
+ * the stack frame. `localno` must be in the range `0..script->nfixed()`.
+ *
+ * Operands named `resumeIndex` (`JOF_RESUMEINDEX`) refer to a resume point in
+ * the current script. `resumeIndex` must be a valid index into
+ * `script->resumeOffsets()`.
+ *
+ * Operands named `hops` and `slot` (`JOF_ENVCOORD`) refer a slot in an
+ * `EnvironmentObject`. At run time, they must point to a fixed slot in an
+ * object on the current environment chain. See `EnvironmentCoordinates`.
+ *
+ * Operands with the following names must be valid indexes into
+ * `script->gcthings()`, and the pointer in the vector must point to the right
+ * type of thing:
+ *
+ * - `objectIndex` (`JOF_OBJECT`): `PlainObject*` or `ArrayObject*`
+ * - `baseobjIndex` (`JOF_OBJECT`): `PlainObject*`
+ * - `funcIndex` (`JOF_OBJECT`): `JSFunction*`
+ * - `regexpIndex` (`JOF_REGEXP`): `RegExpObject*`
+ * - `shapeIndex` (`JOF_SHAPE`): `Shape*`
+ * - `scopeIndex` (`JOF_SCOPE`): `Scope*`
+ * - `lexicalScopeIndex` (`JOF_SCOPE`): `LexicalScope*`
+ * - `classBodyScopeIndex` (`JOF_SCOPE`): `ClassBodyScope*`
+ * - `withScopeIndex` (`JOF_SCOPE`): `WithScope*`
+ * - `bigIntIndex` (`JOF_BIGINT`): `BigInt*`
+ *
+ * Operands named `icIndex` (`JOF_ICINDEX`) must be exactly the number of
+ * preceding instructions in the script that have the JOF_IC flag.
+ * (Rationale: Each JOF_IC instruction has a unique entry in
+ * `script->jitScript()->icEntries()`. At run time, in the bytecode
+ * interpreter, we have to find that entry. We could store the IC index as an
+ * operand to each JOF_IC instruction, but it's more memory-efficient to use a
+ * counter and reset the counter to `icIndex` after each jump.)
+ *
+ * ## Stack depth
+ *
+ * Each instruction has a compile-time stack depth, the number of values on the
+ * interpreter stack just before executing the instruction. It isn't explicitly
+ * present in the bytecode itself, but (for reachable instructions, anyway)
+ * it's a function of the bytecode.
+ *
+ * - The first instruction has stack depth 0.
+ *
+ * - Each successor of an instruction X has a stack depth equal to
+ *
+ * X's stack depth - `js::StackUses(X)` + `js::StackDefs(X)`
+ *
+ * except for `JSOp::Case` (below).
+ *
+ * X's "successors" are: the next instruction in the script, if
+ * `js::FlowsIntoNext(op)` is true for X's opcode; one or more
+ * `JSOp::JumpTarget`s elsewhere, if X is a forward jump or
+ * `JSOp::TableSwitch`; and/or a `JSOp::LoopHead` if it's a backward jump.
+ *
+ * - `JSOp::Case` is a special case because its stack behavior is eccentric.
+ * The formula above is correct for the next instruction. The jump target
+ * has a stack depth that is 1 less.
+ *
+ * - The `JSOp::JumpTarget` instruction immediately following a `JSTRY_CATCH`
+ * or `JSTRY_FINALLY` span has the same stack depth as the `JSOp::Try`
+ * instruction that precedes the span.
+ *
+ * Every instruction covered by the `JSTRY_CATCH` or `JSTRY_FINALLY` span
+ * must have a stack depth >= that value, so that error recovery is
+ * guaranteed to find enough values on the stack to resume there.
+ *
+ * - `script->nslots() - script->nfixed()` must be >= the maximum stack
+ * depth of any instruction in `script`. (The stack frame must be big
+ * enough to run the code.)
+ *
+ * `BytecodeParser::parse()` computes stack depths for every reachable
+ * instruction in a script.
+ *
+ * ## Scopes and environments
+ *
+ * As with stack depth, each instruction has a static scope, which is a
+ * compile-time characterization of the eventual run-time environment chain
+ * when that instruction executes. Just as every instruction has a stack budget
+ * (nuses/ndefs), every instruction either pushes a scope, pops a scope, or
+ * neither. The same successor relation applies as above.
+ *
+ * Every scope used in a script is stored in the `JSScript::gcthings()` vector.
+ * They can be accessed using `getScope(index)` if you know what `index` to
+ * pass.
+ *
+ * The scope of every instruction (that's reachable via the successor relation)
+ * is given in two independent ways: by the bytecode itself and by the scope
+ * notes. The two sources must agree.
+ *
+ * ## Further rules
+ *
+ * All reachable instructions must be reachable without taking any backward
+ * edges.
+ *
+ * Instructions with the `JOF_CHECKSLOPPY` flag must not be used in strict mode
+ * code. `JOF_CHECKSTRICT` instructions must not be used in nonstrict code.
+ *
+ * Many instructions have their own additional rules. These are documented on
+ * the various opcodes below (look for the word "must").
+ */
+// clang-format on
+
+// clang-format off
+/*
+ * SpiderMonkey bytecode categorization (as used in generated documentation):
+ *
+ * [Index]
+ * [Constants]
+ * [Compound primitives]
+ * Record literals
+ * Tuple literals
+ * [Expressions]
+ * Unary operators
+ * Binary operators
+ * Conversions
+ * Other expressions
+ * [Objects]
+ * Creating objects
+ * Defining properties
+ * Accessing properties
+ * Super
+ * Enumeration
+ * Iteration
+ * SetPrototype
+ * Array literals
+ * RegExp literals
+ * Built-in objects
+ * [Functions]
+ * Creating functions
+ * Creating constructors
+ * Calls
+ * Generators and async functions
+ * [Control flow]
+ * Jump targets
+ * Jumps
+ * Return
+ * Exceptions
+ * [Variables and scopes]
+ * Initialization
+ * Looking up bindings
+ * Getting binding values
+ * Setting binding values
+ * Entering and leaving environments
+ * Creating and deleting bindings
+ * Function environment setup
+ * [Stack operations]
+ * [Other]
+ */
+// clang-format on
+
+// clang-format off
+#define FOR_EACH_OPCODE(MACRO) \
+ /*
+ * Push `undefined`.
+ *
+ * Category: Constants
+ * Operands:
+ * Stack: => undefined
+ */ \
+ MACRO(Undefined, undefined, "", 1, 0, 1, JOF_BYTE) \
+ /*
+ * Push `null`.
+ *
+ * Category: Constants
+ * Operands:
+ * Stack: => null
+ */ \
+ MACRO(Null, null, "null", 1, 0, 1, JOF_BYTE) \
+ /*
+ * Push a boolean constant.
+ *
+ * Category: Constants
+ * Operands:
+ * Stack: => true/false
+ */ \
+ MACRO(False, false_, "false", 1, 0, 1, JOF_BYTE) \
+ MACRO(True, true_, "true", 1, 0, 1, JOF_BYTE) \
+ /*
+ * Push the `int32_t` immediate operand as an `Int32Value`.
+ *
+ * `JSOp::Zero`, `JSOp::One`, `JSOp::Int8`, `JSOp::Uint16`, and `JSOp::Uint24`
+ * are all compact encodings for `JSOp::Int32`.
+ *
+ * Category: Constants
+ * Operands: int32_t val
+ * Stack: => val
+ */ \
+ MACRO(Int32, int32, NULL, 5, 0, 1, JOF_INT32) \
+ /*
+ * Push the number `0`.
+ *
+ * Category: Constants
+ * Operands:
+ * Stack: => 0
+ */ \
+ MACRO(Zero, zero, "0", 1, 0, 1, JOF_BYTE) \
+ /*
+ * Push the number `1`.
+ *
+ * Category: Constants
+ * Operands:
+ * Stack: => 1
+ */ \
+ MACRO(One, one, "1", 1, 0, 1, JOF_BYTE) \
+ /*
+ * Push the `int8_t` immediate operand as an `Int32Value`.
+ *
+ * Category: Constants
+ * Operands: int8_t val
+ * Stack: => val
+ */ \
+ MACRO(Int8, int8, NULL, 2, 0, 1, JOF_INT8) \
+ /*
+ * Push the `uint16_t` immediate operand as an `Int32Value`.
+ *
+ * Category: Constants
+ * Operands: uint16_t val
+ * Stack: => val
+ */ \
+ MACRO(Uint16, uint16, NULL, 3, 0, 1, JOF_UINT16) \
+ /*
+ * Push the `uint24_t` immediate operand as an `Int32Value`.
+ *
+ * Category: Constants
+ * Operands: uint24_t val
+ * Stack: => val
+ */ \
+ MACRO(Uint24, uint24, NULL, 4, 0, 1, JOF_UINT24) \
+ /*
+ * Push the 64-bit floating-point immediate operand as a `DoubleValue`.
+ *
+ * If the operand is a NaN, it must be the canonical NaN (see
+ * `JS::detail::CanonicalizeNaN`).
+ *
+ * Category: Constants
+ * Operands: double val
+ * Stack: => val
+ */ \
+ MACRO(Double, double_, NULL, 9, 0, 1, JOF_DOUBLE) \
+ /*
+ * Push the BigInt constant `script->getBigInt(bigIntIndex)`.
+ *
+ * Category: Constants
+ * Operands: uint32_t bigIntIndex
+ * Stack: => bigint
+ */ \
+ MACRO(BigInt, big_int, NULL, 5, 0, 1, JOF_BIGINT) \
+ /*
+ * Push the string constant `script->getAtom(atomIndex)`.
+ *
+ * Category: Constants
+ * Operands: uint32_t atomIndex
+ * Stack: => string
+ */ \
+ MACRO(String, string, NULL, 5, 0, 1, JOF_STRING) \
+ /*
+ * Push a well-known symbol.
+ *
+ * `symbol` must be in range for `JS::SymbolCode`.
+ *
+ * Category: Constants
+ * Operands: uint8_t symbol (the JS::SymbolCode of the symbol to use)
+ * Stack: => symbol
+ */ \
+ MACRO(Symbol, symbol, NULL, 2, 0, 1, JOF_UINT8) \
+ /*
+ * Pop the top value on the stack, discard it, and push `undefined`.
+ *
+ * Implements: [The `void` operator][1], step 3.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-void-operator
+ *
+ * Category: Expressions
+ * Type: Unary operators
+ * Operands:
+ * Stack: val => undefined
+ */ \
+ MACRO(Void, void_, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * [The `typeof` operator][1].
+ *
+ * Infallible. The result is always a string that depends on the [type][2]
+ * of `val`.
+ *
+ * `JSOp::Typeof` and `JSOp::TypeofExpr` are the same except
+ * that--amazingly--`JSOp::Typeof` affects the behavior of an immediately
+ * *preceding* `JSOp::GetName` or `JSOp::GetGName` instruction! This is how
+ * we implement [`typeof`][1] step 2, making `typeof nonExistingVariable`
+ * return `"undefined"` instead of throwing a ReferenceError.
+ *
+ * In a global scope:
+ *
+ * - `typeof x` compiles to `GetGName "x"; Typeof`.
+ * - `typeof (0, x)` compiles to `GetGName "x"; TypeofExpr`.
+ *
+ * Emitting the same bytecode for these two expressions would be a bug.
+ * Per spec, the latter throws a ReferenceError if `x` doesn't exist.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-typeof-operator
+ * [2]: https://tc39.es/ecma262/#sec-ecmascript-language-types
+ *
+ * Category: Expressions
+ * Type: Unary operators
+ * Operands:
+ * Stack: val => (typeof val)
+ */ \
+ MACRO(Typeof, typeof_, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
+ MACRO(TypeofExpr, typeof_expr, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [The unary `+` operator][1].
+ *
+ * `+val` doesn't do any actual math. It just calls [ToNumber][2](val).
+ *
+ * The conversion can call `.toString()`/`.valueOf()` methods and can
+ * throw. The result on success is always a Number. (Per spec, unary `-`
+ * supports BigInts, but unary `+` does not.)
+ *
+ * [1]: https://tc39.es/ecma262/#sec-unary-plus-operator
+ * [2]: https://tc39.es/ecma262/#sec-tonumber
+ *
+ * Category: Expressions
+ * Type: Unary operators
+ * Operands:
+ * Stack: val => (+val)
+ */ \
+ MACRO(Pos, pos, "+ ", 1, 1, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [The unary `-` operator][1].
+ *
+ * Convert `val` to a numeric value, then push `-val`. The conversion can
+ * call `.toString()`/`.valueOf()` methods and can throw. The result on
+ * success is always numeric.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-unary-minus-operator
+ *
+ * Category: Expressions
+ * Type: Unary operators
+ * Operands:
+ * Stack: val => (-val)
+ */ \
+ MACRO(Neg, neg, "- ", 1, 1, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [The bitwise NOT operator][1] (`~`).
+ *
+ * `val` is converted to an integer, then bitwise negated. The conversion
+ * can call `.toString()`/`.valueOf()` methods and can throw. The result on
+ * success is always an Int32 or BigInt value.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-bitwise-not-operator
+ *
+ * Category: Expressions
+ * Type: Unary operators
+ * Operands:
+ * Stack: val => (~val)
+ */ \
+ MACRO(BitNot, bit_not, "~", 1, 1, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [The logical NOT operator][1] (`!`).
+ *
+ * `val` is first converted with [ToBoolean][2], then logically
+ * negated. The result is always a boolean value. This does not call
+ * user-defined methods and can't throw.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-logical-not-operator
+ * [2]: https://tc39.es/ecma262/#sec-toboolean
+ *
+ * Category: Expressions
+ * Type: Unary operators
+ * Operands:
+ * Stack: val => (!val)
+ */ \
+ MACRO(Not, not_, "!", 1, 1, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [Binary bitwise operations][1] (`|`, `^`, `&`).
+ *
+ * The arguments are converted to integers first. The conversion can call
+ * `.toString()`/`.valueOf()` methods and can throw. The result on success
+ * is always an Int32 or BigInt Value.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-binary-bitwise-operators
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: lval, rval => (lval OP rval)
+ */ \
+ MACRO(BitOr, bit_or, "|", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(BitXor, bit_xor, "^", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(BitAnd, bit_and, "&", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Loose equality operators (`==` and `!=`).
+ *
+ * Pop two values, compare them, and push the boolean result. The
+ * comparison may perform conversions that call `.toString()`/`.valueOf()`
+ * methods and can throw.
+ *
+ * Implements: [Abstract Equality Comparison][1].
+ *
+ * [1]: https://tc39.es/ecma262/#sec-abstract-equality-comparison
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: lval, rval => (lval OP rval)
+ */ \
+ MACRO(Eq, eq, "==", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(Ne, ne, "!=", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Strict equality operators (`===` and `!==`).
+ *
+ * Pop two values, check whether they're equal, and push the boolean
+ * result. This does not call user-defined methods and can't throw
+ * (except possibly due to OOM while flattening a string).
+ *
+ * Implements: [Strict Equality Comparison][1].
+ *
+ * [1]: https://tc39.es/ecma262/#sec-strict-equality-comparison
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: lval, rval => (lval OP rval)
+ */ \
+ MACRO(StrictEq, strict_eq, "===", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(StrictNe, strict_ne, "!==", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Relative operators (`<`, `>`, `<=`, `>=`).
+ *
+ * Pop two values, compare them, and push the boolean result. The
+ * comparison may perform conversions that call `.toString()`/`.valueOf()`
+ * methods and can throw.
+ *
+ * Implements: [Relational Operators: Evaluation][1].
+ *
+ * [1]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: lval, rval => (lval OP rval)
+ */ \
+ MACRO(Lt, lt, "<", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(Gt, gt, ">", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(Le, le, "<=", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(Ge, ge, ">=", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [The `instanceof` operator][1].
+ *
+ * This throws a `TypeError` if `target` is not an object. It calls
+ * `target[Symbol.hasInstance](value)` if the method exists. On success,
+ * the result is always a boolean value.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-instanceofoperator
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: value, target => (value instanceof target)
+ */ \
+ MACRO(Instanceof, instanceof, "instanceof", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [The `in` operator][1].
+ *
+ * Push `true` if `obj` has a property with the key `id`. Otherwise push `false`.
+ *
+ * This throws a `TypeError` if `obj` is not an object. This can fire
+ * proxy hooks and can throw. On success, the result is always a boolean
+ * value.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: id, obj => (id in obj)
+ */ \
+ MACRO(In, in_, "in", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [Bitwise shift operators][1] (`<<`, `>>`, `>>>`).
+ *
+ * Pop two values, convert them to integers, perform a bitwise shift, and
+ * push the result.
+ *
+ * Conversion can call `.toString()`/`.valueOf()` methods and can throw.
+ * The result on success is always an Int32 or BigInt Value.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-bitwise-shift-operators
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: lval, rval => (lval OP rval)
+ */ \
+ MACRO(Lsh, lsh, "<<", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(Rsh, rsh, ">>", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(Ursh, ursh, ">>>", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [The binary `+` operator][1].
+ *
+ * Pop two values, convert them to primitive values, add them, and push the
+ * result. If both values are numeric, add them; if either is a
+ * string, do string concatenation instead.
+ *
+ * The conversion can call `.toString()`/`.valueOf()` methods and can throw.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-addition-operator-plus-runtime-semantics-evaluation
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: lval, rval => (lval + rval)
+ */ \
+ MACRO(Add, add, "+", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [The binary `-` operator][1].
+ *
+ * Pop two values, convert them to numeric values, subtract the top value
+ * from the other one, and push the result.
+ *
+ * The conversion can call `.toString()`/`.valueOf()` methods and can
+ * throw. On success, the result is always numeric.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-subtraction-operator-minus-runtime-semantics-evaluation
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: lval, rval => (lval - rval)
+ */ \
+ MACRO(Sub, sub, "-", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Add or subtract 1.
+ *
+ * `val` must already be a numeric value, such as the result of
+ * `JSOp::ToNumeric`.
+ *
+ * Implements: [The `++` and `--` operators][1], step 3 of each algorithm.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-postfix-increment-operator
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: val => (val +/- 1)
+ */ \
+ MACRO(Inc, inc, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
+ MACRO(Dec, dec, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [The multiplicative operators][1] (`*`, `/`, `%`).
+ *
+ * Pop two values, convert them to numeric values, do math, and push the
+ * result.
+ *
+ * The conversion can call `.toString()`/`.valueOf()` methods and can
+ * throw. On success, the result is always numeric.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-multiplicative-operators-runtime-semantics-evaluation
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: lval, rval => (lval OP rval)
+ */ \
+ MACRO(Mul, mul, "*", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(Div, div, "/", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ MACRO(Mod, mod, "%", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * [The exponentiation operator][1] (`**`).
+ *
+ * Pop two values, convert them to numeric values, do exponentiation, and
+ * push the result. The top value is the exponent.
+ *
+ * The conversion can call `.toString()`/`.valueOf()` methods and can
+ * throw. This throws a RangeError if both values are BigInts and the
+ * exponent is negative.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-exp-operator
+ *
+ * Category: Expressions
+ * Type: Binary operators
+ * Operands:
+ * Stack: lval, rval => (lval ** rval)
+ */ \
+ MACRO(Pow, pow, "**", 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Convert a value to a property key.
+ *
+ * Implements: [ToPropertyKey][1], except that if the result would be the
+ * string representation of some integer in the range 0..2^31, we push the
+ * corresponding Int32 value instead. This is because the spec insists that
+ * array indices are strings, whereas for us they are integers.
+ *
+ * This is used for code like `++obj[index]`, which must do both a
+ * `JSOp::GetElem` and a `JSOp::SetElem` with the same property key. Both
+ * instructions would convert `index` to a property key for us, but the
+ * spec says to convert it only once.
+ *
+ * The conversion can call `.toString()`/`.valueOf()` methods and can
+ * throw.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-topropertykey
+ *
+ * Category: Expressions
+ * Type: Conversions
+ * Operands:
+ * Stack: propertyNameValue => propertyKey
+ */ \
+ MACRO(ToPropertyKey, to_property_key, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Convert a value to a numeric value (a Number or BigInt).
+ *
+ * Implements: [ToNumeric][1](val).
+ *
+ * Note: This is used to implement [`++` and `--`][2]. Surprisingly, it's
+ * not possible to get the right behavior using `JSOp::Add` and `JSOp::Sub`
+ * alone. For one thing, `JSOp::Add` sometimes does string concatenation,
+ * while `++` always does numeric addition. More fundamentally, the result
+ * of evaluating `x--` is ToNumeric(old value of `x`), a value that the
+ * sequence `GetLocal "x"; One; Sub; SetLocal "x"` does not give us.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-tonumeric
+ * [2]: https://tc39.es/ecma262/#sec-postfix-increment-operator
+ *
+ * Category: Expressions
+ * Type: Conversions
+ * Operands:
+ * Stack: val => ToNumeric(val)
+ */ \
+ MACRO(ToNumeric, to_numeric, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Convert a value to a string.
+ *
+ * Implements: [ToString][1](val).
+ *
+ * Note: This is used in code for template literals, like `${x}${y}`. Each
+ * substituted value must be converted using ToString. `JSOp::Add` by itself
+ * would do a slightly wrong kind of conversion (hint="number" rather than
+ * hint="string").
+ *
+ * [1]: https://tc39.es/ecma262/#sec-tostring
+ *
+ * Category: Expressions
+ * Type: Conversions
+ * Stack: val => ToString(val)
+ */ \
+ MACRO(ToString, to_string, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * Test whether the value on top of the stack is `NullValue` or
+ * `UndefinedValue` and push the boolean result.
+ *
+ * Category: Expressions
+ * Type: Other expressions
+ * Operands:
+ * Stack: val => val, IsNullOrUndefined(val)
+ */ \
+ MACRO(IsNullOrUndefined, is_null_or_undefined, NULL, 1, 1, 2, JOF_BYTE) \
+ /*
+ * Push the global `this` value. Not to be confused with the `globalThis`
+ * property on the global.
+ *
+ * This must be used only in scopes where `this` refers to the global
+ * `this`.
+ *
+ * Category: Expressions
+ * Type: Other expressions
+ * Operands:
+ * Stack: => this
+ */ \
+ MACRO(GlobalThis, global_this, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Push the global `this` value for non-syntactic scope. Not to be confused
+ * with the `globalThis` property on the global.
+ *
+ * This must be used only in scopes where `this` refers to the global
+ * `this`.
+ *
+ * Category: Expressions
+ * Type: Other expressions
+ * Operands:
+ * Stack: => this
+ */ \
+ MACRO(NonSyntacticGlobalThis, non_syntactic_global_this, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Push the value of `new.target`.
+ *
+ * The result is a constructor or `undefined`.
+ *
+ * This must be used only in non-arrow function scripts.
+ *
+ * Implements: [GetNewTarget][1].
+ *
+ * [1]: https://tc39.es/ecma262/#sec-getnewtarget
+ *
+ * Category: Expressions
+ * Type: Other expressions
+ * Operands:
+ * Stack: => new.target
+ */ \
+ MACRO(NewTarget, new_target, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Dynamic import of the module specified by the string value on the top of
+ * the stack.
+ *
+ * Implements: [Import Calls][1].
+ *
+ * [1]: https://tc39.es/ecma262/#sec-import-calls
+ *
+ * Category: Expressions
+ * Type: Other expressions
+ * Operands:
+ * Stack: moduleId, options => promise
+ */ \
+ MACRO(DynamicImport, dynamic_import, NULL, 1, 2, 1, JOF_BYTE) \
+ /*
+ * Push the `import.meta` object.
+ *
+ * This must be used only in module code.
+ *
+ * Category: Expressions
+ * Type: Other expressions
+ * Operands:
+ * Stack: => import.meta
+ */ \
+ MACRO(ImportMeta, import_meta, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Create and push a new object with no properties.
+ *
+ * Category: Objects
+ * Type: Creating objects
+ * Operands:
+ * Stack: => obj
+ */ \
+ MACRO(NewInit, new_init, NULL, 1, 0, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Create and push a new object of a predetermined shape.
+ *
+ * The new object has the shape `script->getShape(shapeIndex)`.
+ * Subsequent `InitProp` instructions must fill in all slots of the new
+ * object before it is used in any other way.
+ *
+ * Category: Objects
+ * Type: Creating objects
+ * Operands: uint32_t shapeIndex
+ * Stack: => obj
+ */ \
+ MACRO(NewObject, new_object, NULL, 5, 0, 1, JOF_SHAPE|JOF_IC) \
+ /*
+ * Push a preconstructed object.
+ *
+ * Going one step further than `JSOp::NewObject`, this instruction doesn't
+ * just reuse the shape--it actually pushes the preconstructed object
+ * `script->getObject(objectIndex)` right onto the stack. The object must
+ * be a singleton `PlainObject` or `ArrayObject`.
+ *
+ * The spec requires that an *ObjectLiteral* or *ArrayLiteral* creates a
+ * new object every time it's evaluated, so this instruction must not be
+ * used anywhere it might be executed more than once.
+ *
+ * This may only be used in non-function run-once scripts. Care also must
+ * be taken to not emit in loops or other constructs where it could run
+ * more than once.
+ *
+ * Category: Objects
+ * Type: Creating objects
+ * Operands: uint32_t objectIndex
+ * Stack: => obj
+ */ \
+ MACRO(Object, object, NULL, 5, 0, 1, JOF_OBJECT) \
+ /*
+ * Create and push a new ordinary object with the provided [[Prototype]].
+ *
+ * This is used to create the `.prototype` object for derived classes.
+ *
+ * Category: Objects
+ * Type: Creating objects
+ * Operands:
+ * Stack: proto => obj
+ */ \
+ MACRO(ObjWithProto, obj_with_proto, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * Define a data property on an object.
+ *
+ * `obj` must be an object.
+ *
+ * Implements: [CreateDataPropertyOrThrow][1] as used in
+ * [PropertyDefinitionEvaluation][2] of regular and shorthand
+ * *PropertyDefinition*s.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-createdatapropertyorthrow
+ * [2]: https://tc39.es/ecma262/#sec-object-initializer-runtime-semantics-propertydefinitionevaluation
+ *
+ * Category: Objects
+ * Type: Defining properties
+ * Operands: uint32_t nameIndex
+ * Stack: obj, val => obj
+ */ \
+ MACRO(InitProp, init_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_IC) \
+ /*
+ * Like `JSOp::InitProp`, but define a non-enumerable property.
+ *
+ * This is used to define class methods.
+ *
+ * Implements: [PropertyDefinitionEvaluation][1] for methods, steps 3 and
+ * 4, when *enumerable* is false.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation
+ *
+ * Category: Objects
+ * Type: Defining properties
+ * Operands: uint32_t nameIndex
+ * Stack: obj, val => obj
+ */ \
+ MACRO(InitHiddenProp, init_hidden_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_IC) \
+ /*
+ * Like `JSOp::InitProp`, but define a non-enumerable, non-writable,
+ * non-configurable property.
+ *
+ * This is used to define the `.prototype` property on classes.
+ *
+ * Implements: [MakeConstructor][1], step 8, when *writablePrototype* is
+ * false.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-makeconstructor
+ *
+ * Category: Objects
+ * Type: Defining properties
+ * Operands: uint32_t nameIndex
+ * Stack: obj, val => obj
+ */ \
+ MACRO(InitLockedProp, init_locked_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_IC) \
+ /*
+ * Define a data property on `obj` with property key `id` and value `val`.
+ *
+ * `obj` must be an object.
+ *
+ * Implements: [CreateDataPropertyOrThrow][1]. This instruction is used for
+ * object literals like `{0: val}` and `{[id]: val}`, and methods like
+ * `*[Symbol.iterator]() {}`.
+ *
+ * `JSOp::InitHiddenElem` is the same but defines a non-enumerable property,
+ * for class methods.
+ * `JSOp::InitLockedElem` is the same but defines a non-enumerable, non-writable, non-configurable property,
+ * for private class methods.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-createdatapropertyorthrow
+ *
+ * Category: Objects
+ * Type: Defining properties
+ * Operands:
+ * Stack: obj, id, val => obj
+ */ \
+ MACRO(InitElem, init_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC) \
+ MACRO(InitHiddenElem, init_hidden_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC) \
+ MACRO(InitLockedElem, init_locked_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC) \
+ /*
+ * Define an accessor property on `obj` with the given `getter`.
+ * `nameIndex` gives the property name.
+ *
+ * `obj` must be an object and `getter` must be a function.
+ *
+ * `JSOp::InitHiddenPropGetter` is the same but defines a non-enumerable
+ * property, for getters in classes.
+ *
+ * Category: Objects
+ * Type: Defining properties
+ * Operands: uint32_t nameIndex
+ * Stack: obj, getter => obj
+ */ \
+ MACRO(InitPropGetter, init_prop_getter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT) \
+ MACRO(InitHiddenPropGetter, init_hidden_prop_getter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT) \
+ /*
+ * Define an accessor property on `obj` with property key `id` and the given `getter`.
+ *
+ * This is used to implement getters like `get [id]() {}` or `get 0() {}`.
+ *
+ * `obj` must be an object and `getter` must be a function.
+ *
+ * `JSOp::InitHiddenElemGetter` is the same but defines a non-enumerable
+ * property, for getters in classes.
+ *
+ * Category: Objects
+ * Type: Defining properties
+ * Operands:
+ * Stack: obj, id, getter => obj
+ */ \
+ MACRO(InitElemGetter, init_elem_getter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT) \
+ MACRO(InitHiddenElemGetter, init_hidden_elem_getter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT) \
+ /*
+ * Define an accessor property on `obj` with the given `setter`.
+ *
+ * This is used to implement ordinary setters like `set foo(v) {}`.
+ *
+ * `obj` must be an object and `setter` must be a function.
+ *
+ * `JSOp::InitHiddenPropSetter` is the same but defines a non-enumerable
+ * property, for setters in classes.
+ *
+ * Category: Objects
+ * Type: Defining properties
+ * Operands: uint32_t nameIndex
+ * Stack: obj, setter => obj
+ */ \
+ MACRO(InitPropSetter, init_prop_setter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT) \
+ MACRO(InitHiddenPropSetter, init_hidden_prop_setter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT) \
+ /*
+ * Define an accesssor property on `obj` with property key `id` and the
+ * given `setter`.
+ *
+ * This is used to implement setters with computed property keys or numeric
+ * keys.
+ *
+ * `JSOp::InitHiddenElemSetter` is the same but defines a non-enumerable
+ * property, for setters in classes.
+ *
+ * Category: Objects
+ * Type: Defining properties
+ * Operands:
+ * Stack: obj, id, setter => obj
+ */ \
+ MACRO(InitElemSetter, init_elem_setter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT) \
+ MACRO(InitHiddenElemSetter, init_hidden_elem_setter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT) \
+ /*
+ * Get the value of the property `obj.name`. This can call getters and
+ * proxy traps.
+ *
+ * Implements: [GetV][1], [GetValue][2] step 5.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-getv
+ * [2]: https://tc39.es/ecma262/#sec-getvalue
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands: uint32_t nameIndex
+ * Stack: obj => obj[name]
+ */ \
+ MACRO(GetProp, get_prop, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_IC) \
+ /*
+ * Get the value of the property `obj[key]`.
+ *
+ * Implements: [GetV][1], [GetValue][2] step 5.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-getv
+ * [2]: https://tc39.es/ecma262/#sec-getvalue
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands:
+ * Stack: obj, key => obj[key]
+ */ \
+ MACRO(GetElem, get_elem, NULL, 1, 2, 1, JOF_BYTE|JOF_ELEM|JOF_IC) \
+ /*
+ * Non-strict assignment to a property, `obj.name = val`.
+ *
+ * This throws a TypeError if `obj` is null or undefined. If it's a
+ * primitive value, the property is set on ToObject(`obj`), typically with
+ * no effect.
+ *
+ * Implements: [PutValue][1] step 6 for non-strict code.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-putvalue
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands: uint32_t nameIndex
+ * Stack: obj, val => val
+ */ \
+ MACRO(SetProp, set_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSLOPPY|JOF_IC) \
+ /*
+ * Like `JSOp::SetProp`, but for strict mode code. Throw a TypeError if
+ * `obj[key]` exists but is non-writable, if it's an accessor property with
+ * no setter, or if `obj` is a primitive value.
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands: uint32_t nameIndex
+ * Stack: obj, val => val
+ */ \
+ MACRO(StrictSetProp, strict_set_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSTRICT|JOF_IC) \
+ /*
+ * Non-strict assignment to a property, `obj[key] = val`.
+ *
+ * Implements: [PutValue][1] step 6 for non-strict code.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-putvalue
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands:
+ * Stack: obj, key, val => val
+ */ \
+ MACRO(SetElem, set_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSLOPPY|JOF_IC) \
+ /*
+ * Like `JSOp::SetElem`, but for strict mode code. Throw a TypeError if
+ * `obj[key]` exists but is non-writable, if it's an accessor property with
+ * no setter, or if `obj` is a primitive value.
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands:
+ * Stack: obj, key, val => val
+ */ \
+ MACRO(StrictSetElem, strict_set_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSTRICT|JOF_IC) \
+ /*
+ * Delete a property from `obj`. Push true on success, false if the
+ * property existed but could not be deleted. This implements `delete
+ * obj.name` in non-strict code.
+ *
+ * Throws if `obj` is null or undefined. Can call proxy traps.
+ *
+ * Implements: [`delete obj.propname`][1] step 5 in non-strict code.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-delete-operator-runtime-semantics-evaluation
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands: uint32_t nameIndex
+ * Stack: obj => succeeded
+ */ \
+ MACRO(DelProp, del_prop, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_CHECKSLOPPY) \
+ /*
+ * Like `JSOp::DelProp`, but for strict mode code. Push `true` on success,
+ * else throw a TypeError.
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands: uint32_t nameIndex
+ * Stack: obj => succeeded
+ */ \
+ MACRO(StrictDelProp, strict_del_prop, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_CHECKSTRICT) \
+ /*
+ * Delete the property `obj[key]` and push `true` on success, `false`
+ * if the property existed but could not be deleted.
+ *
+ * This throws if `obj` is null or undefined. Can call proxy traps.
+ *
+ * Implements: [`delete obj[key]`][1] step 5 in non-strict code.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-delete-operator-runtime-semantics-evaluation
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands:
+ * Stack: obj, key => succeeded
+ */ \
+ MACRO(DelElem, del_elem, NULL, 1, 2, 1, JOF_BYTE|JOF_ELEM|JOF_CHECKSLOPPY) \
+ /*
+ * Like `JSOp::DelElem, but for strict mode code. Push `true` on success,
+ * else throw a TypeError.
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands:
+ * Stack: obj, key => succeeded
+ */ \
+ MACRO(StrictDelElem, strict_del_elem, NULL, 1, 2, 1, JOF_BYTE|JOF_ELEM|JOF_CHECKSTRICT) \
+ /*
+ * Push true if `obj` has an own property `id`.
+ *
+ * Note that `obj` is the top value, like `JSOp::In`.
+ *
+ * This opcode is not used for normal JS. Self-hosted code uses it by
+ * calling the intrinsic `hasOwn(id, obj)`. For example,
+ * `Object.prototype.hasOwnProperty` is implemented this way (see
+ * js/src/builtin/Object.js).
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands:
+ * Stack: id, obj => (obj.hasOwnProperty(id))
+ */ \
+ MACRO(HasOwn, has_own, NULL, 1, 2, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Push a bool representing the presence of private field id on obj.
+ * May throw, depending on the ThrowCondition.
+ *
+ * Two arguments:
+ * - throwCondition: One of the ThrowConditions defined in
+ * ThrowMsgKind.h. Determines why (or if) this op will throw.
+ * - msgKind: One of the ThrowMsgKinds defined in ThrowMsgKind.h, which
+ * maps to one of the messages in js.msg. Note: It's not possible to
+ * pass arguments to the message at the moment.
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands: ThrowCondition throwCondition, ThrowMsgKind msgKind
+ * Stack: obj, key => obj, key, (obj.hasOwnProperty(id))
+ */ \
+ MACRO(CheckPrivateField, check_private_field, NULL, 3, 2, 3, JOF_TWO_UINT8|JOF_CHECKSTRICT|JOF_IC) \
+ /*
+ * Push a new private name.
+ *
+ * Category: Objects
+ * Type: Accessing properties
+ * Operands: uint32_t nameIndex
+ * Stack: => private_name
+ */ \
+ MACRO(NewPrivateName, new_private_name, NULL, 5, 0, 1, JOF_ATOM) \
+ /*
+ * Push the SuperBase of the method `callee`. The SuperBase is
+ * `callee.[[HomeObject]].[[GetPrototypeOf]]()`, the object where `super`
+ * property lookups should begin.
+ *
+ * `callee` must be a function that has a HomeObject that's an object,
+ * typically produced by `JSOp::Callee` or `JSOp::EnvCallee`.
+ *
+ * Implements: [GetSuperBase][1], except that instead of the environment,
+ * the argument supplies the callee.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-getsuperbase
+ *
+ * Category: Objects
+ * Type: Super
+ * Operands:
+ * Stack: callee => superBase
+ */ \
+ MACRO(SuperBase, super_base, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * Get the value of `receiver.name`, starting the property search at `obj`.
+ * In spec terms, `obj.[[Get]](name, receiver)`.
+ *
+ * Implements: [GetValue][1] for references created by [`super.name`][2].
+ * The `receiver` is `this` and `obj` is the SuperBase of the enclosing
+ * method.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-getvalue
+ * [2]: https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
+ *
+ * Category: Objects
+ * Type: Super
+ * Operands: uint32_t nameIndex
+ * Stack: receiver, obj => super.name
+ */ \
+ MACRO(GetPropSuper, get_prop_super, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_IC) \
+ /*
+ * Get the value of `receiver[key]`, starting the property search at `obj`.
+ * In spec terms, `obj.[[Get]](key, receiver)`.
+ *
+ * Implements: [GetValue][1] for references created by [`super[key]`][2]
+ * (where the `receiver` is `this` and `obj` is the SuperBase of the enclosing
+ * method); [`Reflect.get(obj, key, receiver)`][3].
+ *
+ * [1]: https://tc39.es/ecma262/#sec-getvalue
+ * [2]: https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
+ * [3]: https://tc39.es/ecma262/#sec-reflect.get
+ *
+ * Category: Objects
+ * Type: Super
+ * Operands:
+ * Stack: receiver, key, obj => super[key]
+ */ \
+ MACRO(GetElemSuper, get_elem_super, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_IC) \
+ /*
+ * Assign `val` to `receiver.name`, starting the search for an existing
+ * property at `obj`. In spec terms, `obj.[[Set]](name, val, receiver)`.
+ *
+ * Implements: [PutValue][1] for references created by [`super.name`][2] in
+ * non-strict code. The `receiver` is `this` and `obj` is the SuperBase of
+ * the enclosing method.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-putvalue
+ * [2]: https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
+ *
+ * Category: Objects
+ * Type: Super
+ * Operands: uint32_t nameIndex
+ * Stack: receiver, obj, val => val
+ */ \
+ MACRO(SetPropSuper, set_prop_super, NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSLOPPY) \
+ /*
+ * Like `JSOp::SetPropSuper`, but for strict mode code.
+ *
+ * Category: Objects
+ * Type: Super
+ * Operands: uint32_t nameIndex
+ * Stack: receiver, obj, val => val
+ */ \
+ MACRO(StrictSetPropSuper, strict_set_prop_super, NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSTRICT) \
+ /*
+ * Assign `val` to `receiver[key]`, strating the search for an existing
+ * property at `obj`. In spec terms, `obj.[[Set]](key, val, receiver)`.
+ *
+ * Implements: [PutValue][1] for references created by [`super[key]`][2] in
+ * non-strict code. The `receiver` is `this` and `obj` is the SuperBase of
+ * the enclosing method.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-putvalue
+ * [2]: https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
+ *
+ * Category: Objects
+ * Type: Super
+ * Operands:
+ * Stack: receiver, key, obj, val => val
+ */ \
+ MACRO(SetElemSuper, set_elem_super, NULL, 1, 4, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSLOPPY) \
+ /*
+ * Like `JSOp::SetElemSuper`, but for strict mode code.
+ *
+ * Category: Objects
+ * Type: Super
+ * Operands:
+ * Stack: receiver, key, obj, val => val
+ */ \
+ MACRO(StrictSetElemSuper, strict_set_elem_super, NULL, 1, 4, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSTRICT) \
+ /*
+ * Set up a for-in loop by pushing a `PropertyIteratorObject` over the
+ * enumerable properties of `val`.
+ *
+ * Implements: [ForIn/OfHeadEvaluation][1] step 6,
+ * [EnumerateObjectProperties][1]. (The spec refers to an "Iterator object"
+ * with a `next` method, but notes that it "is never directly accessible"
+ * to scripts. The object we use for this has no public methods.)
+ *
+ * If `val` is null or undefined, this pushes an empty iterator.
+ *
+ * The `iter` object pushed by this instruction must not be used or removed
+ * from the stack except by `JSOp::MoreIter` and `JSOp::EndIter`, or by error
+ * handling.
+ *
+ * The script's `JSScript::trynotes()` must mark the body of the `for-in`
+ * loop, i.e. exactly those instructions that begin executing with `iter`
+ * on the stack, starting with the next instruction (always
+ * `JSOp::LoopHead`). Code must not jump into or out of this region: control
+ * can enter only by executing `JSOp::Iter` and can exit only by executing a
+ * `JSOp::EndIter` or by exception unwinding. (A `JSOp::EndIter` is always
+ * emitted at the end of the loop, and extra copies are emitted on "exit
+ * slides", where a `break`, `continue`, or `return` statement exits the
+ * loop.)
+ *
+ * Typically a single try note entry marks the contiguous chunk of bytecode
+ * from the instruction after `JSOp::Iter` to `JSOp::EndIter` (inclusive);
+ * but if that range contains any instructions on exit slides, after a
+ * `JSOp::EndIter`, then those must be correctly noted as *outside* the
+ * loop.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofheadevaluation-tdznames-expr-iterationkind
+ * [2]: https://tc39.es/ecma262/#sec-enumerate-object-properties
+ *
+ * Category: Objects
+ * Type: Enumeration
+ * Operands:
+ * Stack: val => iter
+ */ \
+ MACRO(Iter, iter, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Get the next property name for a for-in loop.
+ *
+ * `iter` must be a `PropertyIteratorObject` produced by `JSOp::Iter`. This
+ * pushes the property name for the next loop iteration, or
+ * `MagicValue(JS_NO_ITER_VALUE)` if there are no more enumerable
+ * properties to iterate over. The magic value must be used only by
+ * `JSOp::IsNoIter` and `JSOp::EndIter`.
+ *
+ * Category: Objects
+ * Type: Enumeration
+ * Operands:
+ * Stack: iter => iter, name
+ */ \
+ MACRO(MoreIter, more_iter, NULL, 1, 1, 2, JOF_BYTE) \
+ /*
+ * Test whether the value on top of the stack is
+ * `MagicValue(JS_NO_ITER_VALUE)` and push the boolean result.
+ *
+ * Category: Objects
+ * Type: Enumeration
+ * Operands:
+ * Stack: val => val, done
+ */ \
+ MACRO(IsNoIter, is_no_iter, NULL, 1, 1, 2, JOF_BYTE) \
+ /*
+ * Exit a for-in loop, closing the iterator.
+ *
+ * `iter` must be a `PropertyIteratorObject` pushed by `JSOp::Iter`.
+ *
+ * Category: Objects
+ * Type: Enumeration
+ * Operands:
+ * Stack: iter, iterval =>
+ */ \
+ MACRO(EndIter, end_iter, NULL, 1, 2, 0, JOF_BYTE) \
+ /*
+ * If the iterator object on top of the stack has a `return` method,
+ * call that method. If the method exists but does not return an object,
+ * and `kind` is not `CompletionKind::Throw`, throw a TypeError. (If
+ * `kind` is `Throw`, the error we are already throwing takes precedence.)
+ *
+ * `iter` must be an object conforming to the [Iterator][1] interface.
+ *
+ * Implements: [IteratorClose][2]
+ *
+ * [1]: https://tc39.es/ecma262/#sec-iterator-interface
+ * [2]: https://tc39.es/ecma262/#sec-iteratorclose
+ * Category: Objects
+ * Type: Iteration
+ * Operands: CompletionKind kind
+ * Stack: iter =>
+ */ \
+ MACRO(CloseIter, close_iter, NULL, 2, 1, 0, JOF_UINT8|JOF_IC) \
+ /*
+ * Check that the top value on the stack is an object, and throw a
+ * TypeError if not. `kind` is used only to generate an appropriate error
+ * message.
+ *
+ * Implements: [GetIterator][1] step 5, [IteratorNext][2] step 3. Both
+ * operations call a JS method which scripts can define however they want,
+ * so they check afterwards that the method returned an object.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-getiterator
+ * [2]: https://tc39.es/ecma262/#sec-iteratornext
+ *
+ * Category: Objects
+ * Type: Iteration
+ * Operands: CheckIsObjectKind kind
+ * Stack: result => result
+ */ \
+ MACRO(CheckIsObj, check_is_obj, NULL, 2, 1, 1, JOF_UINT8) \
+ /*
+ * Throw a TypeError if `val` is `null` or `undefined`.
+ *
+ * Implements: [RequireObjectCoercible][1]. But most instructions that
+ * require an object will perform this check for us, so of the dozens of
+ * calls to RequireObjectCoercible in the spec, we need this instruction
+ * only for [destructuring assignment][2] and [initialization][3].
+ *
+ * [1]: https://tc39.es/ecma262/#sec-requireobjectcoercible
+ * [2]: https://tc39.es/ecma262/#sec-runtime-semantics-destructuringassignmentevaluation
+ * [3]: https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-bindinginitialization
+ *
+ * Category: Objects
+ * Type: Iteration
+ * Operands:
+ * Stack: val => val
+ */ \
+ MACRO(CheckObjCoercible, check_obj_coercible, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * Create and push an async iterator wrapping the sync iterator `iter`.
+ * `next` should be `iter`'s `.next` method.
+ *
+ * Implements: [CreateAsyncToSyncIterator][1]. The spec says this operation
+ * takes one argument, but that argument is a Record with two relevant
+ * fields, `[[Iterator]]` and `[[NextMethod]]`.
+ *
+ * Used for `for await` loops.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-createasyncfromsynciterator
+ *
+ * Category: Objects
+ * Type: Iteration
+ * Operands:
+ * Stack: iter, next => asynciter
+ */ \
+ MACRO(ToAsyncIter, to_async_iter, NULL, 1, 2, 1, JOF_BYTE) \
+ /*
+ * Set the prototype of `obj`.
+ *
+ * `obj` must be an object.
+ *
+ * Implements: [B.3.1 __proto__ Property Names in Object Initializers][1], step 7.a.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-__proto__-property-names-in-object-initializers
+ *
+ * Category: Objects
+ * Type: SetPrototype
+ * Operands:
+ * Stack: obj, protoVal => obj
+ */ \
+ MACRO(MutateProto, mutate_proto, NULL, 1, 2, 1, JOF_BYTE) \
+ /*
+ * Create and push a new Array object with the given `length`,
+ * preallocating enough memory to hold that many elements.
+ *
+ * Category: Objects
+ * Type: Array literals
+ * Operands: uint32_t length
+ * Stack: => array
+ */ \
+ MACRO(NewArray, new_array, NULL, 5, 0, 1, JOF_UINT32|JOF_IC) \
+ /*
+ * Initialize an array element `array[index]` with value `val`.
+ *
+ * `val` may be `MagicValue(JS_ELEMENTS_HOLE)` pushed by `JSOp::Hole`.
+ *
+ * This never calls setters or proxy traps.
+ *
+ * `array` must be an Array object created by `JSOp::NewArray` with length >
+ * `index`, and never used except by `JSOp::InitElemArray`.
+ *
+ * Implements: [ArrayAccumulation][1], the third algorithm, step 4, in the
+ * common case where *nextIndex* is known.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation
+ *
+ * Category: Objects
+ * Type: Array literals
+ * Operands: uint32_t index
+ * Stack: array, val => array
+ */ \
+ MACRO(InitElemArray, init_elem_array, NULL, 5, 2, 1, JOF_UINT32|JOF_ELEM|JOF_PROPINIT) \
+ /*
+ * Initialize an array element `array[index++]` with value `val`.
+ *
+ * `val` may be `MagicValue(JS_ELEMENTS_HOLE)` pushed by `JSOp::Hole`. If it
+ * is, no element is defined, but the array length and the stack value
+ * `index` are still incremented.
+ *
+ * This never calls setters or proxy traps.
+ *
+ * `array` must be an Array object created by `JSOp::NewArray` and never used
+ * except by `JSOp::InitElemArray` and `JSOp::InitElemInc`.
+ *
+ * `index` must be an integer, `0 <= index <= INT32_MAX`. If `index` is
+ * `INT32_MAX`, this throws a RangeError. Unlike `InitElemArray`, it is not
+ * necessary that the `array` length > `index`.
+ *
+ * This instruction is used when an array literal contains a
+ * *SpreadElement*. In `[a, ...b, c]`, `InitElemArray 0` is used to put
+ * `a` into the array, but `InitElemInc` is used for the elements of `b`
+ * and for `c`.
+ *
+ * Implements: Several steps in [ArrayAccumulation][1] that call
+ * CreateDataProperty, set the array length, and/or increment *nextIndex*.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation
+ *
+ * Category: Objects
+ * Type: Array literals
+ * Operands:
+ * Stack: array, index, val => array, (index + 1)
+ */ \
+ MACRO(InitElemInc, init_elem_inc, NULL, 1, 3, 2, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC) \
+ /*
+ * Push `MagicValue(JS_ELEMENTS_HOLE)`, representing an *Elision* in an
+ * array literal (like the missing property 0 in the array `[, 1]`).
+ *
+ * This magic value must be used only by `JSOp::InitElemArray` or
+ * `JSOp::InitElemInc`.
+ *
+ * Category: Objects
+ * Type: Array literals
+ * Operands:
+ * Stack: => hole
+ */ \
+ MACRO(Hole, hole, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Clone and push a new RegExp object.
+ *
+ * Implements: [Evaluation for *RegularExpressionLiteral*][1].
+ *
+ * [1]: https://tc39.es/ecma262/#sec-regular-expression-literals-runtime-semantics-evaluation
+ *
+ * Category: Objects
+ * Type: RegExp literals
+ * Operands: uint32_t regexpIndex
+ * Stack: => regexp
+ */ \
+ MACRO(RegExp, reg_exp, NULL, 5, 0, 1, JOF_REGEXP) \
+ /*
+ * Initialize a new record, preallocating `length` memory slots. `length` can still grow
+ * if needed, for example when using the spread operator.
+ *
+ * Implements: [RecordLiteral Evaluation][1] step 1.
+ *
+ * [1]: https://tc39.es/proposal-record-tuple/#sec-record-initializer-runtime-semantics-evaluation
+ *
+ * Category: Compound primitives
+ * Type: Record literals
+ * Operands: uint32_t length
+ * Stack: => rval
+ */ \
+ IF_RECORD_TUPLE(MACRO(InitRecord, init_record, NULL, 5, 0, 1, JOF_UINT32)) \
+ /*
+ * Add the last element in the stack to the preceding tuple.
+ *
+ * Implements: [AddPropertyIntoRecordEntriesList][1].
+ *
+ * [1]: https://tc39.es/proposal-record-tuple/#sec-addpropertyintorecordentrieslist
+ *
+ * Category: Compound primitives
+ * Type: Record literals
+ * Operands:
+ * Stack: record, key, value => record
+ */ \
+ IF_RECORD_TUPLE(MACRO(AddRecordProperty, add_record_property, NULL, 1, 3, 1, JOF_BYTE)) \
+ /*
+ * Add the last element in the stack to the preceding tuple.
+ *
+ * Implements: [RecordPropertyDefinitionEvaluation][1] for
+ * RecordPropertyDefinition : ... AssignmentExpression
+ *
+ * [1]: https://tc39.es/proposal-record-tuple/#sec-addpropertyintorecordentrieslist
+ *
+ * Category: Compound primitives
+ * Type: Record literals
+ * Operands:
+ * Stack: record, value => record
+ */ \
+ IF_RECORD_TUPLE(MACRO(AddRecordSpread, add_record_spread, NULL, 1, 2, 1, JOF_BYTE)) \
+ /*
+ * Mark a record as "initialized", going from "write-only" mode to
+ * "read-only" mode.
+ *
+ * Category: Compound primitives
+ * Type: Record literals
+ * Operands:
+ * Stack: record => record
+ */ \
+ IF_RECORD_TUPLE(MACRO(FinishRecord, finish_record, NULL, 1, 1, 1, JOF_BYTE)) \
+ /*
+ * Initialize a new tuple, preallocating `length` memory slots. `length` can still grow
+ * if needed, for example when using the spread operator.
+ *
+ * Implements: [TupleLiteral Evaluation][1] step 1.
+ *
+ * [1]: https://tc39.es/proposal-record-tuple/#sec-tuple-initializer-runtime-semantics-evaluation
+ *
+ * Category: Compound primitives
+ * Type: Tuple literals
+ * Operands: uint32_t length
+ * Stack: => rval
+ */ \
+ IF_RECORD_TUPLE(MACRO(InitTuple, init_tuple, NULL, 5, 0, 1, JOF_UINT32)) \
+ /*
+ * Add the last element in the stack to the preceding tuple.
+ *
+ * Implements: [AddValueToTupleSequenceList][1].
+ *
+ * [1]: https://tc39.es/proposal-record-tuple/#sec-addvaluetotuplesequencelist
+ *
+ * Category: Compound primitives
+ * Type: Tuple literals
+ * Operands:
+ * Stack: tuple, element => tuple
+ */ \
+ IF_RECORD_TUPLE(MACRO(AddTupleElement, add_tuple_element, NULL, 1, 2, 1, JOF_BYTE)) \
+ /*
+ * Mark a tuple as "initialized", going from "write-only" mode to
+ * "read-only" mode.
+ *
+ * Category: Compound primitives
+ * Type: Tuple literals
+ * Operands:
+ * Stack: tuple => tuple
+ */ \
+ IF_RECORD_TUPLE(MACRO(FinishTuple, finish_tuple, NULL, 1, 1, 1, JOF_BYTE)) \
+ /*
+ * Push a new function object.
+ *
+ * The new function inherits the current environment chain.
+ *
+ * Used to create most JS functions. Notable exceptions are derived or
+ * default class constructors.
+ *
+ * Implements: [InstantiateFunctionObject][1], [Evaluation for
+ * *FunctionExpression*][2], and so on.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-function-definitions-runtime-semantics-instantiatefunctionobject
+ * [2]: https://tc39.es/ecma262/#sec-function-definitions-runtime-semantics-evaluation
+ *
+ * Category: Functions
+ * Type: Creating functions
+ * Operands: uint32_t funcIndex
+ * Stack: => fn
+ */ \
+ MACRO(Lambda, lambda, NULL, 5, 0, 1, JOF_OBJECT) \
+ /*
+ * Set the name of a function.
+ *
+ * `fun` must be a function object. `name` must be a string, Int32 value,
+ * or symbol (like the result of `JSOp::ToId`).
+ *
+ * Implements: [SetFunctionName][1], used e.g. to name methods with
+ * computed property names.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-setfunctionname
+ *
+ * Category: Functions
+ * Type: Creating functions
+ * Operands: FunctionPrefixKind prefixKind
+ * Stack: fun, name => fun
+ */ \
+ MACRO(SetFunName, set_fun_name, NULL, 2, 2, 1, JOF_UINT8) \
+ /*
+ * Initialize the home object for functions with super bindings.
+ *
+ * `fun` must be a method, getter, or setter, so that it has a
+ * [[HomeObject]] slot. `homeObject` must be a plain object or (for static
+ * methods) a constructor.
+ *
+ * Category: Functions
+ * Type: Creating functions
+ * Operands:
+ * Stack: fun, homeObject => fun
+ */ \
+ MACRO(InitHomeObject, init_home_object, NULL, 1, 2, 1, JOF_BYTE) \
+ /*
+ * Throw a TypeError if `baseClass` isn't either `null` or a constructor.
+ *
+ * Implements: [ClassDefinitionEvaluation][1] step 6.f.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
+ *
+ * Category: Functions
+ * Type: Creating constructors
+ * Operands:
+ * Stack: baseClass => baseClass
+ */ \
+ MACRO(CheckClassHeritage, check_class_heritage, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * Like `JSOp::Lambda`, but using `proto` as the new function's
+ * `[[Prototype]]` (or `%FunctionPrototype%` if `proto` is `null`).
+ *
+ * `proto` must be either a constructor or `null`. We use
+ * `JSOp::CheckClassHeritage` to check.
+ *
+ * This is used to create the constructor for a derived class.
+ *
+ * Implements: [ClassDefinitionEvaluation][1] steps 6.e.ii, 6.g.iii, and
+ * 12 for derived classes.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
+ *
+ * Category: Functions
+ * Type: Creating constructors
+ * Operands: uint32_t funcIndex
+ * Stack: proto => obj
+ */ \
+ MACRO(FunWithProto, fun_with_proto, NULL, 5, 1, 1, JOF_OBJECT) \
+ /*
+ * Pushes the current global's %BuiltinObject%.
+ *
+ * `kind` must be a valid `BuiltinObjectKind` (and must not be
+ * `BuiltinObjectKind::None`).
+ *
+ * Category: Objects
+ * Type: Built-in objects
+ * Operands: uint8_t kind
+ * Stack: => %BuiltinObject%
+ */ \
+ MACRO(BuiltinObject, builtin_object, NULL, 2, 0, 1, JOF_UINT8) \
+ /*
+ * Invoke `callee` with `this` and `args`, and push the return value. Throw
+ * a TypeError if `callee` isn't a function.
+ *
+ * `JSOp::CallContent` is for `callContentFunction` in self-hosted JS, and
+ * this is for handling it differently in debugger's `onNativeCall` hook.
+ * `onNativeCall` hook disables all JITs, and `JSOp::CallContent` is
+ * treated exactly the same as `JSOP::Call` in JIT.
+ *
+ * `JSOp::CallIter` is used for implicit calls to @@iterator methods, to
+ * ensure error messages are formatted with `JSMSG_NOT_ITERABLE` ("x is not
+ * iterable") rather than `JSMSG_NOT_FUNCTION` ("x[Symbol.iterator] is not
+ * a function"). The `argc` operand must be 0 for this variation.
+ *
+ * `JSOp::CallContentIter` is `JSOp::CallContent` variant of
+ * `JSOp::CallIter`.
+ *
+ * `JSOp::CallIgnoresRv` hints to the VM that the return value is ignored.
+ * This allows alternate faster implementations to be used that avoid
+ * unnecesary allocations.
+ *
+ * Implements: [EvaluateCall][1] steps 4, 5, and 7.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-evaluatecall
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands: uint16_t argc
+ * Stack: callee, this, args[0], ..., args[argc-1] => rval
+ */ \
+ MACRO(Call, call, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC) \
+ MACRO(CallContent, call_content, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC) \
+ MACRO(CallIter, call_iter, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC) \
+ MACRO(CallContentIter, call_content_iter, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC) \
+ MACRO(CallIgnoresRv, call_ignores_rv, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC) \
+ /*
+ * Like `JSOp::Call`, but the arguments are provided in an array rather than
+ * a span of stack slots. Used to implement spread-call syntax:
+ * `f(...args)`.
+ *
+ * `args` must be an Array object containing the actual arguments. The
+ * array must be packed (dense and free of holes; see IsPackedArray).
+ * This can be ensured by creating the array with `JSOp::NewArray` and
+ * populating it using `JSOp::InitElemArray`.
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands:
+ * Stack: callee, this, args => rval
+ */ \
+ MACRO(SpreadCall, spread_call, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_SPREAD|JOF_IC) \
+ /*
+ * Push an array object that can be passed directly as the `args` argument
+ * to `JSOp::SpreadCall`. If the operation can't be optimized, push
+ * `undefined` instead.
+ *
+ * This instruction and the branch around the iterator loop are emitted
+ * only when `iterable` is the sole argument in a call, as in `f(...arr)`.
+ *
+ * See `js::OptimizeSpreadCall`.
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands:
+ * Stack: iterable => array_or_undefined
+ */ \
+ MACRO(OptimizeSpreadCall, optimize_spread_call, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Perform a direct eval in the current environment if `callee` is the
+ * builtin `eval` function, otherwise follow same behaviour as `JSOp::Call`.
+ *
+ * All direct evals use one of the JSOp::*Eval instructions here and these
+ * opcodes are only used when the syntactic conditions for a direct eval
+ * are met. If the builtin `eval` function is called though other means, it
+ * becomes an indirect eval.
+ *
+ * Direct eval causes all bindings in *enclosing* non-global scopes to be
+ * marked "aliased". The optimization that puts bindings in stack slots has
+ * to prove that the bindings won't need to be captured by closures or
+ * accessed using `JSOp::{Get,Bind,Set,Del}Name` instructions. Direct eval
+ * makes that analysis impossible.
+ *
+ * The instruction immediately following any `JSOp::*Eval` instruction must
+ * be `JSOp::Lineno`.
+ *
+ * Implements: [Function Call Evaluation][1], steps 5-7 and 9, when the
+ * syntactic critera for direct eval in step 6 are all met.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-function-calls-runtime-semantics-evaluation
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands: uint16_t argc
+ * Stack: callee, this, args[0], ..., args[argc-1] => rval
+ */ \
+ MACRO(Eval, eval, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CHECKSLOPPY|JOF_IC) \
+ /*
+ * Spread-call variant of `JSOp::Eval`.
+ *
+ * See `JSOp::SpreadCall` for restrictions on `args`.
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands:
+ * Stack: callee, this, args => rval
+ */ \
+ MACRO(SpreadEval, spread_eval, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_SPREAD|JOF_CHECKSLOPPY|JOF_IC) \
+ /*
+ * Like `JSOp::Eval`, but for strict mode code.
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands: uint16_t argc
+ * Stack: evalFn, this, args[0], ..., args[argc-1] => rval
+ */ \
+ MACRO(StrictEval, strict_eval, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CHECKSTRICT|JOF_IC) \
+ /*
+ * Spread-call variant of `JSOp::StrictEval`.
+ *
+ * See `JSOp::SpreadCall` for restrictions on `args`.
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands:
+ * Stack: callee, this, args => rval
+ */ \
+ MACRO(StrictSpreadEval, strict_spread_eval, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_SPREAD|JOF_CHECKSTRICT|JOF_IC) \
+ /*
+ * Push the implicit `this` value for an unqualified function call, like
+ * `foo()`. `nameIndex` gives the name of the function we're calling.
+ *
+ * The result is always `undefined` except when the name refers to a `with`
+ * binding. For example, in `with (date) { getFullYear(); }`, the
+ * implicit `this` passed to `getFullYear` is `date`, not `undefined`.
+ *
+ * This walks the run-time environment chain looking for the environment
+ * record that contains the function. If the function call definitely
+ * refers to a local binding, use `JSOp::Undefined`.
+ *
+ * Implements: [EvaluateCall][1] step 1.b. But not entirely correctly.
+ * See [bug 1166408][2].
+ *
+ * [1]: https://tc39.es/ecma262/#sec-evaluatecall
+ * [2]: https://bugzilla.mozilla.org/show_bug.cgi?id=1166408
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands: uint32_t nameIndex
+ * Stack: => this
+ */ \
+ MACRO(ImplicitThis, implicit_this, "", 5, 0, 1, JOF_ATOM) \
+ /*
+ * Push the call site object for a tagged template call.
+ *
+ * `script->getObject(objectIndex)` is the call site object.
+ *
+ * The call site object will already have the `.raw` property defined on it
+ * and will be frozen.
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands: uint32_t objectIndex
+ * Stack: => callSiteObj
+ */ \
+ MACRO(CallSiteObj, call_site_obj, NULL, 5, 0, 1, JOF_OBJECT) \
+ /*
+ * Push `MagicValue(JS_IS_CONSTRUCTING)`.
+ *
+ * This magic value is a required argument to the `JSOp::New` and
+ * `JSOp::SuperCall` instructions and must not be used any other way.
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands:
+ * Stack: => JS_IS_CONSTRUCTING
+ */ \
+ MACRO(IsConstructing, is_constructing, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Invoke `callee` as a constructor with `args` and `newTarget`, and push
+ * the return value. Throw a TypeError if `callee` isn't a constructor.
+ *
+ * `isConstructing` must be the value pushed by `JSOp::IsConstructing`.
+ *
+ * `JSOp::SuperCall` behaves exactly like `JSOp::New`, but is used for
+ * *SuperCall* expressions, to allow JITs to distinguish them from `new`
+ * expressions.
+ *
+ * `JSOp::NewContent` is for `constructContentFunction` in self-hosted JS.
+ * See the comment for `JSOp::CallContent` for more details.
+ *
+ * Implements: [EvaluateConstruct][1] steps 7 and 8.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-evaluatenew
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands: uint16_t argc
+ * Stack: callee, isConstructing, args[0], ..., args[argc-1], newTarget => rval
+ */ \
+ MACRO(New, new_, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CONSTRUCT|JOF_IC) \
+ MACRO(NewContent, new_content, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CONSTRUCT|JOF_IC) \
+ MACRO(SuperCall, super_call, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CONSTRUCT|JOF_IC) \
+ /*
+ * Spread-call variant of `JSOp::New`.
+ *
+ * Invokes `callee` as a constructor with `args` and `newTarget`, and
+ * pushes the return value onto the stack.
+ *
+ * `isConstructing` must be the value pushed by `JSOp::IsConstructing`.
+ * See `JSOp::SpreadCall` for restrictions on `args`.
+ *
+ * `JSOp::SpreadSuperCall` behaves exactly like `JSOp::SpreadNew`, but is
+ * used for *SuperCall* expressions.
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands:
+ * Stack: callee, isConstructing, args, newTarget => rval
+ */ \
+ MACRO(SpreadNew, spread_new, NULL, 1, 4, 1, JOF_BYTE|JOF_INVOKE|JOF_CONSTRUCT|JOF_SPREAD|JOF_IC) \
+ MACRO(SpreadSuperCall, spread_super_call, NULL, 1, 4, 1, JOF_BYTE|JOF_INVOKE|JOF_CONSTRUCT|JOF_SPREAD|JOF_IC) \
+ /*
+ * Push the prototype of `callee` in preparation for calling `super()`.
+ *
+ * `callee` must be a derived class constructor.
+ *
+ * Implements: [GetSuperConstructor][1], steps 4-7.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-getsuperconstructor
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands:
+ * Stack: callee => superFun
+ */ \
+ MACRO(SuperFun, super_fun, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * Throw a ReferenceError if `thisval` is not
+ * `MagicValue(JS_UNINITIALIZED_LEXICAL)`. Used in derived class
+ * constructors to prohibit calling `super` more than once.
+ *
+ * Implements: [BindThisValue][1], step 3.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-bindthisvalue
+ *
+ * Category: Functions
+ * Type: Calls
+ * Operands:
+ * Stack: thisval => thisval
+ */ \
+ MACRO(CheckThisReinit, check_this_reinit, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * Create and push a generator object for the current frame.
+ *
+ * This instruction must appear only in scripts for generators, async
+ * functions, and async generators. There must not already be a generator
+ * object for the current frame (that is, this instruction must execute at
+ * most once per generator or async call).
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands:
+ * Stack: => gen
+ */ \
+ MACRO(Generator, generator, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Suspend the current generator and return to the caller.
+ *
+ * When a generator is called, its script starts running, like any other JS
+ * function, because [FunctionDeclarationInstantation][1] and other
+ * [generator object setup][2] are implemented mostly in bytecode. However,
+ * the *FunctionBody* of the generator is not supposed to start running
+ * until the first `.next()` call, so after setup the script suspends
+ * itself: the "initial yield".
+ *
+ * Later, when resuming execution, `rval`, `gen` and `resumeKind` will
+ * receive the values passed in by `JSOp::Resume`. `resumeKind` is the
+ * `GeneratorResumeKind` stored as an Int32 value.
+ *
+ * This instruction must appear only in scripts for generators and async
+ * generators. `gen` must be the generator object for the current frame. It
+ * must not have been previously suspended. The resume point indicated by
+ * `resumeIndex` must be the next instruction in the script, which must be
+ * `AfterYield`.
+ *
+ * Implements: [GeneratorStart][3], steps 4-7.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
+ * [2]: https://tc39.es/ecma262/#sec-generator-function-definitions-runtime-semantics-evaluatebody
+ * [3]: https://tc39.es/ecma262/#sec-generatorstart
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands: uint24_t resumeIndex
+ * Stack: gen => rval, gen, resumeKind
+ */ \
+ MACRO(InitialYield, initial_yield, NULL, 4, 1, 3, JOF_RESUMEINDEX) \
+ /*
+ * Bytecode emitted after `yield` expressions. This is useful for the
+ * Debugger and `AbstractGeneratorObject::isAfterYieldOrAwait`. It's
+ * treated as jump target op so that the Baseline Interpreter can
+ * efficiently restore the frame's interpreterICEntry when resuming a
+ * generator.
+ *
+ * The preceding instruction in the script must be `Yield`, `InitialYield`,
+ * or `Await`.
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands: uint32_t icIndex
+ * Stack: =>
+ */ \
+ MACRO(AfterYield, after_yield, NULL, 5, 0, 0, JOF_ICINDEX) \
+ /*
+ * Suspend and close the current generator, async function, or async
+ * generator.
+ *
+ * `gen` must be the generator object for the current frame.
+ *
+ * If the current function is a non-async generator, then the value in the
+ * frame's return value slot is returned to the caller. It should be an
+ * object of the form `{value: returnValue, done: true}`.
+ *
+ * If the current function is an async function or async generator, the
+ * frame's return value slot must contain the current frame's result
+ * promise, which must already be resolved or rejected.
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands:
+ * Stack: gen =>
+ */ \
+ MACRO(FinalYieldRval, final_yield_rval, NULL, 1, 1, 0, JOF_BYTE) \
+ /*
+ * Suspend execution of the current generator or async generator, returning
+ * `rval1`.
+ *
+ * For non-async generators, `rval1` should be an object of the form
+ * `{value: valueToYield, done: true}`. For async generators, `rval1`
+ * should be the value to yield, and the caller is responsible for creating
+ * the iterator result object (under `js::AsyncGeneratorYield`).
+ *
+ * This instruction must appear only in scripts for generators and async
+ * generators. `gen` must be the generator object for the current stack
+ * frame. The resume point indicated by `resumeIndex` must be the next
+ * instruction in the script, which must be `AfterYield`.
+ *
+ * When resuming execution, `rval2`, `gen` and `resumeKind` receive the
+ * values passed in by `JSOp::Resume`.
+ *
+ * Implements: [GeneratorYield][1] and [AsyncGeneratorYield][2].
+ *
+ * [1]: https://tc39.es/ecma262/#sec-generatoryield
+ * [2]: https://tc39.es/ecma262/#sec-asyncgeneratoryield
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands: uint24_t resumeIndex
+ * Stack: rval1, gen => rval2, gen, resumeKind
+ */ \
+ MACRO(Yield, yield, NULL, 4, 2, 3, JOF_RESUMEINDEX) \
+ /*
+ * Pushes a boolean indicating whether the top of the stack is
+ * `MagicValue(JS_GENERATOR_CLOSING)`.
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands:
+ * Stack: val => val, res
+ */ \
+ MACRO(IsGenClosing, is_gen_closing, NULL, 1, 1, 2, JOF_BYTE) \
+ /*
+ * Arrange for this async function to resume asynchronously when `value`
+ * becomes resolved.
+ *
+ * This is the last thing an async function does before suspending for an
+ * `await` expression. It coerces the awaited `value` to a promise and
+ * effectively calls `.then()` on it, passing handler functions that will
+ * resume this async function call later. See `js::AsyncFunctionAwait`.
+ *
+ * This instruction must appear only in non-generator async function
+ * scripts. `gen` must be the internal generator object for the current
+ * frame. After this instruction, the script should suspend itself with
+ * `Await` (rather than exiting any other way).
+ *
+ * The result `promise` is the async function's result promise,
+ * `gen->as<AsyncFunctionGeneratorObject>().promise()`.
+ *
+ * Implements: [Await][1], steps 2-9.
+ *
+ * [1]: https://tc39.github.io/ecma262/#await
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands:
+ * Stack: value, gen => promise
+ */ \
+ MACRO(AsyncAwait, async_await, NULL, 1, 2, 1, JOF_BYTE) \
+ /*
+ * Resolve or reject the current async function's result promise with
+ * 'valueOrReason'.
+ *
+ * This instruction must appear only in non-generator async function
+ * scripts. `gen` must be the internal generator object for the current
+ * frame. This instruction must run at most once per async function call,
+ * as resolving/rejecting an already resolved/rejected promise is not
+ * permitted.
+ *
+ * The result `promise` is the async function's result promise,
+ * `gen->as<AsyncFunctionGeneratorObject>().promise()`.
+ *
+ * Implements: [AsyncFunctionStart][1], step 4.d.i. and 4.e.i.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands: AsyncFunctionResolveKind fulfillOrReject
+ * Stack: valueOrReason, gen => promise
+ */ \
+ MACRO(AsyncResolve, async_resolve, NULL, 2, 2, 1, JOF_UINT8) \
+ /*
+ * Suspend the current frame for an `await` expression.
+ *
+ * This instruction must appear only in scripts for async functions and
+ * async generators. `gen` must be the internal generator object for the
+ * current frame.
+ *
+ * This returns `promise` to the caller. Later, when this async call is
+ * resumed, `resolved`, `gen` and `resumeKind` receive the values passed in
+ * by `JSOp::Resume`, and execution continues at the next instruction,
+ * which must be `AfterYield`.
+ *
+ * This instruction is used in two subtly different ways.
+ *
+ * 1. In async functions:
+ *
+ * ... # valueToAwait
+ * GetAliasedVar ".generator" # valueToAwait gen
+ * AsyncAwait # resultPromise
+ * GetAliasedVar ".generator" # resultPromise gen
+ * Await # resolved gen resumeKind
+ * AfterYield
+ *
+ * `AsyncAwait` arranges for this frame to be resumed later and pushes
+ * its result promise. `Await` then suspends the frame and removes it
+ * from the stack, returning the result promise to the caller. (If this
+ * async call hasn't awaited before, the caller may be user code.
+ * Otherwise, the caller is self-hosted code using `resumeGenerator`.)
+ *
+ * 2. In async generators:
+ *
+ * ... # valueToAwait
+ * GetAliasedVar ".generator" # valueToAwait gen
+ * Await # resolved gen resumeKind
+ * AfterYield
+ *
+ * `AsyncAwait` is not used, so (1) the value returned to the caller by
+ * `Await` is `valueToAwait`, not `resultPromise`; and (2) the caller
+ * is responsible for doing the async-generator equivalent of
+ * `AsyncAwait` (namely, `js::AsyncGeneratorAwait`, called from
+ * `js::AsyncGeneratorResume` after `js::CallSelfHostedFunction`
+ * returns).
+ *
+ * Implements: [Await][1], steps 10-12.
+ *
+ * [1]: https://tc39.es/ecma262/#await
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands: uint24_t resumeIndex
+ * Stack: promise, gen => resolved, gen, resumeKind
+ */ \
+ MACRO(Await, await, NULL, 4, 2, 3, JOF_RESUMEINDEX) \
+ /*
+ * Test if the re-entry to the microtask loop may be skipped.
+ *
+ * This is part of an optimization for `await` expressions. Programs very
+ * often await values that aren't promises, or promises that are already
+ * resolved. We can then sometimes skip suspending the current frame and
+ * returning to the microtask loop. If the circumstances permit the
+ * optimization, `CanSkipAwait` pushes true if the optimization is allowed,
+ * and false otherwise.
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands:
+ * Stack: value => value, can_skip
+ */ \
+ MACRO(CanSkipAwait, can_skip_await, NULL, 1, 1, 2, JOF_BYTE) \
+ /*
+ * Potentially extract an awaited value, if the await is skippable
+ *
+ * If re-entering the microtask loop is skippable (as checked by CanSkipAwait)
+ * if can_skip is true, `MaybeExtractAwaitValue` replaces `value` with the result of the
+ * `await` expression (unwrapping the resolved promise, if any). Otherwise, value remains
+ * as is.
+ *
+ * In both cases, can_skip remains the same.
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands:
+ * Stack: value, can_skip => value_or_resolved, can_skip
+ */ \
+ MACRO(MaybeExtractAwaitValue, maybe_extract_await_value, NULL, 1, 2, 2, JOF_BYTE) \
+ /*
+ * Pushes one of the GeneratorResumeKind values as Int32Value.
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands: GeneratorResumeKind resumeKind (encoded as uint8_t)
+ * Stack: => resumeKind
+ */ \
+ MACRO(ResumeKind, resume_kind, NULL, 2, 0, 1, JOF_UINT8) \
+ /*
+ * Handle Throw and Return resumption.
+ *
+ * `gen` must be the generator object for the current frame. `resumeKind`
+ * must be a `GeneratorResumeKind` stored as an `Int32` value. If it is
+ * `Next`, continue to the next instruction. If `resumeKind` is `Throw` or
+ * `Return`, these completions are handled by throwing an exception. See
+ * `GeneratorThrowOrReturn`.
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands:
+ * Stack: rval, gen, resumeKind => rval
+ */ \
+ MACRO(CheckResumeKind, check_resume_kind, NULL, 1, 3, 1, JOF_BYTE) \
+ /*
+ * Resume execution of a generator, async function, or async generator.
+ *
+ * This behaves something like a call instruction. It pushes a stack frame
+ * (the one saved when `gen` was suspended, rather than a fresh one) and
+ * runs instructions in it. Once `gen` returns or yields, its return value
+ * is pushed to this frame's stack and execution continues in this script.
+ *
+ * This instruction is emitted only for the `resumeGenerator` self-hosting
+ * intrinsic. It is used in the implementation of
+ * `%GeneratorPrototype%.next`, `.throw`, and `.return`.
+ *
+ * `gen` must be a suspended generator object. `resumeKind` must be in
+ * range for `GeneratorResumeKind`.
+ *
+ * Category: Functions
+ * Type: Generators and async functions
+ * Operands:
+ * Stack: gen, val, resumeKind => rval
+ */ \
+ MACRO(Resume, resume, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE) \
+ /*
+ * No-op instruction marking the target of a jump instruction.
+ *
+ * This instruction and a few others (see `js::BytecodeIsJumpTarget`) are
+ * jump target instructions. The Baseline Interpreter uses these
+ * instructions to sync the frame's `interpreterICEntry` after a jump. Ion
+ * uses them to find block boundaries when translating bytecode to MIR.
+ *
+ * Category: Control flow
+ * Type: Jump targets
+ * Operands: uint32_t icIndex
+ * Stack: =>
+ */ \
+ MACRO(JumpTarget, jump_target, NULL, 5, 0, 0, JOF_ICINDEX) \
+ /*
+ * Marks the target of the backwards jump for some loop.
+ *
+ * This is a jump target instruction (see `JSOp::JumpTarget`). Additionally,
+ * it checks for interrupts and handles JIT tiering.
+ *
+ * The `depthHint` operand is a loop depth hint for Ion. It starts at 1 and
+ * deeply nested loops all have the same value.
+ *
+ * For the convenience of the JITs, scripts must not start with this
+ * instruction. See bug 1602390.
+ *
+ * Category: Control flow
+ * Type: Jump targets
+ * Operands: uint32_t icIndex, uint8_t depthHint
+ * Stack: =>
+ */ \
+ MACRO(LoopHead, loop_head, NULL, 6, 0, 0, JOF_LOOPHEAD) \
+ /*
+ * Jump to a 32-bit offset from the current bytecode.
+ *
+ * See "Jump instructions" above for details.
+ *
+ * Category: Control flow
+ * Type: Jumps
+ * Operands: int32_t offset
+ * Stack: =>
+ */ \
+ MACRO(Goto, goto_, NULL, 5, 0, 0, JOF_JUMP) \
+ /*
+ * If ToBoolean(`cond`) is false, jumps to a 32-bit offset from the current
+ * instruction.
+ *
+ * Category: Control flow
+ * Type: Jumps
+ * Operands: int32_t forwardOffset
+ * Stack: cond =>
+ */ \
+ MACRO(JumpIfFalse, jump_if_false, NULL, 5, 1, 0, JOF_JUMP|JOF_IC) \
+ /*
+ * If ToBoolean(`cond`) is true, jump to a 32-bit offset from the current
+ * instruction.
+ *
+ * `offset` may be positive or negative. This is the instruction used at the
+ * end of a do-while loop to jump back to the top.
+ *
+ * Category: Control flow
+ * Type: Jumps
+ * Operands: int32_t offset
+ * Stack: cond =>
+ */ \
+ MACRO(JumpIfTrue, jump_if_true, NULL, 5, 1, 0, JOF_JUMP|JOF_IC) \
+ /*
+ * Short-circuit for logical AND.
+ *
+ * If ToBoolean(`cond`) is false, jump to a 32-bit offset from the current
+ * instruction. The value remains on the stack.
+ *
+ * Category: Control flow
+ * Type: Jumps
+ * Operands: int32_t forwardOffset
+ * Stack: cond => cond
+ */ \
+ MACRO(And, and_, NULL, 5, 1, 1, JOF_JUMP|JOF_IC) \
+ /*
+ * Short-circuit for logical OR.
+ *
+ * If ToBoolean(`cond`) is true, jump to a 32-bit offset from the current
+ * instruction. The value remains on the stack.
+ *
+ * Category: Control flow
+ * Type: Jumps
+ * Operands: int32_t forwardOffset
+ * Stack: cond => cond
+ */ \
+ MACRO(Or, or_, NULL, 5, 1, 1, JOF_JUMP|JOF_IC) \
+ /*
+ * Short-circuiting for nullish coalescing.
+ *
+ * If `val` is not null or undefined, jump to a 32-bit offset from the
+ * current instruction.
+ *
+ * Category: Control flow
+ * Type: Jumps
+ * Operands: int32_t forwardOffset
+ * Stack: val => val
+ */ \
+ MACRO(Coalesce, coalesce, NULL, 5, 1, 1, JOF_JUMP) \
+ /*
+ * Like `JSOp::JumpIfTrue`, but if the branch is taken, pop and discard an
+ * additional stack value.
+ *
+ * This is used to implement `switch` statements when the
+ * `JSOp::TableSwitch` optimization is not possible. The switch statement
+ *
+ * switch (expr) {
+ * case A: stmt1;
+ * case B: stmt2;
+ * }
+ *
+ * compiles to this bytecode:
+ *
+ * # dispatch code - evaluate expr, check it against each `case`,
+ * # jump to the right place in the body or to the end.
+ * <expr>
+ * Dup; <A>; StrictEq; Case L1; JumpTarget
+ * Dup; <B>; StrictEq; Case L2; JumpTarget
+ * Default LE
+ *
+ * # body code
+ * L1: JumpTarget; <stmt1>
+ * L2: JumpTarget; <stmt2>
+ * LE: JumpTarget
+ *
+ * This opcode is weird: it's the only one whose ndefs varies depending on
+ * which way a conditional branch goes. We could implement switch
+ * statements using `JSOp::JumpIfTrue` and `JSOp::Pop`, but that would also
+ * be awkward--putting the `JSOp::Pop` inside the `switch` body would
+ * complicate fallthrough.
+ *
+ * Category: Control flow
+ * Type: Jumps
+ * Operands: int32_t forwardOffset
+ * Stack: val, cond => val (if !cond)
+ */ \
+ MACRO(Case, case_, NULL, 5, 2, 1, JOF_JUMP) \
+ /*
+ * Like `JSOp::Goto`, but pop and discard an additional stack value.
+ *
+ * This appears after all cases for a non-optimized `switch` statement. If
+ * there's a `default:` label, it jumps to that point in the body;
+ * otherwise it jumps to the next statement.
+ *
+ * Category: Control flow
+ * Type: Jumps
+ * Operands: int32_t forwardOffset
+ * Stack: lval =>
+ */ \
+ MACRO(Default, default_, NULL, 5, 1, 0, JOF_JUMP) \
+ /*
+ * Optimized switch-statement dispatch, used when all `case` labels are
+ * small integer constants.
+ *
+ * If `low <= i <= high`, jump to the instruction at the offset given by
+ * `script->resumeOffsets()[firstResumeIndex + i - low]`, in bytes from the
+ * start of the current script's bytecode. Otherwise, jump to the
+ * instruction at `defaultOffset` from the current instruction. All of
+ * these offsets must be in range for the current script and must point to
+ * `JSOp::JumpTarget` instructions.
+ *
+ * The following inequalities must hold: `low <= high` and
+ * `firstResumeIndex + high - low < resumeOffsets().size()`.
+ *
+ * Category: Control flow
+ * Type: Jumps
+ * Operands: int32_t defaultOffset, int32_t low, int32_t high,
+ * uint24_t firstResumeIndex
+ * Stack: i =>
+ */ \
+ MACRO(TableSwitch, table_switch, NULL, 16, 1, 0, JOF_TABLESWITCH) \
+ /*
+ * Return `rval`.
+ *
+ * This must not be used in derived class constructors. Instead use
+ * `JSOp::SetRval`, `JSOp::CheckReturn`, and `JSOp::RetRval`.
+ *
+ * Category: Control flow
+ * Type: Return
+ * Operands:
+ * Stack: rval =>
+ */ \
+ MACRO(Return, return_, NULL, 1, 1, 0, JOF_BYTE) \
+ /*
+ * Push the current stack frame's `returnValue`. If no `JSOp::SetRval`
+ * instruction has been executed in this stack frame, this is `undefined`.
+ *
+ * Every stack frame has a `returnValue` slot, used by top-level scripts,
+ * generators, async functions, and derived class constructors. Plain
+ * functions usually use `JSOp::Return` instead.
+ *
+ * Category: Control flow
+ * Type: Return
+ * Operands:
+ * Stack: => rval
+ */ \
+ MACRO(GetRval, get_rval, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Store `rval` in the current stack frame's `returnValue` slot.
+ *
+ * This instruction must not be used in a toplevel script compiled with the
+ * `noScriptRval` option.
+ *
+ * Category: Control flow
+ * Type: Return
+ * Operands:
+ * Stack: rval =>
+ */ \
+ MACRO(SetRval, set_rval, NULL, 1, 1, 0, JOF_BYTE) \
+ /*
+ * Stop execution and return the current stack frame's `returnValue`. If no
+ * `JSOp::SetRval` instruction has been executed in this stack frame, this
+ * is `undefined`.
+ *
+ * Also emitted at end of every script so consumers don't need to worry
+ * about running off the end.
+ *
+ * If the current script is a derived class constructor, `returnValue` must
+ * be an object. The script can use `JSOp::CheckReturn` to ensure this.
+ *
+ * Category: Control flow
+ * Type: Return
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(RetRval, ret_rval, NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Check the return value in a derived class constructor.
+ *
+ * - If the current stack frame's `returnValue` is an object, push
+ * `returnValue` onto the stack.
+ *
+ * - Otherwise, if the `returnValue` is undefined and `thisval` is an
+ * object, push `thisval` onto the stack.
+ *
+ * - Otherwise, throw a TypeError.
+ *
+ * This is exactly what has to happen when a derived class constructor
+ * returns. `thisval` should be the current value of `this`, or
+ * `MagicValue(JS_UNINITIALIZED_LEXICAL)` if `this` is uninitialized.
+ *
+ * Implements: [The [[Construct]] internal method of JS functions][1],
+ * steps 13 and 15.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget
+ *
+ * Category: Control flow
+ * Type: Return
+ * Operands:
+ * Stack: thisval => rval
+ */ \
+ MACRO(CheckReturn, check_return, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * Throw `exc`. (ノಠ益ಠ)ノ彡┴──┴
+ *
+ * This sets the pending exception to `exc` and jumps to error-handling
+ * code. If we're in a `try` block, error handling adjusts the stack and
+ * environment chain and resumes execution at the top of the `catch` or
+ * `finally` block. Otherwise it starts unwinding the stack.
+ *
+ * Implements: [*ThrowStatement* Evaluation][1], step 3.
+ *
+ * This is also used in for-of loops. If the body of the loop throws an
+ * exception, we catch it, close the iterator, then use `JSOp::Throw` to
+ * rethrow.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-throw-statement-runtime-semantics-evaluation
+ *
+ * Category: Control flow
+ * Type: Exceptions
+ * Operands:
+ * Stack: exc =>
+ */ \
+ MACRO(Throw, throw_, NULL, 1, 1, 0, JOF_BYTE) \
+ /*
+ * Create and throw an Error object.
+ *
+ * Sometimes we know at emit time that an operation always throws. For
+ * example, `delete super.prop;` is allowed in methods, but always throws a
+ * ReferenceError.
+ *
+ * `msgNumber` determines the `.message` and [[Prototype]] of the new Error
+ * object. It must be an error number in js/public/friend/ErrorNumbers.msg.
+ * The number of arguments in the error message must be 0.
+ *
+ * Category: Control flow
+ * Type: Exceptions
+ * Operands: ThrowMsgKind msgNumber
+ * Stack: =>
+ */ \
+ MACRO(ThrowMsg, throw_msg, NULL, 2, 0, 0, JOF_UINT8) \
+ /*
+ * Throws a runtime TypeError for invalid assignment to a `const` binding.
+ *
+ * Category: Control flow
+ * Type: Exceptions
+ * Operands: uint32_t nameIndex
+ * Stack:
+ */ \
+ MACRO(ThrowSetConst, throw_set_const, NULL, 5, 0, 0, JOF_ATOM|JOF_NAME) \
+ /*
+ * No-op instruction that marks the top of the bytecode for a
+ * *TryStatement*.
+ *
+ * Location information for catch/finally blocks is stored in a side table,
+ * `script->trynotes()`.
+ *
+ * Category: Control flow
+ * Type: Exceptions
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(Try, try_, NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * No-op instruction used by the exception unwinder to determine the
+ * correct environment to unwind to when performing IteratorClose due to
+ * destructuring.
+ *
+ * This instruction must appear immediately before each
+ * `JSTRY_DESTRUCTURING` span in a script's try notes.
+ *
+ * Category: Control flow
+ * Type: Exceptions
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(TryDestructuring, try_destructuring, NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Push and clear the pending exception. ┬──┬◡ノ(° -°ノ)
+ *
+ * This must be used only in the fixed sequence of instructions following a
+ * `JSTRY_CATCH` span (see "Bytecode Invariants" above), as that's the only
+ * way instructions would run with an exception pending.
+ *
+ * Used to implement catch-blocks, including the implicit ones generated as
+ * part of for-of iteration.
+ *
+ * Category: Control flow
+ * Type: Exceptions
+ * Operands:
+ * Stack: => exception
+ */ \
+ MACRO(Exception, exception, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * No-op instruction that marks the start of a `finally` block.
+ *
+ * Category: Control flow
+ * Type: Exceptions
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(Finally, finally, NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Push `MagicValue(JS_UNINITIALIZED_LEXICAL)`, a magic value used to mark
+ * a binding as uninitialized.
+ *
+ * This magic value must be used only by `JSOp::InitLexical`.
+ *
+ * Category: Variables and scopes
+ * Type: Initialization
+ * Operands:
+ * Stack: => uninitialized
+ */ \
+ MACRO(Uninitialized, uninitialized, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Initialize an optimized local lexical binding; or mark it as
+ * uninitialized.
+ *
+ * This stores the value `v` in the fixed slot `localno` in the current
+ * stack frame. If `v` is the magic value produced by `JSOp::Uninitialized`,
+ * this marks the binding as uninitialized. Otherwise this initializes the
+ * binding with value `v`.
+ *
+ * Implements: [CreateMutableBinding][1] step 3, substep "record that it is
+ * uninitialized", and [InitializeBinding][2], for optimized locals. (Note:
+ * this is how `const` bindings are initialized.)
+ *
+ * [1]: https://tc39.es/ecma262/#sec-declarative-environment-records-createmutablebinding-n-d
+ * [2]: https://tc39.es/ecma262/#sec-declarative-environment-records-initializebinding-n-v
+ *
+ * Category: Variables and scopes
+ * Type: Initialization
+ * Operands: uint24_t localno
+ * Stack: v => v
+ */ \
+ MACRO(InitLexical, init_lexical, NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME) \
+ /*
+ * Initialize a global lexical binding.
+ *
+ * The binding must already have been created by
+ * `GlobalOrEvalDeclInstantiation` and must be uninitialized.
+ *
+ * Like `JSOp::InitLexical` but for global lexicals. Unlike `InitLexical`
+ * this can't be used to mark a binding as uninitialized.
+ *
+ * Category: Variables and scopes
+ * Type: Initialization
+ * Operands: uint32_t nameIndex
+ * Stack: val => val
+ */ \
+ MACRO(InitGLexical, init_g_lexical, NULL, 5, 1, 1, JOF_ATOM|JOF_NAME|JOF_PROPINIT|JOF_GNAME|JOF_IC) \
+ /*
+ * Initialize an aliased lexical binding; or mark it as uninitialized.
+ *
+ * Like `JSOp::InitLexical` but for aliased bindings.
+ *
+ * Note: There is no even-less-optimized `InitName` instruction because JS
+ * doesn't need it. We always know statically which binding we're
+ * initializing.
+ *
+ * `hops` is usually 0, but in `function f(a=eval("var b;")) { }`, the
+ * argument `a` is initialized from inside a nested scope, so `hops == 1`.
+ *
+ * Category: Variables and scopes
+ * Type: Initialization
+ * Operands: uint8_t hops, uint24_t slot
+ * Stack: v => v
+ */ \
+ MACRO(InitAliasedLexical, init_aliased_lexical, NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME|JOF_PROPINIT) \
+ /*
+ * Throw a ReferenceError if the value on top of the stack is uninitialized.
+ *
+ * Typically used after `JSOp::GetLocal` with the same `localno`.
+ *
+ * Implements: [GetBindingValue][1] step 3 and [SetMutableBinding][2] step
+ * 4 for declarative Environment Records.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-declarative-environment-records-getbindingvalue-n-s
+ * [2]: https://tc39.es/ecma262/#sec-declarative-environment-records-setmutablebinding-n-v-s
+ *
+ * Category: Variables and scopes
+ * Type: Initialization
+ * Operands: uint24_t localno
+ * Stack: v => v
+ */ \
+ MACRO(CheckLexical, check_lexical, NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME) \
+ /*
+ * Like `JSOp::CheckLexical` but for aliased bindings.
+ *
+ * Typically used after `JSOp::GetAliasedVar` with the same hops/slot.
+ *
+ * Note: There are no `CheckName` or `CheckGName` instructions because
+ * they're unnecessary. `JSOp::{Get,Set}{Name,GName}` all check for
+ * uninitialized lexicals and throw if needed.
+ *
+ * Category: Variables and scopes
+ * Type: Initialization
+ * Operands: uint8_t hops, uint24_t slot
+ * Stack: v => v
+ */ \
+ MACRO(CheckAliasedLexical, check_aliased_lexical, NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME) \
+ /*
+ * Throw a ReferenceError if the value on top of the stack is
+ * `MagicValue(JS_UNINITIALIZED_LEXICAL)`. Used in derived class
+ * constructors to check `this` (which needs to be initialized before use,
+ * by calling `super()`).
+ *
+ * Implements: [GetThisBinding][1] step 3.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-function-environment-records-getthisbinding
+ *
+ * Category: Variables and scopes
+ * Type: Initialization
+ * Operands:
+ * Stack: this => this
+ */ \
+ MACRO(CheckThis, check_this, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * Look up a name on the global lexical environment's chain and push the
+ * environment which contains a binding for that name. If no such binding
+ * exists, push the global lexical environment.
+ *
+ * Category: Variables and scopes
+ * Type: Looking up bindings
+ * Operands: uint32_t nameIndex
+ * Stack: => global
+ */ \
+ MACRO(BindGName, bind_g_name, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_GNAME|JOF_IC) \
+ /*
+ * Look up a name on the environment chain and push the environment which
+ * contains a binding for that name. If no such binding exists, push the
+ * global lexical environment.
+ *
+ * Category: Variables and scopes
+ * Type: Looking up bindings
+ * Operands: uint32_t nameIndex
+ * Stack: => env
+ */ \
+ MACRO(BindName, bind_name, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_IC) \
+ /*
+ * Find a binding on the environment chain and push its value.
+ *
+ * If the binding is an uninitialized lexical, throw a ReferenceError. If
+ * no such binding exists, throw a ReferenceError unless the next
+ * instruction is `JSOp::Typeof`, in which case push `undefined`.
+ *
+ * Implements: [ResolveBinding][1] followed by [GetValue][2]
+ * (adjusted hackily for `typeof`).
+ *
+ * This is the fallback `Get` instruction that handles all unoptimized
+ * cases. Optimized instructions follow.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-resolvebinding
+ * [2]: https://tc39.es/ecma262/#sec-getvalue
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands: uint32_t nameIndex
+ * Stack: => val
+ */ \
+ MACRO(GetName, get_name, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_IC) \
+ /*
+ * Find a global binding and push its value.
+ *
+ * This searches the global lexical environment and, failing that, the
+ * global object. (Unlike most declarative environments, the global lexical
+ * environment can gain more bindings after compilation, possibly shadowing
+ * global object properties.)
+ *
+ * This is an optimized version of `JSOp::GetName` that skips all local
+ * scopes, for use when the name doesn't refer to any local binding.
+ * `NonSyntacticVariablesObject`s break this optimization, so if the
+ * current script has a non-syntactic global scope, use `JSOp::GetName`
+ * instead.
+ *
+ * Like `JSOp::GetName`, this throws a ReferenceError if no such binding is
+ * found (unless the next instruction is `JSOp::Typeof`) or if the binding
+ * is an uninitialized lexical.
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands: uint32_t nameIndex
+ * Stack: => val
+ */ \
+ MACRO(GetGName, get_g_name, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_GNAME|JOF_IC) \
+ /*
+ * Push the value of an argument that is stored in the stack frame
+ * or in an `ArgumentsObject`.
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands: uint16_t argno
+ * Stack: => arguments[argno]
+ */ \
+ MACRO(GetArg, get_arg, NULL, 3, 0, 1, JOF_QARG|JOF_NAME) \
+ /*
+ * Push the value of an optimized local variable.
+ *
+ * If the variable is an uninitialized lexical, push
+ * `MagicValue(JS_UNINIITALIZED_LEXICAL)`.
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands: uint24_t localno
+ * Stack: => val
+ */ \
+ MACRO(GetLocal, get_local, NULL, 4, 0, 1, JOF_LOCAL|JOF_NAME) \
+ /*
+ * Push the value of an aliased binding.
+ *
+ * Local bindings that aren't closed over or dynamically accessed are
+ * stored in stack slots. Global and `with` bindings are object properties.
+ * All other bindings are called "aliased" and stored in
+ * `EnvironmentObject`s.
+ *
+ * Where possible, `Aliased` instructions are used to access aliased
+ * bindings. (There's no difference in meaning between `AliasedVar` and
+ * `AliasedLexical`.) Each of these instructions has operands `hops` and
+ * `slot` that encode an [`EnvironmentCoordinate`][1], directions to the
+ * binding from the current environment object.
+ *
+ * `Aliased` instructions can't be used when there's a dynamic scope (due
+ * to non-strict `eval` or `with`) that might shadow the aliased binding.
+ *
+ * [1]: https://searchfox.org/mozilla-central/search?q=symbol:T_js%3A%3AEnvironmentCoordinate
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands: uint8_t hops, uint24_t slot
+ * Stack: => aliasedVar
+ */ \
+ MACRO(GetAliasedVar, get_aliased_var, NULL, 5, 0, 1, JOF_ENVCOORD|JOF_NAME) \
+ /*
+ * Push the value of an aliased binding, which may have to bypass a DebugEnvironmentProxy
+ * on the environment chain.
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands: uint8_t hops, uint24_t slot
+ * Stack: => aliasedVar
+ */ \
+ MACRO(GetAliasedDebugVar, get_aliased_debug_var, NULL, 5, 0, 1, JOF_DEBUGCOORD|JOF_NAME) \
+ /*
+ * Get the value of a module import by name and pushes it onto the stack.
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands: uint32_t nameIndex
+ * Stack: => val
+ */ \
+ MACRO(GetImport, get_import, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME) \
+ /*
+ * Get the value of a binding from the environment `env`. If the name is
+ * not bound in `env`, throw a ReferenceError.
+ *
+ * `env` must be an environment currently on the environment chain, pushed
+ * by `JSOp::BindName` or `JSOp::BindVar`.
+ *
+ * Note: `JSOp::BindName` and `JSOp::GetBoundName` are the two halves of the
+ * `JSOp::GetName` operation: finding and reading a variable. This
+ * decomposed version is needed to implement the compound assignment and
+ * increment/decrement operators, which get and then set a variable. The
+ * spec says the variable lookup is done only once. If we did the lookup
+ * twice, there would be observable bugs, thanks to dynamic scoping. We
+ * could set the wrong variable or call proxy traps incorrectly.
+ *
+ * Implements: [GetValue][1] steps 4 and 6.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-getvalue
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands: uint32_t nameIndex
+ * Stack: env => v
+ */ \
+ MACRO(GetBoundName, get_bound_name, NULL, 5, 1, 1, JOF_ATOM|JOF_NAME|JOF_IC) \
+ /*
+ * Push the value of an intrinsic onto the stack.
+ *
+ * Non-standard. Intrinsics are slots in the intrinsics holder object (see
+ * `GlobalObject::getIntrinsicsHolder`), which is used in lieu of global
+ * bindings in self-hosting code.
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands: uint32_t nameIndex
+ * Stack: => intrinsic[name]
+ */ \
+ MACRO(GetIntrinsic, get_intrinsic, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_IC) \
+ /*
+ * Pushes the currently executing function onto the stack.
+ *
+ * The current script must be a function script.
+ *
+ * Used to implement `super`. This is also used sometimes as a minor
+ * optimization when a named function expression refers to itself by name:
+ *
+ * f = function fac(n) { ... fac(n - 1) ... };
+ *
+ * This lets us optimize away a lexical environment that contains only the
+ * binding for `fac`, unless it's otherwise observable (via `with`, `eval`,
+ * or a nested closure).
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands:
+ * Stack: => callee
+ */ \
+ MACRO(Callee, callee, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Load the callee stored in a CallObject on the environment chain. The
+ * `numHops` operand is the number of environment objects to skip on the
+ * environment chain. The environment chain element indicated by `numHops`
+ * must be a CallObject.
+ *
+ * Category: Variables and scopes
+ * Type: Getting binding values
+ * Operands: uint8_t numHops
+ * Stack: => callee
+ */ \
+ MACRO(EnvCallee, env_callee, NULL, 2, 0, 1, JOF_UINT8) \
+ /*
+ * Assign `val` to the binding in `env` with the name given by `nameIndex`.
+ * Throw a ReferenceError if the binding is an uninitialized lexical.
+ * This can call setters and/or proxy traps.
+ *
+ * `env` must be an environment currently on the environment chain,
+ * pushed by `JSOp::BindName` or `JSOp::BindVar`.
+ *
+ * This is the fallback `Set` instruction that handles all unoptimized
+ * cases. Optimized instructions follow.
+ *
+ * Implements: [PutValue][1] steps 5 and 7 for unoptimized bindings.
+ *
+ * Note: `JSOp::BindName` and `JSOp::SetName` are the two halves of simple
+ * assignment: finding and setting a variable. They are two separate
+ * instructions because, per spec, the "finding" part happens before
+ * evaluating the right-hand side of the assignment, and the "setting" part
+ * after. Optimized cases don't need a `Bind` instruction because the
+ * "finding" is done statically.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-putvalue
+ *
+ * Category: Variables and scopes
+ * Type: Setting binding values
+ * Operands: uint32_t nameIndex
+ * Stack: env, val => val
+ */ \
+ MACRO(SetName, set_name, NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_CHECKSLOPPY|JOF_IC) \
+ /*
+ * Like `JSOp::SetName`, but throw a TypeError if there is no binding for
+ * the specified name in `env`, or if the binding is immutable (a `const`
+ * or read-only property).
+ *
+ * Implements: [PutValue][1] steps 5 and 7 for strict mode code.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-putvalue
+ *
+ * Category: Variables and scopes
+ * Type: Setting binding values
+ * Operands: uint32_t nameIndex
+ * Stack: env, val => val
+ */ \
+ MACRO(StrictSetName, strict_set_name, NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_CHECKSTRICT|JOF_IC) \
+ /*
+ * Like `JSOp::SetName`, but for assigning to globals. `env` must be an
+ * environment pushed by `JSOp::BindGName`.
+ *
+ * Category: Variables and scopes
+ * Type: Setting binding values
+ * Operands: uint32_t nameIndex
+ * Stack: env, val => val
+ */ \
+ MACRO(SetGName, set_g_name, NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_GNAME|JOF_CHECKSLOPPY|JOF_IC) \
+ /*
+ * Like `JSOp::StrictSetGName`, but for assigning to globals. `env` must be
+ * an environment pushed by `JSOp::BindGName`.
+ *
+ * Category: Variables and scopes
+ * Type: Setting binding values
+ * Operands: uint32_t nameIndex
+ * Stack: env, val => val
+ */ \
+ MACRO(StrictSetGName, strict_set_g_name, NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_GNAME|JOF_CHECKSTRICT|JOF_IC) \
+ /*
+ * Assign `val` to an argument binding that's stored in the stack frame or
+ * in an `ArgumentsObject`.
+ *
+ * Category: Variables and scopes
+ * Type: Setting binding values
+ * Operands: uint16_t argno
+ * Stack: val => val
+ */ \
+ MACRO(SetArg, set_arg, NULL, 3, 1, 1, JOF_QARG|JOF_NAME) \
+ /*
+ * Assign to an optimized local binding.
+ *
+ * Category: Variables and scopes
+ * Type: Setting binding values
+ * Operands: uint24_t localno
+ * Stack: v => v
+ */ \
+ MACRO(SetLocal, set_local, NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME) \
+ /*
+ * Assign to an aliased binding.
+ *
+ * Implements: [SetMutableBinding for declarative Environment Records][1],
+ * in certain cases where it's known that the binding exists, is mutable,
+ * and has been initialized.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-declarative-environment-records-setmutablebinding-n-v-s
+ *
+ * Category: Variables and scopes
+ * Type: Setting binding values
+ * Operands: uint8_t hops, uint24_t slot
+ * Stack: val => val
+ */ \
+ MACRO(SetAliasedVar, set_aliased_var, NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME|JOF_PROPSET) \
+ /*
+ * Assign to an intrinsic.
+ *
+ * Nonstandard. Intrinsics are used in lieu of global bindings in self-
+ * hosted code. The value is actually stored in the intrinsics holder
+ * object, `GlobalObject::getIntrinsicsHolder`. (Self-hosted code doesn't
+ * have many global `var`s, but it has many `function`s.)
+ *
+ * Category: Variables and scopes
+ * Type: Setting binding values
+ * Operands: uint32_t nameIndex
+ * Stack: val => val
+ */ \
+ MACRO(SetIntrinsic, set_intrinsic, NULL, 5, 1, 1, JOF_ATOM|JOF_NAME) \
+ /*
+ * Push a lexical environment onto the environment chain.
+ *
+ * The `LexicalScope` indicated by `lexicalScopeIndex` determines the shape
+ * of the new `BlockLexicalEnvironmentObject`. All bindings in the new
+ * environment are marked as uninitialized.
+ *
+ * Implements: [Evaluation of *Block*][1], steps 1-4.
+ *
+ * #### Fine print for environment chain instructions
+ *
+ * The following rules for `JSOp::{Push,Pop}LexicalEnv` also apply to
+ * `JSOp::PushClassBodyEnv`, `JSOp::PushVarEnv`, and
+ * `JSOp::{Enter,Leave}With`.
+ *
+ * Each `JSOp::PopLexicalEnv` instruction matches a particular
+ * `JSOp::PushLexicalEnv` instruction in the same script and must have the
+ * same scope and stack depth as the instruction immediately after that
+ * `PushLexicalEnv`.
+ *
+ * `JSOp::PushLexicalEnv` enters a scope that extends to some set of
+ * instructions in the script. Code must not jump into or out of this
+ * region: control can enter only by executing `PushLexicalEnv` and can
+ * exit only by executing a `PopLexicalEnv` or by exception unwinding. (A
+ * `JSOp::PopLexicalEnv` is always emitted at the end of the block, and
+ * extra copies are emitted on "exit slides", where a `break`, `continue`,
+ * or `return` statement exits the scope.)
+ *
+ * The script's `JSScript::scopeNotes()` must identify exactly which
+ * instructions begin executing in this scope. Typically this means a
+ * single entry marking the contiguous chunk of bytecode from the
+ * instruction after `JSOp::PushLexicalEnv` to `JSOp::PopLexicalEnv`
+ * (inclusive); but if that range contains any instructions on exit slides,
+ * after a `JSOp::PopLexicalEnv`, then those must be correctly noted as
+ * *outside* the scope.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
+ *
+ * Category: Variables and scopes
+ * Type: Entering and leaving environments
+ * Operands: uint32_t lexicalScopeIndex
+ * Stack: =>
+ */ \
+ MACRO(PushLexicalEnv, push_lexical_env, NULL, 5, 0, 0, JOF_SCOPE) \
+ /*
+ * Pop a lexical or class-body environment from the environment chain.
+ *
+ * See `JSOp::PushLexicalEnv` for the fine print.
+ *
+ * Category: Variables and scopes
+ * Type: Entering and leaving environments
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(PopLexicalEnv, pop_lexical_env, NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * No-op instruction that indicates leaving an optimized lexical scope.
+ *
+ * If all bindings in a lexical scope are optimized into stack slots, then
+ * the runtime environment objects for that scope are optimized away. No
+ * `JSOp::{Push,Pop}LexicalEnv` instructions are emitted. However, the
+ * debugger still needs to be notified when control exits a scope; that's
+ * what this instruction does.
+ *
+ * The last instruction in a lexical or class-body scope, as indicated by
+ * scope notes, must be either this instruction (if the scope is optimized)
+ * or `JSOp::PopLexicalEnv` (if not).
+ *
+ * Category: Variables and scopes
+ * Type: Entering and leaving environments
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(DebugLeaveLexicalEnv, debug_leave_lexical_env, NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Replace the current block on the environment chain with a fresh block
+ * with uninitialized bindings. This implements the behavior of inducing a
+ * fresh lexical environment for every iteration of a for-in/of loop whose
+ * loop-head declares lexical variables that may be captured.
+ *
+ * The current environment must be a BlockLexicalEnvironmentObject.
+ *
+ * Category: Variables and scopes
+ * Type: Entering and leaving environments
+ * Operands: uint32_t lexicalScopeIndex
+ * Stack: =>
+ */ \
+ MACRO(RecreateLexicalEnv, recreate_lexical_env, NULL, 5, 0, 0, JOF_SCOPE) \
+ /*
+ * Like `JSOp::RecreateLexicalEnv`, but the values of all the bindings are
+ * copied from the old block to the new one. This is used for C-style
+ * `for(let ...; ...; ...)` loops.
+ *
+ * Category: Variables and scopes
+ * Type: Entering and leaving environments
+ * Operands: uint32_t lexicalScopeIndex
+ * Stack: =>
+ */ \
+ MACRO(FreshenLexicalEnv, freshen_lexical_env, NULL, 5, 0, 0, JOF_SCOPE) \
+ /*
+ * Push a ClassBody environment onto the environment chain.
+ *
+ * Like `JSOp::PushLexicalEnv`, but pushes a `ClassBodyEnvironmentObject`
+ * rather than a `BlockLexicalEnvironmentObject`. `JSOp::PopLexicalEnv` is
+ * used to pop class-body environments as well as lexical environments.
+ *
+ * See `JSOp::PushLexicalEnv` for the fine print.
+ *
+ * Category: Variables and scopes
+ * Type: Entering and leaving environments
+ * Operands: uint32_t lexicalScopeIndex
+ * Stack: =>
+ */ \
+ MACRO(PushClassBodyEnv, push_class_body_env, NULL, 5, 0, 0, JOF_SCOPE) \
+ /*
+ * Push a var environment onto the environment chain.
+ *
+ * Like `JSOp::PushLexicalEnv`, but pushes a `VarEnvironmentObject` rather
+ * than a `BlockLexicalEnvironmentObject`. The difference is that
+ * non-strict direct `eval` can add bindings to a var environment; see
+ * `VarScope` in Scope.h.
+ *
+ * See `JSOp::PushLexicalEnv` for the fine print.
+ *
+ * There is no corresponding `JSOp::PopVarEnv` operation, because a
+ * `VarEnvironmentObject` is never popped from the environment chain.
+ *
+ * Implements: Places in the spec where the VariableEnvironment is set:
+ *
+ * - The bit in [PerformEval][1] where, in strict direct eval, the new
+ * eval scope is taken as *varEnv* and becomes "*runningContext*'s
+ * VariableEnvironment".
+ *
+ * - The weird scoping rules for functions with default parameter
+ * expressions, as specified in [FunctionDeclarationInstantiation][2]
+ * step 28 ("NOTE: A separate Environment Record is needed...").
+ *
+ * Note: The spec also pushes a new VariableEnvironment on entry to every
+ * function, but the VM takes care of that as part of pushing the stack
+ * frame, before the function script starts to run, so `JSOp::PushVarEnv` is
+ * not needed.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-performeval
+ * [2]: https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
+ *
+ * Category: Variables and scopes
+ * Type: Entering and leaving environments
+ * Operands: uint32_t scopeIndex
+ * Stack: =>
+ */ \
+ MACRO(PushVarEnv, push_var_env, NULL, 5, 0, 0, JOF_SCOPE) \
+ /*
+ * Push a `WithEnvironmentObject` wrapping ToObject(`val`) to the
+ * environment chain.
+ *
+ * Implements: [Evaluation of `with` statements][1], steps 2-6.
+ *
+ * Operations that may need to consult a WithEnvironment can't be correctly
+ * implemented using optimized instructions like `JSOp::GetLocal`. A script
+ * must use the deoptimized `JSOp::GetName`, `BindName`, `SetName`, and
+ * `DelName` instead. Since those instructions don't work correctly with
+ * optimized locals and arguments, all bindings in scopes enclosing a
+ * `with` statement are marked as "aliased" and deoptimized too.
+ *
+ * See `JSOp::PushLexicalEnv` for the fine print.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-with-statement-runtime-semantics-evaluation
+ *
+ * Category: Variables and scopes
+ * Type: Entering and leaving environments
+ * Operands: uint32_t staticWithIndex
+ * Stack: val =>
+ */ \
+ MACRO(EnterWith, enter_with, NULL, 5, 1, 0, JOF_SCOPE) \
+ /*
+ * Pop a `WithEnvironmentObject` from the environment chain.
+ *
+ * See `JSOp::PushLexicalEnv` for the fine print.
+ *
+ * Implements: [Evaluation of `with` statements][1], step 8.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-with-statement-runtime-semantics-evaluation
+ *
+ * Category: Variables and scopes
+ * Type: Entering and leaving environments
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(LeaveWith, leave_with, NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Push the current VariableEnvironment (the environment on the environment
+ * chain designated to receive new variables).
+ *
+ * Implements: [Annex B.3.3.1, changes to FunctionDeclarationInstantiation
+ * for block-level functions][1], step 1.a.ii.3.a, and similar steps in
+ * other Annex B.3.3 algorithms, when setting the function's second binding
+ * can't be optimized.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation
+ *
+ * Category: Variables and scopes
+ * Type: Creating and deleting bindings
+ * Operands:
+ * Stack: => env
+ */ \
+ MACRO(BindVar, bind_var, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Check for conflicting bindings and then initialize them in global or
+ * sloppy eval scripts. This is required for global scripts with any
+ * top-level bindings, or any sloppy-eval scripts with any non-lexical
+ * top-level bindings.
+ *
+ * Implements: [GlobalDeclarationInstantiation][1] and
+ * [EvalDeclarationInstantiation][2] (except step 12).
+ *
+ * The `lastFun` argument is a GCThingIndex of the last hoisted top-level
+ * function that is part of top-level script initialization. The gcthings
+ * from index `0` thru `lastFun` contain only scopes and hoisted functions.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
+ * [2]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation
+ *
+ * Category: Variables and scopes
+ * Type: Creating and deleting bindings
+ * Operands: uint32_t lastFun
+ * Stack: =>
+ */ \
+ MACRO(GlobalOrEvalDeclInstantiation, global_or_eval_decl_instantiation, NULL, 5, 0, 0, JOF_GCTHING) \
+ /*
+ * Look up a variable on the environment chain and delete it. Push `true`
+ * on success (if a binding was deleted, or if no such binding existed in
+ * the first place), `false` otherwise (most kinds of bindings can't be
+ * deleted).
+ *
+ * Implements: [`delete` *Identifier*][1], which [is a SyntaxError][2] in
+ * strict mode code.
+ *
+ * [1]: https://tc39.es/ecma262/#sec-delete-operator-runtime-semantics-evaluation
+ * [2]: https://tc39.es/ecma262/#sec-delete-operator-static-semantics-early-errors
+ *
+ * Category: Variables and scopes
+ * Type: Creating and deleting bindings
+ * Operands: uint32_t nameIndex
+ * Stack: => succeeded
+ */ \
+ MACRO(DelName, del_name, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_CHECKSLOPPY) \
+ /*
+ * Create and push the `arguments` object for the current function activation.
+ *
+ * When it exists, `arguments` is stored in an ordinary local variable.
+ * `JSOp::Arguments` is used in function preludes, to populate that variable
+ * before the function body runs, *not* each time `arguments` appears in a
+ * function.
+ *
+ * If a function clearly doesn't use `arguments`, we optimize it away when
+ * emitting bytecode. The function's script won't use `JSOp::Arguments` at
+ * all.
+ *
+ * The current script must be a function script. This instruction must
+ * execute at most once per function activation.
+ *
+ * Category: Variables and scopes
+ * Type: Function environment setup
+ * Operands:
+ * Stack: => arguments
+ */ \
+ MACRO(Arguments, arguments, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Create and push the rest parameter array for current function call.
+ *
+ * This must appear only in a script for a function that has a rest
+ * parameter.
+ *
+ * Category: Variables and scopes
+ * Type: Function environment setup
+ * Operands:
+ * Stack: => rest
+ */ \
+ MACRO(Rest, rest, NULL, 1, 0, 1, JOF_BYTE|JOF_IC) \
+ /*
+ * Determines the `this` value for current function frame and pushes it
+ * onto the stack.
+ *
+ * In functions, `this` is stored in a local variable. This instruction is
+ * used in the function prologue to get the value to initialize that
+ * variable. (This doesn't apply to arrow functions, becauses they don't
+ * have a `this` binding; also, `this` is optimized away if it's unused.)
+ *
+ * Functions that have a `this` binding have a local variable named
+ * `".this"`, which is initialized using this instruction in the function
+ * prologue.
+ *
+ * In non-strict functions, `this` is always an object. Undefined/null
+ * `this` is converted into the global `this` value. Other primitive values
+ * are boxed. See `js::BoxNonStrictThis`.
+ *
+ * Category: Variables and scopes
+ * Type: Function environment setup
+ * Operands:
+ * Stack: => this
+ */ \
+ MACRO(FunctionThis, function_this, NULL, 1, 0, 1, JOF_BYTE) \
+ /*
+ * Pop the top value from the stack and discard it.
+ *
+ * Category: Stack operations
+ * Operands:
+ * Stack: v =>
+ */ \
+ MACRO(Pop, pop, NULL, 1, 1, 0, JOF_BYTE) \
+ /*
+ * Pop the top `n` values from the stack. `n` must be <= the current stack
+ * depth.
+ *
+ * Category: Stack operations
+ * Operands: uint16_t n
+ * Stack: v[n-1], ..., v[1], v[0] =>
+ */ \
+ MACRO(PopN, pop_n, NULL, 3, -1, 0, JOF_UINT16) \
+ /*
+ * Push a copy of the top value on the stack.
+ *
+ * Category: Stack operations
+ * Operands:
+ * Stack: v => v, v
+ */ \
+ MACRO(Dup, dup, NULL, 1, 1, 2, JOF_BYTE) \
+ /*
+ * Duplicate the top two values on the stack.
+ *
+ * Category: Stack operations
+ * Operands:
+ * Stack: v1, v2 => v1, v2, v1, v2
+ */ \
+ MACRO(Dup2, dup2, NULL, 1, 2, 4, JOF_BYTE) \
+ /*
+ * Push a copy of the nth value from the top of the stack.
+ *
+ * `n` must be less than the current stack depth.
+ *
+ * Category: Stack operations
+ * Operands: uint24_t n
+ * Stack: v[n], v[n-1], ..., v[1], v[0] =>
+ * v[n], v[n-1], ..., v[1], v[0], v[n]
+ */ \
+ MACRO(DupAt, dup_at, NULL, 4, 0, 1, JOF_UINT24) \
+ /*
+ * Swap the top two values on the stack.
+ *
+ * Category: Stack operations
+ * Operands:
+ * Stack: v1, v2 => v2, v1
+ */ \
+ MACRO(Swap, swap, NULL, 1, 2, 2, JOF_BYTE) \
+ /*
+ * Pick the nth element from the stack and move it to the top of the stack.
+ *
+ * Category: Stack operations
+ * Operands: uint8_t n
+ * Stack: v[n], v[n-1], ..., v[1], v[0] => v[n-1], ..., v[1], v[0], v[n]
+ */ \
+ MACRO(Pick, pick, NULL, 2, 0, 0, JOF_UINT8) \
+ /*
+ * Move the top of the stack value under the `n`th element of the stack.
+ * `n` must not be 0.
+ *
+ * Category: Stack operations
+ * Operands: uint8_t n
+ * Stack: v[n], v[n-1], ..., v[1], v[0] => v[0], v[n], v[n-1], ..., v[1]
+ */ \
+ MACRO(Unpick, unpick, NULL, 2, 0, 0, JOF_UINT8) \
+ /*
+ * Do nothing. This is used when we need distinct bytecode locations for
+ * various mechanisms.
+ *
+ * Category: Other
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(Nop, nop, NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * No-op instruction emitted immediately after `JSOp::*Eval` so that direct
+ * eval does not have to do slow pc-to-line mapping.
+ *
+ * The `lineno` operand should agree with this script's source notes about
+ * the line number of the preceding `*Eval` instruction.
+ *
+ * Category: Other
+ * Operands: uint32_t lineno
+ * Stack: =>
+ */ \
+ MACRO(Lineno, lineno, NULL, 5, 0, 0, JOF_UINT32) \
+ /*
+ * No-op instruction to hint that the top stack value is uninteresting.
+ *
+ * This affects only debug output and some error messages.
+ * In array destructuring, we emit bytecode that is roughly equivalent to
+ * `result.done ? undefined : result.value`.
+ * `NopDestructuring` is emitted after the `undefined`, so that the
+ * expression decompiler and disassembler know to casually ignore the
+ * possibility of `undefined`, and render the result of the conditional
+ * expression simply as "`result.value`".
+ *
+ * Category: Other
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(NopDestructuring, nop_destructuring, NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * No-op instruction only emitted in some self-hosted functions. Not
+ * handled by the JITs or Baseline Interpreter so the script always runs in
+ * the C++ interpreter.
+ *
+ * Category: Other
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(ForceInterpreter, force_interpreter, NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Examine the top stack value, asserting that it's either a self-hosted
+ * function or a self-hosted intrinsic. This does nothing in a non-debug
+ * build.
+ *
+ * Category: Other
+ * Operands:
+ * Stack: checkVal => checkVal
+ */ \
+ MACRO(DebugCheckSelfHosted, debug_check_self_hosted, NULL, 1, 1, 1, JOF_BYTE) \
+ /*
+ * Break in the debugger, if one is attached. Otherwise this is a no-op.
+ *
+ * The [`Debugger` API][1] offers a way to hook into this instruction.
+ *
+ * Implements: [Evaluation for *DebuggerStatement*][2].
+ *
+ * [1]: https://developer.mozilla.org/en-US/docs/Tools/Debugger-API/Debugger
+ * [2]: https://tc39.es/ecma262/#sec-debugger-statement-runtime-semantics-evaluation
+ *
+ * Category: Other
+ * Operands:
+ * Stack: =>
+ */ \
+ MACRO(Debugger, debugger, NULL, 1, 0, 0, JOF_BYTE)
+
+// clang-format on
+
+/*
+ * In certain circumstances it may be useful to "pad out" the opcode space to
+ * a power of two. Use this macro to do so.
+ */
+#define FOR_EACH_TRAILING_UNUSED_OPCODE(MACRO) \
+ IF_RECORD_TUPLE(/* empty */, MACRO(227)) \
+ IF_RECORD_TUPLE(/* empty */, MACRO(228)) \
+ IF_RECORD_TUPLE(/* empty */, MACRO(229)) \
+ IF_RECORD_TUPLE(/* empty */, MACRO(230)) \
+ IF_RECORD_TUPLE(/* empty */, MACRO(231)) \
+ IF_RECORD_TUPLE(/* empty */, MACRO(232)) \
+ IF_RECORD_TUPLE(/* empty */, MACRO(233)) \
+ MACRO(234) \
+ MACRO(235) \
+ MACRO(236) \
+ MACRO(237) \
+ MACRO(238) \
+ MACRO(239) \
+ MACRO(240) \
+ MACRO(241) \
+ MACRO(242) \
+ MACRO(243) \
+ MACRO(244) \
+ MACRO(245) \
+ MACRO(246) \
+ MACRO(247) \
+ MACRO(248) \
+ MACRO(249) \
+ MACRO(250) \
+ MACRO(251) \
+ MACRO(252) \
+ MACRO(253) \
+ MACRO(254) \
+ MACRO(255)
+
+namespace js {
+
+// Sanity check that opcode values and trailing unused opcodes completely cover
+// the [0, 256) range. Avert your eyes! You don't want to know how the
+// sausage gets made.
+
+// clang-format off
+#define PLUS_ONE(...) \
+ + 1
+constexpr int JSOP_LIMIT = 0 FOR_EACH_OPCODE(PLUS_ONE);
+#undef PLUS_ONE
+
+#define TRAILING_VALUE_AND_VALUE_PLUS_ONE(val) \
+ val) && (val + 1 ==
+static_assert((JSOP_LIMIT ==
+ FOR_EACH_TRAILING_UNUSED_OPCODE(TRAILING_VALUE_AND_VALUE_PLUS_ONE)
+ 256),
+ "trailing unused opcode values monotonically increase "
+ "from JSOP_LIMIT to 255");
+#undef TRAILING_VALUE_AND_VALUE_PLUS_ONE
+// clang-format on
+
+// Define JSOpLength_* constants for all ops.
+#define DEFINE_LENGTH_CONSTANT(op, op_snake, image, len, ...) \
+ constexpr size_t JSOpLength_##op = len;
+FOR_EACH_OPCODE(DEFINE_LENGTH_CONSTANT)
+#undef DEFINE_LENGTH_CONSTANT
+
+} // namespace js
+
+/*
+ * JS operation bytecodes.
+ */
+enum class JSOp : uint8_t {
+#define ENUMERATE_OPCODE(op, ...) op,
+ FOR_EACH_OPCODE(ENUMERATE_OPCODE)
+#undef ENUMERATE_OPCODE
+};
+
+#endif // vm_Opcodes_h
diff --git a/third_party/rust/jsparagus-stencil/src/copy/SourceNotes.h b/third_party/rust/jsparagus-stencil/src/copy/SourceNotes.h
new file mode 100644
index 0000000000..dd382bc7dc
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/SourceNotes.h
@@ -0,0 +1,428 @@
+/* -*- 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 frontend_SourceNotes_h
+#define frontend_SourceNotes_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+
+#include <algorithm> // std::min
+#include <stddef.h> // ptrdiff_t, size_t
+#include <stdint.h> // int8_t, uint8_t, uint32_t
+
+#include "jstypes.h" // js::{Bit, BitMask}
+
+namespace js {
+
+/*
+ * Source notes generated along with bytecode for decompiling and debugging.
+ * A source note is a uint8_t with 4 bits of type and 4 of offset from the pc
+ * of the previous note. If 4 bits of offset aren't enough, extended delta
+ * notes (XDelta) consisting of 1 set high order bit followed by 7 offset
+ * bits are emitted before the next note. Some notes have operand offsets
+ * encoded immediately after them, in note bytes or byte-triples.
+ *
+ * Source Note Extended Delta
+ * +7-6-5-4+3-2-1-0+ +7+6-5-4-3-2-1-0+
+ * | type | delta | |1| ext-delta |
+ * +-------+-------+ +-+-------------+
+ *
+ * At most one "gettable" note (i.e., a note of type other than NewLine,
+ * ColSpan, SetLine, and XDelta) applies to a given bytecode.
+ *
+ * NB: the js::SrcNote::specs_ array is indexed by this enum, so its
+ * initializers need to match the order here.
+ */
+
+#define FOR_EACH_SRC_NOTE_TYPE(M) \
+ /* Terminates a note vector. */ \
+ M(Null, "null", 0) \
+ /* += or another assign-op follows. */ \
+ M(AssignOp, "assignop", 0) \
+ /* All notes above here are "gettable". See SrcNote::isGettable below. */ \
+ M(ColSpan, "colspan", int8_t(SrcNote::ColSpan::Operands::Count)) \
+ /* Bytecode follows a source newline. */ \
+ M(NewLine, "newline", 0) \
+ M(SetLine, "setline", int8_t(SrcNote::SetLine::Operands::Count)) \
+ /* Bytecode is a recommended breakpoint. */ \
+ M(Breakpoint, "breakpoint", 0) \
+ /* Bytecode is the first in a new steppable area. */ \
+ M(StepSep, "step-sep", 0) \
+ M(Unused7, "unused", 0) \
+ /* 8-15 (0b1xxx) are for extended delta notes. */ \
+ M(XDelta, "xdelta", 0)
+
+// Note: need to add a new source note? If there's no Unused* note left,
+// consider bumping SrcNoteType::XDelta to 12-15 and change
+// SrcNote::XDeltaBits from 7 to 6.
+
+enum class SrcNoteType : uint8_t {
+#define DEFINE_SRC_NOTE_TYPE(sym, name, arity) sym,
+ FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_TYPE)
+#undef DEFINE_SRC_NOTE_TYPE
+
+ Last,
+ LastGettable = AssignOp
+};
+
+static_assert(uint8_t(SrcNoteType::XDelta) == 8, "XDelta should be 8");
+
+class SrcNote {
+ struct Spec {
+ const char* name_;
+ int8_t arity_;
+ };
+
+ static const Spec specs_[];
+
+ static constexpr unsigned TypeBits = 4;
+ static constexpr unsigned DeltaBits = 4;
+ static constexpr unsigned XDeltaBits = 7;
+
+ static constexpr uint8_t TypeMask = js::BitMask(TypeBits) << DeltaBits;
+ static constexpr ptrdiff_t DeltaMask = js::BitMask(DeltaBits);
+ static constexpr ptrdiff_t XDeltaMask = js::BitMask(XDeltaBits);
+
+ static constexpr ptrdiff_t DeltaLimit = js::Bit(DeltaBits);
+ static constexpr ptrdiff_t XDeltaLimit = js::Bit(XDeltaBits);
+
+ static constexpr inline uint8_t toShiftedTypeBits(SrcNoteType type) {
+ return (uint8_t(type) << DeltaBits);
+ }
+
+ static inline uint8_t noteValue(SrcNoteType type, ptrdiff_t delta) {
+ MOZ_ASSERT((delta & DeltaMask) == delta);
+ return noteValueUnchecked(type, delta);
+ }
+
+ static constexpr inline uint8_t noteValueUnchecked(SrcNoteType type,
+ ptrdiff_t delta) {
+ return toShiftedTypeBits(type) | (delta & DeltaMask);
+ }
+
+ static inline uint8_t xDeltaValue(ptrdiff_t delta) {
+ return toShiftedTypeBits(SrcNoteType::XDelta) | (delta & XDeltaMask);
+ }
+
+ uint8_t value_;
+
+ constexpr explicit SrcNote(uint8_t value) : value_(value) {}
+
+ public:
+ constexpr SrcNote() : value_(noteValueUnchecked(SrcNoteType::Null, 0)){};
+
+ SrcNote(const SrcNote& other) = default;
+ SrcNote& operator=(const SrcNote& other) = default;
+
+ SrcNote(SrcNote&& other) = default;
+ SrcNote& operator=(SrcNote&& other) = default;
+
+ static constexpr SrcNote terminator() { return SrcNote(); }
+
+ private:
+ inline uint8_t typeBits() const { return (value_ >> DeltaBits); }
+
+ inline bool isXDelta() const {
+ return typeBits() >= uint8_t(SrcNoteType::XDelta);
+ }
+
+ inline bool isFourBytesOperand() const {
+ return value_ & FourBytesOperandFlag;
+ }
+
+ // number of operands
+ inline unsigned arity() const {
+ MOZ_ASSERT(uint8_t(type()) < uint8_t(SrcNoteType::Last));
+ return specs_[uint8_t(type())].arity_;
+ }
+
+ public:
+ inline SrcNoteType type() const {
+ if (isXDelta()) {
+ return SrcNoteType::XDelta;
+ }
+ return SrcNoteType(typeBits());
+ }
+
+ // name for disassembly/debugging output
+ const char* name() const {
+ MOZ_ASSERT(uint8_t(type()) < uint8_t(SrcNoteType::Last));
+ return specs_[uint8_t(type())].name_;
+ }
+
+ inline bool isGettable() const {
+ return uint8_t(type()) <= uint8_t(SrcNoteType::LastGettable);
+ }
+
+ inline bool isTerminator() const {
+ return value_ == uint8_t(SrcNoteType::Null);
+ }
+
+ inline ptrdiff_t delta() const {
+ if (isXDelta()) {
+ return value_ & XDeltaMask;
+ }
+ return value_ & DeltaMask;
+ }
+
+ private:
+ /*
+ * Operand fields follow certain notes and are frequency-encoded: an operand
+ * in [0,0x7f] consumes one byte, an operand in [0x80,0x7fffffff] takes four,
+ * and the high bit of the first byte is set.
+ */
+ static constexpr unsigned FourBytesOperandFlag = 0x80;
+ static constexpr unsigned FourBytesOperandMask = 0x7f;
+
+ static constexpr unsigned OperandBits = 31;
+
+ public:
+ static constexpr size_t MaxOperand = (size_t(1) << OperandBits) - 1;
+
+ static inline bool isRepresentableOperand(ptrdiff_t operand) {
+ return 0 <= operand && size_t(operand) <= MaxOperand;
+ }
+
+ class ColSpan {
+ public:
+ enum class Operands {
+ // The column span (the diff between the column corresponds to the
+ // current op and last known column).
+ Span,
+ Count
+ };
+
+ private:
+ /*
+ * SrcNoteType::ColSpan values represent changes to the column number.
+ * Colspans are signed: negative changes arise in describing constructs like
+ * for(;;) loops, that generate code in non-source order. (Negative colspans
+ * also have a history of indicating bugs in updating ParseNodes' source
+ * locations.)
+ *
+ * We store colspans in operands. However, unlike normal operands, colspans
+ * are signed, so we truncate colspans (toOperand) for storage as
+ * operands, and sign-extend operands into colspans when we read them
+ * (fromOperand).
+ */
+ static constexpr ptrdiff_t ColSpanSignBit = 1 << (OperandBits - 1);
+
+ static inline ptrdiff_t fromOperand(ptrdiff_t operand) {
+ // There should be no bits set outside the field we're going to
+ // sign-extend.
+ MOZ_ASSERT(!(operand & ~((1U << OperandBits) - 1)));
+
+ // Sign-extend the least significant OperandBits bits.
+ return (operand ^ ColSpanSignBit) - ColSpanSignBit;
+ }
+
+ public:
+ static constexpr ptrdiff_t MinColSpan = -ColSpanSignBit;
+ static constexpr ptrdiff_t MaxColSpan = ColSpanSignBit - 1;
+
+ static inline ptrdiff_t toOperand(ptrdiff_t colspan) {
+ // Truncate the two's complement colspan, for storage as an operand.
+ ptrdiff_t operand = colspan & ((1U << OperandBits) - 1);
+
+ // When we read this back, we'd better get the value we stored.
+ MOZ_ASSERT(fromOperand(operand) == colspan);
+ return operand;
+ }
+
+ static inline ptrdiff_t getSpan(const SrcNote* sn);
+ };
+
+ class SetLine {
+ public:
+ enum class Operands {
+ // The file-absolute source line number of the current op.
+ Line,
+ Count
+ };
+
+ private:
+ static inline size_t fromOperand(ptrdiff_t operand) {
+ return size_t(operand);
+ }
+
+ public:
+ static inline unsigned lengthFor(unsigned line, size_t initialLine) {
+ unsigned operandSize = toOperand(line, initialLine) >
+ ptrdiff_t(SrcNote::FourBytesOperandMask)
+ ? 4
+ : 1;
+ return 1 /* SetLine */ + operandSize;
+ }
+
+ static inline ptrdiff_t toOperand(size_t line, size_t initialLine) {
+ MOZ_ASSERT(line >= initialLine);
+ return ptrdiff_t(line - initialLine);
+ }
+
+ static inline size_t getLine(const SrcNote* sn, size_t initialLine);
+ };
+
+ friend class SrcNoteWriter;
+ friend class SrcNoteReader;
+ friend class SrcNoteIterator;
+};
+
+class SrcNoteWriter {
+ public:
+ // Write a source note with given `type`, and `delta` from the last source
+ // note. This writes the source note itself, and `XDelta`s if necessary.
+ //
+ // This doesn't write or allocate space for operands.
+ // If the source note is not nullary, the caller is responsible for calling
+ // `writeOperand` immediately after this.
+ //
+ // `allocator` is called with the number of bytes required to store the notes.
+ // `allocator` can be called multiple times for each source note.
+ // The last call corresponds to the source note for `type`.
+ template <typename T>
+ static bool writeNote(SrcNoteType type, ptrdiff_t delta, T allocator) {
+ while (delta >= SrcNote::DeltaLimit) {
+ ptrdiff_t xdelta = std::min(delta, SrcNote::XDeltaMask);
+ SrcNote* sn = allocator(1);
+ if (!sn) {
+ return false;
+ }
+ sn->value_ = SrcNote::xDeltaValue(xdelta);
+ delta -= xdelta;
+ }
+
+ SrcNote* sn = allocator(1);
+ if (!sn) {
+ return false;
+ }
+ sn->value_ = SrcNote::noteValue(type, delta);
+ return true;
+ }
+
+ // Write source note operand.
+ //
+ // `allocator` is called with the number of bytes required to store the
+ // operand. `allocator` is called only once.
+ template <typename T>
+ static bool writeOperand(ptrdiff_t operand, T allocator) {
+ if (operand > ptrdiff_t(SrcNote::FourBytesOperandMask)) {
+ SrcNote* sn = allocator(4);
+ if (!sn) {
+ return false;
+ }
+
+ sn[0].value_ = (SrcNote::FourBytesOperandFlag | (operand >> 24));
+ sn[1].value_ = operand >> 16;
+ sn[2].value_ = operand >> 8;
+ sn[3].value_ = operand;
+ } else {
+ SrcNote* sn = allocator(1);
+ if (!sn) {
+ return false;
+ }
+
+ sn[0].value_ = operand;
+ }
+
+ return true;
+ }
+};
+
+class SrcNoteReader {
+ template <typename T>
+ static T getOperandHead(T sn, unsigned which) {
+ MOZ_ASSERT(sn->type() != SrcNoteType::XDelta);
+ MOZ_ASSERT(uint8_t(which) < sn->arity());
+
+ T curr = sn + 1;
+ for (; which; which--) {
+ if (curr->isFourBytesOperand()) {
+ curr += 4;
+ } else {
+ curr++;
+ }
+ }
+ return curr;
+ }
+
+ public:
+ // Return the operand of source note `sn`, specified by `which`.
+ static ptrdiff_t getOperand(const SrcNote* sn, unsigned which) {
+ const SrcNote* head = getOperandHead(sn, which);
+
+ if (head->isFourBytesOperand()) {
+ return ptrdiff_t(
+ (uint32_t(head[0].value_ & SrcNote::FourBytesOperandMask) << 24) |
+ (uint32_t(head[1].value_) << 16) | (uint32_t(head[2].value_) << 8) |
+ uint32_t(head[3].value_));
+ }
+
+ return ptrdiff_t(head[0].value_);
+ }
+};
+
+/* static */
+inline ptrdiff_t SrcNote::ColSpan::getSpan(const SrcNote* sn) {
+ return fromOperand(SrcNoteReader::getOperand(sn, unsigned(Operands::Span)));
+}
+
+/* static */
+inline size_t SrcNote::SetLine::getLine(const SrcNote* sn, size_t initialLine) {
+ return initialLine +
+ fromOperand(SrcNoteReader::getOperand(sn, unsigned(Operands::Line)));
+}
+
+// Iterate over SrcNote array, until it hits terminator.
+//
+// Usage:
+// for (SrcNoteIterator iter(notes); !iter.atEnd(); ++iter) {
+// auto sn = *iter; // `sn` is `const SrcNote*` typed.
+// ...
+// }
+class SrcNoteIterator {
+ const SrcNote* current_;
+
+ void next() {
+ unsigned arity = current_->arity();
+ current_++;
+
+ for (; arity; arity--) {
+ if (current_->isFourBytesOperand()) {
+ current_ += 4;
+ } else {
+ current_++;
+ }
+ }
+ }
+
+ public:
+ SrcNoteIterator() = delete;
+
+ SrcNoteIterator(const SrcNoteIterator& other) = delete;
+ SrcNoteIterator& operator=(const SrcNoteIterator& other) = delete;
+
+ SrcNoteIterator(SrcNoteIterator&& other) = default;
+ SrcNoteIterator& operator=(SrcNoteIterator&& other) = default;
+
+ explicit SrcNoteIterator(const SrcNote* sn) : current_(sn) {}
+
+ bool atEnd() const { return current_->isTerminator(); }
+
+ const SrcNote* operator*() const { return current_; }
+
+ // Pre-increment
+ SrcNoteIterator& operator++() {
+ next();
+ return *this;
+ }
+
+ // Post-increment
+ SrcNoteIterator operator++(int) = delete;
+};
+
+} // namespace js
+
+#endif /* frontend_SourceNotes_h */
diff --git a/third_party/rust/jsparagus-stencil/src/copy/StencilEnums.h b/third_party/rust/jsparagus-stencil/src/copy/StencilEnums.h
new file mode 100644
index 0000000000..e752df815f
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/StencilEnums.h
@@ -0,0 +1,340 @@
+/* -*- 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_StencilEnums_h
+#define vm_StencilEnums_h
+
+#include <stdint.h> // uint8_t
+
+//
+// Enum definitions shared between frontend, stencil, and the VM.
+//
+
+namespace js {
+
+// [SMDOC] Try Notes
+//
+// Trynotes are attached to regions that are involved with
+// exception unwinding. They can be broken up into four categories:
+//
+// 1. Catch and Finally: Basic exception handling. A Catch trynote
+// covers the range of the associated try. A Finally trynote covers
+// the try and the catch.
+//
+// 2. ForIn and Destructuring: These operations create an iterator
+// which must be cleaned up (by calling IteratorClose) during
+// exception unwinding.
+//
+// 3. ForOf and ForOfIterclose: For-of loops handle unwinding using
+// catch blocks. These trynotes are used for for-of breaks/returns,
+// which create regions that are lexically within a for-of block,
+// but logically outside of it. See TryNoteIter::settle for more
+// details.
+//
+// 4. Loop: This represents normal for/while/do-while loops. It is
+// unnecessary for exception unwinding, but storing the boundaries
+// of loops here is helpful for heuristics that need to know
+// whether a given op is inside a loop.
+enum class TryNoteKind : uint8_t {
+ Catch,
+ Finally,
+ ForIn,
+ Destructuring,
+ ForOf,
+ ForOfIterClose,
+ Loop
+};
+
+// [SMDOC] Script Flags
+//
+// Interpreted scripts represented by the BaseScript type use two flag words to
+// encode an assortment of conditions and attributes about the script.
+//
+// The "immutable" flags are a combination of input flags describing aspects of
+// the execution context that affect parsing (such as if we are an ES module or
+// normal script), and flags derived from source text. These flags are preserved
+// during cloning and serializing. As well, they should never change after the
+// BaseScript is created (although there are currently a few exceptions for
+// de-/re-lazification that remain).
+//
+// The "mutable" flags are temporary flags that are used by subsystems in the
+// engine such as the debugger or JITs. These flags are not preserved through
+// serialization or cloning since the attributes are generally associated with
+// one specific instance of a BaseScript.
+
+enum class ImmutableScriptFlagsEnum : uint32_t {
+ // Input Flags
+ //
+ // These flags are from CompileOptions or the Parser entry point. They
+ // generally cannot be derived from the source text alone.
+ // ----
+
+ // A script may have one of the following kinds: Global, Eval, Module,
+ // Function. At most one flag can be set, with a default of Global.
+ IsForEval = 1 << 0,
+ IsModule = 1 << 1,
+ IsFunction = 1 << 2,
+
+ // The script is compiled as engine-internal self-hosted JavaScript. This mode
+ // is used to implement certain library functions and has special parse,
+ // bytecode, and runtime behaviour that differs from normal script.
+ SelfHosted = 1 << 3,
+
+ // The script was compiled with the default mode set to strict mode. Note that
+ // this tracks the default value, while the actual mode used (after processing
+ // source and its directives) is the `Strict` flag below.
+ ForceStrict = 1 << 4,
+
+ // The script has a non-syntactic scope on its environment chain. That is,
+ // there may be objects about which we know nothing between the outermost
+ // syntactic scope and the global.
+ HasNonSyntacticScope = 1 << 5,
+
+ // The script return value will not be used and simplified code will be
+ // generated. This can only be applied to top-level scripts. The value this
+ // script returns will be UndefinedValue instead of what the spec normally
+ // prescribes.
+ NoScriptRval = 1 << 6,
+
+ // TreatAsRunOnce roughly indicates that a script is expected to be run no
+ // more than once. This affects optimizations and heuristics.
+ //
+ // On top-level global/eval/module scripts, this is set when the embedding
+ // ensures this script will not be re-used. In this case, parser literals may
+ // be exposed directly instead of being cloned.
+ TreatAsRunOnce = 1 << 7,
+ // ----
+
+ // Parser Flags
+ //
+ // Flags computed by the Parser from the source text and input flags.
+ // ----
+
+ // Generated code will execute in strict mode. This is due to either the
+ // ForceStrict flag being specified above, or due to source text itself (such
+ // as "use strict" directives).
+ Strict = 1 << 8,
+
+ // Script is parsed with a top-level goal of Module. This may be a top-level
+ // or an inner-function script.
+ HasModuleGoal = 1 << 9,
+
+ // Script contains inner functions.
+ //
+ // Note: This prevents relazification since inner function close-over the
+ // current scripts scopes.
+ HasInnerFunctions = 1 << 10,
+
+ // There is a direct eval statement in this script OR in any of its inner
+ // functions.
+ //
+ // Note: This prevents relazification since it can introduce inner functions.
+ HasDirectEval = 1 << 11,
+
+ // The (static) bindings of this script must support dynamic name access for
+ // read/write. The environment chain is used to do these dynamic lookups and
+ // optimizations to avoid allocating environments are suppressed.
+ //
+ // This includes direct-eval, `with`, and `delete` in this script OR in any of
+ // its inner functions.
+ //
+ // Note: Access through the arguments object is not considered dynamic binding
+ // access since it does not go through the normal name lookup mechanism.
+ BindingsAccessedDynamically = 1 << 12,
+
+ // A tagged template exists in the body (which will use JSOp::CallSiteObj in
+ // bytecode).
+ //
+ // Note: This prevents relazification since the template's object is
+ // observable to the user and cannot be recreated.
+ HasCallSiteObj = 1 << 13,
+
+ // Parser Flags for Functions
+ // ----
+
+ // This function's initial prototype is one of Function, GeneratorFunction,
+ // AsyncFunction, or AsyncGeneratorFunction as indicated by these flags.
+ //
+ // If either of these flags is set, the script may suspend and resume as it
+ // executes. Stack frames for this script also have a generator object.
+ IsAsync = 1 << 14,
+ IsGenerator = 1 << 15,
+
+ // This function's body serves as the `var` environment for a non-strict
+ // direct eval. This matters because it's the only way bindings can be
+ // dynamically added to a local environment, possibly shadowing other
+ // variables.
+ FunHasExtensibleScope = 1 << 16,
+
+ // This function has an internal .this binding and we need to emit
+ // JSOp::FunctionThis in the prologue to initialize it. This binding may be
+ // used directly for "this", or indirectly (such as class constructors).
+ FunctionHasThisBinding = 1 << 17,
+
+ // This function is a class method that must uses an internal [[HomeObject]]
+ // slot. This slot is initialized when the class definition is executed in the
+ // enclosing function.
+ NeedsHomeObject = 1 << 18,
+
+ // This function is a constructor for a derived class. This is a class that
+ // uses the `extends` syntax.
+ IsDerivedClassConstructor = 1 << 19,
+
+ // This function is synthesized by the Parser. This is used for field
+ // initializer lambdas and missing constructors for classes. These functions
+ // have unusual source coordinates and may be hidden from things like
+ // Reflect.parse.
+ IsSyntheticFunction = 1 << 20,
+
+ // This function is a class constructor that has MemberInitializer data
+ // associated with it.
+ UseMemberInitializers = 1 << 21,
+
+ // This function has a rest (`...`) parameter.
+ HasRest = 1 << 22,
+
+ // This function needs a call object or named lambda environment to be created
+ // in order to execute the function. This is done in the Stack or JIT frame
+ // setup code _before_ the bytecode prologue starts.
+ NeedsFunctionEnvironmentObjects = 1 << 23,
+
+ // An extra VarScope is used as the body scope instead of the normal
+ // FunctionScope. This is needed when parameter expressions are used AND the
+ // function has var bindings or a sloppy-direct-eval. For example,
+ // `function(x = eval("")) { var y; }`
+ FunctionHasExtraBodyVarScope = 1 << 24,
+
+ // This function must define the implicit `arguments` binding on the function
+ // scope. If there are no free uses or an appropriate explicit binding exists,
+ // then this flag is unset.
+ //
+ // Note: Parameter expressions will not see an explicit `var arguments;`
+ // binding in the body and an implicit binding on the function-scope must
+ // still be used in that case.
+ ShouldDeclareArguments = 1 << 25,
+
+ // This function has a local (implicit or explicit) `arguments` binding. This
+ // binding is initialized by the JSOp::Arguments bytecode.
+ //
+ // Technically, every function has a binding named `arguments`. Internally,
+ // this binding is only added when `arguments` is mentioned by the function
+ // body.
+ //
+ // Examples:
+ // ```
+ // // Explicit definition
+ // function f() { var arguments; return arguments; }
+ //
+ // // Implicit use
+ // function f() { return arguments; }
+ //
+ // // Implicit use in arrow function
+ // function f() { return () => arguments; }
+ //
+ // // Implicit use in parameter expression
+ // function f(a = arguments) { return a; }
+ // ```
+ NeedsArgsObj = 1 << 26,
+
+ // This function must use the "mapped" form of an arguments object. This flag
+ // is set independently of whether we actually use an `arguments` binding. The
+ // conditions are specified in the ECMAScript spec.
+ HasMappedArgsObj = 1 << 27,
+
+ // Large self-hosted methods that should be inlined anyway by the JIT for
+ // performance reasons can be marked with this flag.
+ IsInlinableLargeFunction = 1 << 28,
+
+ // This function has an internal .newTarget binding and we need to emit
+ // JSOp::NewTarget in the prologue to initialize it. This binding may be
+ // used directly for "new.target", or indirectly (e.g. in super() calls).
+ FunctionHasNewTargetBinding = 1 << 29,
+};
+
+enum class MutableScriptFlagsEnum : uint32_t {
+ // Number of times the |warmUpCount| was forcibly discarded. The counter is
+ // reset when a script is successfully jit-compiled.
+ WarmupResets_MASK = 0xFF,
+
+ // If treatAsRunOnce, whether script has executed.
+ HasRunOnce = 1 << 8,
+
+ // Script has been reused for a clone.
+ HasBeenCloned = 1 << 9,
+
+ // Script has an entry in Realm::scriptCountsMap.
+ HasScriptCounts = 1 << 10,
+
+ // Script has an entry in Realm::debugScriptMap.
+ HasDebugScript = 1 << 11,
+
+ // (1 << 12) is unused.
+ // (1 << 13) is unused.
+
+ // Script supports relazification where it releases bytecode and gcthings to
+ // save memory. This process is opt-in since various complexities may disallow
+ // this for some scripts.
+ // NOTE: Must check for isRelazifiable() before setting this flag.
+ AllowRelazify = 1 << 14,
+
+ // Set if the script has opted into spew.
+ SpewEnabled = 1 << 15,
+
+ // Set if we care about a script's final warmup count.
+ NeedsFinalWarmUpCount = 1 << 16,
+
+ //
+ // IonMonkey compilation hints.
+ //
+
+ // Whether Baseline or Ion compilation has been disabled for this script.
+ // IonDisabled is equivalent to |jitScript->canIonCompile() == false| but
+ // JitScript can be discarded on GC and we don't want this to affect
+ // observable behavior (see ArgumentsGetterImpl comment).
+ BaselineDisabled = 1 << 17,
+ IonDisabled = 1 << 18,
+
+ // This script should not be inlined into others. This happens after inlining
+ // has failed.
+ Uninlineable = 1 << 19,
+
+ // (1 << 20) is unused.
+
+ // *****************************************************************
+ // The flags below are set when we bail out and invalidate a script.
+ // When we recompile, we will be more conservative.
+ // *****************************************************************
+
+ // A hoisted bounds check bailed out.
+ FailedBoundsCheck = 1 << 21,
+
+ // An instruction hoisted by LICM bailed out.
+ HadLICMInvalidation = 1 << 22,
+
+ // An instruction hoisted by InstructionReordering bailed out.
+ HadReorderingBailout = 1 << 23,
+
+ // An instruction inserted or truncated by Range Analysis bailed out.
+ HadEagerTruncationBailout = 1 << 24,
+
+ // A lexical check bailed out.
+ FailedLexicalCheck = 1 << 25,
+
+ // A guard inserted by phi specialization bailed out.
+ HadSpeculativePhiBailout = 1 << 26,
+
+ // An unbox folded with a load bailed out.
+ HadUnboxFoldingBailout = 1 << 27,
+};
+
+// Retrievable source can be retrieved using the source hook (and therefore
+// need not be XDR'd, can be discarded if desired because it can always be
+// reconstituted later, etc.).
+enum class SourceRetrievable { No = 0, Yes };
+
+} // namespace js
+
+#endif /* vm_StencilEnums_h */
diff --git a/third_party/rust/jsparagus-stencil/src/copy/Symbol.h b/third_party/rust/jsparagus-stencil/src/copy/Symbol.h
new file mode 100644
index 0000000000..1e16552b36
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/Symbol.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* Symbols. */
+
+#ifndef js_Symbol_h
+#define js_Symbol_h
+
+#include "js/shadow/Symbol.h" // JS::shadow::Symbol::WellKnownAPILimit
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uintptr_t, uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/TypeDecls.h"
+
+namespace JS {
+
+class JS_PUBLIC_API Symbol;
+
+/**
+ * Create a new Symbol with the given description. This function never returns
+ * a Symbol that is in the Runtime-wide symbol registry.
+ *
+ * If description is null, the new Symbol's [[Description]] attribute is
+ * undefined.
+ */
+extern JS_PUBLIC_API Symbol* NewSymbol(JSContext* cx,
+ Handle<JSString*> description);
+
+/**
+ * Symbol.for as specified in ES6.
+ *
+ * Get a Symbol with the description 'key' from the Runtime-wide symbol
+ * registry. If there is not already a Symbol with that description in the
+ * registry, a new Symbol is created and registered. 'key' must not be null.
+ */
+extern JS_PUBLIC_API Symbol* GetSymbolFor(JSContext* cx, Handle<JSString*> key);
+
+/**
+ * Get the [[Description]] attribute of the given symbol.
+ *
+ * This function is infallible. If it returns null, that means the symbol's
+ * [[Description]] is undefined.
+ */
+extern JS_PUBLIC_API JSString* GetSymbolDescription(Handle<Symbol*> symbol);
+
+/* Well-known symbols. */
+#define JS_FOR_EACH_WELL_KNOWN_SYMBOL(MACRO) \
+ MACRO(isConcatSpreadable) \
+ MACRO(iterator) \
+ MACRO(match) \
+ MACRO(replace) \
+ MACRO(search) \
+ MACRO(species) \
+ MACRO(hasInstance) \
+ MACRO(split) \
+ MACRO(toPrimitive) \
+ MACRO(toStringTag) \
+ MACRO(unscopables) \
+ MACRO(asyncIterator) \
+ MACRO(matchAll)
+
+enum class SymbolCode : uint32_t {
+// There is one SymbolCode for each well-known symbol.
+#define JS_DEFINE_SYMBOL_ENUM(name) name,
+ JS_FOR_EACH_WELL_KNOWN_SYMBOL(
+ JS_DEFINE_SYMBOL_ENUM) // SymbolCode::iterator, etc.
+#undef JS_DEFINE_SYMBOL_ENUM
+ Limit,
+ WellKnownAPILimit = JS::shadow::Symbol::WellKnownAPILimit,
+ PrivateNameSymbol = 0xfffffffd, // created by the #PrivateName syntax.
+ InSymbolRegistry =
+ 0xfffffffe, // created by Symbol.for() or JS::GetSymbolFor()
+ UniqueSymbol = 0xffffffff // created by Symbol() or JS::NewSymbol()
+};
+
+/* For use in loops that iterate over the well-known symbols. */
+const size_t WellKnownSymbolLimit = size_t(SymbolCode::Limit);
+
+/**
+ * Return the SymbolCode telling what sort of symbol `symbol` is.
+ *
+ * A symbol's SymbolCode never changes once it is created.
+ */
+extern JS_PUBLIC_API SymbolCode GetSymbolCode(Handle<Symbol*> symbol);
+
+/**
+ * Get one of the well-known symbols defined by ES6. A single set of well-known
+ * symbols is shared by all compartments in a JSRuntime.
+ *
+ * `which` must be in the range [0, WellKnownSymbolLimit).
+ */
+extern JS_PUBLIC_API Symbol* GetWellKnownSymbol(JSContext* cx,
+ SymbolCode which);
+
+/**
+ * Return true if the given JSPropertySpec::name or JSFunctionSpec::name value
+ * is actually a symbol code and not a string. See JS_SYM_FN.
+ */
+inline bool PropertySpecNameIsSymbol(uintptr_t name) {
+ return name != 0 && name - 1 < WellKnownSymbolLimit;
+}
+
+} // namespace JS
+
+#endif /* js_Symbol_h */
diff --git a/third_party/rust/jsparagus-stencil/src/copy/ThrowMsgKind.h b/third_party/rust/jsparagus-stencil/src/copy/ThrowMsgKind.h
new file mode 100644
index 0000000000..2631ed011a
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/copy/ThrowMsgKind.h
@@ -0,0 +1,37 @@
+/* -*- 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_ThrowMsgKind_h
+#define vm_ThrowMsgKind_h
+
+#include <stdint.h> // uint8_t
+
+#include "js/friend/ErrorMessages.h" // JSErrNum
+
+namespace js {
+
+enum class ThrowMsgKind : uint8_t {
+ AssignToCall,
+ IteratorNoThrow,
+ CantDeleteSuper,
+ // Private Fields:
+ PrivateDoubleInit,
+ PrivateBrandDoubleInit,
+ MissingPrivateOnGet,
+ MissingPrivateOnSet,
+ AssignToPrivateMethod,
+ // Decorators:
+ DecoratorInvalidReturnType,
+};
+
+JSErrNum ThrowMsgKindToErrNum(ThrowMsgKind kind);
+
+// Used for CheckPrivateField
+enum class ThrowCondition : uint8_t { ThrowHas, ThrowHasNot, OnlyCheckRhs };
+
+} // namespace js
+
+#endif /* vm_ThrowMsgKind_h */
diff --git a/third_party/rust/jsparagus-stencil/src/env_coord.rs b/third_party/rust/jsparagus-stencil/src/env_coord.rs
new file mode 100644
index 0000000000..257afbeb39
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/env_coord.rs
@@ -0,0 +1,50 @@
+//! (EnvironmentSlot,EnvironmentHops) pair represent the reference to a slot in
+//! an environment object in the environment chain.
+//!
+//! See the SMDOC comment in m-c/js/src/vm/EnvironmentObject.h for the details
+//! about environment object and environment chain.
+
+/// Slot in the environmentslot object.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
+pub struct EnvironmentSlot {
+ slot: u32,
+}
+
+impl EnvironmentSlot {
+ pub fn new(slot: u32) -> Self {
+ Self { slot }
+ }
+
+ pub fn next(&mut self) {
+ self.slot += 1;
+ }
+}
+
+impl From<EnvironmentSlot> for u32 {
+ fn from(slot: EnvironmentSlot) -> u32 {
+ slot.slot
+ }
+}
+
+/// The number of environment objects to skip (hop) on the environment chain in
+/// order to find the associated environment object.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
+pub struct EnvironmentHops {
+ hops: u8,
+}
+
+impl EnvironmentHops {
+ pub fn new(hops: u8) -> Self {
+ Self { hops }
+ }
+
+ pub fn next(&mut self) {
+ self.hops += 1;
+ }
+}
+
+impl From<EnvironmentHops> for u8 {
+ fn from(hops: EnvironmentHops) -> u8 {
+ hops.hops
+ }
+}
diff --git a/third_party/rust/jsparagus-stencil/src/frame_slot.rs b/third_party/rust/jsparagus-stencil/src/frame_slot.rs
new file mode 100644
index 0000000000..84ae4e4c02
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/frame_slot.rs
@@ -0,0 +1,21 @@
+/// Slot in the stack frame.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
+pub struct FrameSlot {
+ slot: u32,
+}
+
+impl FrameSlot {
+ pub fn new(slot: u32) -> Self {
+ Self { slot }
+ }
+
+ pub fn next(&mut self) {
+ self.slot += 1;
+ }
+}
+
+impl From<FrameSlot> for u32 {
+ fn from(slot: FrameSlot) -> u32 {
+ slot.slot
+ }
+}
diff --git a/third_party/rust/jsparagus-stencil/src/function.rs b/third_party/rust/jsparagus-stencil/src/function.rs
new file mode 100644
index 0000000000..212491e610
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/function.rs
@@ -0,0 +1,222 @@
+#[derive(Debug)]
+pub struct FunctionFlags {
+ flags: u16,
+}
+
+// WARNING
+// The following section is generated by update_stencil.py.
+// Do mot modify manually.
+//
+// @@@@ BEGIN TYPES @@@@
+#[derive(Debug, Clone, Copy)]
+pub enum FunctionKind {
+ NormalFunction = 0,
+ Arrow = 1,
+ Method = 2,
+ ClassConstructor = 3,
+ Getter = 4,
+ Setter = 5,
+ AsmJS = 6,
+ Wasm = 7,
+ FunctionKindLimit = 8,
+}
+
+#[allow(dead_code)]
+const FUNCTION_KIND_SHIFT: u16 = 0;
+#[allow(dead_code)]
+const FUNCTION_KIND_MASK: u16 = 0x0007;
+#[allow(dead_code)]
+const EXTENDED: u16 = 1 << 3;
+#[allow(dead_code)]
+const SELF_HOSTED: u16 = 1 << 4;
+#[allow(dead_code)]
+const BASESCRIPT: u16 = 1 << 5;
+#[allow(dead_code)]
+const SELFHOSTLAZY: u16 = 1 << 6;
+#[allow(dead_code)]
+const CONSTRUCTOR: u16 = 1 << 7;
+#[allow(dead_code)]
+const LAMBDA: u16 = 1 << 9;
+#[allow(dead_code)]
+const WASM_JIT_ENTRY: u16 = 1 << 10;
+#[allow(dead_code)]
+const HAS_INFERRED_NAME: u16 = 1 << 11;
+#[allow(dead_code)]
+const HAS_GUESSED_ATOM: u16 = 1 << 12;
+#[allow(dead_code)]
+const RESOLVED_NAME: u16 = 1 << 13;
+#[allow(dead_code)]
+const RESOLVED_LENGTH: u16 = 1 << 14;
+#[allow(dead_code)]
+const GHOST_FUNCTION: u16 = 1 << 15;
+#[allow(dead_code)]
+const NORMAL_KIND: u16 = (FunctionKind::NormalFunction as u16) << FUNCTION_KIND_SHIFT;
+#[allow(dead_code)]
+const ASMJS_KIND: u16 = (FunctionKind::AsmJS as u16) << FUNCTION_KIND_SHIFT;
+#[allow(dead_code)]
+const WASM_KIND: u16 = (FunctionKind::Wasm as u16) << FUNCTION_KIND_SHIFT;
+#[allow(dead_code)]
+const ARROW_KIND: u16 = (FunctionKind::Arrow as u16) << FUNCTION_KIND_SHIFT;
+#[allow(dead_code)]
+const METHOD_KIND: u16 = (FunctionKind::Method as u16) << FUNCTION_KIND_SHIFT;
+#[allow(dead_code)]
+const CLASSCONSTRUCTOR_KIND: u16 = (FunctionKind::ClassConstructor as u16) << FUNCTION_KIND_SHIFT;
+#[allow(dead_code)]
+const GETTER_KIND: u16 = (FunctionKind::Getter as u16) << FUNCTION_KIND_SHIFT;
+#[allow(dead_code)]
+const SETTER_KIND: u16 = (FunctionKind::Setter as u16) << FUNCTION_KIND_SHIFT;
+#[allow(dead_code)]
+const NATIVE_FUN: u16 = NORMAL_KIND;
+#[allow(dead_code)]
+const NATIVE_CTOR: u16 = CONSTRUCTOR | NORMAL_KIND;
+#[allow(dead_code)]
+const ASMJS_CTOR: u16 = CONSTRUCTOR | ASMJS_KIND;
+#[allow(dead_code)]
+const ASMJS_LAMBDA_CTOR: u16 = CONSTRUCTOR | LAMBDA | ASMJS_KIND;
+#[allow(dead_code)]
+const WASM: u16 = WASM_KIND;
+#[allow(dead_code)]
+const INTERPRETED_NORMAL: u16 = BASESCRIPT | CONSTRUCTOR | NORMAL_KIND;
+#[allow(dead_code)]
+const INTERPRETED_CLASS_CTOR: u16 = BASESCRIPT | CONSTRUCTOR | CLASSCONSTRUCTOR_KIND;
+#[allow(dead_code)]
+const INTERPRETED_GENERATOR_OR_ASYNC: u16 = BASESCRIPT | NORMAL_KIND;
+#[allow(dead_code)]
+const INTERPRETED_LAMBDA: u16 = BASESCRIPT | LAMBDA | CONSTRUCTOR | NORMAL_KIND;
+#[allow(dead_code)]
+const INTERPRETED_LAMBDA_ARROW: u16 = BASESCRIPT | LAMBDA | ARROW_KIND;
+#[allow(dead_code)]
+const INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC: u16 = BASESCRIPT | LAMBDA | NORMAL_KIND;
+#[allow(dead_code)]
+const INTERPRETED_GETTER: u16 = BASESCRIPT | GETTER_KIND;
+#[allow(dead_code)]
+const INTERPRETED_SETTER: u16 = BASESCRIPT | SETTER_KIND;
+#[allow(dead_code)]
+const INTERPRETED_METHOD: u16 = BASESCRIPT | METHOD_KIND;
+#[allow(dead_code)]
+const MUTABLE_FLAGS: u16 = RESOLVED_NAME | RESOLVED_LENGTH;
+#[allow(dead_code)]
+const STABLE_ACROSS_CLONES: u16 =
+ CONSTRUCTOR | LAMBDA | SELF_HOSTED | FUNCTION_KIND_MASK | GHOST_FUNCTION;
+// @@@@ END TYPES @@@@
+
+#[derive(Debug)]
+pub struct FunctionSyntaxKind {
+ kind: FunctionKind,
+ is_lambda: bool,
+ is_generator: bool,
+ is_async: bool,
+}
+
+impl FunctionSyntaxKind {
+ pub fn function_declaration(is_generator: bool, is_async: bool) -> Self {
+ Self {
+ kind: FunctionKind::NormalFunction,
+ is_lambda: false,
+ is_generator,
+ is_async,
+ }
+ }
+
+ pub fn function_expression(is_generator: bool, is_async: bool) -> Self {
+ Self {
+ kind: FunctionKind::NormalFunction,
+ is_lambda: true,
+ is_generator,
+ is_async,
+ }
+ }
+
+ pub fn method(is_generator: bool, is_async: bool) -> Self {
+ Self {
+ kind: FunctionKind::Method,
+ is_lambda: false,
+ is_generator,
+ is_async,
+ }
+ }
+
+ pub fn getter() -> Self {
+ FunctionSyntaxKind {
+ kind: FunctionKind::Getter,
+ is_lambda: false,
+ is_generator: false,
+ is_async: false,
+ }
+ }
+
+ pub fn setter() -> Self {
+ FunctionSyntaxKind {
+ kind: FunctionKind::Setter,
+ is_lambda: false,
+ is_generator: false,
+ is_async: false,
+ }
+ }
+
+ pub fn arrow(is_async: bool) -> Self {
+ Self {
+ kind: FunctionKind::Arrow,
+ is_lambda: true,
+ is_generator: false,
+ is_async,
+ }
+ }
+
+ pub fn is_generator(&self) -> bool {
+ self.is_generator
+ }
+
+ pub fn is_async(&self) -> bool {
+ self.is_async
+ }
+}
+
+impl FunctionFlags {
+ fn new(flags: u16) -> Self {
+ debug_assert!(
+ (((FunctionKind::FunctionKindLimit as u16) - 1) << FUNCTION_KIND_SHIFT)
+ <= FUNCTION_KIND_MASK
+ );
+
+ Self { flags }
+ }
+
+ /// Returns empty flag that is used for top level script
+ pub fn empty() -> Self {
+ Self { flags: 0 }
+ }
+
+ pub fn interpreted(syntax_kind: FunctionSyntaxKind) -> Self {
+ let kind_flag = (syntax_kind.kind as u16) << FUNCTION_KIND_SHIFT;
+ let mut flags = BASESCRIPT | kind_flag;
+ match syntax_kind.kind {
+ FunctionKind::NormalFunction => {
+ if !syntax_kind.is_generator && !syntax_kind.is_async {
+ flags |= CONSTRUCTOR;
+ }
+ if syntax_kind.is_lambda {
+ flags |= LAMBDA;
+ }
+ }
+ FunctionKind::ClassConstructor => {
+ debug_assert!(!syntax_kind.is_generator);
+ debug_assert!(!syntax_kind.is_async);
+ flags |= CONSTRUCTOR;
+ }
+ _ => {}
+ }
+ Self::new(flags)
+ }
+
+ pub fn is_arrow(&self) -> bool {
+ let kind_num = (self.flags >> FUNCTION_KIND_SHIFT) & FUNCTION_KIND_MASK;
+ kind_num == FunctionKind::Arrow as u16
+ }
+}
+
+impl From<FunctionFlags> for u16 {
+ fn from(flags: FunctionFlags) -> u16 {
+ flags.flags
+ }
+}
diff --git a/third_party/rust/jsparagus-stencil/src/gcthings.rs b/third_party/rust/jsparagus-stencil/src/gcthings.rs
new file mode 100644
index 0000000000..bbe18fd20f
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/gcthings.rs
@@ -0,0 +1,77 @@
+use crate::regexp::RegExpIndex;
+use crate::scope::ScopeIndex;
+use crate::script::ScriptStencilIndex;
+use ast::source_atom_set::SourceAtomSetIndex;
+
+/// Corresponds to js::frontend::GCThingList::ListType
+/// in m-c/js/src/frontend/BytecodeSection.h.
+#[derive(Debug)]
+pub enum GCThing {
+ Null,
+ Atom(SourceAtomSetIndex),
+ Function(ScriptStencilIndex),
+ RegExp(RegExpIndex),
+ Scope(ScopeIndex),
+}
+
+/// Index into GCThingList.things.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct GCThingIndex {
+ index: usize,
+}
+
+impl GCThingIndex {
+ fn new(index: usize) -> Self {
+ Self { index }
+ }
+}
+
+impl From<GCThingIndex> for usize {
+ fn from(index: GCThingIndex) -> usize {
+ index.index
+ }
+}
+
+/// List of GC things.
+///
+/// Maps to JSScript::gcthings() in js/src/vm/JSScript.h.
+#[derive(Debug)]
+pub struct GCThingList {
+ things: Vec<GCThing>,
+}
+
+impl GCThingList {
+ pub fn new() -> Self {
+ Self { things: Vec::new() }
+ }
+
+ pub fn push_atom(&mut self, atom_index: SourceAtomSetIndex) -> GCThingIndex {
+ let index = self.things.len();
+ self.things.push(GCThing::Atom(atom_index));
+ GCThingIndex::new(index)
+ }
+
+ pub fn push_function(&mut self, fun_index: ScriptStencilIndex) -> GCThingIndex {
+ let index = self.things.len();
+ self.things.push(GCThing::Function(fun_index));
+ GCThingIndex::new(index)
+ }
+
+ pub fn push_regexp(&mut self, regexp_index: RegExpIndex) -> GCThingIndex {
+ let index = self.things.len();
+ self.things.push(GCThing::RegExp(regexp_index));
+ GCThingIndex::new(index)
+ }
+
+ pub fn push_scope(&mut self, scope_index: ScopeIndex) -> GCThingIndex {
+ let index = self.things.len();
+ self.things.push(GCThing::Scope(scope_index));
+ GCThingIndex::new(index)
+ }
+}
+
+impl From<GCThingList> for Vec<GCThing> {
+ fn from(list: GCThingList) -> Vec<GCThing> {
+ list.things
+ }
+}
diff --git a/third_party/rust/jsparagus-stencil/src/lib.rs b/third_party/rust/jsparagus-stencil/src/lib.rs
new file mode 100644
index 0000000000..e20a4305f6
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/lib.rs
@@ -0,0 +1,14 @@
+pub mod bytecode_offset;
+pub mod env_coord;
+pub mod frame_slot;
+pub mod function;
+pub mod gcthings;
+pub mod opcode;
+pub mod opcode_info;
+pub mod regexp;
+pub mod result;
+pub mod scope;
+pub mod scope_notes;
+pub mod script;
+
+extern crate jsparagus_ast as ast;
diff --git a/third_party/rust/jsparagus-stencil/src/opcode.rs b/third_party/rust/jsparagus-stencil/src/opcode.rs
new file mode 100644
index 0000000000..9331f9f5b8
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/opcode.rs
@@ -0,0 +1,586 @@
+use std::convert::TryFrom;
+
+macro_rules! using_opcode_database {
+ ( $macro:ident ! ( ) ) => {
+ $macro! {
+ [
+ // WARNING
+ // The following section is generated by update_stencil.py.
+ // Do mot modify manually.
+ //
+ // @@@@ BEGIN OPCODES @@@@
+ (Undefined, undefined, "", 1, 0, 1, JOF_BYTE),
+ (Null, null, "null", 1, 0, 1, JOF_BYTE),
+ (False, false_, "false", 1, 0, 1, JOF_BYTE),
+ (True, true_, "true", 1, 0, 1, JOF_BYTE),
+ (Int32, int32, NULL, 5, 0, 1, JOF_INT32),
+ (Zero, zero, "0", 1, 0, 1, JOF_BYTE),
+ (One, one, "1", 1, 0, 1, JOF_BYTE),
+ (Int8, int8, NULL, 2, 0, 1, JOF_INT8),
+ (Uint16, uint16, NULL, 3, 0, 1, JOF_UINT16),
+ (Uint24, uint24, NULL, 4, 0, 1, JOF_UINT24),
+ (Double, double_, NULL, 9, 0, 1, JOF_DOUBLE),
+ (BigInt, big_int, NULL, 5, 0, 1, JOF_BIGINT),
+ (String, string, NULL, 5, 0, 1, JOF_STRING),
+ (Symbol, symbol, NULL, 2, 0, 1, JOF_UINT8),
+ (Void, void_, NULL, 1, 1, 1, JOF_BYTE),
+ (Typeof, typeof_, NULL, 1, 1, 1, JOF_BYTE|JOF_IC),
+ (TypeofExpr, typeof_expr, NULL, 1, 1, 1, JOF_BYTE|JOF_IC),
+ (Pos, pos, "+ ", 1, 1, 1, JOF_BYTE|JOF_IC),
+ (Neg, neg, "- ", 1, 1, 1, JOF_BYTE|JOF_IC),
+ (BitNot, bit_not, "~", 1, 1, 1, JOF_BYTE|JOF_IC),
+ (Not, not_, "!", 1, 1, 1, JOF_BYTE|JOF_IC),
+ (BitOr, bit_or, "|", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (BitXor, bit_xor, "^", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (BitAnd, bit_and, "&", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Eq, eq, "==", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Ne, ne, "!=", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (StrictEq, strict_eq, "===", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (StrictNe, strict_ne, "!==", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Lt, lt, "<", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Gt, gt, ">", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Le, le, "<=", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Ge, ge, ">=", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Instanceof, instanceof, "instanceof", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (In, in_, "in", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Lsh, lsh, "<<", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Rsh, rsh, ">>", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Ursh, ursh, ">>>", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Add, add, "+", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Sub, sub, "-", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Inc, inc, NULL, 1, 1, 1, JOF_BYTE|JOF_IC),
+ (Dec, dec, NULL, 1, 1, 1, JOF_BYTE|JOF_IC),
+ (Mul, mul, "*", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Div, div, "/", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Mod, mod, "%", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (Pow, pow, "**", 1, 2, 1, JOF_BYTE|JOF_IC),
+ (ToPropertyKey, to_property_key, NULL, 1, 1, 1, JOF_BYTE|JOF_IC),
+ (ToNumeric, to_numeric, NULL, 1, 1, 1, JOF_BYTE|JOF_IC),
+ (ToString, to_string, NULL, 1, 1, 1, JOF_BYTE),
+ (IsNullOrUndefined, is_null_or_undefined, NULL, 1, 1, 2, JOF_BYTE),
+ (GlobalThis, global_this, NULL, 1, 0, 1, JOF_BYTE),
+ (NonSyntacticGlobalThis, non_syntactic_global_this, NULL, 1, 0, 1, JOF_BYTE),
+ (NewTarget, new_target, NULL, 1, 0, 1, JOF_BYTE),
+ (DynamicImport, dynamic_import, NULL, 1, 2, 1, JOF_BYTE),
+ (ImportMeta, import_meta, NULL, 1, 0, 1, JOF_BYTE),
+ (NewInit, new_init, NULL, 1, 0, 1, JOF_BYTE|JOF_IC),
+ (NewObject, new_object, NULL, 5, 0, 1, JOF_SHAPE|JOF_IC),
+ (Object, object, NULL, 5, 0, 1, JOF_OBJECT),
+ (ObjWithProto, obj_with_proto, NULL, 1, 1, 1, JOF_BYTE),
+ (InitProp, init_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_IC),
+ (InitHiddenProp, init_hidden_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_IC),
+ (InitLockedProp, init_locked_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_IC),
+ (InitElem, init_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC),
+ (InitHiddenElem, init_hidden_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC),
+ (InitLockedElem, init_locked_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC),
+ (InitPropGetter, init_prop_getter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT),
+ (InitHiddenPropGetter, init_hidden_prop_getter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT),
+ (InitElemGetter, init_elem_getter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT),
+ (InitHiddenElemGetter, init_hidden_elem_getter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT),
+ (InitPropSetter, init_prop_setter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT),
+ (InitHiddenPropSetter, init_hidden_prop_setter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT),
+ (InitElemSetter, init_elem_setter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT),
+ (InitHiddenElemSetter, init_hidden_elem_setter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT),
+ (GetProp, get_prop, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_IC),
+ (GetElem, get_elem, NULL, 1, 2, 1, JOF_BYTE|JOF_ELEM|JOF_IC),
+ (SetProp, set_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSLOPPY|JOF_IC),
+ (StrictSetProp, strict_set_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSTRICT|JOF_IC),
+ (SetElem, set_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSLOPPY|JOF_IC),
+ (StrictSetElem, strict_set_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSTRICT|JOF_IC),
+ (DelProp, del_prop, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_CHECKSLOPPY),
+ (StrictDelProp, strict_del_prop, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_CHECKSTRICT),
+ (DelElem, del_elem, NULL, 1, 2, 1, JOF_BYTE|JOF_ELEM|JOF_CHECKSLOPPY),
+ (StrictDelElem, strict_del_elem, NULL, 1, 2, 1, JOF_BYTE|JOF_ELEM|JOF_CHECKSTRICT),
+ (HasOwn, has_own, NULL, 1, 2, 1, JOF_BYTE|JOF_IC),
+ (CheckPrivateField, check_private_field, NULL, 3, 2, 3, JOF_TWO_UINT8|JOF_CHECKSTRICT|JOF_IC),
+ (NewPrivateName, new_private_name, NULL, 5, 0, 1, JOF_ATOM),
+ (SuperBase, super_base, NULL, 1, 1, 1, JOF_BYTE),
+ (GetPropSuper, get_prop_super, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_IC),
+ (GetElemSuper, get_elem_super, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_IC),
+ (SetPropSuper, set_prop_super, NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSLOPPY),
+ (StrictSetPropSuper, strict_set_prop_super, NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSTRICT),
+ (SetElemSuper, set_elem_super, NULL, 1, 4, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSLOPPY),
+ (StrictSetElemSuper, strict_set_elem_super, NULL, 1, 4, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSTRICT),
+ (Iter, iter, NULL, 1, 1, 1, JOF_BYTE|JOF_IC),
+ (MoreIter, more_iter, NULL, 1, 1, 2, JOF_BYTE),
+ (IsNoIter, is_no_iter, NULL, 1, 1, 2, JOF_BYTE),
+ (EndIter, end_iter, NULL, 1, 2, 0, JOF_BYTE),
+ (CloseIter, close_iter, NULL, 2, 1, 0, JOF_UINT8|JOF_IC),
+ (CheckIsObj, check_is_obj, NULL, 2, 1, 1, JOF_UINT8),
+ (CheckObjCoercible, check_obj_coercible, NULL, 1, 1, 1, JOF_BYTE),
+ (ToAsyncIter, to_async_iter, NULL, 1, 2, 1, JOF_BYTE),
+ (MutateProto, mutate_proto, NULL, 1, 2, 1, JOF_BYTE),
+ (NewArray, new_array, NULL, 5, 0, 1, JOF_UINT32|JOF_IC),
+ (InitElemArray, init_elem_array, NULL, 5, 2, 1, JOF_UINT32|JOF_ELEM|JOF_PROPINIT),
+ (InitElemInc, init_elem_inc, NULL, 1, 3, 2, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC),
+ (Hole, hole, NULL, 1, 0, 1, JOF_BYTE),
+ (RegExp, reg_exp, NULL, 5, 0, 1, JOF_REGEXP),
+ (Lambda, lambda, NULL, 5, 0, 1, JOF_OBJECT),
+ (SetFunName, set_fun_name, NULL, 2, 2, 1, JOF_UINT8),
+ (InitHomeObject, init_home_object, NULL, 1, 2, 1, JOF_BYTE),
+ (CheckClassHeritage, check_class_heritage, NULL, 1, 1, 1, JOF_BYTE),
+ (FunWithProto, fun_with_proto, NULL, 5, 1, 1, JOF_OBJECT),
+ (BuiltinObject, builtin_object, NULL, 2, 0, 1, JOF_UINT8),
+ (Call, call, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC),
+ (CallContent, call_content, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC),
+ (CallIter, call_iter, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC),
+ (CallContentIter, call_content_iter, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC),
+ (CallIgnoresRv, call_ignores_rv, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC),
+ (SpreadCall, spread_call, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_SPREAD|JOF_IC),
+ (OptimizeSpreadCall, optimize_spread_call, NULL, 1, 1, 1, JOF_BYTE|JOF_IC),
+ (Eval, eval, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CHECKSLOPPY|JOF_IC),
+ (SpreadEval, spread_eval, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_SPREAD|JOF_CHECKSLOPPY|JOF_IC),
+ (StrictEval, strict_eval, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CHECKSTRICT|JOF_IC),
+ (StrictSpreadEval, strict_spread_eval, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_SPREAD|JOF_CHECKSTRICT|JOF_IC),
+ (ImplicitThis, implicit_this, "", 5, 0, 1, JOF_ATOM),
+ (CallSiteObj, call_site_obj, NULL, 5, 0, 1, JOF_OBJECT),
+ (IsConstructing, is_constructing, NULL, 1, 0, 1, JOF_BYTE),
+ (New, new_, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CONSTRUCT|JOF_IC),
+ (NewContent, new_content, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CONSTRUCT|JOF_IC),
+ (SuperCall, super_call, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CONSTRUCT|JOF_IC),
+ (SpreadNew, spread_new, NULL, 1, 4, 1, JOF_BYTE|JOF_INVOKE|JOF_CONSTRUCT|JOF_SPREAD|JOF_IC),
+ (SpreadSuperCall, spread_super_call, NULL, 1, 4, 1, JOF_BYTE|JOF_INVOKE|JOF_CONSTRUCT|JOF_SPREAD|JOF_IC),
+ (SuperFun, super_fun, NULL, 1, 1, 1, JOF_BYTE),
+ (CheckThisReinit, check_this_reinit, NULL, 1, 1, 1, JOF_BYTE),
+ (Generator, generator, NULL, 1, 0, 1, JOF_BYTE),
+ (InitialYield, initial_yield, NULL, 4, 1, 3, JOF_RESUMEINDEX),
+ (AfterYield, after_yield, NULL, 5, 0, 0, JOF_ICINDEX),
+ (FinalYieldRval, final_yield_rval, NULL, 1, 1, 0, JOF_BYTE),
+ (Yield, yield, NULL, 4, 2, 3, JOF_RESUMEINDEX),
+ (IsGenClosing, is_gen_closing, NULL, 1, 1, 2, JOF_BYTE),
+ (AsyncAwait, async_await, NULL, 1, 2, 1, JOF_BYTE),
+ (AsyncResolve, async_resolve, NULL, 2, 2, 1, JOF_UINT8),
+ (Await, await, NULL, 4, 2, 3, JOF_RESUMEINDEX),
+ (CanSkipAwait, can_skip_await, NULL, 1, 1, 2, JOF_BYTE),
+ (MaybeExtractAwaitValue, maybe_extract_await_value, NULL, 1, 2, 2, JOF_BYTE),
+ (ResumeKind, resume_kind, NULL, 2, 0, 1, JOF_UINT8),
+ (CheckResumeKind, check_resume_kind, NULL, 1, 3, 1, JOF_BYTE),
+ (Resume, resume, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE),
+ (JumpTarget, jump_target, NULL, 5, 0, 0, JOF_ICINDEX),
+ (LoopHead, loop_head, NULL, 6, 0, 0, JOF_LOOPHEAD),
+ (Goto, goto_, NULL, 5, 0, 0, JOF_JUMP),
+ (JumpIfFalse, jump_if_false, NULL, 5, 1, 0, JOF_JUMP|JOF_IC),
+ (JumpIfTrue, jump_if_true, NULL, 5, 1, 0, JOF_JUMP|JOF_IC),
+ (And, and_, NULL, 5, 1, 1, JOF_JUMP|JOF_IC),
+ (Or, or_, NULL, 5, 1, 1, JOF_JUMP|JOF_IC),
+ (Coalesce, coalesce, NULL, 5, 1, 1, JOF_JUMP),
+ (Case, case_, NULL, 5, 2, 1, JOF_JUMP),
+ (Default, default_, NULL, 5, 1, 0, JOF_JUMP),
+ (TableSwitch, table_switch, NULL, 16, 1, 0, JOF_TABLESWITCH),
+ (Return, return_, NULL, 1, 1, 0, JOF_BYTE),
+ (GetRval, get_rval, NULL, 1, 0, 1, JOF_BYTE),
+ (SetRval, set_rval, NULL, 1, 1, 0, JOF_BYTE),
+ (RetRval, ret_rval, NULL, 1, 0, 0, JOF_BYTE),
+ (CheckReturn, check_return, NULL, 1, 1, 1, JOF_BYTE),
+ (Throw, throw_, NULL, 1, 1, 0, JOF_BYTE),
+ (ThrowMsg, throw_msg, NULL, 2, 0, 0, JOF_UINT8),
+ (ThrowSetConst, throw_set_const, NULL, 5, 0, 0, JOF_ATOM|JOF_NAME),
+ (Try, try_, NULL, 1, 0, 0, JOF_BYTE),
+ (TryDestructuring, try_destructuring, NULL, 1, 0, 0, JOF_BYTE),
+ (Exception, exception, NULL, 1, 0, 1, JOF_BYTE),
+ (Finally, finally, NULL, 1, 0, 0, JOF_BYTE),
+ (Uninitialized, uninitialized, NULL, 1, 0, 1, JOF_BYTE),
+ (InitLexical, init_lexical, NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME),
+ (InitGLexical, init_g_lexical, NULL, 5, 1, 1, JOF_ATOM|JOF_NAME|JOF_PROPINIT|JOF_GNAME|JOF_IC),
+ (InitAliasedLexical, init_aliased_lexical, NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME|JOF_PROPINIT),
+ (CheckLexical, check_lexical, NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME),
+ (CheckAliasedLexical, check_aliased_lexical, NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME),
+ (CheckThis, check_this, NULL, 1, 1, 1, JOF_BYTE),
+ (BindGName, bind_g_name, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_GNAME|JOF_IC),
+ (BindName, bind_name, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_IC),
+ (GetName, get_name, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_IC),
+ (GetGName, get_g_name, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_GNAME|JOF_IC),
+ (GetArg, get_arg, NULL, 3, 0, 1, JOF_QARG|JOF_NAME),
+ (GetLocal, get_local, NULL, 4, 0, 1, JOF_LOCAL|JOF_NAME),
+ (GetAliasedVar, get_aliased_var, NULL, 5, 0, 1, JOF_ENVCOORD|JOF_NAME),
+ (GetAliasedDebugVar, get_aliased_debug_var, NULL, 5, 0, 1, JOF_DEBUGCOORD|JOF_NAME),
+ (GetImport, get_import, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME),
+ (GetBoundName, get_bound_name, NULL, 5, 1, 1, JOF_ATOM|JOF_NAME|JOF_IC),
+ (GetIntrinsic, get_intrinsic, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_IC),
+ (Callee, callee, NULL, 1, 0, 1, JOF_BYTE),
+ (EnvCallee, env_callee, NULL, 2, 0, 1, JOF_UINT8),
+ (SetName, set_name, NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_CHECKSLOPPY|JOF_IC),
+ (StrictSetName, strict_set_name, NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_CHECKSTRICT|JOF_IC),
+ (SetGName, set_g_name, NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_GNAME|JOF_CHECKSLOPPY|JOF_IC),
+ (StrictSetGName, strict_set_g_name, NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_GNAME|JOF_CHECKSTRICT|JOF_IC),
+ (SetArg, set_arg, NULL, 3, 1, 1, JOF_QARG|JOF_NAME),
+ (SetLocal, set_local, NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME),
+ (SetAliasedVar, set_aliased_var, NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME|JOF_PROPSET),
+ (SetIntrinsic, set_intrinsic, NULL, 5, 1, 1, JOF_ATOM|JOF_NAME),
+ (PushLexicalEnv, push_lexical_env, NULL, 5, 0, 0, JOF_SCOPE),
+ (PopLexicalEnv, pop_lexical_env, NULL, 1, 0, 0, JOF_BYTE),
+ (DebugLeaveLexicalEnv, debug_leave_lexical_env, NULL, 1, 0, 0, JOF_BYTE),
+ (RecreateLexicalEnv, recreate_lexical_env, NULL, 5, 0, 0, JOF_SCOPE),
+ (FreshenLexicalEnv, freshen_lexical_env, NULL, 5, 0, 0, JOF_SCOPE),
+ (PushClassBodyEnv, push_class_body_env, NULL, 5, 0, 0, JOF_SCOPE),
+ (PushVarEnv, push_var_env, NULL, 5, 0, 0, JOF_SCOPE),
+ (EnterWith, enter_with, NULL, 5, 1, 0, JOF_SCOPE),
+ (LeaveWith, leave_with, NULL, 1, 0, 0, JOF_BYTE),
+ (BindVar, bind_var, NULL, 1, 0, 1, JOF_BYTE),
+ (GlobalOrEvalDeclInstantiation, global_or_eval_decl_instantiation, NULL, 5, 0, 0, JOF_GCTHING),
+ (DelName, del_name, NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_CHECKSLOPPY),
+ (Arguments, arguments, NULL, 1, 0, 1, JOF_BYTE),
+ (Rest, rest, NULL, 1, 0, 1, JOF_BYTE|JOF_IC),
+ (FunctionThis, function_this, NULL, 1, 0, 1, JOF_BYTE),
+ (Pop, pop, NULL, 1, 1, 0, JOF_BYTE),
+ (PopN, pop_n, NULL, 3, -1, 0, JOF_UINT16),
+ (Dup, dup, NULL, 1, 1, 2, JOF_BYTE),
+ (Dup2, dup2, NULL, 1, 2, 4, JOF_BYTE),
+ (DupAt, dup_at, NULL, 4, 0, 1, JOF_UINT24),
+ (Swap, swap, NULL, 1, 2, 2, JOF_BYTE),
+ (Pick, pick, NULL, 2, 0, 0, JOF_UINT8),
+ (Unpick, unpick, NULL, 2, 0, 0, JOF_UINT8),
+ (Nop, nop, NULL, 1, 0, 0, JOF_BYTE),
+ (Lineno, lineno, NULL, 5, 0, 0, JOF_UINT32),
+ (NopDestructuring, nop_destructuring, NULL, 1, 0, 0, JOF_BYTE),
+ (ForceInterpreter, force_interpreter, NULL, 1, 0, 0, JOF_BYTE),
+ (DebugCheckSelfHosted, debug_check_self_hosted, NULL, 1, 1, 1, JOF_BYTE),
+ (Debugger, debugger, NULL, 1, 0, 0, JOF_BYTE),
+ // @@@@ END OPCODES @@@@
+ ]
+ }
+ }
+}
+
+macro_rules! define_opcode_enum {
+ ( [ $( ( $op:ident , $( $_etc:tt )* ), )* ] ) => {
+ #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+ #[repr(u8)]
+ pub enum Opcode {
+ $( $op, )*
+ }
+ }
+}
+
+using_opcode_database!(define_opcode_enum!());
+
+macro_rules! count_rows {
+ ( [ $( $ignored_row:tt , )* ] ) => {
+ 0 $( + count_rows!(one for $ignored_row) )*
+ };
+
+ ( one for $ignored_row:tt ) => {
+ 1
+ }
+}
+
+const JSOP_LIMIT: usize = using_opcode_database!(count_rows!());
+
+// WARNING
+// The following section is generated by update_stencil.py.
+// Do mot modify manually.
+//
+// @@@@ BEGIN FLAGS @@@@
+/// single bytecode, no immediates
+const JOF_BYTE: u32 = 0;
+
+/// unspecified uint8_t argument
+const JOF_UINT8: u32 = 1;
+
+/// unspecified uint16_t argument
+const JOF_UINT16: u32 = 2;
+
+/// unspecified uint24_t argument
+const JOF_UINT24: u32 = 3;
+
+/// unspecified uint32_t argument
+const JOF_UINT32: u32 = 4;
+
+/// int8_t literal
+const JOF_INT8: u32 = 5;
+
+/// int32_t literal
+const JOF_INT32: u32 = 6;
+
+/// int32_t jump offset
+const JOF_JUMP: u32 = 7;
+
+/// table switch
+const JOF_TABLESWITCH: u32 = 8;
+
+/// embedded ScopeCoordinate immediate
+const JOF_ENVCOORD: u32 = 9;
+
+/// uint16_t argument count
+const JOF_ARGC: u32 = 10;
+
+/// function argument index
+const JOF_QARG: u32 = 11;
+
+/// var or block-local variable
+const JOF_LOCAL: u32 = 12;
+
+/// yield or await resume index
+const JOF_RESUMEINDEX: u32 = 13;
+
+/// inline DoubleValue
+const JOF_DOUBLE: u32 = 14;
+
+/// uint32_t generic gc-thing index
+const JOF_GCTHING: u32 = 15;
+
+/// uint32_t constant index
+const JOF_ATOM: u32 = 16;
+
+/// uint32_t object index
+const JOF_OBJECT: u32 = 17;
+
+/// uint32_t regexp index
+const JOF_REGEXP: u32 = 18;
+
+/// uint32_t scope index
+const JOF_SCOPE: u32 = 19;
+
+/// uint32_t index for BigInt value
+const JOF_BIGINT: u32 = 20;
+
+/// uint32_t IC index
+const JOF_ICINDEX: u32 = 21;
+
+/// JSOp::LoopHead, combines JOF_ICINDEX and JOF_UINT8
+const JOF_LOOPHEAD: u32 = 22;
+
+/// A pair of unspecified uint8_t arguments
+const JOF_TWO_UINT8: u32 = 23;
+
+/// An embedded ScopeCoordinate immediate that may traverse DebugEnvironmentProxies
+const JOF_DEBUGCOORD: u32 = 24;
+
+/// uint32_t shape index
+const JOF_SHAPE: u32 = 25;
+
+/// uint32_t constant index
+const JOF_STRING: u32 = 26;
+
+/// mask for above immediate types
+const JOF_TYPEMASK: u32 = 0xFF;
+
+/// name operation
+const JOF_NAME: u32 = 1 << 8;
+
+/// obj.prop operation
+const JOF_PROP: u32 = 2 << 8;
+
+/// obj[index] operation
+const JOF_ELEM: u32 = 3 << 8;
+
+/// property/element/name set operation
+const JOF_PROPSET: u32 = 1 << 16;
+
+/// property/element/name init operation
+const JOF_PROPINIT: u32 = 1 << 17;
+
+/// op can only be generated in sloppy mode
+const JOF_CHECKSLOPPY: u32 = 1 << 18;
+
+/// op can only be generated in strict mode
+const JOF_CHECKSTRICT: u32 = 1 << 19;
+
+/// any call, construct, or eval instruction
+const JOF_INVOKE: u32 = 1 << 20;
+
+/// invoke instruction using [[Construct]] entry
+const JOF_CONSTRUCT: u32 = 1 << 21;
+
+/// invoke instruction using spread argument
+const JOF_SPREAD: u32 = 1 << 22;
+
+/// predicted global name
+const JOF_GNAME: u32 = 1 << 23;
+
+/// baseline may use an IC for this op
+const JOF_IC: u32 = 1 << 24;
+
+// @@@@ END FLAGS @@@@
+
+impl Opcode {
+ /// Return the numeric bytecode value for this opcode, as understood by the
+ /// SpiderMonkey interpreter and the rest of the VM.
+ pub fn to_byte(&self) -> u8 {
+ *self as u8
+ }
+
+ /// True if this opcode takes no operands (is a one-byte opcode) and
+ /// implements a unary operator that operates on one stack value.
+ ///
+ /// The operators `typeof` and `delete` have different bytecode patterns
+ /// and thus are not considered "simple"; `new` is considered a special
+ /// kind of function call, not a unary operator.
+ pub fn is_simple_unary_operator(self) -> bool {
+ self == Opcode::Pos
+ || self == Opcode::Neg
+ || self == Opcode::Not
+ || self == Opcode::BitNot
+ || self == Opcode::Void
+ }
+
+ /// True if this opcode takes no operands (is a one-byte opcode) and
+ /// implements a binary operator that operates on two stack values.
+ ///
+ /// The operators `||`, `&&`, and `,` have different bytecode patterns
+ /// and thus are not considered "simple".
+ pub fn is_simple_binary_operator(self) -> bool {
+ self == Opcode::BitOr
+ || self == Opcode::BitXor
+ || self == Opcode::BitAnd
+ || self == Opcode::Eq
+ || self == Opcode::Ne
+ || self == Opcode::StrictEq
+ || self == Opcode::StrictNe
+ || self == Opcode::Lt
+ || self == Opcode::Gt
+ || self == Opcode::Le
+ || self == Opcode::Ge
+ || self == Opcode::Instanceof
+ || self == Opcode::In
+ || self == Opcode::Lsh
+ || self == Opcode::Rsh
+ || self == Opcode::Ursh
+ || self == Opcode::Add
+ || self == Opcode::Sub
+ || self == Opcode::Mul
+ || self == Opcode::Div
+ || self == Opcode::Mod
+ || self == Opcode::Pow
+ }
+
+ pub fn is_jump_target(self) -> bool {
+ self == Opcode::JumpTarget || self == Opcode::LoopHead || self == Opcode::AfterYield
+ }
+
+ pub fn is_jump(self) -> bool {
+ self == Opcode::Goto
+ || self == Opcode::JumpIfFalse
+ || self == Opcode::JumpIfTrue
+ || self == Opcode::Or
+ || self == Opcode::And
+ || self == Opcode::Coalesce
+ }
+}
+
+impl TryFrom<u8> for Opcode {
+ type Error = ();
+
+ fn try_from(value: u8) -> Result<Opcode, ()> {
+ if (value as usize) < JSOP_LIMIT {
+ Ok(TABLE[value as usize])
+ } else {
+ Err(())
+ }
+ }
+}
+
+macro_rules! define_table {
+ ( [ $(
+ (
+ $op:ident , $op_snake:ident , $str:expr ,
+ $length:expr , $nuses:expr , $ndefs:expr , $format:expr
+ ) ,
+ )* ] ) => {
+ pub static TABLE: [Opcode; JSOP_LIMIT] = [
+ $( Opcode::$op , )*
+ ];
+ }
+}
+
+using_opcode_database!(define_table!());
+
+impl Opcode {
+ /// Length of this instruction, in bytes.
+ pub fn instruction_length(self) -> usize {
+ macro_rules! select_length {
+ ( [ $(
+ (
+ $op:ident , $op_snake:ident , $str:expr ,
+ $length:expr , $nuses:expr , $ndefs:expr , $format:expr
+ ) ,
+ )* ] ) => {
+ match self {
+ $( Opcode::$op => $length , )*
+ }
+ }
+ }
+
+ using_opcode_database!(select_length!())
+ }
+
+ /// Number of stack slots consumed by this instruction, or -1 for variadic
+ /// instructions.
+ pub fn nuses(self) -> isize {
+ macro_rules! select_nuses {
+ ( [ $(
+ (
+ $op:ident , $op_snake:ident , $str:expr ,
+ $length:expr , $nuses:expr , $ndefs:expr , $format:expr
+ ) ,
+ )* ] ) => {
+ match self {
+ $( Opcode::$op => $nuses , )*
+ }
+ }
+ }
+
+ using_opcode_database!(select_nuses!())
+ }
+
+ pub fn ndefs(self) -> usize {
+ macro_rules! select_ndefs {
+ ( [ $(
+ (
+ $op:ident , $op_snake:ident , $str:expr ,
+ $length:expr , $nuses:expr , $ndefs:expr , $format:expr
+ ) ,
+ )* ] ) => {
+ match self {
+ $( Opcode::$op => $ndefs , )*
+ }
+ }
+ }
+
+ using_opcode_database!(select_ndefs!())
+ }
+
+ fn format_bits(self) -> u32 {
+ macro_rules! select_format {
+ ( [ $(
+ (
+ $op:ident , $op_snake:ident , $str:expr ,
+ $length:expr , $nuses:expr , $ndefs:expr , $format:expr
+ ) ,
+ )* ] ) => {
+ match self {
+ $( Opcode::$op => $format , )*
+ }
+ }
+ }
+
+ using_opcode_database!(select_format!())
+ }
+
+ pub fn has_ic_entry(self) -> bool {
+ self.format_bits() & JOF_IC != 0
+ }
+
+ pub fn has_ic_index(self) -> bool {
+ match self.format_bits() & JOF_TYPEMASK {
+ JOF_LOOPHEAD | JOF_ICINDEX => true,
+ _ => false,
+ }
+ }
+
+ pub fn has_argc(self) -> bool {
+ self.format_bits() & JOF_TYPEMASK == JOF_ARGC
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn table_matches() {
+ for (i, t) in TABLE.iter().enumerate() {
+ assert_eq!(i, t.to_byte() as usize);
+ }
+ }
+}
diff --git a/third_party/rust/jsparagus-stencil/src/opcode_info.rs b/third_party/rust/jsparagus-stencil/src/opcode_info.rs
new file mode 100644
index 0000000000..a841cc0774
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/opcode_info.rs
@@ -0,0 +1,47 @@
+pub fn get_async_function_resolve_kind() -> &'static str {
+ include_str!("copy/AsyncFunctionResolveKind.h")
+}
+
+pub fn get_bytecode_format_flags() -> &'static str {
+ include_str!("copy/BytecodeFormatFlags.h")
+}
+
+pub fn get_check_is_object_kind() -> &'static str {
+ include_str!("copy/CheckIsObjectKind.h")
+}
+
+pub fn get_function_flags() -> &'static str {
+ include_str!("copy/FunctionFlags.h")
+}
+
+pub fn get_generator_and_async_kind() -> &'static str {
+ include_str!("copy/GeneratorAndAsyncKind.h")
+}
+
+pub fn get_function_prefix_kind() -> &'static str {
+ include_str!("copy/FunctionPrefixKind.h")
+}
+
+pub fn get_generator_resume_kind() -> &'static str {
+ include_str!("copy/GeneratorResumeKind.h")
+}
+
+pub fn get_opcodes_source() -> &'static str {
+ include_str!("copy/Opcodes.h")
+}
+
+pub fn get_source_notes() -> &'static str {
+ include_str!("copy/SourceNotes.h")
+}
+
+pub fn get_stencil_enums() -> &'static str {
+ include_str!("copy/StencilEnums.h")
+}
+
+pub fn get_symbol() -> &'static str {
+ include_str!("copy/Symbol.h")
+}
+
+pub fn get_throw_msg_kind() -> &'static str {
+ include_str!("copy/ThrowMsgKind.h")
+}
diff --git a/third_party/rust/jsparagus-stencil/src/regexp.rs b/third_party/rust/jsparagus-stencil/src/regexp.rs
new file mode 100644
index 0000000000..47356b1fd3
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/regexp.rs
@@ -0,0 +1,54 @@
+use ast::source_slice_list::SourceSliceIndex;
+
+#[derive(Debug)]
+pub struct RegExpItem {
+ pub pattern: SourceSliceIndex,
+ pub global: bool,
+ pub ignore_case: bool,
+ pub multi_line: bool,
+ pub dot_all: bool,
+ pub sticky: bool,
+ pub unicode: bool,
+}
+
+/// Index into RegExpList.atoms.
+#[derive(Debug, Clone, Copy)]
+pub struct RegExpIndex {
+ index: usize,
+}
+
+impl RegExpIndex {
+ fn new(index: usize) -> Self {
+ Self { index }
+ }
+}
+
+impl From<RegExpIndex> for usize {
+ fn from(index: RegExpIndex) -> usize {
+ index.index
+ }
+}
+
+/// List of RegExp.
+#[derive(Debug)]
+pub struct RegExpList {
+ items: Vec<RegExpItem>,
+}
+
+impl RegExpList {
+ pub fn new() -> Self {
+ Self { items: Vec::new() }
+ }
+
+ pub fn push(&mut self, item: RegExpItem) -> RegExpIndex {
+ let index = self.items.len();
+ self.items.push(item);
+ RegExpIndex::new(index)
+ }
+}
+
+impl From<RegExpList> for Vec<RegExpItem> {
+ fn from(list: RegExpList) -> Vec<RegExpItem> {
+ list.items
+ }
+}
diff --git a/third_party/rust/jsparagus-stencil/src/result.rs b/third_party/rust/jsparagus-stencil/src/result.rs
new file mode 100644
index 0000000000..6cb2fb7716
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/result.rs
@@ -0,0 +1,35 @@
+use crate::regexp::RegExpItem;
+use crate::scope::ScopeData;
+use crate::script::{ImmutableScriptData, ScriptStencil};
+
+/// The result of emitter.
+#[derive(Debug)]
+pub struct EmitResult<'alloc> {
+ pub atoms: Vec<&'alloc str>,
+ pub slices: Vec<&'alloc str>,
+ pub scopes: Vec<ScopeData>,
+ pub regexps: Vec<RegExpItem>,
+
+ pub scripts: Vec<ScriptStencil>,
+ pub script_data_list: Vec<ImmutableScriptData>,
+}
+
+impl<'alloc> EmitResult<'alloc> {
+ pub fn new(
+ atoms: Vec<&'alloc str>,
+ slices: Vec<&'alloc str>,
+ scopes: Vec<ScopeData>,
+ regexps: Vec<RegExpItem>,
+ scripts: Vec<ScriptStencil>,
+ script_data_list: Vec<ImmutableScriptData>,
+ ) -> Self {
+ Self {
+ atoms,
+ slices,
+ scopes,
+ regexps,
+ scripts,
+ script_data_list,
+ }
+ }
+}
diff --git a/third_party/rust/jsparagus-stencil/src/scope.rs b/third_party/rust/jsparagus-stencil/src/scope.rs
new file mode 100644
index 0000000000..9b4fa3a94b
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/scope.rs
@@ -0,0 +1,639 @@
+//! Types for the output of scope analysis.
+//!
+//! The top-level output of this analysis is the `ScopeDataMap`, which holds
+//! the following:
+//! * `LexicalScopeData` for each lexial scope in the AST
+//! * `GlobalScopeData` for the global scope
+//! * `VarScopeData` for extra body var scope in function
+//! * `FunctionScopeData` for the function scope
+//!
+//! Each scope contains a list of bindings (`BindingName`).
+
+use crate::frame_slot::FrameSlot;
+use crate::script::ScriptStencilIndex;
+use ast::associated_data::AssociatedData;
+use ast::source_atom_set::SourceAtomSetIndex;
+use ast::source_location_accessor::SourceLocationAccessor;
+use ast::type_id::NodeTypeIdAccessor;
+
+/// Corresponds to js::BindingKind in m-c/js/src/vm/Scope.h.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum BindingKind {
+ Var,
+ Let,
+ Const,
+}
+
+/// Information about a single binding in a JS script.
+///
+/// Corresponds to `js::BindingName` in m-c/js/src/vm/Scope.h.
+#[derive(Debug)]
+pub struct BindingName {
+ pub name: SourceAtomSetIndex,
+ pub is_closed_over: bool,
+ pub is_top_level_function: bool,
+}
+
+impl BindingName {
+ pub fn new(name: SourceAtomSetIndex, is_closed_over: bool) -> Self {
+ Self {
+ name,
+ is_closed_over,
+ is_top_level_function: false,
+ }
+ }
+
+ pub fn new_top_level_function(name: SourceAtomSetIndex, is_closed_over: bool) -> Self {
+ Self {
+ name,
+ is_closed_over,
+ is_top_level_function: true,
+ }
+ }
+}
+
+/// Corresponds to the accessor part of `js::BindingIter` in
+/// m-c/js/src/vm/Scope.h.
+pub struct BindingIterItem<'a> {
+ name: &'a BindingName,
+ kind: BindingKind,
+}
+
+impl<'a> BindingIterItem<'a> {
+ fn new(name: &'a BindingName, kind: BindingKind) -> Self {
+ Self { name, kind }
+ }
+
+ pub fn name(&self) -> SourceAtomSetIndex {
+ self.name.name
+ }
+
+ pub fn is_top_level_function(&self) -> bool {
+ self.name.is_top_level_function
+ }
+
+ pub fn is_closed_over(&self) -> bool {
+ self.name.is_closed_over
+ }
+
+ pub fn kind(&self) -> BindingKind {
+ self.kind
+ }
+}
+
+/// Accessor for both BindingName/Option<BindingName>.
+pub trait MaybeBindingName {
+ fn is_closed_over(&self) -> bool;
+}
+impl MaybeBindingName for BindingName {
+ fn is_closed_over(&self) -> bool {
+ self.is_closed_over
+ }
+}
+impl MaybeBindingName for Option<BindingName> {
+ fn is_closed_over(&self) -> bool {
+ match self {
+ Some(b) => b.is_closed_over,
+ None => false,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct BaseScopeData<BindingItemT>
+where
+ BindingItemT: MaybeBindingName,
+{
+ /// Corresponds to `*Scope::Data.{length, trailingNames}.`
+ /// The layout is defined by *ScopeData structs below, that has
+ /// this struct.
+ pub bindings: Vec<BindingItemT>,
+ pub has_eval: bool,
+ pub has_with: bool,
+}
+
+impl<BindingItemT> BaseScopeData<BindingItemT>
+where
+ BindingItemT: MaybeBindingName,
+{
+ pub fn new(bindings_count: usize) -> Self {
+ Self {
+ bindings: Vec::with_capacity(bindings_count),
+ has_eval: false,
+ has_with: false,
+ }
+ }
+
+ pub fn is_all_bindings_closed_over(&self) -> bool {
+ // `with` and direct `eval` can dynamically access any binding in this
+ // scope.
+ self.has_eval || self.has_with
+ }
+
+ /// Returns true if this scope needs to be allocated on heap as
+ /// EnvironmentObject.
+ pub fn needs_environment_object(&self) -> bool {
+ // `with` and direct `eval` can dynamically access bindings in this
+ // scope.
+ if self.is_all_bindings_closed_over() {
+ return true;
+ }
+
+ // If a binding in this scope is closed over by inner function,
+ // it can be accessed when this frame isn't on the stack.
+ for binding in &self.bindings {
+ if binding.is_closed_over() {
+ return true;
+ }
+ }
+
+ false
+ }
+}
+
+/// Bindings created in global environment.
+///
+/// Maps to js::GlobalScope::Data in m-c/js/src/vm/Scope.h.
+#[derive(Debug)]
+pub struct GlobalScopeData {
+ /// Bindings are sorted by kind:
+ ///
+ /// * `base.bindings[0..let_start]` - `var`s
+ /// * `base.bindings[let_start..const_start]` - `let`s
+ /// * `base.bindings[const_start..]` - `const`s
+ pub base: BaseScopeData<BindingName>,
+
+ pub let_start: usize,
+ pub const_start: usize,
+
+ /// The global functions in this script.
+ pub functions: Vec<ScriptStencilIndex>,
+}
+
+impl GlobalScopeData {
+ pub fn new(
+ var_count: usize,
+ let_count: usize,
+ const_count: usize,
+ functions: Vec<ScriptStencilIndex>,
+ ) -> Self {
+ let capacity = var_count + let_count + const_count;
+
+ Self {
+ base: BaseScopeData::new(capacity),
+ let_start: var_count,
+ const_start: var_count + let_count,
+ functions,
+ }
+ }
+
+ pub fn iter<'a>(&'a self) -> GlobalBindingIter<'a> {
+ GlobalBindingIter::new(self)
+ }
+}
+
+/// Corresponds to the iteration part of js::BindingIter
+/// in m-c/js/src/vm/Scope.h.
+pub struct GlobalBindingIter<'a> {
+ data: &'a GlobalScopeData,
+ i: usize,
+}
+
+impl<'a> GlobalBindingIter<'a> {
+ fn new(data: &'a GlobalScopeData) -> Self {
+ Self { data, i: 0 }
+ }
+}
+
+impl<'a> Iterator for GlobalBindingIter<'a> {
+ type Item = BindingIterItem<'a>;
+
+ fn next(&mut self) -> Option<BindingIterItem<'a>> {
+ if self.i == self.data.base.bindings.len() {
+ return None;
+ }
+
+ let kind = if self.i < self.data.let_start {
+ BindingKind::Var
+ } else if self.i < self.data.const_start {
+ BindingKind::Let
+ } else {
+ BindingKind::Const
+ };
+
+ let name = &self.data.base.bindings[self.i];
+
+ self.i += 1;
+
+ Some(BindingIterItem::new(name, kind))
+ }
+}
+
+/// Bindings created in var environment.
+///
+/// Maps to js::VarScope::Data in m-c/js/src/vm/Scope.h,
+/// and the parameter for js::frontend::ScopeCreationData::create
+/// in m-c/js/src/frontend/Stencil.h
+#[derive(Debug)]
+pub struct VarScopeData {
+ /// All bindings are `var`.
+ pub base: BaseScopeData<BindingName>,
+
+ /// The first frame slot of this scope.
+ ///
+ /// Unlike VarScope::Data, this struct holds the first frame slot,
+ /// instead of the next frame slot.
+ ///
+ /// This is because ScopeCreationData::create receives the first frame slot
+ /// and VarScope::Data.nextFrameSlot is calculated there.
+ pub first_frame_slot: FrameSlot,
+
+ pub function_has_extensible_scope: bool,
+
+ /// ScopeIndex of the enclosing scope.
+ ///
+ /// A parameter for ScopeCreationData::create.
+ pub enclosing: ScopeIndex,
+}
+
+impl VarScopeData {
+ pub fn new(
+ var_count: usize,
+ function_has_extensible_scope: bool,
+ enclosing: ScopeIndex,
+ ) -> Self {
+ let capacity = var_count;
+
+ Self {
+ base: BaseScopeData::new(capacity),
+ // Set to the correct value in EmitterScopeStack::enter_lexical.
+ first_frame_slot: FrameSlot::new(0),
+ function_has_extensible_scope,
+ enclosing,
+ }
+ }
+}
+
+/// Bindings created in lexical environment.
+///
+/// Maps to js::LexicalScope::Data in m-c/js/src/vm/Scope.h,
+/// and the parameter for js::frontend::ScopeCreationData::create
+/// in m-c/js/src/frontend/Stencil.h
+#[derive(Debug)]
+pub struct LexicalScopeData {
+ /// Bindings are sorted by kind:
+ ///
+ /// * `base.bindings[0..const_start]` - `let`s
+ /// * `base.bindings[const_start..]` - `const`s
+ pub base: BaseScopeData<BindingName>,
+
+ pub const_start: usize,
+
+ /// The first frame slot of this scope.
+ ///
+ /// Unlike LexicalScope::Data, this struct holds the first frame slot,
+ /// instead of the next frame slot.
+ ///
+ /// This is because ScopeCreationData::create receives the first frame slot
+ /// and LexicalScope::Data.nextFrameSlot is calculated there.
+ pub first_frame_slot: FrameSlot,
+
+ /// ScopeIndex of the enclosing scope.
+ ///
+ /// A parameter for ScopeCreationData::create.
+ pub enclosing: ScopeIndex,
+
+ /// Functions in this scope.
+ pub functions: Vec<ScriptStencilIndex>,
+}
+
+impl LexicalScopeData {
+ fn new(
+ let_count: usize,
+ const_count: usize,
+ enclosing: ScopeIndex,
+ functions: Vec<ScriptStencilIndex>,
+ ) -> Self {
+ let capacity = let_count + const_count;
+
+ Self {
+ base: BaseScopeData::new(capacity),
+ const_start: let_count,
+ // Set to the correct value in EmitterScopeStack::enter_lexical.
+ first_frame_slot: FrameSlot::new(0),
+ enclosing,
+ functions,
+ }
+ }
+
+ pub fn new_block(
+ let_count: usize,
+ const_count: usize,
+ enclosing: ScopeIndex,
+ functions: Vec<ScriptStencilIndex>,
+ ) -> Self {
+ Self::new(let_count, const_count, enclosing, functions)
+ }
+
+ pub fn new_named_lambda(enclosing: ScopeIndex) -> Self {
+ Self::new(0, 1, enclosing, Vec::new())
+ }
+
+ pub fn new_function_lexical(
+ let_count: usize,
+ const_count: usize,
+ enclosing: ScopeIndex,
+ ) -> Self {
+ Self::new(let_count, const_count, enclosing, Vec::new())
+ }
+
+ pub fn iter<'a>(&'a self) -> LexicalBindingIter<'a> {
+ LexicalBindingIter::new(self)
+ }
+}
+
+/// Corresponds to the iteration part of js::BindingIter
+/// in m-c/js/src/vm/Scope.h.
+pub struct LexicalBindingIter<'a> {
+ data: &'a LexicalScopeData,
+ i: usize,
+}
+
+impl<'a> LexicalBindingIter<'a> {
+ fn new(data: &'a LexicalScopeData) -> Self {
+ Self { data, i: 0 }
+ }
+}
+
+impl<'a> Iterator for LexicalBindingIter<'a> {
+ type Item = BindingIterItem<'a>;
+
+ fn next(&mut self) -> Option<BindingIterItem<'a>> {
+ if self.i == self.data.base.bindings.len() {
+ return None;
+ }
+
+ let kind = if self.i < self.data.const_start {
+ BindingKind::Let
+ } else {
+ BindingKind::Const
+ };
+
+ let name = &self.data.base.bindings[self.i];
+
+ self.i += 1;
+
+ Some(BindingIterItem::new(name, kind))
+ }
+}
+
+/// Bindings created in function environment.
+///
+/// Maps to js::FunctionScope::Data in m-c/js/src/vm/Scope.h,
+/// and the parameter for js::frontend::ScopeCreationData::create
+/// in m-c/js/src/frontend/Stencil.h
+#[derive(Debug)]
+pub struct FunctionScopeData {
+ /// Bindings are sorted by kind:
+ ///
+ /// * `base.bindings[0..non_positional_formal_start]` -
+ /// positional foparameters:
+ /// - single binding parameter with/without default
+ /// - single binding rest parameter
+ /// * `base.bindings[non_positional_formal_start..var_start]` -
+ /// non positional parameters:
+ /// - destructuring parameter
+ /// - destructuring rest parameter
+ /// * `base.bindings[var_start..]` - `var`s
+ ///
+ /// Given positional parameters range can have null slot for destructuring,
+ /// use Vec of Option<BindingName>, instead of BindingName like others.
+ pub base: BaseScopeData<Option<BindingName>>,
+
+ pub has_parameter_exprs: bool,
+
+ pub non_positional_formal_start: usize,
+ pub var_start: usize,
+
+ /// The first frame slot of this scope.
+ ///
+ /// Unlike FunctionScope::Data, this struct holds the first frame slot,
+ /// instead of the next frame slot.
+ ///
+ /// This is because ScopeCreationData::create receives the first frame slot
+ /// and FunctionScope::Data.nextFrameSlot is calculated there.
+ pub first_frame_slot: FrameSlot,
+
+ /// ScopeIndex of the enclosing scope.
+ ///
+ /// A parameter for ScopeCreationData::create.
+ pub enclosing: ScopeIndex,
+
+ pub function_index: ScriptStencilIndex,
+
+ /// True if the function is an arrow function.
+ pub is_arrow: bool,
+}
+
+impl FunctionScopeData {
+ pub fn new(
+ has_parameter_exprs: bool,
+ positional_parameter_count: usize,
+ non_positional_formal_start: usize,
+ max_var_count: usize,
+ enclosing: ScopeIndex,
+ function_index: ScriptStencilIndex,
+ is_arrow: bool,
+ ) -> Self {
+ let capacity = positional_parameter_count + non_positional_formal_start + max_var_count;
+
+ Self {
+ base: BaseScopeData::new(capacity),
+ has_parameter_exprs,
+ non_positional_formal_start: positional_parameter_count,
+ var_start: positional_parameter_count + non_positional_formal_start,
+ // Set to the correct value in EmitterScopeStack::enter_function.
+ first_frame_slot: FrameSlot::new(0),
+ enclosing,
+ function_index,
+ is_arrow,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum ScopeData {
+ /// No scope should be generated, but this scope becomes an alias to
+ /// enclosing scope. This is used, for example, when we see a function,
+ /// and set aside a ScopeData for its lexical bindings, but upon
+ /// reaching the end of the function body, we find that there were no
+ /// lexical bindings and the spec actually says not to generate a Lexical
+ /// Environment when this function is called.
+ ///
+ /// In other places, the spec does say to create a Lexical Environment, but
+ /// it turns out it doesn't have any bindings in it and we can optimize it
+ /// away.
+ ///
+ /// NOTE: Alias can be chained.
+ Alias(ScopeIndex),
+
+ Global(GlobalScopeData),
+ Var(VarScopeData),
+ Lexical(LexicalScopeData),
+ Function(FunctionScopeData),
+}
+
+/// Index into ScopeDataList.scopes.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct ScopeIndex {
+ index: usize,
+}
+impl ScopeIndex {
+ fn new(index: usize) -> Self {
+ Self { index }
+ }
+
+ pub fn next(&self) -> Self {
+ Self {
+ index: self.index + 1,
+ }
+ }
+}
+
+impl From<ScopeIndex> for usize {
+ fn from(index: ScopeIndex) -> usize {
+ index.index
+ }
+}
+
+/// A vector of scopes, incrementally populated during analysis.
+/// The goal is to build a `Vec<ScopeData>`.
+#[derive(Debug)]
+pub struct ScopeDataList {
+ /// Uses Option to allow `allocate()` and `populate()` to be called
+ /// separately.
+ scopes: Vec<Option<ScopeData>>,
+}
+
+impl ScopeDataList {
+ pub fn new() -> Self {
+ Self { scopes: Vec::new() }
+ }
+
+ pub fn push(&mut self, scope: ScopeData) -> ScopeIndex {
+ let index = self.scopes.len();
+ self.scopes.push(Some(scope));
+ ScopeIndex::new(index)
+ }
+
+ pub fn allocate(&mut self) -> ScopeIndex {
+ let index = self.scopes.len();
+ self.scopes.push(None);
+ ScopeIndex::new(index)
+ }
+
+ pub fn populate(&mut self, index: ScopeIndex, scope: ScopeData) {
+ self.scopes[usize::from(index)].replace(scope);
+ }
+
+ fn get(&self, index: ScopeIndex) -> &ScopeData {
+ self.scopes[usize::from(index)]
+ .as_ref()
+ .expect("Should be populated")
+ }
+
+ pub fn get_mut(&mut self, index: ScopeIndex) -> &mut ScopeData {
+ self.scopes[usize::from(index)]
+ .as_mut()
+ .expect("Should be populated")
+ }
+}
+
+impl From<ScopeDataList> for Vec<ScopeData> {
+ fn from(list: ScopeDataList) -> Vec<ScopeData> {
+ list.scopes
+ .into_iter()
+ .map(|g| g.expect("Should be populated"))
+ .collect()
+ }
+}
+
+/// The collection of all scope data associated with bindings and scopes in the
+/// AST.
+#[derive(Debug)]
+pub struct ScopeDataMap {
+ scopes: ScopeDataList,
+ global: ScopeIndex,
+
+ /// Associates every AST node that's a scope with an index into `scopes`.
+ non_global: AssociatedData<ScopeIndex>,
+}
+
+impl ScopeDataMap {
+ pub fn new(
+ scopes: ScopeDataList,
+ global: ScopeIndex,
+ non_global: AssociatedData<ScopeIndex>,
+ ) -> Self {
+ Self {
+ scopes,
+ global,
+ non_global,
+ }
+ }
+
+ pub fn get_global_index(&self) -> ScopeIndex {
+ self.global
+ }
+
+ pub fn get_global_at(&self, index: ScopeIndex) -> &GlobalScopeData {
+ match self.scopes.get(index) {
+ ScopeData::Global(scope) => scope,
+ _ => panic!("Unexpected scope data for global"),
+ }
+ }
+
+ pub fn get_index<NodeT>(&self, node: &NodeT) -> ScopeIndex
+ where
+ NodeT: SourceLocationAccessor + NodeTypeIdAccessor,
+ {
+ self.non_global
+ .get(node)
+ .expect("There should be a scope data associated")
+ .clone()
+ }
+
+ pub fn get_lexical_at(&self, index: ScopeIndex) -> &LexicalScopeData {
+ match self.scopes.get(index) {
+ ScopeData::Lexical(scope) => scope,
+ _ => panic!("Unexpected scope data for lexical"),
+ }
+ }
+
+ pub fn get_lexical_at_mut(&mut self, index: ScopeIndex) -> &mut LexicalScopeData {
+ match self.scopes.get_mut(index) {
+ ScopeData::Lexical(scope) => scope,
+ _ => panic!("Unexpected scope data for lexical"),
+ }
+ }
+
+ pub fn get_function_at_mut(&mut self, index: ScopeIndex) -> &mut FunctionScopeData {
+ match self.scopes.get_mut(index) {
+ ScopeData::Function(scope) => scope,
+ _ => panic!("Unexpected scope data for function"),
+ }
+ }
+
+ pub fn is_alias(&mut self, index: ScopeIndex) -> bool {
+ match self.scopes.get(index) {
+ ScopeData::Alias(_) => true,
+ _ => false,
+ }
+ }
+}
+
+impl From<ScopeDataMap> for Vec<ScopeData> {
+ fn from(map: ScopeDataMap) -> Vec<ScopeData> {
+ map.scopes.into()
+ }
+}
diff --git a/third_party/rust/jsparagus-stencil/src/scope_notes.rs b/third_party/rust/jsparagus-stencil/src/scope_notes.rs
new file mode 100644
index 0000000000..fb9a494775
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/scope_notes.rs
@@ -0,0 +1,81 @@
+use crate::bytecode_offset::BytecodeOffset;
+use crate::gcthings::GCThingIndex;
+
+/// Maps to js::ScopeNote in m-c/js/src/vm//JSScript.h.
+#[derive(Debug)]
+pub struct ScopeNote {
+ pub index: GCThingIndex,
+ pub start: BytecodeOffset,
+ pub end: BytecodeOffset,
+ pub parent: Option<ScopeNoteIndex>,
+}
+
+impl ScopeNote {
+ fn new(index: GCThingIndex, start: BytecodeOffset, parent: Option<ScopeNoteIndex>) -> Self {
+ Self {
+ index,
+ start: start.clone(),
+ end: start,
+ parent,
+ }
+ }
+}
+
+/// Index into ScopeNoteList.notes.
+#[derive(Debug, Clone, Copy)]
+pub struct ScopeNoteIndex {
+ index: usize,
+}
+
+impl ScopeNoteIndex {
+ fn new(index: usize) -> Self {
+ Self { index }
+ }
+}
+
+impl From<ScopeNoteIndex> for usize {
+ fn from(index: ScopeNoteIndex) -> usize {
+ index.index
+ }
+}
+
+/// List of scope notes.
+///
+/// Maps to JSScript::scopeNotes() in js/src/vm/JSScript.h.
+pub struct ScopeNoteList {
+ notes: Vec<ScopeNote>,
+}
+
+impl ScopeNoteList {
+ pub fn new() -> Self {
+ Self { notes: Vec::new() }
+ }
+
+ pub fn enter_scope(
+ &mut self,
+ index: GCThingIndex,
+ offset: BytecodeOffset,
+ parent: Option<ScopeNoteIndex>,
+ ) -> ScopeNoteIndex {
+ let note_index = self.notes.len();
+ self.notes.push(ScopeNote::new(index, offset, parent));
+ ScopeNoteIndex::new(note_index)
+ }
+
+ pub fn get_scope_hole_gcthing_index(&self, scope_note_index: &ScopeNoteIndex) -> GCThingIndex {
+ self.notes
+ .get(scope_note_index.index)
+ .expect("Scope note should exist")
+ .index
+ }
+
+ pub fn leave_scope(&mut self, index: ScopeNoteIndex, offset: BytecodeOffset) {
+ self.notes[usize::from(index)].end = offset;
+ }
+}
+
+impl From<ScopeNoteList> for Vec<ScopeNote> {
+ fn from(list: ScopeNoteList) -> Vec<ScopeNote> {
+ list.notes
+ }
+}
diff --git a/third_party/rust/jsparagus-stencil/src/script.rs b/third_party/rust/jsparagus-stencil/src/script.rs
new file mode 100644
index 0000000000..ab97b88177
--- /dev/null
+++ b/third_party/rust/jsparagus-stencil/src/script.rs
@@ -0,0 +1,574 @@
+//! The result of emitter
+
+use crate::frame_slot::FrameSlot;
+use crate::function::FunctionFlags;
+use crate::gcthings::GCThing;
+use crate::scope::ScopeIndex;
+use crate::scope_notes::ScopeNote;
+use ast::source_atom_set::SourceAtomSetIndex;
+
+// WARNING
+// The following section is generated by update_stencil.py.
+// Do mot modify manually.
+//
+// @@@@ BEGIN TYPES @@@@
+#[derive(Debug, Clone, Copy)]
+pub enum ImmutableScriptFlagsEnum {
+ #[allow(dead_code)]
+ IsForEval = 1 << 0,
+ #[allow(dead_code)]
+ IsModule = 1 << 1,
+ #[allow(dead_code)]
+ IsFunction = 1 << 2,
+ #[allow(dead_code)]
+ SelfHosted = 1 << 3,
+ #[allow(dead_code)]
+ ForceStrict = 1 << 4,
+ #[allow(dead_code)]
+ HasNonSyntacticScope = 1 << 5,
+ #[allow(dead_code)]
+ NoScriptRval = 1 << 6,
+ #[allow(dead_code)]
+ TreatAsRunOnce = 1 << 7,
+ #[allow(dead_code)]
+ Strict = 1 << 8,
+ #[allow(dead_code)]
+ HasModuleGoal = 1 << 9,
+ #[allow(dead_code)]
+ HasInnerFunctions = 1 << 10,
+ #[allow(dead_code)]
+ HasDirectEval = 1 << 11,
+ #[allow(dead_code)]
+ BindingsAccessedDynamically = 1 << 12,
+ #[allow(dead_code)]
+ HasCallSiteObj = 1 << 13,
+ #[allow(dead_code)]
+ IsAsync = 1 << 14,
+ #[allow(dead_code)]
+ IsGenerator = 1 << 15,
+ #[allow(dead_code)]
+ FunHasExtensibleScope = 1 << 16,
+ #[allow(dead_code)]
+ FunctionHasThisBinding = 1 << 17,
+ #[allow(dead_code)]
+ NeedsHomeObject = 1 << 18,
+ #[allow(dead_code)]
+ IsDerivedClassConstructor = 1 << 19,
+ #[allow(dead_code)]
+ IsSyntheticFunction = 1 << 20,
+ #[allow(dead_code)]
+ UseMemberInitializers = 1 << 21,
+ #[allow(dead_code)]
+ HasRest = 1 << 22,
+ #[allow(dead_code)]
+ NeedsFunctionEnvironmentObjects = 1 << 23,
+ #[allow(dead_code)]
+ FunctionHasExtraBodyVarScope = 1 << 24,
+ #[allow(dead_code)]
+ ShouldDeclareArguments = 1 << 25,
+ #[allow(dead_code)]
+ NeedsArgsObj = 1 << 26,
+ #[allow(dead_code)]
+ HasMappedArgsObj = 1 << 27,
+ #[allow(dead_code)]
+ IsInlinableLargeFunction = 1 << 28,
+ #[allow(dead_code)]
+ FunctionHasNewTargetBinding = 1 << 29,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum MutableScriptFlagsEnum {
+ #[allow(dead_code)]
+ HasRunOnce = 1 << 8,
+ #[allow(dead_code)]
+ HasBeenCloned = 1 << 9,
+ #[allow(dead_code)]
+ HasScriptCounts = 1 << 10,
+ #[allow(dead_code)]
+ HasDebugScript = 1 << 11,
+ #[allow(dead_code)]
+ AllowRelazify = 1 << 14,
+ #[allow(dead_code)]
+ SpewEnabled = 1 << 15,
+ #[allow(dead_code)]
+ NeedsFinalWarmUpCount = 1 << 16,
+ #[allow(dead_code)]
+ BaselineDisabled = 1 << 17,
+ #[allow(dead_code)]
+ IonDisabled = 1 << 18,
+ #[allow(dead_code)]
+ Uninlineable = 1 << 19,
+ #[allow(dead_code)]
+ FailedBoundsCheck = 1 << 21,
+ #[allow(dead_code)]
+ HadLICMInvalidation = 1 << 22,
+ #[allow(dead_code)]
+ HadReorderingBailout = 1 << 23,
+ #[allow(dead_code)]
+ HadEagerTruncationBailout = 1 << 24,
+ #[allow(dead_code)]
+ FailedLexicalCheck = 1 << 25,
+ #[allow(dead_code)]
+ HadSpeculativePhiBailout = 1 << 26,
+ #[allow(dead_code)]
+ HadUnboxFoldingBailout = 1 << 27,
+}
+
+// @@@@ END TYPES @@@@
+
+#[derive(Debug)]
+pub struct ImmutableScriptFlags {
+ value: u32,
+}
+
+impl ImmutableScriptFlags {
+ pub fn new() -> Self {
+ Self { value: 0 }
+ }
+
+ pub fn from_raw(bits: u32) -> Self {
+ Self { value: bits }
+ }
+
+ pub fn set(&mut self, bit: ImmutableScriptFlagsEnum) {
+ self.value |= bit as u32;
+ }
+
+ pub fn has(&self, bit: ImmutableScriptFlagsEnum) -> bool {
+ (self.value & bit as u32) != 0
+ }
+
+ pub fn reset(&mut self, bit: ImmutableScriptFlagsEnum) {
+ self.value &= !(bit as u32)
+ }
+}
+
+impl From<ImmutableScriptFlags> for u32 {
+ fn from(flags: ImmutableScriptFlags) -> u32 {
+ flags.value
+ }
+}
+
+/// Maps to js::ImmutableScriptData in m-c/js/src/vm/SharedStencil.h.
+#[derive(Debug)]
+pub struct ImmutableScriptData {
+ pub main_offset: u32,
+ pub nfixed: FrameSlot,
+ pub nslots: u32,
+ pub body_scope_index: u32,
+ pub num_ic_entries: u32,
+ pub fun_length: u16,
+ pub bytecode: Vec<u8>,
+ pub scope_notes: Vec<ScopeNote>,
+ // TODO: Add resume_offsets and try_notes.
+}
+
+/// Index into ImmutableScriptDataList.scripts.
+#[derive(Debug, Clone, Copy)]
+pub struct ImmutableScriptDataIndex {
+ index: usize,
+}
+
+impl ImmutableScriptDataIndex {
+ fn new(index: usize) -> Self {
+ Self { index }
+ }
+}
+
+impl From<ImmutableScriptDataIndex> for usize {
+ fn from(index: ImmutableScriptDataIndex) -> usize {
+ index.index
+ }
+}
+
+/// List of ImmutableScriptData.
+#[derive(Debug)]
+pub struct ImmutableScriptDataList {
+ /// Uses Option to allow `allocate()` and `populate()` to be called
+ /// separately.
+ items: Vec<Option<ImmutableScriptData>>,
+}
+
+impl ImmutableScriptDataList {
+ pub fn new() -> Self {
+ Self { items: Vec::new() }
+ }
+
+ pub fn push(&mut self, script: ImmutableScriptData) -> ImmutableScriptDataIndex {
+ let index = self.items.len();
+ self.items.push(Some(script));
+ ImmutableScriptDataIndex::new(index)
+ }
+
+ pub fn allocate(&mut self) -> ImmutableScriptDataIndex {
+ let index = self.items.len();
+ self.items.push(None);
+ ImmutableScriptDataIndex::new(index)
+ }
+
+ pub fn populate(&mut self, index: ImmutableScriptDataIndex, script: ImmutableScriptData) {
+ self.items[usize::from(index)].replace(script);
+ }
+}
+
+impl From<ImmutableScriptDataList> for Vec<ImmutableScriptData> {
+ fn from(list: ImmutableScriptDataList) -> Vec<ImmutableScriptData> {
+ list.items
+ .into_iter()
+ .map(|g| g.expect("Should be populated"))
+ .collect()
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct SourceExtent {
+ pub source_start: u32,
+ pub source_end: u32,
+ pub to_string_start: u32,
+ pub to_string_end: u32,
+
+ pub lineno: u32,
+ pub column: u32,
+}
+
+impl SourceExtent {
+ fn new() -> Self {
+ Self {
+ source_start: 0,
+ source_end: 0,
+ to_string_start: 0,
+ to_string_end: 0,
+
+ lineno: 0,
+ column: 0,
+ }
+ }
+
+ pub fn top_level_script(length: u32, lineno: u32, column: u32) -> Self {
+ Self {
+ source_start: 0,
+ source_end: length,
+ to_string_start: 0,
+ to_string_end: length,
+
+ lineno,
+ column,
+ }
+ }
+}
+
+/// Maps to js::frontend::ScriptStencil in m-c/js/src/frontend/Stencil.h.
+#[derive(Debug)]
+pub struct ScriptStencil {
+ // Fields for BaseScript.
+ // Used by:
+ // * Global script
+ // * Eval
+ // * Module
+ // * non-lazy Function (except asm.js module)
+ // * lazy Function (cannot be asm.js module)
+ /// See `BaseScript::immutableFlags_`.
+ pub immutable_flags: ImmutableScriptFlags,
+
+ /// For top level script and non-lazy function script,
+ /// this is a list of GC things referred by bytecode and scope.
+ ///
+ /// For lazy function script, this contains the list of inner functions,
+ /// followed by the list of names defined and closed over by inner script.
+ /// The list of names are delimited by GCThing::Null for each scope.
+ ///
+ /// The order of scopes are depth-first post-order, and names inside each
+ /// scope is in not defined.
+ ///
+ /// Trailing scopes without any names are omitted for space efficiency.
+ pub gcthings: Vec<GCThing>,
+
+ /// See `BaseScript::sharedData_`.
+ pub immutable_script_data: Option<ImmutableScriptDataIndex>,
+
+ /// The location of this script in the source.
+ pub extent: SourceExtent,
+
+ // Fields for JSFunction.
+ // Used by:
+ // * non-lazy Function
+ // * lazy Function
+ // * asm.js module
+ /// The explicit or implicit name of the function. The FunctionFlags
+ /// indicate the kind of name.
+ pub fun_name: Option<SourceAtomSetIndex>,
+
+ /// See `JSFunction::nargs_`.
+ pub fun_nargs: u16,
+
+ /// See: `FunctionFlags`.
+ pub fun_flags: FunctionFlags,
+
+ /// If this ScriptStencil refers to a lazy child of the function being
+ /// compiled, this field holds the child's immediately enclosing scope's
+ /// index. Once compilation succeeds, we will store the scope pointed by
+ /// this in the child's BaseScript. (Debugger may become confused if lazy
+ /// scripts refer to partially initialized enclosing scopes, so we must
+ /// avoid storing the scope in the BaseScript until compilation has
+ /// completed successfully.)
+ pub lazy_function_enclosing_scope_index: Option<ScopeIndex>,
+
+ /// This function is a standalone function that is not syntactically part of
+ /// another script. Eg. Created by `new Function("")`.
+ pub is_standalone_function: bool,
+
+ /// This is set by the emitter of the enclosing script when a
+ /// reference to this function is generated.
+ pub was_function_emitted: bool,
+
+ /// This function should be marked as a singleton. It is expected to be
+ /// defined at most once. This is a heuristic only and does not affect
+ /// correctness.
+ pub is_singleton_function: bool,
+}
+
+impl ScriptStencil {
+ fn empty_top_level_script() -> Self {
+ Self {
+ immutable_flags: ImmutableScriptFlags::new(),
+ gcthings: Vec::new(),
+ immutable_script_data: None,
+ extent: SourceExtent::new(),
+ fun_name: None,
+ fun_nargs: 0,
+ fun_flags: FunctionFlags::empty(),
+ lazy_function_enclosing_scope_index: None,
+ is_standalone_function: false,
+ was_function_emitted: false,
+ is_singleton_function: false,
+ }
+ }
+
+ pub fn top_level_script(
+ gcthings: Vec<GCThing>,
+ immutable_script_data: ImmutableScriptDataIndex,
+ extent: SourceExtent,
+ ) -> Self {
+ Self {
+ immutable_flags: ImmutableScriptFlags::new(),
+ gcthings,
+ immutable_script_data: Some(immutable_script_data),
+ extent,
+ fun_name: None,
+ fun_nargs: 0,
+ fun_flags: FunctionFlags::empty(),
+ lazy_function_enclosing_scope_index: None,
+ is_standalone_function: false,
+ was_function_emitted: false,
+ is_singleton_function: false,
+ }
+ }
+
+ pub fn lazy_function(
+ extent: SourceExtent,
+ fun_name: Option<SourceAtomSetIndex>,
+ is_generator: bool,
+ is_async: bool,
+ fun_flags: FunctionFlags,
+ lazy_function_enclosing_scope_index: Option<ScopeIndex>,
+ ) -> Self {
+ let mut flags = ImmutableScriptFlagsEnum::IsFunction as u32;
+ if is_generator {
+ flags |= ImmutableScriptFlagsEnum::IsGenerator as u32;
+ }
+ if is_async {
+ flags |= ImmutableScriptFlagsEnum::IsAsync as u32;
+ }
+
+ Self {
+ immutable_flags: ImmutableScriptFlags::from_raw(flags),
+ gcthings: Vec::new(),
+ immutable_script_data: None,
+ extent,
+ fun_name,
+ fun_nargs: 0,
+ fun_flags,
+ lazy_function_enclosing_scope_index,
+ is_standalone_function: false,
+ was_function_emitted: false,
+ is_singleton_function: false,
+ }
+ }
+
+ pub fn is_function(&self) -> bool {
+ self.immutable_flags
+ .has(ImmutableScriptFlagsEnum::IsFunction)
+ }
+
+ pub fn is_non_lazy_function(&self) -> bool {
+ self.is_function() && self.immutable_script_data.is_some()
+ }
+
+ pub fn is_lazy_function(&self) -> bool {
+ self.is_function() && self.immutable_script_data.is_none()
+ }
+
+ pub fn set_function_has_this_binding(&mut self) {
+ debug_assert!(self.is_lazy_function());
+ self.immutable_flags
+ .set(ImmutableScriptFlagsEnum::FunctionHasThisBinding);
+ }
+
+ pub fn set_has_rest(&mut self) {
+ debug_assert!(self.is_lazy_function());
+ self.immutable_flags.set(ImmutableScriptFlagsEnum::HasRest);
+ }
+
+ pub fn set_needs_function_environment_objects(&mut self) {
+ debug_assert!(self.is_lazy_function());
+ self.immutable_flags
+ .set(ImmutableScriptFlagsEnum::NeedsFunctionEnvironmentObjects);
+ }
+
+ pub fn set_function_has_extra_body_var_scope(&mut self) {
+ debug_assert!(self.is_lazy_function());
+ self.immutable_flags
+ .set(ImmutableScriptFlagsEnum::FunctionHasExtraBodyVarScope);
+ }
+
+ pub fn set_should_declare_arguments(&mut self) {
+ debug_assert!(self.is_lazy_function());
+ self.immutable_flags
+ .set(ImmutableScriptFlagsEnum::ShouldDeclareArguments);
+ }
+
+ pub fn set_needs_args_obj(&mut self) {
+ debug_assert!(self.is_lazy_function());
+ self.immutable_flags
+ .set(ImmutableScriptFlagsEnum::NeedsArgsObj);
+ }
+
+ pub fn set_has_mapped_args_obj(&mut self) {
+ debug_assert!(self.is_lazy_function());
+ self.immutable_flags
+ .set(ImmutableScriptFlagsEnum::HasMappedArgsObj);
+ }
+
+ pub fn set_function_has_new_target_binding(&mut self) {
+ debug_assert!(self.is_lazy_function());
+ self.immutable_flags
+ .set(ImmutableScriptFlagsEnum::FunctionHasNewTargetBinding);
+ }
+
+ pub fn is_arrow_function(&self) -> bool {
+ self.is_function() && self.fun_flags.is_arrow()
+ }
+
+ pub fn set_fun_name(&mut self, name: SourceAtomSetIndex) {
+ debug_assert!(self.is_function());
+ self.fun_name = Some(name);
+ }
+
+ pub fn fun_name<'a>(&'a self) -> &'a Option<SourceAtomSetIndex> {
+ debug_assert!(self.is_function());
+ &self.fun_name
+ }
+
+ pub fn add_fun_nargs(&mut self) {
+ debug_assert!(self.is_function());
+ self.fun_nargs += 1;
+ }
+
+ /// source_start should point the start of parameter for functions.
+ pub fn set_source_starts(&mut self, source_start: usize) {
+ self.extent.source_start = source_start as u32;
+ }
+
+ /// to_string_end should point the end of function body for function,
+ /// and the end of class for constructor.
+ pub fn set_to_string_end(&mut self, to_string_end: usize) {
+ self.extent.to_string_end = to_string_end as u32;
+ }
+
+ /// source_end should point the end of function body.
+ pub fn set_source_end(&mut self, source_end: usize) {
+ self.extent.source_end = source_end as u32;
+ }
+
+ pub fn set_function_emitted(&mut self) {
+ self.was_function_emitted = true;
+ }
+
+ pub fn push_inner_function(&mut self, fun: ScriptStencilIndex) {
+ self.immutable_flags
+ .set(ImmutableScriptFlagsEnum::HasInnerFunctions);
+ self.gcthings.push(GCThing::Function(fun));
+ }
+
+ pub fn push_closed_over_bindings(&mut self, name: SourceAtomSetIndex) {
+ debug_assert!(self.is_lazy_function());
+ self.gcthings.push(GCThing::Atom(name));
+ }
+
+ pub fn push_closed_over_bindings_delimiter(&mut self) {
+ debug_assert!(self.is_lazy_function());
+ self.gcthings.push(GCThing::Null);
+ }
+}
+
+/// Index into ScriptStencilList.scripts.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ScriptStencilIndex {
+ index: usize,
+}
+
+impl ScriptStencilIndex {
+ fn new(index: usize) -> Self {
+ Self { index }
+ }
+}
+
+impl From<ScriptStencilIndex> for usize {
+ fn from(index: ScriptStencilIndex) -> usize {
+ index.index
+ }
+}
+
+/// List of stencil scripts.
+#[derive(Debug)]
+pub struct ScriptStencilList {
+ scripts: Vec<ScriptStencil>,
+}
+
+impl ScriptStencilList {
+ pub fn new() -> Self {
+ Self {
+ scripts: Vec::new(),
+ }
+ }
+
+ pub fn new_with_empty_top_level() -> Self {
+ Self {
+ scripts: vec![ScriptStencil::empty_top_level_script()],
+ }
+ }
+
+ pub fn push(&mut self, script: ScriptStencil) -> ScriptStencilIndex {
+ let index = self.scripts.len();
+ self.scripts.push(script);
+ ScriptStencilIndex::new(index)
+ }
+
+ pub fn get<'a>(&'a self, index: ScriptStencilIndex) -> &'a ScriptStencil {
+ &self.scripts[usize::from(index)]
+ }
+
+ pub fn get_mut<'a>(&'a mut self, index: ScriptStencilIndex) -> &'a mut ScriptStencil {
+ &mut self.scripts[usize::from(index)]
+ }
+
+ pub fn set_top_level(&mut self, top_level: ScriptStencil) {
+ self.scripts[0] = top_level;
+ }
+}
+
+impl From<ScriptStencilList> for Vec<ScriptStencil> {
+ fn from(list: ScriptStencilList) -> Vec<ScriptStencil> {
+ list.scripts.into_iter().collect()
+ }
+}