js/src/frontend/BytecodeEmitter.h
author Eric Faust <efaustbmo@gmail.com>
Tue, 29 Mar 2016 16:46:20 -0700
changeset 291006 cd2868983231ad1dcf5c4c9e12d0f03b12cbf950
parent 284369 966f47ed2f25eb54fb1f967d4443b3c2b8b63220
child 291081 57e8e4c71ecf66ccf431eebf8c88083df266f65c
permissions -rw-r--r--
Bug 1260577 - Fix |obj[expr] += e2| erroneously calling expr.toString() twice. (r=till)

/* -*- 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/. */

#ifndef frontend_BytecodeEmitter_h
#define frontend_BytecodeEmitter_h

/*
 * JS bytecode generation.
 */

#include "jscntxt.h"
#include "jsopcode.h"
#include "jsscript.h"

#include "frontend/ParseMaps.h"
#include "frontend/Parser.h"
#include "frontend/SharedContext.h"
#include "frontend/SourceNotes.h"

namespace js {

class ScopeObject;

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) {}
    bool append(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*          lastbox;   /* last emitted object */

    CGObjectList() : length(0), lastbox(nullptr) {}

    unsigned add(ObjectBox* objbox);
    unsigned indexOf(JSObject* obj);
    void finish(ObjectArray* array);
    ObjectBox* find(uint32_t index);
};

struct CGTryNoteList {
    Vector<JSTryNote> list;
    explicit CGTryNoteList(ExclusiveContext* cx) : list(cx) {}

    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 CGBlockScopeNote : public BlockScopeNote
{
    // 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 CGBlockScopeList {
    Vector<CGBlockScopeNote> list;
    explicit CGBlockScopeList(ExclusiveContext* cx) : list(cx) {}

    bool append(uint32_t scopeObjectIndex, uint32_t offset, bool inPrologue, uint32_t parent);
    uint32_t findEnclosingScope(uint32_t index);
    void recordEnd(uint32_t index, uint32_t offset, bool inPrologue);
    size_t length() const { return list.length(); }
    void finish(BlockScopeArray* array, uint32_t prologueLength);
};

struct CGYieldOffsetList {
    Vector<uint32_t> list;
    explicit CGYieldOffsetList(ExclusiveContext* cx) : list(cx) {}

    bool append(uint32_t offset) { return list.append(offset); }
    size_t length() const { return list.length(); }
    void finish(YieldOffsetArray& array, uint32_t prologueLength);
};

struct LoopStmtInfo;
struct StmtInfoBCE;

// 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;

// This enum tells BytecodeEmitter::emitVariables and the destructuring
// methods how emit the given Parser::variables parse tree.
enum VarEmitOption {
    // The normal case. Emit code to evaluate initializer expressions and
    // assign them to local variables. Also emit JSOP_DEF{VAR,LET,CONST}
    // opcodes in the prologue if the declaration occurs at toplevel.
    InitializeVars,

    // Emit only JSOP_DEFVAR opcodes, in the prologue, if necessary. This is
    // used in one case: `for (var $BindingPattern in/of obj)`. If we're at
    // toplevel, the variable(s) must be defined with JSOP_DEFVAR, but they're
    // populated inside the loop, via emitAssignment.
    DefineVars,

    // Emit code to evaluate initializer expressions and leave those values on
    // the stack. This is used to implement `for (let/const ...;;)` and
    // deprecated `let` blocks.
    PushInitialValues,

    // Like InitializeVars, but bind using BINDVAR instead of
    // BINDNAME/BINDGNAME. Only used for emitting declarations synthesized for
    // Annex B block-scoped function semantics.
    AnnexB,
};

struct BytecodeEmitter
{
    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 */

        EmitSection(ExclusiveContext* cx, uint32_t lineNum)
          : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0)
        {}
    };
    EmitSection prologue, main, *current;

    /* the parser */
    Parser<FullParseHandler>* const parser;

    HandleScript    evalCaller;     /* scripted caller info for eval and dbgapi */

    StmtInfoStack<StmtInfoBCE> stmtStack;

    OwnedAtomIndexMapPtr atomIndices; /* literals indexed for mapping */
    unsigned        firstLine;      /* first line, for JSScript::initFromEmitter */

    /*
     * Only unaliased locals have stack slots assigned to them. This vector is
     * used to map a local index (which includes unaliased and aliased locals)
     * to its stack slot index.
     */
    Vector<uint32_t, 16> localsToFrameSlots_;

    int32_t         stackDepth;     /* current stack depth in script frame */
    uint32_t        maxStackDepth;  /* maximum stack depth so far */

    uint32_t        arrayCompDepth; /* stack depth of array in comprehension */

    unsigned        emitLevel;      /* emitTree recursion level */

    CGConstList     constList;      /* constants to be included with the script */

    CGObjectList    objectList;     /* list of emitted objects */
    CGTryNoteList   tryNoteList;    /* list of emitted try notes */
    CGBlockScopeList blockScopeList;/* 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            emittingForInit:1;  /* true while emitting init expr of for; exclude 'in' */

    bool            emittingRunOnceLambda:1; /* true while emitting a lambda which is only
                                                expected to run once. */

    bool isRunOnceLambda();

    bool            insideEval:1;       /* True if compiling an eval-expression or a function
                                           nested inside an eval. */

    const bool      insideNonGlobalEval:1;  /* True if this is a direct eval
                                               call in some non-global scope. */

    bool            insideModule:1;     /* True if compiling inside a module. */

    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;

    /*
     * 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,
                    bool insideEval, HandleScript evalCaller,
                    bool insideNonGlobalEval, uint32_t lineNum, EmitterMode emitterMode = Normal);
    bool init();
    bool updateLocalsToFrameSlots();

    StmtInfoBCE* innermostStmt() const { return stmtStack.innermost(); }
    StmtInfoBCE* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); }
    JSObject* innermostStaticScope() const;
    JSObject* blockScopeOfDef(Definition* dn) const {
        return parser->blockScopes[dn->pn_blockid];
    }

    bool atBodyLevel(StmtInfoBCE* stmt) const;
    bool atBodyLevel() const {
        return atBodyLevel(innermostStmt());
    }
    uint32_t computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut);
    bool isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn);
    bool computeDefinitionIsAliased(BytecodeEmitter* bceOfDef, Definition* dn, JSOp* op);

    MOZ_ALWAYS_INLINE
    bool makeAtomIndex(JSAtom* atom, jsatomid* indexp) {
        AtomIndexAddPtr p = atomIndices->lookupForAdd(atom);
        if (p) {
            *indexp = p.value();
            return true;
        }

        jsatomid index = atomIndices->count();
        if (!atomIndices->add(p, atom, index))
            return false;

        *indexp = index;
        return true;
    }

    bool isInLoop();
    bool checkSingletonContext();

    // Check whether our function is in a run-once context (a toplevel
    // run-one script or a run-once lambda).
    bool checkRunOnceContext();

    bool needsImplicitThis();

    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; }

    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.
    bool checkSideEffects(ParseNode* pn, bool* answer);

#ifdef DEBUG
    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.
    bool newSrcNote(SrcNoteType type, unsigned* indexp = nullptr);
    bool newSrcNote2(SrcNoteType type, ptrdiff_t offset, unsigned* indexp = nullptr);
    bool newSrcNote3(SrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2,
                     unsigned* indexp = nullptr);

    void copySrcNotes(jssrcnote* destination, uint32_t nsrcnotes);
    bool setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offset);

    // NB: this function can add at most one extra extended delta note.
    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.
    bool finishTakingSrcNotes(uint32_t* out);

    void setJumpOffsetAt(ptrdiff_t off);

    // Control whether emitTree emits a line number note.
    enum EmitLineNumberNote {
        EMIT_LINENOTE,
        SUPPRESS_LINENOTE
    };

    // Emit code for the tree rooted at pn.
    bool emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote = EMIT_LINENOTE);

    // Emit function code for the tree rooted at body.
    bool emitFunctionScript(ParseNode* body);

    // Emit module code for the tree rooted at body.
    bool emitModuleScript(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);
    bool updateLineNumberNotes(uint32_t offset);
    bool updateSourceCoordNotes(uint32_t offset);

    bool bindNameToSlot(ParseNode* pn);
    bool bindNameToSlotHelper(ParseNode* pn);

    void strictifySetNameNode(ParseNode* pn);
    JSOp strictifySetNameOp(JSOp op);

    bool tryConvertFreeName(ParseNode* pn);

    void popStatement();
    void pushStatement(StmtInfoBCE* stmt, StmtType type, ptrdiff_t top);
    void pushStatementInner(StmtInfoBCE* stmt, StmtType type, ptrdiff_t top);
    void pushLoopStatement(LoopStmtInfo* stmt, StmtType type, ptrdiff_t top);

    bool enterNestedScope(StmtInfoBCE* stmt, ObjectBox* objbox, StmtType stmtType);
    bool leaveNestedScope(StmtInfoBCE* stmt);

    bool enterBlockScope(StmtInfoBCE* stmtInfo, ObjectBox* objbox, JSOp initialValueOp,
                         unsigned alreadyPushed = 0);

    bool computeAliasedSlots(Handle<StaticBlockScope*> blockScope);

    bool lookupAliasedName(HandleScript script, PropertyName* name, uint32_t* pslot,
                           ParseNode* pn = nullptr);
    bool lookupAliasedNameSlot(PropertyName* name, ScopeCoordinate* sc);

    // In a function, block-scoped locals go after the vars, and form part of the
    // fixed part of a stack frame.  Outside a function, there are no fixed vars,
    // but block-scoped locals still form part of the fixed part of a stack frame
    // and are thus addressable via GETLOCAL and friends.
    void computeLocalOffset(Handle<StaticBlockScope*> blockScope);

    bool flushPops(int* npops);

    bool emitCheck(ptrdiff_t delta, ptrdiff_t* offset);

    // Emit one bytecode.
    bool emit1(JSOp op);

    // Emit two bytecodes, an opcode (op) with a byte of immediate operand
    // (op1).
    bool emit2(JSOp op, uint8_t op1);

    // Emit three bytecodes, an opcode with two bytes of immediate operands.
    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.
    bool emitDupAt(unsigned slotFromTop);

    // Emit a bytecode followed by an uint16 immediate operand stored in
    // big-endian order.
    bool emitUint16Operand(JSOp op, uint32_t operand);

    // Emit a bytecode followed by an uint32 immediate operand.
    bool emitUint32Operand(JSOp op, uint32_t operand);

    // Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
    bool emitN(JSOp op, size_t extra, ptrdiff_t* offset = nullptr);

    bool emitNumberOp(double dval);

    bool emitThisLiteral(ParseNode* pn);
    bool emitCreateFunctionThis();
    bool emitGetFunctionThis(ParseNode* pn);
    bool emitGetThisForSuperBase(ParseNode* pn);
    bool emitSetThis(ParseNode* pn);

    // These functions are used to emit GETLOCAL/GETALIASEDVAR or
    // SETLOCAL/SETALIASEDVAR for a particular binding on a function's
    // CallObject.
    bool emitLoadFromEnclosingFunctionScope(BindingIter& bi);
    bool emitStoreToEnclosingFunctionScope(BindingIter& bi);

    uint32_t computeHopsToEnclosingFunction();

    bool emitJump(JSOp op, ptrdiff_t off, ptrdiff_t* jumpOffset = nullptr);
    bool emitCall(JSOp op, uint16_t argc, ParseNode* pn = nullptr);

    bool emitLoopHead(ParseNode* nextpn);
    bool emitLoopEntry(ParseNode* nextpn);

    // Emit a backpatch op with offset pointing to the previous jump of this
    // type, so that we can walk back up the chain fixing up the op and jump
    // offset.
    bool emitBackPatchOp(ptrdiff_t* lastp);
    void backPatch(ptrdiff_t last, jsbytecode* target, jsbytecode op);

    bool emitGoto(StmtInfoBCE* toStmt, ptrdiff_t* lastp, SrcNoteType noteType = SRC_NULL);

    bool emitIndex32(JSOp op, uint32_t index);
    bool emitIndexOp(JSOp op, uint32_t index);

    bool emitAtomOp(JSAtom* atom, JSOp op);
    bool emitAtomOp(ParseNode* pn, JSOp op);

    bool emitArrayLiteral(ParseNode* pn);
    bool emitArray(ParseNode* pn, uint32_t count, JSOp op);
    bool emitArrayComp(ParseNode* pn);

    bool emitInternedObjectOp(uint32_t index, JSOp op);
    bool emitObjectOp(ObjectBox* objbox, JSOp op);
    bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op);
    bool emitRegExp(uint32_t index);

    MOZ_NEVER_INLINE bool emitFunction(ParseNode* pn, bool needsProto = false);
    MOZ_NEVER_INLINE bool emitObject(ParseNode* pn);

    bool emitHoistedFunctionsInList(ParseNode* pn);

    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.
    bool emitLocalOp(JSOp op, uint32_t slot);

    bool emitScopeCoordOp(JSOp op, ScopeCoordinate sc);
    bool emitAliasedVarOp(JSOp op, ParseNode* pn);
    bool emitAliasedVarOp(JSOp op, ScopeCoordinate sc, MaybeCheckLexical checkLexical);
    bool emitUnaliasedVarOp(JSOp op, uint32_t slot, MaybeCheckLexical checkLexical);

    bool emitVarOp(ParseNode* pn, JSOp op);
    bool emitVarIncDec(ParseNode* pn);

    bool emitNameOp(ParseNode* pn, bool callContext);
    bool emitNameIncDec(ParseNode* pn);

    bool maybeEmitVarDecl(JSOp prologueOp, ParseNode* pn, jsatomid* result);
    bool emitVariables(ParseNode* pn, VarEmitOption emitOption);
    bool emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode* initializer,
                            VarEmitOption emitOption);

    bool emitNewInit(JSProtoKey key);
    bool emitSingletonInitialiser(ParseNode* pn);

    bool emitPrepareIteratorResult();
    bool emitFinishIteratorResult(bool done);
    bool iteratorResultShape(unsigned* shape);

    bool emitYield(ParseNode* pn);
    bool emitYieldOp(JSOp op);
    bool emitYieldStar(ParseNode* iter, ParseNode* gen);

    bool emitPropLHS(ParseNode* pn);
    bool emitPropOp(ParseNode* pn, JSOp op);
    bool emitPropIncDec(ParseNode* pn);

    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, SelfAssign };
    bool emitElemOperands(ParseNode* pn, EmitElemOption opts);

    bool emitElemOpBase(JSOp op);
    bool emitElemOp(ParseNode* pn, JSOp op);
    bool emitElemIncDec(ParseNode* pn);

    bool emitCatch(ParseNode* pn);
    bool emitIf(ParseNode* pn);
    bool emitWith(ParseNode* pn);

    MOZ_NEVER_INLINE bool emitLabeledStatement(const LabeledStatement* pn);
    MOZ_NEVER_INLINE bool emitLetBlock(ParseNode* pnLet);
    MOZ_NEVER_INLINE bool emitLexicalScope(ParseNode* pn);
    MOZ_NEVER_INLINE bool emitSwitch(ParseNode* pn);
    MOZ_NEVER_INLINE bool emitTry(ParseNode* pn);

    // 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).
    //
    // If emitOption is InitializeVars, the to-be-destructured value is assigned to
    // locals and ultimately the initial slot is popped (-1 total depth change).
    //
    // If emitOption is PushInitialValues, the to-be-destructured value is replaced
    // with the initial values of the N (where 0 <= N) variables assigned in the
    // lhs expression. (Same post-condition as EmitDestructuringOpsHelper)
    bool emitDestructuringLHS(ParseNode* target, VarEmitOption emitOption);

    bool emitDestructuringOps(ParseNode* pattern, bool isLet = false);
    bool emitDestructuringOpsHelper(ParseNode* pattern, VarEmitOption emitOption);
    bool emitDestructuringOpsArrayHelper(ParseNode* pattern, VarEmitOption emitOption);
    bool emitDestructuringOpsObjectHelper(ParseNode* pattern, VarEmitOption emitOption);

    typedef bool
    (*DestructuringDeclEmitter)(BytecodeEmitter* bce, JSOp prologueOp, ParseNode* pn);

    template <DestructuringDeclEmitter EmitName>
    bool emitDestructuringDeclsWithEmitter(JSOp prologueOp, ParseNode* pattern);

    bool emitDestructuringDecls(JSOp prologueOp, ParseNode* pattern);

    // Emit code to initialize all destructured names to the value on the top of
    // the stack.
    bool emitInitializeDestructuringDecls(JSOp prologueOp, ParseNode* pattern);

    // Throw a TypeError if the value atop the stack isn't convertible to an
    // object, with no overall effect on the stack.
    bool emitRequireObjectCoercible();

    // emitIterator expects the iterable to already be on the stack.
    // It will replace that stack value with the corresponding iterator
    bool emitIterator();

    // Pops iterator from the top of the stack. Pushes the result of |.next()|
    // onto the stack.
    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|.
    bool emitDefault(ParseNode* defaultExpr);

    bool emitCallSiteObject(ParseNode* pn);
    bool emitTemplateString(ParseNode* pn);
    bool emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs);

    bool emitReturn(ParseNode* pn);
    bool emitStatement(ParseNode* pn);
    bool emitStatementList(ParseNode* pn);

    bool emitDeleteName(ParseNode* pn);
    bool emitDeleteProperty(ParseNode* pn);
    bool emitDeleteElement(ParseNode* pn);
    bool emitDeleteExpression(ParseNode* pn);

    // |op| must be JSOP_TYPEOF or JSOP_TYPEOFEXPR.
    bool emitTypeof(ParseNode* node, JSOp op);

    bool emitUnary(ParseNode* pn);
    bool emitRightAssociative(ParseNode* pn);
    bool emitLeftAssociative(ParseNode* pn);
    bool emitLogical(ParseNode* pn);
    bool emitSequenceExpr(ParseNode* pn);

    MOZ_NEVER_INLINE bool emitIncOrDec(ParseNode* pn);

    bool emitConditionalExpression(ConditionalExpression& conditional);

    bool isRestParameter(ParseNode* pn, bool* result);
    bool emitOptimizeSpread(ParseNode* arg0, ptrdiff_t* jmp, bool* emitted);

    bool emitCallOrNew(ParseNode* pn);
    bool emitDebugOnlyCheckSelfHosted();
    bool emitSelfHostedCallFunction(ParseNode* pn);
    bool emitSelfHostedResumeGenerator(ParseNode* pn);
    bool emitSelfHostedForceInterpreter(ParseNode* pn);
    bool emitSelfHostedAllowContentSpread(ParseNode* pn);

    bool emitComprehensionFor(ParseNode* compFor);
    bool emitComprehensionForIn(ParseNode* pn);
    bool emitComprehensionForInOrOfVariables(ParseNode* pn, bool* letBlockScope);
    bool emitComprehensionForOf(ParseNode* pn);

    bool emitDo(ParseNode* pn);
    bool emitFor(ParseNode* pn);
    bool emitForIn(ParseNode* pn);
    bool emitForInOrOfVariables(ParseNode* pn);
    bool emitCStyleFor(ParseNode* pn);
    bool emitWhile(ParseNode* pn);

    bool emitBreak(PropertyName* label);
    bool emitContinue(PropertyName* label);

    bool emitArgsBody(ParseNode* pn);
    bool emitDefaultsAndDestructuring(ParseNode* pn);
    bool emitLexicalInitialization(ParseNode* pn, JSOp globalDefOp);

    bool pushInitialConstants(JSOp op, unsigned n);
    bool initializeBlockScopedLocalsFromStack(Handle<StaticBlockScope*> blockScope);

    // 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|.
    bool emitSpread(bool allowSelfHosted = false);

    // Emit bytecode for a for-of loop.  pn should be PNK_FOR, and pn->pn_left
    // should be PNK_FOROF.
    bool emitForOf(ParseNode* pn);

    bool emitClass(ParseNode* pn);
    bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false);
    bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false);
    bool emitSuperElemOperands(ParseNode* pn, EmitElemOption opts = EmitElemOption::Get);
    bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false);
};

} /* namespace frontend */
} /* namespace js */

#endif /* frontend_BytecodeEmitter_h */