diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/jsparagus-stencil/src | |
parent | Initial commit. (diff) | |
download | firefox-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')
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() + } +} |