diff options
Diffstat (limited to 'js/src/frontend/BytecodeEmitter.h')
-rw-r--r-- | js/src/frontend/BytecodeEmitter.h | 763 |
1 files changed, 763 insertions, 0 deletions
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h new file mode 100644 index 000000000..1bb4191ee --- /dev/null +++ b/js/src/frontend/BytecodeEmitter.h @@ -0,0 +1,763 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 "jscntxt.h" +#include "jsopcode.h" +#include "jsscript.h" + +#include "ds/InlineTable.h" +#include "frontend/Parser.h" +#include "frontend/SharedContext.h" +#include "frontend/SourceNotes.h" +#include "vm/Interpreter.h" + +namespace js { +namespace frontend { + +class FullParseHandler; +class ObjectBox; +class ParseNode; +template <typename ParseHandler> class Parser; +class SharedContext; +class TokenStream; + +class CGConstList { + Vector<Value> list; + public: + explicit CGConstList(ExclusiveContext* cx) : list(cx) {} + MOZ_MUST_USE bool append(const Value& v) { + MOZ_ASSERT_IF(v.isString(), v.toString()->isAtom()); + return list.append(v); + } + size_t length() const { return list.length(); } + void finish(ConstArray* array); +}; + +struct CGObjectList { + uint32_t length; /* number of emitted so far objects */ + ObjectBox* firstbox; /* first emitted object */ + ObjectBox* lastbox; /* last emitted object */ + + CGObjectList() : length(0), firstbox(nullptr), lastbox(nullptr) {} + + bool isAdded(ObjectBox* objbox); + unsigned add(ObjectBox* objbox); + unsigned indexOf(JSObject* obj); + void finish(ObjectArray* array); + ObjectBox* find(uint32_t index); +}; + +struct MOZ_STACK_CLASS CGScopeList { + Rooted<GCVector<Scope*>> vector; + + explicit CGScopeList(ExclusiveContext* cx) + : vector(cx, GCVector<Scope*>(cx)) + { } + + bool append(Scope* scope) { return vector.append(scope); } + uint32_t length() const { return vector.length(); } + void finish(ScopeArray* array); +}; + +struct CGTryNoteList { + Vector<JSTryNote> list; + explicit CGTryNoteList(ExclusiveContext* cx) : list(cx) {} + + MOZ_MUST_USE bool append(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end); + size_t length() const { return list.length(); } + void finish(TryNoteArray* array); +}; + +struct CGScopeNote : public ScopeNote +{ + // The end offset. Used to compute the length; may need adjusting first if + // in the prologue. + uint32_t end; + + // Is the start offset in the prologue? + bool startInPrologue; + + // Is the end offset in the prologue? + bool endInPrologue; +}; + +struct CGScopeNoteList { + Vector<CGScopeNote> list; + explicit CGScopeNoteList(ExclusiveContext* cx) : list(cx) {} + + MOZ_MUST_USE bool append(uint32_t scopeIndex, uint32_t offset, bool inPrologue, + uint32_t parent); + void recordEnd(uint32_t index, uint32_t offset, bool inPrologue); + size_t length() const { return list.length(); } + void finish(ScopeNoteArray* array, uint32_t prologueLength); +}; + +struct CGYieldOffsetList { + Vector<uint32_t> list; + explicit CGYieldOffsetList(ExclusiveContext* cx) : list(cx) {} + + MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); } + size_t length() const { return list.length(); } + void finish(YieldOffsetArray& array, uint32_t prologueLength); +}; + +// Use zero inline elements because these go on the stack and affect how many +// nested functions are possible. +typedef Vector<jsbytecode, 0> BytecodeVector; +typedef Vector<jssrcnote, 0> SrcNotesVector; + +// Linked list of jump instructions that need to be patched. The linked list is +// stored in the bytes of the incomplete bytecode that will be patched, so no +// extra memory is needed, and patching the instructions destroys the list. +// +// Example: +// +// JumpList brList; +// if (!emitJump(JSOP_IFEQ, &brList)) +// return false; +// ... +// JumpTarget label; +// if (!emitJumpTarget(&label)) +// return false; +// ... +// if (!emitJump(JSOP_GOTO, &brList)) +// return false; +// ... +// patchJumpsToTarget(brList, label); +// +// +-> -1 +// | +// | +// ifeq .. <+ + +-+ ifeq .. +// .. | | .. +// label: | +-> label: +// jumptarget | | jumptarget +// .. | | .. +// goto .. <+ + +-+ goto .. <+ +// | | +// | | +// + + +// brList brList +// +// | ^ +// +------- patchJumpsToTarget -------+ +// + +// Offset of a jump target instruction, used for patching jump instructions. +struct JumpTarget { + ptrdiff_t offset; +}; + +struct JumpList { + // -1 is used to mark the end of jump lists. + JumpList() : offset(-1) {} + ptrdiff_t offset; + + // Add a jump instruction to the list. + void push(jsbytecode* code, ptrdiff_t jumpOffset); + + // Patch all jump instructions in this list to jump to `target`. This + // clobbers the list. + void patchAll(jsbytecode* code, JumpTarget target); +}; + +struct MOZ_STACK_CLASS BytecodeEmitter +{ + class TDZCheckCache; + class NestableControl; + class EmitterScope; + + SharedContext* const sc; /* context shared between parsing and bytecode generation */ + + ExclusiveContext* const cx; + + BytecodeEmitter* const parent; /* enclosing function or global context */ + + Rooted<JSScript*> script; /* the JSScript we're ultimately producing */ + + Rooted<LazyScript*> lazyScript; /* the lazy script if mode is LazyFunction, + nullptr otherwise. */ + + struct EmitSection { + BytecodeVector code; /* bytecode */ + SrcNotesVector notes; /* source notes, see below */ + ptrdiff_t lastNoteOffset; /* code offset for last source note */ + uint32_t currentLine; /* line number for tree-based srcnote gen */ + uint32_t lastColumn; /* zero-based column index on currentLine of + last SRC_COLSPAN-annotated opcode */ + JumpTarget lastTarget; // Last jump target emitted. + + EmitSection(ExclusiveContext* cx, uint32_t lineNum) + : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0), + lastTarget{ -1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH) } + {} + }; + EmitSection prologue, main, *current; + + Parser<FullParseHandler>* const parser; + + PooledMapPtr<AtomIndexMap> atomIndices; /* literals indexed for mapping */ + unsigned firstLine; /* first line, for JSScript::initFromEmitter */ + + uint32_t maxFixedSlots; /* maximum number of fixed frame slots so far */ + uint32_t maxStackDepth; /* maximum number of expression stack slots so far */ + + int32_t stackDepth; /* current stack depth in script frame */ + + uint32_t arrayCompDepth; /* stack depth of array in comprehension */ + + unsigned emitLevel; /* emitTree recursion level */ + + uint32_t bodyScopeIndex; /* index into scopeList of the body scope */ + + EmitterScope* varEmitterScope; + NestableControl* innermostNestableControl; + EmitterScope* innermostEmitterScope; + TDZCheckCache* innermostTDZCheckCache; + + CGConstList constList; /* constants to be included with the script */ + CGObjectList objectList; /* list of emitted objects */ + CGScopeList scopeList; /* list of emitted scopes */ + CGTryNoteList tryNoteList; /* list of emitted try notes */ + CGScopeNoteList scopeNoteList; /* list of emitted block scope notes */ + + /* + * For each yield op, map the yield index (stored as bytecode operand) to + * the offset of the next op. + */ + CGYieldOffsetList yieldOffsetList; + + uint16_t typesetCount; /* Number of JOF_TYPESET opcodes generated */ + + bool hasSingletons:1; /* script contains singleton initializer JSOP_OBJECT */ + + bool hasTryFinally:1; /* script contains finally block */ + + bool emittingRunOnceLambda:1; /* true while emitting a lambda which is only + expected to run once. */ + + bool isRunOnceLambda(); + + 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; + + // The end location of a function body that is being emitted. + uint32_t functionBodyEndPos; + // Whether functionBodyEndPos was set. + bool functionBodyEndPosSet; + + /* + * 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. + */ + BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc, + HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum, + EmitterMode emitterMode = Normal); + + // An alternate constructor that uses a TokenPos for the starting + // line and that sets functionBodyEndPos as well. + BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc, + HandleScript script, Handle<LazyScript*> lazyScript, + TokenPos bodyPosition, EmitterMode emitterMode = Normal); + + MOZ_MUST_USE bool init(); + + template <typename Predicate /* (NestableControl*) -> bool */> + NestableControl* findInnermostNestableControl(Predicate predicate) const; + + template <typename T> + T* findInnermostNestableControl() const; + + template <typename T, typename Predicate /* (T*) -> bool */> + T* findInnermostNestableControl(Predicate predicate) const; + + NameLocation lookupName(JSAtom* name); + + // 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(JSAtom* name, EmitterScope* target); + + // Get the location of a name known to be bound in the function scope, + // starting at the source scope. + mozilla::Maybe<NameLocation> locationOfNameBoundInFunctionScope(JSAtom* name, + EmitterScope* source); + + mozilla::Maybe<NameLocation> locationOfNameBoundInFunctionScope(JSAtom* name) { + return locationOfNameBoundInFunctionScope(name, innermostEmitterScope); + } + + void setVarEmitterScope(EmitterScope* emitterScope) { + MOZ_ASSERT(emitterScope); + MOZ_ASSERT(!varEmitterScope); + varEmitterScope = emitterScope; + } + + Scope* bodyScope() const { return scopeList.vector[bodyScopeIndex]; } + Scope* outermostScope() const { return scopeList.vector[0]; } + Scope* innermostScope() const; + + MOZ_ALWAYS_INLINE + MOZ_MUST_USE bool makeAtomIndex(JSAtom* atom, uint32_t* indexp) { + MOZ_ASSERT(atomIndices); + AtomIndexMap::AddPtr p = atomIndices->lookupForAdd(atom); + if (p) { + *indexp = p->value(); + return true; + } + + uint32_t index = atomIndices->count(); + if (!atomIndices->add(p, atom, index)) + return false; + + *indexp = index; + return true; + } + + bool isInLoop(); + MOZ_MUST_USE bool checkSingletonContext(); + + // Check whether our function is in a run-once context (a toplevel + // run-one script or a run-once lambda). + MOZ_MUST_USE bool checkRunOnceContext(); + + bool needsImplicitThis(); + + MOZ_MUST_USE bool maybeSetDisplayURL(); + MOZ_MUST_USE bool maybeSetSourceMap(); + void tellDebuggerAboutCompiledScript(ExclusiveContext* cx); + + inline TokenStream* tokenStream(); + + BytecodeVector& code() const { return current->code; } + jsbytecode* code(ptrdiff_t offset) const { return current->code.begin() + offset; } + ptrdiff_t offset() const { return current->code.end() - current->code.begin(); } + ptrdiff_t prologueOffset() const { return prologue.code.end() - prologue.code.begin(); } + void switchToMain() { current = &main; } + void switchToPrologue() { current = &prologue; } + bool inPrologue() const { return current == &prologue; } + + SrcNotesVector& notes() const { return current->notes; } + ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; } + unsigned currentLine() const { return current->currentLine; } + unsigned lastColumn() const { return current->lastColumn; } + + // Check if the last emitted opcode is a jump target. + bool lastOpcodeIsJumpTarget() const { + return offset() - current->lastTarget.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH); + } + + // JumpTarget should not be part of the emitted statement, as they can be + // aliased by multiple statements. If we included the jump target as part of + // the statement we might have issues where the enclosing statement might + // not contain all the opcodes of the enclosed statements. + ptrdiff_t lastNonJumpTargetOffset() const { + return lastOpcodeIsJumpTarget() ? current->lastTarget.offset : offset(); + } + + void setFunctionBodyEndPos(TokenPos pos) { + functionBodyEndPos = pos.end; + functionBodyEndPosSet = true; + } + + bool reportError(ParseNode* pn, unsigned errorNumber, ...); + bool reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...); + bool reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...); + + // 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. + MOZ_MUST_USE bool checkSideEffects(ParseNode* pn, bool* answer); + +#ifdef DEBUG + MOZ_MUST_USE bool checkStrictOrSloppy(JSOp op); +#endif + + // 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. + MOZ_MUST_USE bool newSrcNote(SrcNoteType type, unsigned* indexp = nullptr); + MOZ_MUST_USE bool newSrcNote2(SrcNoteType type, ptrdiff_t offset, unsigned* indexp = nullptr); + MOZ_MUST_USE bool newSrcNote3(SrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2, + unsigned* indexp = nullptr); + + void copySrcNotes(jssrcnote* destination, uint32_t nsrcnotes); + MOZ_MUST_USE bool setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offset); + + // NB: this function can add at most one extra extended delta note. + MOZ_MUST_USE bool addToSrcNoteDelta(jssrcnote* sn, ptrdiff_t delta); + + // Finish taking source notes in cx's notePool. If successful, the final + // source note count is stored in the out outparam. + MOZ_MUST_USE bool finishTakingSrcNotes(uint32_t* out); + + // Control whether emitTree emits a line number note. + enum EmitLineNumberNote { + EMIT_LINENOTE, + SUPPRESS_LINENOTE + }; + + // Emit code for the tree rooted at pn. + MOZ_MUST_USE bool emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote = EMIT_LINENOTE); + + // Emit code for the tree rooted at pn with its own TDZ cache. + MOZ_MUST_USE bool emitConditionallyExecutedTree(ParseNode* pn); + + // Emit global, eval, or module code for tree rooted at body. Always + // encompasses the entire source. + MOZ_MUST_USE bool emitScript(ParseNode* body); + + // Emit function code for the tree rooted at body. + MOZ_MUST_USE bool emitFunctionScript(ParseNode* body); + + // If op is JOF_TYPESET (see the type barriers comment in TypeInference.h), + // reserve a type set to store its result. + void checkTypeSet(JSOp op); + + void updateDepth(ptrdiff_t target); + MOZ_MUST_USE bool updateLineNumberNotes(uint32_t offset); + MOZ_MUST_USE bool updateSourceCoordNotes(uint32_t offset); + + JSOp strictifySetNameOp(JSOp op); + + MOZ_MUST_USE bool flushPops(int* npops); + + MOZ_MUST_USE bool emitCheck(ptrdiff_t delta, ptrdiff_t* offset); + + // Emit one bytecode. + MOZ_MUST_USE bool emit1(JSOp op); + + // Emit two bytecodes, an opcode (op) with a byte of immediate operand + // (op1). + MOZ_MUST_USE bool emit2(JSOp op, uint8_t op1); + + // Emit three bytecodes, an opcode with two bytes of immediate operands. + MOZ_MUST_USE bool emit3(JSOp op, jsbytecode op1, jsbytecode op2); + + // Helper to emit JSOP_DUPAT. The argument is the value's depth on the + // JS stack, as measured from the top. + MOZ_MUST_USE bool emitDupAt(unsigned slotFromTop); + + // Helper to emit JSOP_CHECKISOBJ. + MOZ_MUST_USE bool emitCheckIsObj(CheckIsObjectKind kind); + + // Emit a bytecode followed by an uint16 immediate operand stored in + // big-endian order. + MOZ_MUST_USE bool emitUint16Operand(JSOp op, uint32_t operand); + + // Emit a bytecode followed by an uint32 immediate operand. + MOZ_MUST_USE bool emitUint32Operand(JSOp op, uint32_t operand); + + // Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. + MOZ_MUST_USE bool emitN(JSOp op, size_t extra, ptrdiff_t* offset = nullptr); + + MOZ_MUST_USE bool emitNumberOp(double dval); + + MOZ_MUST_USE bool emitThisLiteral(ParseNode* pn); + MOZ_MUST_USE bool emitGetFunctionThis(ParseNode* pn); + MOZ_MUST_USE bool emitGetThisForSuperBase(ParseNode* pn); + MOZ_MUST_USE bool emitSetThis(ParseNode* pn); + MOZ_MUST_USE bool emitCheckDerivedClassConstructorReturn(); + + // Handle jump opcodes and jump targets. + MOZ_MUST_USE bool emitJumpTarget(JumpTarget* target); + MOZ_MUST_USE bool emitJumpNoFallthrough(JSOp op, JumpList* jump); + MOZ_MUST_USE bool emitJump(JSOp op, JumpList* jump); + MOZ_MUST_USE bool emitBackwardJump(JSOp op, JumpTarget target, JumpList* jump, + JumpTarget* fallthrough); + void patchJumpsToTarget(JumpList jump, JumpTarget target); + MOZ_MUST_USE bool emitJumpTargetAndPatch(JumpList jump); + + MOZ_MUST_USE bool emitCall(JSOp op, uint16_t argc, ParseNode* pn = nullptr); + MOZ_MUST_USE bool emitCallIncDec(ParseNode* incDec); + + MOZ_MUST_USE bool emitLoopHead(ParseNode* nextpn, JumpTarget* top); + MOZ_MUST_USE bool emitLoopEntry(ParseNode* nextpn, JumpList entryJump); + + MOZ_MUST_USE bool emitGoto(NestableControl* target, JumpList* jumplist, + SrcNoteType noteType = SRC_NULL); + + MOZ_MUST_USE bool emitIndex32(JSOp op, uint32_t index); + MOZ_MUST_USE bool emitIndexOp(JSOp op, uint32_t index); + + MOZ_MUST_USE bool emitAtomOp(JSAtom* atom, JSOp op); + MOZ_MUST_USE bool emitAtomOp(ParseNode* pn, JSOp op); + + MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn); + MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count, JSOp op); + MOZ_MUST_USE bool emitArrayComp(ParseNode* pn); + + MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op); + MOZ_MUST_USE bool emitInternedObjectOp(uint32_t index, JSOp op); + MOZ_MUST_USE bool emitObjectOp(ObjectBox* objbox, JSOp op); + MOZ_MUST_USE bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op); + MOZ_MUST_USE bool emitRegExp(uint32_t index); + + MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(ParseNode* pn, bool needsProto = false); + MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ParseNode* pn); + + MOZ_MUST_USE bool emitHoistedFunctionsInList(ParseNode* pn); + + MOZ_MUST_USE bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, + PropListType type); + + // 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. + MOZ_MUST_USE bool emitLocalOp(JSOp op, uint32_t slot); + + MOZ_MUST_USE bool emitArgOp(JSOp op, uint16_t slot); + MOZ_MUST_USE bool emitEnvCoordOp(JSOp op, EnvironmentCoordinate ec); + + MOZ_MUST_USE bool emitGetNameAtLocation(JSAtom* name, const NameLocation& loc, + bool callContext = false); + MOZ_MUST_USE bool emitGetName(JSAtom* name, bool callContext = false) { + return emitGetNameAtLocation(name, lookupName(name), callContext); + } + MOZ_MUST_USE bool emitGetName(ParseNode* pn, bool callContext = false); + + template <typename RHSEmitter> + MOZ_MUST_USE bool emitSetOrInitializeNameAtLocation(HandleAtom name, const NameLocation& loc, + RHSEmitter emitRhs, bool initialize); + template <typename RHSEmitter> + MOZ_MUST_USE bool emitSetOrInitializeName(HandleAtom name, RHSEmitter emitRhs, + bool initialize) + { + return emitSetOrInitializeNameAtLocation(name, lookupName(name), emitRhs, initialize); + } + template <typename RHSEmitter> + MOZ_MUST_USE bool emitSetName(ParseNode* pn, RHSEmitter emitRhs) { + RootedAtom name(cx, pn->name()); + return emitSetName(name, emitRhs); + } + template <typename RHSEmitter> + MOZ_MUST_USE bool emitSetName(HandleAtom name, RHSEmitter emitRhs) { + return emitSetOrInitializeName(name, emitRhs, false); + } + template <typename RHSEmitter> + MOZ_MUST_USE bool emitInitializeName(ParseNode* pn, RHSEmitter emitRhs) { + RootedAtom name(cx, pn->name()); + return emitInitializeName(name, emitRhs); + } + template <typename RHSEmitter> + MOZ_MUST_USE bool emitInitializeName(HandleAtom name, RHSEmitter emitRhs) { + return emitSetOrInitializeName(name, emitRhs, true); + } + + MOZ_MUST_USE bool emitTDZCheckIfNeeded(JSAtom* name, const NameLocation& loc); + + MOZ_MUST_USE bool emitNameIncDec(ParseNode* pn); + + MOZ_MUST_USE bool emitDeclarationList(ParseNode* decls); + MOZ_MUST_USE bool emitSingleDeclaration(ParseNode* decls, ParseNode* decl, + ParseNode* initializer); + + MOZ_MUST_USE bool emitNewInit(JSProtoKey key); + MOZ_MUST_USE bool emitSingletonInitialiser(ParseNode* pn); + + MOZ_MUST_USE bool emitPrepareIteratorResult(); + MOZ_MUST_USE bool emitFinishIteratorResult(bool done); + MOZ_MUST_USE bool iteratorResultShape(unsigned* shape); + + MOZ_MUST_USE bool emitYield(ParseNode* pn); + MOZ_MUST_USE bool emitYieldOp(JSOp op); + MOZ_MUST_USE bool emitYieldStar(ParseNode* iter, ParseNode* gen); + + MOZ_MUST_USE bool emitPropLHS(ParseNode* pn); + MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op); + MOZ_MUST_USE bool emitPropIncDec(ParseNode* pn); + + MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow); + MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow); + + MOZ_MUST_USE bool emitComputedPropertyName(ParseNode* computedPropName); + + // 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, Set, Call, IncDec, CompoundAssign }; + MOZ_MUST_USE bool emitElemOperands(ParseNode* pn, EmitElemOption opts); + + MOZ_MUST_USE bool emitElemOpBase(JSOp op); + MOZ_MUST_USE bool emitElemOp(ParseNode* pn, JSOp op); + MOZ_MUST_USE bool emitElemIncDec(ParseNode* pn); + + MOZ_MUST_USE bool emitCatch(ParseNode* pn); + MOZ_MUST_USE bool emitIf(ParseNode* pn); + MOZ_MUST_USE bool emitWith(ParseNode* pn); + + MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(const LabeledStatement* pn); + MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(ParseNode* pn); + MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body, + EmitLineNumberNote emitLineNote = EMIT_LINENOTE); + MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(ParseNode* pn); + MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(ParseNode* pn); + + enum DestructuringFlavor { + // Destructuring into a declaration. + DestructuringDeclaration, + + // Destructuring into a formal parameter, when the formal parameters + // contain an expression that might be evaluated, and thus require + // this destructuring to assign not into the innermost scope that + // contains the function body's vars, but into its enclosing scope for + // parameter expressions. + DestructuringFormalParameterInVarScope, + + // Destructuring as part of an AssignmentExpression. + DestructuringAssignment + }; + + // emitDestructuringLHS assumes the to-be-destructured value has been pushed on + // the stack and emits code to destructure a single lhs expression (either a + // name or a compound []/{} expression). + MOZ_MUST_USE bool emitDestructuringLHS(ParseNode* target, DestructuringFlavor flav); + MOZ_MUST_USE bool emitConditionallyExecutedDestructuringLHS(ParseNode* target, + DestructuringFlavor flav); + + // 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. + MOZ_MUST_USE bool emitDestructuringOps(ParseNode* pattern, DestructuringFlavor flav); + MOZ_MUST_USE bool emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlavor flav); + MOZ_MUST_USE bool emitDestructuringOpsObject(ParseNode* pattern, DestructuringFlavor flav); + + typedef bool + (*DestructuringDeclEmitter)(BytecodeEmitter* bce, ParseNode* pn); + + template <typename NameEmitter> + MOZ_MUST_USE bool emitDestructuringDeclsWithEmitter(ParseNode* pattern, NameEmitter emitName); + + // Throw a TypeError if the value atop the stack isn't convertible to an + // object, with no overall effect on the stack. + MOZ_MUST_USE bool emitRequireObjectCoercible(); + + // emitIterator expects the iterable to already be on the stack. + // It will replace that stack value with the corresponding iterator + MOZ_MUST_USE bool emitIterator(); + + // Pops iterator from the top of the stack. Pushes the result of |.next()| + // onto the stack. + MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false); + + // 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|. + MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr); + + MOZ_MUST_USE bool emitCallSiteObject(ParseNode* pn); + MOZ_MUST_USE bool emitTemplateString(ParseNode* pn); + MOZ_MUST_USE bool emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs); + + MOZ_MUST_USE bool emitReturn(ParseNode* pn); + MOZ_MUST_USE bool emitStatement(ParseNode* pn); + MOZ_MUST_USE bool emitStatementList(ParseNode* pn); + + MOZ_MUST_USE bool emitDeleteName(ParseNode* pn); + MOZ_MUST_USE bool emitDeleteProperty(ParseNode* pn); + MOZ_MUST_USE bool emitDeleteElement(ParseNode* pn); + MOZ_MUST_USE bool emitDeleteExpression(ParseNode* pn); + + // |op| must be JSOP_TYPEOF or JSOP_TYPEOFEXPR. + MOZ_MUST_USE bool emitTypeof(ParseNode* node, JSOp op); + + MOZ_MUST_USE bool emitUnary(ParseNode* pn); + MOZ_MUST_USE bool emitRightAssociative(ParseNode* pn); + MOZ_MUST_USE bool emitLeftAssociative(ParseNode* pn); + MOZ_MUST_USE bool emitLogical(ParseNode* pn); + MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn); + + MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn); + + MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional); + + MOZ_MUST_USE bool isRestParameter(ParseNode* pn, bool* result); + MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted); + + MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn); + MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn); + MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn); + MOZ_MUST_USE bool emitSelfHostedForceInterpreter(ParseNode* pn); + MOZ_MUST_USE bool emitSelfHostedAllowContentSpread(ParseNode* pn); + + MOZ_MUST_USE bool emitComprehensionFor(ParseNode* compFor); + MOZ_MUST_USE bool emitComprehensionForIn(ParseNode* pn); + MOZ_MUST_USE bool emitComprehensionForInOrOfVariables(ParseNode* pn, bool* lexicalScope); + MOZ_MUST_USE bool emitComprehensionForOf(ParseNode* pn); + + MOZ_MUST_USE bool emitDo(ParseNode* pn); + MOZ_MUST_USE bool emitWhile(ParseNode* pn); + + MOZ_MUST_USE bool emitFor(ParseNode* pn, EmitterScope* headLexicalEmitterScope = nullptr); + MOZ_MUST_USE bool emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterScope); + MOZ_MUST_USE bool emitForIn(ParseNode* pn, EmitterScope* headLexicalEmitterScope); + MOZ_MUST_USE bool emitForOf(ParseNode* pn, EmitterScope* headLexicalEmitterScope); + + MOZ_MUST_USE bool emitInitializeForInOrOfTarget(ParseNode* forHead); + + MOZ_MUST_USE bool emitBreak(PropertyName* label); + MOZ_MUST_USE bool emitContinue(PropertyName* label); + + MOZ_MUST_USE bool emitFunctionFormalParametersAndBody(ParseNode* pn); + MOZ_MUST_USE bool emitFunctionFormalParameters(ParseNode* pn); + MOZ_MUST_USE bool emitInitializeFunctionSpecialNames(); + MOZ_MUST_USE bool emitFunctionBody(ParseNode* pn); + MOZ_MUST_USE bool emitLexicalInitialization(ParseNode* pn); + + // Emit bytecode for the spread operator. + // + // emitSpread expects the current index (I) of the array, the array itself + // and the iterator to be on the stack in that order (iterator on the bottom). + // It will pop the iterator and I, then iterate over the iterator by calling + // |.next()| and put the results into the I-th element of array with + // incrementing I, then push the result I (it will be original I + + // iteration count). The stack after iteration will look like |ARRAY INDEX|. + MOZ_MUST_USE bool emitSpread(bool allowSelfHosted = false); + + MOZ_MUST_USE bool emitClass(ParseNode* pn); + MOZ_MUST_USE bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false); + MOZ_MUST_USE bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false); + MOZ_MUST_USE bool emitSuperElemOperands(ParseNode* pn, + EmitElemOption opts = EmitElemOption::Get); + MOZ_MUST_USE bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false); +}; + +} /* namespace frontend */ +} /* namespace js */ + +#endif /* frontend_BytecodeEmitter_h */ |