summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/BytecodeEmitter.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/frontend/BytecodeEmitter.h
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/frontend/BytecodeEmitter.h')
-rw-r--r--js/src/frontend/BytecodeEmitter.h1116
1 files changed, 1116 insertions, 0 deletions
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
new file mode 100644
index 0000000000..251797d1f7
--- /dev/null
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -0,0 +1,1116 @@
+/* -*- 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/. */
+
+/* JS bytecode generation. */
+
+#ifndef frontend_BytecodeEmitter_h
+#define frontend_BytecodeEmitter_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_STACK_CLASS, MOZ_ALWAYS_INLINE, MOZ_NEVER_INLINE, MOZ_RAII
+#include "mozilla/Maybe.h" // mozilla::Maybe, mozilla::Some
+#include "mozilla/Saturate.h" // mozilla::SaturateUint8
+#include "mozilla/Span.h" // mozilla::Span
+
+#include <stddef.h> // ptrdiff_t
+#include <stdint.h> // uint16_t, uint32_t
+
+#include "frontend/AbstractScopePtr.h" // ScopeIndex
+#include "frontend/BytecodeSection.h" // BytecodeSection, PerScriptData, GCThingList
+#include "frontend/DestructuringFlavor.h" // DestructuringFlavor
+#include "frontend/EitherParser.h" // EitherParser
+#include "frontend/IteratorKind.h" // IteratorKind
+#include "frontend/JumpList.h" // JumpList, JumpTarget
+#include "frontend/NameAnalysisTypes.h" // NameLocation
+#include "frontend/NameCollections.h" // AtomIndexMap
+#include "frontend/ParseNode.h" // ParseNode and subclasses
+#include "frontend/Parser.h" // Parser, PropListType
+#include "frontend/ParserAtom.h" // TaggedParserAtomIndex, ParserAtom
+#include "frontend/ScriptIndex.h" // ScriptIndex
+#include "frontend/SelfHostedIter.h" // SelfHostedIter
+#include "frontend/SourceNotes.h" // SrcNoteType
+#include "frontend/ValueUsage.h" // ValueUsage
+#include "js/AllocPolicy.h" // ReportOutOfMemory
+#include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin
+#include "js/TypeDecls.h" // jsbytecode
+#include "vm/BuiltinObjectKind.h" // BuiltinObjectKind
+#include "vm/CheckIsObjectKind.h" // CheckIsObjectKind
+#include "vm/CompletionKind.h" // CompletionKind
+#include "vm/FunctionPrefixKind.h" // FunctionPrefixKind
+#include "vm/GeneratorResumeKind.h" // GeneratorResumeKind
+#include "vm/Opcodes.h" // JSOp
+#include "vm/SharedStencil.h" // GCThingIndex, MemberInitializers
+#include "vm/StencilEnums.h" // TryNoteKind
+#include "vm/ThrowMsgKind.h" // ThrowMsgKind, ThrowCondition
+
+namespace js {
+
+class FrontendContext;
+
+namespace frontend {
+
+class BytecodeOffset;
+class CallOrNewEmitter;
+class ClassEmitter;
+class ElemOpEmitter;
+class EmitterScope;
+class ErrorReporter;
+class FullParseHandler;
+class NestableControl;
+class PrivateOpEmitter;
+class PropertyEmitter;
+class PropOpEmitter;
+class OptionalEmitter;
+class SharedContext;
+class TDZCheckCache;
+class TryEmitter;
+
+struct TokenPos;
+
+enum class ValueIsOnStack { Yes, No };
+
+// [SMDOC] Bytecode emission
+//
+// Bytecode emitter class and helper classes for generating bytecode and related
+// stencil data from AST generated by JS parser.
+//
+//
+// BytecodeEmitter
+// ---------------
+//
+// BytecodeEmitter receives an AST, and utilizes helper classes to generate the
+// bytecode sequence, and related stencil data.
+//
+// BytecodeEmitter can be nested, in order to emit inner non-lazy function
+// scripts.
+//
+//
+// Bytecode structures
+// -------------------
+//
+// While bytecode is being emitted, it is separated into 2 parts, the prologue
+// and the main part. The prologue part contains instantiation of the declared
+// variables, functions, and special names in function. The main part contains
+// the remaining part of the bytecode.
+//
+// The generated bytecode is stored into the following 2 classes, before
+// converting them into stencil data (See ImmutableScriptData and
+// BytecodeEmitter::createImmutableScriptData):
+//
+// * BytecodeSection
+// * PerScriptData
+//
+// BytecodeSection stores the bytecode sequence and data directly associated
+// with opcode or index inside the bytecode sequence.
+//
+// PerScriptData contains data referred from the bytecode, that is mostly the
+// list of GC things.
+//
+//
+// Bindings
+// --------
+//
+// # Scope and bindings
+//
+// When emitting AST node that's associated with a given scope, EmitterScope is
+// allocated to store/cache the bindings information.
+//
+// This information is used when emitting an opcode that accesses bindings, to
+// determine where the binding is stored, and how the binding should be
+// accessed, including which opcode to use and what operand to use for it.
+//
+//
+// # Temporal Dead Zone (TDZ) check cache
+//
+// The spec requires TDZ check for all lexical variable access, but emitting
+// TDZ check for all operation increases the bytecode size and affects the
+// performance. TDZCheckCache is a cache to optimize away unnecessary TDZ check
+// operations.
+//
+// See comments for TDZCheckCache for more details.
+//
+//
+// Control structures
+// ------------------
+//
+// # Jump list
+//
+// When emitting jump-related bytecode (if-else, break/continue, try-catch),
+// forward jump is tracked by JumpList class, in order to patch the jump
+// after the jump target is emitted.
+//
+// See the comment above JumpList class for mode details.
+//
+//
+// # Loop and label
+//
+// Control structure related to break/continue is handled by NestableControl and
+// its subclasses. Those classes handle jump with labelled and un-labelled
+// break/continue, stack balancing around them, TDZ check cache for the
+// loop's basic block, and association between the control and the scope.
+//
+//
+// Emitter helpers
+// ---------------
+//
+// Bytecode sequence or structure specific to certain syntax (e.g. if, for, try)
+// are handled by emitter helper classes.
+//
+// Each emitter helper is defined in *Emitter.{cpp,h} in this directory.
+//
+// Emitter helpers should meet the following requirements:
+// * helper classes should be ParseNode-agnostic
+// * helper classes shouldn't contain `JS::Rooted` field, given they can be
+// held in `mozilla::Maybe` in the consumer or other helper classes
+// * instantiation (ctor/dtor) of the emitter helper class shouldn't
+// modify BytecodeEmitter, except for nestable controls
+// * instantiation (ctor/dtor) of the emitter helper class shouldn't
+// read BytecodeEmitter field that can change before the first method call.
+// Such data should be explicitly passed as parameter, or be accessed inside
+// the method
+// * methods that emits bytecode should be named `emit*` or `prepareFor*`
+// * methods and their names shouldn't require the consumer knowing the
+// details of the bytecode sequence/structure that the helper emits
+// * implicit branch or scope/control handling should be hidden from the
+// consumer
+// * If there are multiple operations between bytecode that the consumer
+// emits, they should be wrapped into single `emit*` or `prepareFor*`
+// method
+// e.g.
+// // Bad!
+// helper.emitJumpAroundA();
+// helper.allocateScopeForA();
+// ... // emit bytecode for A here
+// helper.deallocateScopeForA();
+// helper.emitJumpAroundB();
+// helper.allocateScopeForB();
+// ... // emit bytecode for B here
+// helper.deallocateScopeForB();
+// helper.emitJumpTarget();
+//
+// // Good!
+// helper.prepareForA();
+// ... // emit bytecode for A here
+// helper.prepareForB();
+// ... // emit bytecode for B here
+// helper.emitEnd();
+// * helper classes should track state transition and assert it in each
+// method call, to avoid misuse
+// * it's recommended to defer receiving parameter until the parameter value
+// is actually used in the method, instead of receiving and storing them
+// into instance fields
+//
+// See comment block above each helper class for more details and example usage.
+
+struct MOZ_STACK_CLASS BytecodeEmitter {
+ // Context shared between parsing and bytecode generation.
+ SharedContext* const sc = nullptr;
+
+ FrontendContext* const fc = nullptr;
+
+ // Enclosing function or global context.
+ BytecodeEmitter* const parent = nullptr;
+
+ BytecodeSection bytecodeSection_;
+
+ static constexpr unsigned LastSrcNoteIsNotLineOnly = unsigned(-1);
+
+ unsigned lastLineOnlySrcNoteIndex = LastSrcNoteIsNotLineOnly;
+
+ public:
+ BytecodeSection& bytecodeSection() { return bytecodeSection_; }
+ const BytecodeSection& bytecodeSection() const { return bytecodeSection_; }
+
+ private:
+ PerScriptData perScriptData_;
+
+ public:
+ PerScriptData& perScriptData() { return perScriptData_; }
+ const PerScriptData& perScriptData() const { return perScriptData_; }
+
+ private:
+ // switchToMain sets this to the bytecode offset of the main section.
+ mozilla::Maybe<uint32_t> mainOffset_ = {};
+
+ // Private storage for parser wrapper. DO NOT REFERENCE INTERNALLY. May not be
+ // initialized.
+ mozilla::Maybe<EitherParser> ep_ = {};
+
+ const ErrorReporter& errorReporter_;
+
+ public:
+ CompilationState& compilationState;
+
+ uint32_t maxFixedSlots = 0; /* maximum number of fixed frame slots so far */
+
+ // Index into scopeList of the body scope.
+ GCThingIndex bodyScopeIndex = ScopeNote::NoScopeIndex;
+
+ EmitterScope* varEmitterScope = nullptr;
+ NestableControl* innermostNestableControl = nullptr;
+ EmitterScope* innermostEmitterScope_ = nullptr;
+ TDZCheckCache* innermostTDZCheckCache = nullptr;
+
+ // When compiling in self-hosted mode, we have special intrinsics that act as
+ // decorators for exported functions. To keeps things simple, we only allow
+ // these to target the last top-level function emitted. This field tracks that
+ // function.
+ FunctionBox* prevSelfHostedTopLevelFunction = nullptr;
+
+#ifdef DEBUG
+ bool unstableEmitterScope = false;
+
+ friend class AutoCheckUnstableEmitterScope;
+#endif
+
+ const ErrorReporter& errorReporter() const { return errorReporter_; }
+
+ ParserAtomsTable& parserAtoms() { return compilationState.parserAtoms; }
+ const ParserAtomsTable& parserAtoms() const {
+ return compilationState.parserAtoms;
+ }
+
+ EmitterScope* innermostEmitterScope() const {
+ MOZ_ASSERT(!unstableEmitterScope);
+ return innermostEmitterScopeNoCheck();
+ }
+ EmitterScope* innermostEmitterScopeNoCheck() const {
+ return innermostEmitterScope_;
+ }
+
+ // When parsing internal code such as self-hosted functions or synthetic
+ // class constructors, we do not emit breakpoint and srcnote data since there
+ // is no direcly corresponding user-visible sources.
+ const bool suppressBreakpointsAndSourceNotes = false;
+
+ // Script contains finally block.
+ bool hasTryFinally = false;
+
+ enum EmitterMode {
+ Normal,
+
+ // Emit JSOp::GetIntrinsic instead of JSOp::GetName and assert that
+ // JSOp::GetName and JSOp::*GName don't ever get emitted. See the comment
+ // for the field |selfHostingMode| in Parser.h for details.
+ SelfHosting,
+
+ // Check the static scope chain of the root function for resolving free
+ // variable accesses in the script.
+ LazyFunction
+ };
+
+ const EmitterMode emitterMode = Normal;
+
+ mozilla::Maybe<uint32_t> scriptStartOffset = {};
+
+ // The end location of a function body that is being emitted.
+ mozilla::Maybe<uint32_t> functionBodyEndPos = {};
+
+ // Jump target just before the final CheckReturn opcode in a derived class
+ // constructor body.
+ JumpList endOfDerivedClassConstructorBody = {};
+
+ // Jump target just before the final yield in a generator or async function.
+ JumpList finalYields = {};
+
+ // In order to heuristically determine the size of the allocation if this is a
+ // constructor function, we track expressions which add properties in the
+ // constructor.
+ mozilla::SaturateUint8 propertyAdditionEstimate = {};
+
+ /*
+ * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
+ * space above their tempMark points. This means that you cannot alloc from
+ * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
+ * destruction.
+ */
+ private:
+ // Internal constructor, for delegation use only.
+ BytecodeEmitter(BytecodeEmitter* parent, FrontendContext* fc,
+ SharedContext* sc, const ErrorReporter& errorReporter,
+ CompilationState& compilationState, EmitterMode emitterMode);
+
+ void initFromBodyPosition(TokenPos bodyPosition);
+
+ public:
+ BytecodeEmitter(FrontendContext* fc, const EitherParser& parser,
+ SharedContext* sc, CompilationState& compilationState,
+ EmitterMode emitterMode = Normal);
+
+ template <typename Unit>
+ BytecodeEmitter(FrontendContext* fc, Parser<FullParseHandler, Unit>* parser,
+ SharedContext* sc, CompilationState& compilationState,
+ EmitterMode emitterMode = Normal)
+ : BytecodeEmitter(fc, EitherParser(parser), sc, compilationState,
+ emitterMode) {}
+
+ BytecodeEmitter(BytecodeEmitter* parent, SharedContext* sc);
+
+ [[nodiscard]] bool init();
+ [[nodiscard]] bool init(TokenPos bodyPosition);
+
+ template <typename T>
+ T* findInnermostNestableControl() const;
+
+ template <typename T, typename Predicate /* (T*) -> bool */>
+ T* findInnermostNestableControl(Predicate predicate) const;
+
+ NameLocation lookupName(TaggedParserAtomIndex name);
+
+ // See EmitterScope::lookupPrivate for details around brandLoc
+ void lookupPrivate(TaggedParserAtomIndex name, NameLocation& loc,
+ mozilla::Maybe<NameLocation>& brandLoc);
+
+ // To implement Annex B and the formal parameter defaults scope semantics
+ // requires accessing names that would otherwise be shadowed. This method
+ // returns the access location of a name that is known to be bound in a
+ // target scope.
+ mozilla::Maybe<NameLocation> locationOfNameBoundInScope(
+ TaggedParserAtomIndex name, EmitterScope* target);
+
+ // Get the location of a name known to be bound in a given scope,
+ // starting at the source scope.
+ template <typename T>
+ mozilla::Maybe<NameLocation> locationOfNameBoundInScopeType(
+ TaggedParserAtomIndex name, EmitterScope* source);
+
+ // Get the location of a name known to be bound in the function scope,
+ // starting at the source scope.
+ mozilla::Maybe<NameLocation> locationOfNameBoundInFunctionScope(
+ TaggedParserAtomIndex name) {
+ return locationOfNameBoundInScopeType<FunctionScope>(
+ name, innermostEmitterScope());
+ }
+
+ void setVarEmitterScope(EmitterScope* emitterScope) {
+ MOZ_ASSERT(emitterScope);
+ MOZ_ASSERT(!varEmitterScope);
+ varEmitterScope = emitterScope;
+ }
+
+ AbstractScopePtr outermostScope() const {
+ return perScriptData().gcThingList().firstScope();
+ }
+ AbstractScopePtr innermostScope() const;
+ ScopeIndex innermostScopeIndex() const;
+
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool makeAtomIndex(
+ TaggedParserAtomIndex atom, ParserAtom::Atomize atomize,
+ GCThingIndex* indexp) {
+ MOZ_ASSERT(perScriptData().atomIndices());
+ AtomIndexMap::AddPtr p = perScriptData().atomIndices()->lookupForAdd(atom);
+ if (p) {
+ compilationState.parserAtoms.markAtomize(atom, atomize);
+ *indexp = GCThingIndex(p->value());
+ return true;
+ }
+
+ GCThingIndex index;
+ if (!perScriptData().gcThingList().append(atom, atomize, &index)) {
+ return false;
+ }
+
+ // `atomIndices()` uses uint32_t instead of GCThingIndex, because
+ // GCThingIndex isn't trivial type.
+ if (!perScriptData().atomIndices()->add(p, atom, index.index)) {
+ ReportOutOfMemory(fc);
+ return false;
+ }
+
+ *indexp = index;
+ return true;
+ }
+
+ bool isInLoop();
+ [[nodiscard]] bool checkSingletonContext();
+
+ bool needsImplicitThis();
+
+ size_t countThisEnvironmentHops();
+ [[nodiscard]] bool emitThisEnvironmentCallee();
+ [[nodiscard]] bool emitSuperBase();
+
+ uint32_t mainOffset() const { return *mainOffset_; }
+
+ bool inPrologue() const { return mainOffset_.isNothing(); }
+
+ void switchToMain() {
+ MOZ_ASSERT(inPrologue());
+ mainOffset_.emplace(bytecodeSection().code().length());
+ }
+
+ void setFunctionBodyEndPos(uint32_t pos) {
+ functionBodyEndPos = mozilla::Some(pos);
+ }
+
+ void setScriptStartOffsetIfUnset(uint32_t pos) {
+ if (scriptStartOffset.isNothing()) {
+ scriptStartOffset = mozilla::Some(pos);
+ }
+ }
+
+ void reportError(ParseNode* pn, unsigned errorNumber, ...);
+ void reportError(uint32_t offset, unsigned errorNumber, ...);
+
+ // Fill in a ScriptStencil using this BCE data.
+ bool intoScriptStencil(ScriptIndex scriptIndex);
+
+ // If pn contains a useful expression, return true with *answer set to true.
+ // If pn contains a useless expression, return true with *answer set to
+ // false. Return false on error.
+ //
+ // The caller should initialize *answer to false and invoke this function on
+ // an expression statement or similar subtree to decide whether the tree
+ // could produce code that has any side effects. For an expression
+ // statement, we define useless code as code with no side effects, because
+ // the main effect, the value left on the stack after the code executes,
+ // will be discarded by a pop bytecode.
+ [[nodiscard]] bool checkSideEffects(ParseNode* pn, bool* answer);
+
+#ifdef DEBUG
+ [[nodiscard]] bool checkStrictOrSloppy(JSOp op);
+#endif
+
+ // Add TryNote to the tryNoteList array. The start and end offset are
+ // relative to current section.
+ [[nodiscard]] bool addTryNote(TryNoteKind kind, uint32_t stackDepth,
+ BytecodeOffset start, BytecodeOffset end);
+
+ // Indicates the emitter should not generate location or debugger source
+ // notes. This lets us avoid generating notes for non-user code.
+ bool skipLocationSrcNotes() const {
+ return inPrologue() || suppressBreakpointsAndSourceNotes;
+ }
+ bool skipBreakpointSrcNotes() const {
+ return inPrologue() || suppressBreakpointsAndSourceNotes;
+ }
+
+ // Append a new source note of the given type (and therefore size) to the
+ // notes dynamic array, updating noteCount. Return the new note's index
+ // within the array pointed at by current->notes as outparam.
+ [[nodiscard]] bool newSrcNote(SrcNoteType type, unsigned* indexp = nullptr);
+ [[nodiscard]] bool newSrcNote2(SrcNoteType type, ptrdiff_t operand,
+ unsigned* indexp = nullptr);
+ [[nodiscard]] bool convertLastNewLineToNewLineColumn(
+ JS::LimitedColumnNumberOneOrigin column);
+ [[nodiscard]] bool convertLastSetLineToSetLineColumn(
+ JS::LimitedColumnNumberOneOrigin column);
+
+ [[nodiscard]] bool newSrcNoteOperand(ptrdiff_t operand);
+
+ // Control whether emitTree emits a line number note.
+ enum EmitLineNumberNote { EMIT_LINENOTE, SUPPRESS_LINENOTE };
+
+ // Emit code for the tree rooted at pn.
+ [[nodiscard]] bool emitTree(ParseNode* pn,
+ ValueUsage valueUsage = ValueUsage::WantValue,
+ EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
+
+ [[nodiscard]] bool emitOptionalTree(
+ ParseNode* pn, OptionalEmitter& oe,
+ ValueUsage valueUsage = ValueUsage::WantValue);
+
+ [[nodiscard]] bool emitDeclarationInstantiation(ParseNode* body);
+
+ // Emit global, eval, or module code for tree rooted at body. Always
+ // encompasses the entire source.
+ [[nodiscard]] bool emitScript(ParseNode* body);
+
+ // Calculate the `nslots` value for ImmutableScriptData constructor parameter.
+ // Fails if it overflows.
+ [[nodiscard]] bool getNslots(uint32_t* nslots);
+
+ // Emit function code for the tree rooted at body.
+ [[nodiscard]] bool emitFunctionScript(FunctionNode* funNode);
+
+ [[nodiscard]] bool markStepBreakpoint();
+ [[nodiscard]] bool markSimpleBreakpoint();
+ [[nodiscard]] bool updateLineNumberNotes(uint32_t offset);
+ [[nodiscard]] bool updateSourceCoordNotes(uint32_t offset);
+ [[nodiscard]] bool updateSourceCoordNotesIfNonLiteral(ParseNode* node);
+
+ JSOp strictifySetNameOp(JSOp op);
+
+ [[nodiscard]] bool emitCheck(JSOp op, ptrdiff_t delta,
+ BytecodeOffset* offset);
+
+ // Emit one bytecode.
+ [[nodiscard]] bool emit1(JSOp op);
+
+ // Emit two bytecodes, an opcode (op) with a byte of immediate operand
+ // (op1).
+ [[nodiscard]] bool emit2(JSOp op, uint8_t op1);
+
+ // Emit three bytecodes, an opcode with two bytes of immediate operands.
+ [[nodiscard]] bool emit3(JSOp op, jsbytecode op1, jsbytecode op2);
+
+ // Helper to duplicate one or more stack values. |slotFromTop| is the value's
+ // depth on the JS stack, as measured from the top. |count| is the number of
+ // values to duplicate, in theiro original order.
+ [[nodiscard]] bool emitDupAt(unsigned slotFromTop, unsigned count = 1);
+
+ // Helper to emit JSOp::Pop or JSOp::PopN.
+ [[nodiscard]] bool emitPopN(unsigned n);
+
+ // Helper to emit JSOp::Swap or JSOp::Pick.
+ [[nodiscard]] bool emitPickN(uint8_t n);
+
+ // Helper to emit JSOp::Swap or JSOp::Unpick.
+ [[nodiscard]] bool emitUnpickN(uint8_t n);
+
+ // Helper to emit JSOp::CheckIsObj.
+ [[nodiscard]] bool emitCheckIsObj(CheckIsObjectKind kind);
+
+ // Helper to emit JSOp::BuiltinObject.
+ [[nodiscard]] bool emitBuiltinObject(BuiltinObjectKind kind);
+
+ // Emit a bytecode followed by an uint16 immediate operand stored in
+ // big-endian order.
+ [[nodiscard]] bool emitUint16Operand(JSOp op, uint32_t operand);
+
+ // Emit a bytecode followed by an uint32 immediate operand.
+ [[nodiscard]] bool emitUint32Operand(JSOp op, uint32_t operand);
+
+ // Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
+ [[nodiscard]] bool emitN(JSOp op, size_t extra,
+ BytecodeOffset* offset = nullptr);
+
+ [[nodiscard]] bool emitDouble(double dval);
+ [[nodiscard]] bool emitNumberOp(double dval);
+
+ [[nodiscard]] bool emitBigIntOp(BigIntLiteral* bigint);
+
+ [[nodiscard]] bool emitThisLiteral(ThisLiteral* pn);
+ [[nodiscard]] bool emitGetFunctionThis(NameNode* thisName);
+ [[nodiscard]] bool emitGetThisForSuperBase(UnaryNode* superBase);
+ [[nodiscard]] bool emitSetThis(BinaryNode* setThisNode);
+ [[nodiscard]] bool emitCheckDerivedClassConstructorReturn();
+
+ private:
+ [[nodiscard]] bool emitNewTarget();
+
+ public:
+ [[nodiscard]] bool emitNewTarget(NewTargetNode* pn);
+ [[nodiscard]] bool emitNewTarget(CallNode* pn);
+
+ // Handle jump opcodes and jump targets.
+ [[nodiscard]] bool emitJumpTargetOp(JSOp op, BytecodeOffset* off);
+ [[nodiscard]] bool emitJumpTarget(JumpTarget* target);
+ [[nodiscard]] bool emitJumpNoFallthrough(JSOp op, JumpList* jump);
+ [[nodiscard]] bool emitJump(JSOp op, JumpList* jump);
+ void patchJumpsToTarget(JumpList jump, JumpTarget target);
+ [[nodiscard]] bool emitJumpTargetAndPatch(JumpList jump);
+
+ [[nodiscard]] bool emitCall(
+ JSOp op, uint16_t argc,
+ const mozilla::Maybe<uint32_t>& sourceCoordOffset);
+ [[nodiscard]] bool emitCall(JSOp op, uint16_t argc, ParseNode* pn = nullptr);
+ [[nodiscard]] bool emitCallIncDec(UnaryNode* incDec);
+
+ uint32_t getOffsetForLoop(ParseNode* nextpn);
+
+ enum class GotoKind { Break, Continue };
+ [[nodiscard]] bool emitGoto(NestableControl* target, GotoKind kind);
+
+ [[nodiscard]] bool emitGCIndexOp(JSOp op, GCThingIndex index);
+
+ [[nodiscard]] bool emitAtomOp(JSOp op, TaggedParserAtomIndex atom);
+ [[nodiscard]] bool emitAtomOp(JSOp op, GCThingIndex atomIndex);
+
+ [[nodiscard]] bool emitStringOp(JSOp op, TaggedParserAtomIndex atom);
+ [[nodiscard]] bool emitStringOp(JSOp op, GCThingIndex atomIndex);
+
+ [[nodiscard]] bool emitArrayLiteral(ListNode* array);
+ [[nodiscard]] bool emitArray(ListNode* array);
+ [[nodiscard]] bool emitSpreadIntoArray(UnaryNode* elem);
+
+ [[nodiscard]] bool emitInternedScopeOp(GCThingIndex index, JSOp op);
+ [[nodiscard]] bool emitInternedObjectOp(GCThingIndex index, JSOp op);
+ [[nodiscard]] bool emitRegExp(GCThingIndex index);
+
+ [[nodiscard]] MOZ_NEVER_INLINE bool emitFunction(FunctionNode* funNode,
+ bool needsProto = false);
+ [[nodiscard]] MOZ_NEVER_INLINE bool emitObject(ListNode* objNode);
+
+ [[nodiscard]] bool emitHoistedFunctionsInList(ListNode* stmtList);
+
+ // Can we use the object-literal writer either in singleton-object mode (with
+ // values) or in template mode (field names only, no values) for the property
+ // list?
+ void isPropertyListObjLiteralCompatible(ListNode* obj, bool* withValues,
+ bool* withoutValues);
+ bool isArrayObjLiteralCompatible(ListNode* array);
+
+ [[nodiscard]] bool emitPropertyList(ListNode* obj, PropertyEmitter& pe,
+ PropListType type);
+
+ [[nodiscard]] bool emitPropertyListObjLiteral(ListNode* obj, JSOp op,
+ bool useObjLiteralValues);
+
+ [[nodiscard]] bool emitDestructuringRestExclusionSetObjLiteral(
+ ListNode* pattern);
+
+ [[nodiscard]] bool emitObjLiteralArray(ListNode* array);
+
+ // Is a field value JSOp::Object-compatible?
+ [[nodiscard]] bool isRHSObjLiteralCompatible(ParseNode* value);
+
+ [[nodiscard]] bool emitObjLiteralValue(ObjLiteralWriter& writer,
+ ParseNode* value);
+
+ mozilla::Maybe<MemberInitializers> setupMemberInitializers(
+ ListNode* classMembers, FieldPlacement placement);
+ [[nodiscard]] bool emitCreateFieldKeys(ListNode* obj,
+ FieldPlacement placement);
+ [[nodiscard]] bool emitCreateMemberInitializers(ClassEmitter& ce,
+ ListNode* obj,
+ FieldPlacement placement
+#ifdef ENABLE_DECORATORS
+ ,
+ bool hasHeritage
+#endif
+ );
+ const MemberInitializers& findMemberInitializersForCall();
+ [[nodiscard]] bool emitInitializeInstanceMembers(
+ bool isDerivedClassConstructor);
+ [[nodiscard]] bool emitInitializeStaticFields(ListNode* classMembers);
+
+ [[nodiscard]] bool emitPrivateMethodInitializers(ClassEmitter& ce,
+ ListNode* obj);
+ [[nodiscard]] bool emitPrivateMethodInitializer(
+ ClassMethod* classMethod, TaggedParserAtomIndex storedMethodAtom);
+
+ // To catch accidental misuse, emitUint16Operand/emit3 assert that they are
+ // not used to unconditionally emit JSOp::GetLocal. Variable access should
+ // instead be emitted using EmitVarOp. In special cases, when the caller
+ // definitely knows that a given local slot is unaliased, this function may be
+ // used as a non-asserting version of emitUint16Operand.
+ [[nodiscard]] bool emitLocalOp(JSOp op, uint32_t slot);
+
+ [[nodiscard]] bool emitArgOp(JSOp op, uint16_t slot);
+ [[nodiscard]] bool emitEnvCoordOp(JSOp op, EnvironmentCoordinate ec);
+
+ [[nodiscard]] bool emitGetNameAtLocation(TaggedParserAtomIndex name,
+ const NameLocation& loc);
+ [[nodiscard]] bool emitGetName(TaggedParserAtomIndex name) {
+ return emitGetNameAtLocation(name, lookupName(name));
+ }
+ [[nodiscard]] bool emitGetName(NameNode* name);
+ [[nodiscard]] bool emitGetPrivateName(NameNode* name);
+ [[nodiscard]] bool emitGetPrivateName(TaggedParserAtomIndex name);
+
+ [[nodiscard]] bool emitTDZCheckIfNeeded(TaggedParserAtomIndex name,
+ const NameLocation& loc,
+ ValueIsOnStack isOnStack);
+
+ [[nodiscard]] bool emitNameIncDec(UnaryNode* incDec, ValueUsage valueUsage);
+
+ [[nodiscard]] bool emitDeclarationList(ListNode* declList);
+ [[nodiscard]] bool emitSingleDeclaration(ListNode* declList, NameNode* decl,
+ ParseNode* initializer);
+ [[nodiscard]] bool emitAssignmentRhs(ParseNode* rhs,
+ TaggedParserAtomIndex anonFunctionName);
+ [[nodiscard]] bool emitAssignmentRhs(uint8_t offset);
+
+ [[nodiscard]] bool emitPrepareIteratorResult();
+ [[nodiscard]] bool emitFinishIteratorResult(bool done);
+
+ // Convert and add `writer` data to stencil.
+ // Iff it suceeds, `outIndex` out parameter is initialized to the index of the
+ // object in GC things vector.
+ [[nodiscard]] bool addObjLiteralData(ObjLiteralWriter& writer,
+ GCThingIndex* outIndex);
+
+ [[nodiscard]] bool emitGetDotGeneratorInInnermostScope() {
+ return emitGetDotGeneratorInScope(*innermostEmitterScope());
+ }
+ [[nodiscard]] bool emitGetDotGeneratorInScope(EmitterScope& currentScope);
+
+ [[nodiscard]] bool allocateResumeIndex(BytecodeOffset offset,
+ uint32_t* resumeIndex);
+ [[nodiscard]] bool allocateResumeIndexRange(
+ mozilla::Span<BytecodeOffset> offsets, uint32_t* firstResumeIndex);
+
+ [[nodiscard]] bool emitInitialYield(UnaryNode* yieldNode);
+ [[nodiscard]] bool emitYield(UnaryNode* yieldNode);
+ [[nodiscard]] bool emitYieldOp(JSOp op);
+ [[nodiscard]] bool emitYieldStar(ParseNode* iter);
+ [[nodiscard]] bool emitAwaitInInnermostScope() {
+ return emitAwaitInScope(*innermostEmitterScope());
+ }
+ [[nodiscard]] bool emitAwaitInInnermostScope(UnaryNode* awaitNode);
+ [[nodiscard]] bool emitAwaitInScope(EmitterScope& currentScope);
+
+ [[nodiscard]] bool emitPushResumeKind(GeneratorResumeKind kind);
+
+ [[nodiscard]] bool emitPropLHS(PropertyAccess* prop);
+ [[nodiscard]] bool emitPropIncDec(UnaryNode* incDec, ValueUsage valueUsage);
+
+ [[nodiscard]] bool emitComputedPropertyName(UnaryNode* computedPropName);
+
+ [[nodiscard]] bool emitObjAndKey(ParseNode* exprOrSuper, ParseNode* key,
+ ElemOpEmitter& eoe);
+
+ // Emit bytecode to put operands for a JSOp::GetElem/CallElem/SetElem/DelElem
+ // opcode onto the stack in the right order. In the case of SetElem, the
+ // value to be assigned must already be pushed.
+ enum class EmitElemOption { Get, Call, IncDec, CompoundAssign, Ref };
+ [[nodiscard]] bool emitElemOperands(PropertyByValue* elem,
+ EmitElemOption opts);
+
+ [[nodiscard]] bool emitElemObjAndKey(PropertyByValue* elem, bool isSuper,
+ ElemOpEmitter& eoe);
+ [[nodiscard]] bool emitElemOpBase(JSOp op);
+
+ [[nodiscard]] bool emitElemIncDec(UnaryNode* incDec, ValueUsage valueUsage);
+ [[nodiscard]] bool emitObjAndPrivateName(PrivateMemberAccess* elem,
+ ElemOpEmitter& eoe);
+ [[nodiscard]] bool emitPrivateIncDec(UnaryNode* incDec,
+ ValueUsage valueUsage);
+
+ [[nodiscard]] bool emitCatch(BinaryNode* catchClause);
+ [[nodiscard]] bool emitIf(TernaryNode* ifNode);
+ [[nodiscard]] bool emitWith(BinaryNode* withNode);
+
+ [[nodiscard]] MOZ_NEVER_INLINE bool emitLabeledStatement(
+ const LabeledStatement* labeledStmt);
+ [[nodiscard]] MOZ_NEVER_INLINE bool emitLexicalScope(
+ LexicalScopeNode* lexicalScope);
+ [[nodiscard]] bool emitLexicalScopeBody(
+ ParseNode* body, EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
+ [[nodiscard]] MOZ_NEVER_INLINE bool emitSwitch(SwitchStatement* switchStmt);
+ [[nodiscard]] MOZ_NEVER_INLINE bool emitTry(TryNode* tryNode);
+
+ [[nodiscard]] bool emitJumpToFinally(JumpList* jump, uint32_t idx);
+
+ // emitDestructuringLHSRef emits the lhs expression's reference.
+ // If the lhs expression is object property |OBJ.prop|, it emits |OBJ|.
+ // If it's object element |OBJ[ELEM]|, it emits |OBJ| and |ELEM|.
+ // If there's nothing to evaluate for the reference, it emits nothing.
+ // |emitted| parameter receives the number of values pushed onto the stack.
+ [[nodiscard]] bool emitDestructuringLHSRef(ParseNode* target,
+ size_t* emitted);
+
+ // emitSetOrInitializeDestructuring assumes the lhs expression's reference
+ // and the to-be-destructured value has been pushed on the stack. It emits
+ // code to destructure a single lhs expression (either a name or a compound
+ // []/{} expression).
+ [[nodiscard]] bool emitSetOrInitializeDestructuring(ParseNode* target,
+ DestructuringFlavor flav);
+
+ // emitDestructuringObjRestExclusionSet emits the property exclusion set
+ // for the rest-property in an object pattern.
+ [[nodiscard]] bool emitDestructuringObjRestExclusionSet(ListNode* pattern);
+
+ // emitDestructuringOps assumes the to-be-destructured value has been
+ // pushed on the stack and emits code to destructure each part of a [] or
+ // {} lhs expression.
+ [[nodiscard]] bool emitDestructuringOps(ListNode* pattern,
+ DestructuringFlavor flav);
+ [[nodiscard]] bool emitDestructuringOpsArray(ListNode* pattern,
+ DestructuringFlavor flav);
+ [[nodiscard]] bool emitDestructuringOpsObject(ListNode* pattern,
+ DestructuringFlavor flav);
+
+ enum class CopyOption { Filtered, Unfiltered };
+
+ // Calls either the |CopyDataProperties| or the
+ // |CopyDataPropertiesUnfiltered| intrinsic function, consumes three (or
+ // two in the latter case) elements from the stack.
+ [[nodiscard]] bool emitCopyDataProperties(CopyOption option);
+
+ JSOp getIterCallOp(JSOp callOp, SelfHostedIter selfHostedIter);
+
+ // Push the operands for emit(Async)Iterator onto the stack.
+ [[nodiscard]] bool emitIterable(ParseNode* value,
+ SelfHostedIter selfHostedIter,
+ IteratorKind iterKind = IteratorKind::Sync);
+
+ // emitIterator expects the iterable to already be on the stack.
+ // It will replace that stack value with the corresponding iterator
+ [[nodiscard]] bool emitIterator(SelfHostedIter selfHostedIter);
+
+ [[nodiscard]] bool emitAsyncIterator(SelfHostedIter selfHostedIter);
+
+ // Pops iterator from the top of the stack. Pushes the result of |.next()|
+ // onto the stack.
+ [[nodiscard]] bool emitIteratorNext(
+ const mozilla::Maybe<uint32_t>& callSourceCoordOffset,
+ IteratorKind kind = IteratorKind::Sync,
+ SelfHostedIter selfHostedIter = SelfHostedIter::Deny);
+ [[nodiscard]] bool emitIteratorCloseInScope(
+ EmitterScope& currentScope, IteratorKind iterKind = IteratorKind::Sync,
+ CompletionKind completionKind = CompletionKind::Normal,
+ SelfHostedIter selfHostedIter = SelfHostedIter::Deny);
+ [[nodiscard]] bool emitIteratorCloseInInnermostScope(
+ IteratorKind iterKind = IteratorKind::Sync,
+ CompletionKind completionKind = CompletionKind::Normal,
+ SelfHostedIter selfHostedIter = SelfHostedIter::Deny) {
+ return emitIteratorCloseInScope(*innermostEmitterScope(), iterKind,
+ completionKind, selfHostedIter);
+ }
+
+ template <typename InnerEmitter>
+ [[nodiscard]] bool wrapWithDestructuringTryNote(int32_t iterDepth,
+ InnerEmitter emitter);
+
+ [[nodiscard]] bool defineHoistedTopLevelFunctions(ParseNode* body);
+
+ // Check if the value on top of the stack is "undefined". If so, replace
+ // that value on the stack with the value defined by |defaultExpr|.
+ // |pattern| is a lhs node of the default expression. If it's an
+ // identifier and |defaultExpr| is an anonymous function, |SetFunctionName|
+ // is called at compile time.
+ [[nodiscard]] bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern);
+
+ [[nodiscard]] bool emitAnonymousFunctionWithName(ParseNode* node,
+ TaggedParserAtomIndex name);
+
+ [[nodiscard]] bool emitAnonymousFunctionWithComputedName(
+ ParseNode* node, FunctionPrefixKind prefixKind);
+
+ [[nodiscard]] bool setFunName(FunctionBox* fun, TaggedParserAtomIndex name);
+ [[nodiscard]] bool emitInitializer(ParseNode* initializer,
+ ParseNode* pattern);
+
+ [[nodiscard]] bool emitCallSiteObjectArray(ObjLiteralWriter& writer,
+ ListNode* cookedOrRaw,
+ ParseNode* head, uint32_t count);
+ [[nodiscard]] bool emitCallSiteObject(CallSiteNode* callSiteObj);
+ [[nodiscard]] bool emitTemplateString(ListNode* templateString);
+ [[nodiscard]] bool emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs,
+ ParseNode* rhs);
+ [[nodiscard]] bool emitShortCircuitAssignment(AssignmentNode* node);
+
+ [[nodiscard]] bool emitReturn(UnaryNode* returnNode);
+ [[nodiscard]] bool finishReturn(BytecodeOffset setRvalOffset);
+
+ [[nodiscard]] bool emitExpressionStatement(UnaryNode* exprStmt);
+ [[nodiscard]] bool emitStatementList(ListNode* stmtList);
+
+ [[nodiscard]] bool emitDeleteName(UnaryNode* deleteNode);
+ [[nodiscard]] bool emitDeleteProperty(UnaryNode* deleteNode);
+ [[nodiscard]] bool emitDeleteElement(UnaryNode* deleteNode);
+ [[nodiscard]] bool emitDeleteExpression(UnaryNode* deleteNode);
+
+ // Optional methods which emit Optional Jump Target
+ [[nodiscard]] bool emitOptionalChain(UnaryNode* expr, ValueUsage valueUsage);
+ [[nodiscard]] bool emitCalleeAndThisForOptionalChain(UnaryNode* expr,
+ CallNode* callNode,
+ CallOrNewEmitter& cone);
+ [[nodiscard]] bool emitDeleteOptionalChain(UnaryNode* deleteNode);
+
+ // Optional methods which emit a shortCircuit jump. They need to be called by
+ // a method which emits an Optional Jump Target, see below.
+ [[nodiscard]] bool emitOptionalDotExpression(PropertyAccessBase* expr,
+ PropOpEmitter& poe, bool isSuper,
+ OptionalEmitter& oe);
+ [[nodiscard]] bool emitOptionalElemExpression(PropertyByValueBase* elem,
+ ElemOpEmitter& eoe,
+ bool isSuper,
+ OptionalEmitter& oe);
+ [[nodiscard]] bool emitOptionalPrivateExpression(
+ PrivateMemberAccessBase* privateExpr, PrivateOpEmitter& xoe,
+ OptionalEmitter& oe);
+ [[nodiscard]] bool emitOptionalCall(CallNode* callNode, OptionalEmitter& oe,
+ ValueUsage valueUsage);
+ [[nodiscard]] bool emitDeletePropertyInOptChain(PropertyAccessBase* propExpr,
+ OptionalEmitter& oe);
+ [[nodiscard]] bool emitDeleteElementInOptChain(PropertyByValueBase* elemExpr,
+ OptionalEmitter& oe);
+
+ // |op| must be JSOp::Typeof or JSOp::TypeofExpr.
+ [[nodiscard]] bool emitTypeof(UnaryNode* typeofNode, JSOp op);
+
+ [[nodiscard]] bool emitUnary(UnaryNode* unaryNode);
+ [[nodiscard]] bool emitRightAssociative(ListNode* node);
+ [[nodiscard]] bool emitLeftAssociative(ListNode* node);
+ [[nodiscard]] bool emitPrivateInExpr(ListNode* node);
+ [[nodiscard]] bool emitShortCircuit(ListNode* node, ValueUsage valueUsage);
+ [[nodiscard]] bool emitSequenceExpr(ListNode* node, ValueUsage valueUsage);
+
+ [[nodiscard]] MOZ_NEVER_INLINE bool emitIncOrDec(UnaryNode* incDec,
+ ValueUsage valueUsage);
+
+ [[nodiscard]] bool emitConditionalExpression(
+ ConditionalExpression& conditional, ValueUsage valueUsage);
+
+ [[nodiscard]] ParseNode* getCoordNode(ParseNode* callNode,
+ ParseNode* calleeNode, JSOp op,
+ ListNode* argsList);
+
+ [[nodiscard]] bool emitArguments(ListNode* argsList, bool isCall,
+ bool isSpread, CallOrNewEmitter& cone);
+ [[nodiscard]] bool emitCallOrNew(CallNode* callNode, ValueUsage valueUsage);
+ [[nodiscard]] bool emitDebugCheckSelfHosted();
+ [[nodiscard]] bool emitSelfHostedCallFunction(CallNode* callNode, JSOp op);
+ [[nodiscard]] bool emitSelfHostedResumeGenerator(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedForceInterpreter();
+ [[nodiscard]] bool emitSelfHostedAllowContentIter(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedAllowContentIterWith(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedAllowContentIterWithNext(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedDefineDataProperty(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedGetPropertySuper(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedHasOwn(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedToNumeric(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedToString(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedIsNullOrUndefined(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedIteratorClose(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedGetBuiltinConstructor(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedGetBuiltinPrototype(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedGetBuiltinSymbol(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedSetIsInlinableLargeFunction(
+ CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedSetCanonicalName(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedArgumentsLength(CallNode* callNode);
+ [[nodiscard]] bool emitSelfHostedGetArgument(CallNode* callNode);
+#ifdef DEBUG
+ void assertSelfHostedExpectedTopLevel(ParseNode* node);
+ void assertSelfHostedUnsafeGetReservedSlot(ListNode* argsList);
+ void assertSelfHostedUnsafeSetReservedSlot(ListNode* argsList);
+#endif
+
+ [[nodiscard]] bool emitDo(BinaryNode* doNode);
+ [[nodiscard]] bool emitWhile(BinaryNode* whileNode);
+
+ [[nodiscard]] bool emitFor(
+ ForNode* forNode, const EmitterScope* headLexicalEmitterScope = nullptr);
+ [[nodiscard]] bool emitCStyleFor(ForNode* forNode,
+ const EmitterScope* headLexicalEmitterScope);
+ [[nodiscard]] bool emitForIn(ForNode* forNode,
+ const EmitterScope* headLexicalEmitterScope);
+ [[nodiscard]] bool emitForOf(ForNode* forNode,
+ const EmitterScope* headLexicalEmitterScope);
+
+ [[nodiscard]] bool emitInitializeForInOrOfTarget(TernaryNode* forHead);
+
+ [[nodiscard]] bool emitBreak(TaggedParserAtomIndex label);
+ [[nodiscard]] bool emitContinue(TaggedParserAtomIndex label);
+
+ [[nodiscard]] bool emitFunctionFormalParameters(ParamsBodyNode* paramsBody);
+ [[nodiscard]] bool emitInitializeFunctionSpecialNames();
+ [[nodiscard]] bool emitLexicalInitialization(NameNode* name);
+ [[nodiscard]] bool emitLexicalInitialization(TaggedParserAtomIndex name);
+
+ // Emit bytecode for the spread operator.
+ //
+ // emitSpread expects some values representing the spread target (an array or
+ // a tuple), the iterator and it's next() method to be on the stack in that
+ // order (iterator's next() on the bottom).
+ // The number of values representing the spread target is
+ // `spreadeeStackItems`: it's 2 for arrays (one for the array and one for the
+ // index) and 1 for tuples (the tuple itself).
+ // Since arrays and tuples use different opcodes to initialize new elements,
+ // it must be specified using `storeElementOp`.
+ // When emitSpread() finishes, the stack only contains the values representing
+ // the spread target.
+ [[nodiscard]] bool emitSpread(SelfHostedIter selfHostedIter,
+ int spreadeeStackItems, JSOp storeElementOp);
+ // This shortcut can be used when spreading into arrays, as it assumes
+ // `spreadeeStackItems = 2` (|ARRAY INDEX|) and `storeElementOp =
+ // JSOp::InitElemInc`
+ [[nodiscard]] bool emitSpread(SelfHostedIter selfHostedIter);
+
+ enum class ClassNameKind {
+ // The class name is defined through its BindingIdentifier, if present.
+ BindingName,
+
+ // The class is anonymous and has a statically inferred name.
+ InferredName,
+
+ // The class is anonymous and has a dynamically computed name.
+ ComputedName
+ };
+
+ [[nodiscard]] bool emitClass(
+ ClassNode* classNode, ClassNameKind nameKind = ClassNameKind::BindingName,
+ TaggedParserAtomIndex nameForAnonymousClass =
+ TaggedParserAtomIndex::null());
+
+ [[nodiscard]] bool emitSuperElemOperands(
+ PropertyByValue* elem, EmitElemOption opts = EmitElemOption::Get);
+ [[nodiscard]] bool emitSuperGetElem(PropertyByValue* elem,
+ bool isCall = false);
+
+ [[nodiscard]] bool emitCalleeAndThis(ParseNode* callee, CallNode* maybeCall,
+ CallOrNewEmitter& cone);
+
+ [[nodiscard]] bool emitOptionalCalleeAndThis(ParseNode* callee,
+ CallNode* call,
+ CallOrNewEmitter& cone,
+ OptionalEmitter& oe);
+
+#ifdef ENABLE_RECORD_TUPLE
+ [[nodiscard]] bool emitRecordLiteral(ListNode* record);
+ [[nodiscard]] bool emitTupleLiteral(ListNode* tuple);
+#endif
+
+ [[nodiscard]] bool emitExportDefault(BinaryNode* exportNode);
+
+ [[nodiscard]] bool emitReturnRval() { return emit1(JSOp::RetRval); }
+
+ [[nodiscard]] bool emitCheckPrivateField(ThrowCondition throwCondition,
+ ThrowMsgKind msgKind) {
+ return emit3(JSOp::CheckPrivateField, uint8_t(throwCondition),
+ uint8_t(msgKind));
+ }
+
+ [[nodiscard]] bool emitNewPrivateName(TaggedParserAtomIndex bindingName,
+ TaggedParserAtomIndex symbolName);
+
+ template <class ClassMemberType>
+ [[nodiscard]] bool emitNewPrivateNames(ListNode* classMembers);
+
+ [[nodiscard]] bool emitNewPrivateNames(TaggedParserAtomIndex privateBrandName,
+ ListNode* classMembers);
+
+ [[nodiscard]] js::UniquePtr<ImmutableScriptData> createImmutableScriptData();
+
+#ifdef ENABLE_DECORATORS
+ [[nodiscard]] bool emitCheckIsCallable();
+#endif
+
+ private:
+ [[nodiscard]] SelfHostedIter getSelfHostedIterFor(ParseNode* parseNode);
+
+ [[nodiscard]] bool emitSelfHostedGetBuiltinConstructorOrPrototype(
+ CallNode* callNode, bool isConstructor);
+
+ public:
+#if defined(DEBUG) || defined(JS_JITSPEW)
+ void dumpAtom(TaggedParserAtomIndex index) const;
+#endif
+};
+
+class MOZ_RAII AutoCheckUnstableEmitterScope {
+#ifdef DEBUG
+ bool prev_;
+ BytecodeEmitter* bce_;
+#endif
+
+ public:
+ AutoCheckUnstableEmitterScope() = delete;
+ explicit AutoCheckUnstableEmitterScope(BytecodeEmitter* bce)
+#ifdef DEBUG
+ : bce_(bce)
+#endif
+ {
+#ifdef DEBUG
+ prev_ = bce_->unstableEmitterScope;
+ bce_->unstableEmitterScope = true;
+#endif
+ }
+ ~AutoCheckUnstableEmitterScope() {
+#ifdef DEBUG
+ bce_->unstableEmitterScope = prev_;
+#endif
+ }
+};
+
+} /* namespace frontend */
+} /* namespace js */
+
+#endif /* frontend_BytecodeEmitter_h */