Bug 1143704 part 12 - Move remaining functions to BytecodeEmitter. r=bhackett
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 24 Mar 2015 09:45:17 +0100
changeset 264343 ff0c37c1837b67bf5129ef2a7d189664c848d8de
parent 264342 4b576200b148d2f02afed6df6c013b7fb2fdd1e1
child 264344 5e2113e373009f9a0aa9ac48d26e061fbf1521ee
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs1143704
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1143704 part 12 - Move remaining functions to BytecodeEmitter. r=bhackett
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/jsscript.cpp
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -522,17 +522,17 @@ frontend::CompileLazyFunction(JSContext 
     BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, lazy,
                         /* insideEval = */ false, /* evalCaller = */ js::NullPtr(),
                         /* evalStaticScope = */ js::NullPtr(),
                         /* insideNonGlobalEval = */ false, options.lineno,
                         BytecodeEmitter::LazyFunction);
     if (!bce.init())
         return false;
 
-    return EmitFunctionScript(cx, &bce, pn->pn_body);
+    return bce.emitFunctionScript(pn->pn_body);
 }
 
 // Compile a JS function body, which might appear as the value of an event
 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
 static bool
 CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, const ReadOnlyCompileOptions &options,
                     const AutoNameVector &formals, SourceBufferHolder &srcBuf,
                     HandleObject enclosingStaticScope, GeneratorKind generatorKind)
@@ -647,17 +647,17 @@ CompileFunctionBody(JSContext *cx, Mutab
         BytecodeEmitter funbce(/* parent = */ nullptr, &parser, fn->pn_funbox, script,
                                /* lazyScript = */ js::NullPtr(), /* insideEval = */ false,
                                /* evalCaller = */ js::NullPtr(),
                                /* evalStaticScope = */ js::NullPtr(),
                                /* insideNonGlobalEval = */ false, options.lineno);
         if (!funbce.init())
             return false;
 
-        if (!EmitFunctionScript(cx, &funbce, fn->pn_body))
+        if (!funbce.emitFunctionScript(fn->pn_body))
             return false;
     } else {
         fun.set(fn->pn_funbox->function());
         MOZ_ASSERT(IsAsmJSModuleNative(fun->native()));
     }
 
     if (!sct.complete())
         return false;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -80,37 +80,32 @@ struct frontend::StmtInfoBCE : public St
     }
 
     ptrdiff_t &guardJump() {
         MOZ_ASSERT(type == STMT_TRY || type == STMT_FINALLY);
         return continues;
     }
 };
 
-
-namespace {
-
-struct LoopStmtInfo : public StmtInfoBCE
+struct frontend::LoopStmtInfo : public StmtInfoBCE
 {
     int32_t         stackDepth;     // Stack depth when this loop was pushed.
     uint32_t        loopDepth;      // Loop depth.
 
     // Can we OSR into Ion from here?  True unless there is non-loop state on the stack.
     bool            canIonOsr;
 
     explicit LoopStmtInfo(ExclusiveContext *cx) : StmtInfoBCE(cx) {}
 
     static LoopStmtInfo* fromStmtInfo(StmtInfoBCE *stmt) {
         MOZ_ASSERT(stmt->isLoop());
         return static_cast<LoopStmtInfo*>(stmt);
     }
 };
 
-} // anonymous namespace
-
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
                                  Parser<FullParseHandler> *parser, SharedContext *sc,
                                  HandleScript script, Handle<LazyScript *> lazyScript,
                                  bool insideEval, HandleScript evalCaller,
                                  Handle<StaticEvalObject *> staticEvalScope,
                                  bool insideNonGlobalEval, uint32_t lineNum,
                                  EmitterMode emitterMode)
   : sc(sc),
@@ -235,60 +230,60 @@ BytecodeEmitter::updateDepth(ptrdiff_t t
     stackDepth -= nuses;
     MOZ_ASSERT(stackDepth >= 0);
     stackDepth += ndefs;
     if ((uint32_t)stackDepth > maxStackDepth)
         maxStackDepth = stackDepth;
 }
 
 #ifdef DEBUG
-static bool
-CheckStrictOrSloppy(BytecodeEmitter *bce, JSOp op)
-{
-    if (IsCheckStrictOp(op) && !bce->sc->strict())
-        return false;
-    if (IsCheckSloppyOp(op) && bce->sc->strict())
+bool
+BytecodeEmitter::checkStrictOrSloppy(JSOp op)
+{
+    if (IsCheckStrictOp(op) && !sc->strict())
+        return false;
+    if (IsCheckSloppyOp(op) && sc->strict())
         return false;
     return true;
 }
 #endif
 
 bool
 BytecodeEmitter::emit1(JSOp op)
 {
-    MOZ_ASSERT(CheckStrictOrSloppy(this, op));
+    MOZ_ASSERT(checkStrictOrSloppy(op));
     ptrdiff_t offset = emitCheck(1);
     if (offset < 0)
         return false;
 
     jsbytecode *code = this->code(offset);
     code[0] = jsbytecode(op);
     updateDepth(offset);
     return true;
 }
 
 bool
 BytecodeEmitter::emit2(JSOp op, jsbytecode op1)
 {
-    MOZ_ASSERT(CheckStrictOrSloppy(this, op));
+    MOZ_ASSERT(checkStrictOrSloppy(op));
     ptrdiff_t offset = emitCheck(2);
     if (offset < 0)
         return false;
 
     jsbytecode *code = this->code(offset);
     code[0] = jsbytecode(op);
     code[1] = op1;
     updateDepth(offset);
     return true;
 }
 
 bool
 BytecodeEmitter::emit3(JSOp op, jsbytecode op1, jsbytecode op2)
 {
-    MOZ_ASSERT(CheckStrictOrSloppy(this, op));
+    MOZ_ASSERT(checkStrictOrSloppy(op));
 
     /* These should filter through emitVarOp. */
     MOZ_ASSERT(!IsArgOp(op));
     MOZ_ASSERT(!IsLocalOp(op));
 
     ptrdiff_t offset = emitCheck(3);
     if (offset < 0)
         return false;
@@ -299,17 +294,17 @@ BytecodeEmitter::emit3(JSOp op, jsbyteco
     code[2] = op2;
     updateDepth(offset);
     return true;
 }
 
 ptrdiff_t
 BytecodeEmitter::emitN(JSOp op, size_t extra)
 {
-    MOZ_ASSERT(CheckStrictOrSloppy(this, op));
+    MOZ_ASSERT(checkStrictOrSloppy(op));
     ptrdiff_t length = 1 + (ptrdiff_t)extra;
     ptrdiff_t offset = emitCheck(length);
     if (offset < 0)
         return -1;
 
     jsbytecode *code = this->code(offset);
     code[0] = jsbytecode(op);
     /* The remaining |extra| bytes are set by the caller */
@@ -446,21 +441,21 @@ BytecodeEmitter::updateLineNumberNotes(u
          * loops where the update part is emitted after the body, but its
          * line number is <= any line number in the body) here by letting
          * unsigned delta_ wrap to a very large number, which triggers a
          * SRC_SETLINE.
          */
         current->currentLine = line;
         current->lastColumn  = 0;
         if (delta >= LengthOfSetLine(line)) {
-            if (NewSrcNote2(cx, this, SRC_SETLINE, (ptrdiff_t)line) < 0)
+            if (newSrcNote2(SRC_SETLINE, ptrdiff_t(line)) < 0)
                 return false;
         } else {
             do {
-                if (NewSrcNote(cx, this, SRC_NEWLINE) < 0)
+                if (newSrcNote(SRC_NEWLINE) < 0)
                     return false;
             } while (--delta != 0);
         }
     }
     return true;
 }
 
 /* Updates the line number and column number information in the source notes. */
@@ -475,17 +470,17 @@ BytecodeEmitter::updateSourceCoordNotes(
     if (colspan != 0) {
         // If the column span is so large that we can't store it, then just
         // discard this information. This can happen with minimized or otherwise
         // machine-generated code. Even gigantic column numbers are still
         // valuable if you have a source map to relate them to something real;
         // but it's better to fail soft here.
         if (!SN_REPRESENTABLE_COLSPAN(colspan))
             return true;
-        if (NewSrcNote2(cx, this, SRC_COLSPAN, SN_COLSPAN_TO_OFFSET(colspan)) < 0)
+        if (newSrcNote2(SRC_COLSPAN, SN_COLSPAN_TO_OFFSET(colspan)) < 0)
             return false;
         current->lastColumn = columnIndex;
     }
     return true;
 }
 
 bool
 BytecodeEmitter::emitLoopHead(ParseNode *nextpn)
@@ -550,37 +545,29 @@ BytecodeEmitter::flushPops(int *npops)
     MOZ_ASSERT(*npops != 0);
     if (!emitUint16Operand(JSOP_POPN, *npops))
         return false;
 
     *npops = 0;
     return true;
 }
 
-static bool
-PopIterator(ExclusiveContext *cx, BytecodeEmitter *bce)
-{
-    return bce->emit1(JSOP_ENDITER);
-}
-
 namespace {
 
 class NonLocalExitScope {
-    ExclusiveContext *cx;
     BytecodeEmitter *bce;
     const uint32_t savedScopeIndex;
     const int savedDepth;
     uint32_t openScopeIndex;
 
     NonLocalExitScope(const NonLocalExitScope &) = delete;
 
   public:
-    explicit NonLocalExitScope(ExclusiveContext *cx_, BytecodeEmitter *bce_)
-      : cx(cx_),
-        bce(bce_),
+    explicit NonLocalExitScope(BytecodeEmitter *bce_)
+      : bce(bce_),
         savedScopeIndex(bce->blockScopeList.length()),
         savedDepth(bce->stackDepth),
         openScopeIndex(UINT32_MAX) {
         if (bce->staticScope) {
             StmtInfoBCE *stmt = bce->topStmt;
             while (1) {
                 MOZ_ASSERT(stmt);
                 if (stmt->isNestedScope) {
@@ -640,17 +627,17 @@ NonLocalExitScope::prepareForNonLocalJum
           case STMT_FOR_OF_LOOP:
             npops += 2;
             break;
 
           case STMT_FOR_IN_LOOP:
             /* The iterator and the current value are on the stack. */
             npops += 1;
             FLUSH_POPS();
-            if (!PopIterator(cx, bce))
+            if (!bce->emit1(JSOP_ENDITER))
                 return false;
             break;
 
           case STMT_SPREAD:
             MOZ_ASSERT_UNREACHABLE("can't break/continue/return from inside a spread");
             break;
 
           case STMT_SUBROUTINE:
@@ -684,23 +671,23 @@ NonLocalExitScope::prepareForNonLocalJum
 #undef FLUSH_POPS
 }
 
 }  // anonymous namespace
 
 ptrdiff_t
 BytecodeEmitter::emitGoto(StmtInfoBCE *toStmt, ptrdiff_t *lastp, SrcNoteType noteType)
 {
-    NonLocalExitScope nle(cx, this);
+    NonLocalExitScope nle(this);
 
     if (!nle.prepareForNonLocalJump(toStmt))
         return -1;
 
     if (noteType != SRC_NULL) {
-        if (NewSrcNote(cx, this, noteType) < 0)
+        if (newSrcNote(noteType) < 0)
             return -1;
     }
 
     if (!emitBackPatchOp(lastp))
         return -1;
     return *lastp;
 }
 
@@ -730,31 +717,31 @@ BytecodeEmitter::pushStatementInner(Stmt
 
 void
 BytecodeEmitter::pushStatement(StmtInfoBCE *stmt, StmtType type, ptrdiff_t top)
 {
     pushStatementInner(stmt, type, top);
     MOZ_ASSERT(!stmt->isLoop());
 }
 
-static void
-PushLoopStatement(BytecodeEmitter *bce, LoopStmtInfo *stmt, StmtType type, ptrdiff_t top)
-{
-    bce->pushStatementInner(stmt, type, top);
+void
+BytecodeEmitter::pushLoopStatement(LoopStmtInfo *stmt, StmtType type, ptrdiff_t top)
+{
+    pushStatementInner(stmt, type, top);
     MOZ_ASSERT(stmt->isLoop());
 
     LoopStmtInfo *downLoop = nullptr;
     for (StmtInfoBCE *outer = stmt->down; outer; outer = outer->down) {
         if (outer->isLoop()) {
             downLoop = LoopStmtInfo::fromStmtInfo(outer);
             break;
         }
     }
 
-    stmt->stackDepth = bce->stackDepth;
+    stmt->stackDepth = this->stackDepth;
     stmt->loopDepth = downLoop ? downLoop->loopDepth + 1 : 1;
 
     int loopSlots;
     if (type == STMT_SPREAD)
         loopSlots = 3;
     else if (type == STMT_FOR_IN_LOOP || type == STMT_FOR_OF_LOOP)
         loopSlots = 2;
     else
@@ -764,108 +751,100 @@ PushLoopStatement(BytecodeEmitter *bce, 
 
     if (downLoop)
         stmt->canIonOsr = (downLoop->canIonOsr &&
                            stmt->stackDepth == downLoop->stackDepth + loopSlots);
     else
         stmt->canIonOsr = stmt->stackDepth == loopSlots;
 }
 
-/*
- * Return the enclosing lexical scope, which is the innermost enclosing static
- * block object or compiler created function.
- */
-static JSObject *
-EnclosingStaticScope(BytecodeEmitter *bce)
-{
-    if (bce->staticScope)
-        return bce->staticScope;
-
-    if (!bce->sc->isFunctionBox()) {
-        MOZ_ASSERT(!bce->parent);
+JSObject *
+BytecodeEmitter::enclosingStaticScope()
+{
+    if (staticScope)
+        return staticScope;
+
+    if (!sc->isFunctionBox()) {
+        MOZ_ASSERT(!parent);
 
         // Top-level eval scripts have a placeholder static scope so that
         // StaticScopeIter may iterate through evals.
-        return bce->evalStaticScope;
-    }
-
-    return bce->sc->asFunctionBox()->function();
+        return evalStaticScope;
+    }
+
+    return sc->asFunctionBox()->function();
 }
 
 #ifdef DEBUG
 static bool
 AllLocalsAliased(StaticBlockObject &obj)
 {
     for (unsigned i = 0; i < obj.numVariables(); i++)
         if (!obj.isAliased(i))
             return false;
     return true;
 }
 #endif
 
-static bool
-ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, Handle<StaticBlockObject *> blockObj)
-{
-    uint32_t numAliased = bce->script->bindings.numAliasedBodyLevelLocals();
+bool
+BytecodeEmitter::computeAliasedSlots(Handle<StaticBlockObject *> blockObj)
+{
+    uint32_t numAliased = script->bindings.numAliasedBodyLevelLocals();
 
     for (unsigned i = 0; i < blockObj->numVariables(); i++) {
         Definition *dn = blockObj->definitionParseNode(i);
 
         MOZ_ASSERT(dn->isDefn());
 
         // blockIndexToLocalIndex returns the frame slot following the unaliased
         // locals. We add numAliased so that the cookie's slot value comes after
         // all (aliased and unaliased) body level locals.
-        if (!dn->pn_cookie.set(bce->parser->tokenStream, dn->pn_cookie.level(),
+        if (!dn->pn_cookie.set(parser->tokenStream, dn->pn_cookie.level(),
                                numAliased + blockObj->blockIndexToLocalIndex(dn->frameSlot())))
         {
             return false;
         }
 
 #ifdef DEBUG
         for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
             MOZ_ASSERT(pnu->pn_lexdef == dn);
             MOZ_ASSERT(!(pnu->pn_dflags & PND_BOUND));
             MOZ_ASSERT(pnu->pn_cookie.isFree());
         }
 #endif
 
-        blockObj->setAliased(i, bce->isAliasedName(dn));
-    }
-
-    MOZ_ASSERT_IF(bce->sc->allLocalsAliased(), AllLocalsAliased(*blockObj));
+        blockObj->setAliased(i, isAliasedName(dn));
+    }
+
+    MOZ_ASSERT_IF(sc->allLocalsAliased(), AllLocalsAliased(*blockObj));
 
     return true;
 }
 
-// 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.
-static void
-ComputeLocalOffset(ExclusiveContext *cx, BytecodeEmitter *bce, Handle<StaticBlockObject *> blockObj)
-{
-    unsigned nbodyfixed = bce->sc->isFunctionBox()
-                          ? bce->script->bindings.numUnaliasedBodyLevelLocals()
+void
+BytecodeEmitter::computeLocalOffset(Handle<StaticBlockObject *> blockObj)
+{
+    unsigned nbodyfixed = sc->isFunctionBox()
+                          ? script->bindings.numUnaliasedBodyLevelLocals()
                           : 0;
     unsigned localOffset = nbodyfixed;
 
-    if (bce->staticScope) {
-        Rooted<NestedScopeObject *> outer(cx, bce->staticScope);
+    if (staticScope) {
+        Rooted<NestedScopeObject *> outer(cx, staticScope);
         for (; outer; outer = outer->enclosingNestedScope()) {
             if (outer->is<StaticBlockObject>()) {
                 StaticBlockObject &outerBlock = outer->as<StaticBlockObject>();
                 localOffset = outerBlock.localOffset() + outerBlock.numVariables();
                 break;
             }
         }
     }
 
     MOZ_ASSERT(localOffset + blockObj->numVariables()
-               <= nbodyfixed + bce->script->bindings.numBlockScoped());
+               <= nbodyfixed + script->bindings.numBlockScoped());
 
     blockObj->setLocalOffset(localOffset);
 }
 
 // ~ Nested Scopes ~
 //
 // A nested scope is a region of a compilation unit (function, script, or eval
 // code) with an additional node on the scope chain.  This node may either be a
@@ -897,69 +876,68 @@ ComputeLocalOffset(ExclusiveContext *cx,
 //
 // To assist the debugger, we emit a DEBUGLEAVEBLOCK opcode before leaving a
 // block scope, even if the block has no aliased locals.  This allows
 // DebugScopes to invalidate any association between a debugger scope object,
 // which can proxy access to unaliased stack locals, and the actual live frame.
 // In normal, non-debug mode, this opcode does not cause any baseline code to be
 // emitted.
 //
-// Enter a nested scope with EnterNestedScope.  It will emit
+// Enter a nested scope with enterNestedScope.  It will emit
 // PUSHBLOCKSCOPE/ENTERWITH if needed, and arrange to record the PC bounds of
-// the scope.  Leave a nested scope with LeaveNestedScope, which, for blocks,
+// the scope.  Leave a nested scope with leaveNestedScope, which, for blocks,
 // will emit DEBUGLEAVEBLOCK and may emit POPBLOCKSCOPE.  (For "with" scopes it
-// emits LEAVEWITH, of course.)  Pass EnterNestedScope a fresh StmtInfoBCE
-// object, and pass that same object to the corresponding LeaveNestedScope.  If
+// emits LEAVEWITH, of course.)  Pass enterNestedScope a fresh StmtInfoBCE
+// object, and pass that same object to the corresponding leaveNestedScope.  If
 // the statement is a block scope, pass STMT_BLOCK as stmtType; otherwise for
 // with scopes pass STMT_WITH.
 //
-static bool
-EnterNestedScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox,
-                 StmtType stmtType)
+bool
+BytecodeEmitter::enterNestedScope(StmtInfoBCE *stmt, ObjectBox *objbox, StmtType stmtType)
 {
     Rooted<NestedScopeObject *> scopeObj(cx, &objbox->object->as<NestedScopeObject>());
-    uint32_t scopeObjectIndex = bce->objectList.add(objbox);
+    uint32_t scopeObjectIndex = objectList.add(objbox);
 
     switch (stmtType) {
       case STMT_BLOCK: {
         Rooted<StaticBlockObject *> blockObj(cx, &scopeObj->as<StaticBlockObject>());
 
-        ComputeLocalOffset(cx, bce, blockObj);
-
-        if (!ComputeAliasedSlots(cx, bce, blockObj))
+        computeLocalOffset(blockObj);
+
+        if (!computeAliasedSlots(blockObj))
             return false;
 
         if (blockObj->needsClone()) {
-            if (!bce->emitInternedObjectOp(scopeObjectIndex, JSOP_PUSHBLOCKSCOPE))
+            if (!emitInternedObjectOp(scopeObjectIndex, JSOP_PUSHBLOCKSCOPE))
                 return false;
         }
         break;
       }
       case STMT_WITH:
         MOZ_ASSERT(scopeObj->is<StaticWithObject>());
-        if (!bce->emitInternedObjectOp(scopeObjectIndex, JSOP_ENTERWITH))
+        if (!emitInternedObjectOp(scopeObjectIndex, JSOP_ENTERWITH))
             return false;
         break;
       default:
         MOZ_CRASH("Unexpected scope statement");
     }
 
     uint32_t parent = BlockScopeNote::NoBlockScopeIndex;
-    if (StmtInfoBCE *stmt = bce->topScopeStmt) {
-        for (; stmt->staticScope != bce->staticScope; stmt = stmt->down) {}
+    if (StmtInfoBCE *stmt = topScopeStmt) {
+        for (; stmt->staticScope != staticScope; stmt = stmt->down) {}
         parent = stmt->blockScopeIndex;
     }
 
-    stmt->blockScopeIndex = bce->blockScopeList.length();
-    if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
-        return false;
-
-    bce->pushStatement(stmt, stmtType, bce->offset());
-    scopeObj->initEnclosingNestedScope(EnclosingStaticScope(bce));
-    FinishPushNestedScope(bce, stmt, *scopeObj);
+    stmt->blockScopeIndex = blockScopeList.length();
+    if (!blockScopeList.append(scopeObjectIndex, offset(), parent))
+        return false;
+
+    pushStatement(stmt, stmtType, offset());
+    scopeObj->initEnclosingNestedScope(enclosingStaticScope());
+    FinishPushNestedScope(this, stmt, *scopeObj);
     MOZ_ASSERT(stmt->isNestedScope);
     stmt->isBlockScope = (stmtType == STMT_BLOCK);
 
     return true;
 }
 
 // Patches |breaks| and |continues| unless the top statement info record
 // represents a try-catch-finally suite.
@@ -969,53 +947,53 @@ BytecodeEmitter::popStatement()
     if (!topStmt->isTrying()) {
         backPatch(topStmt->breaks, code().end(), JSOP_GOTO);
         backPatch(topStmt->continues, code(topStmt->update), JSOP_GOTO);
     }
 
     FinishPopStatement(this);
 }
 
-static bool
-LeaveNestedScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt)
-{
-    MOZ_ASSERT(stmt == bce->topStmt);
+bool
+BytecodeEmitter::leaveNestedScope(StmtInfoBCE *stmt)
+{
+    MOZ_ASSERT(stmt == topStmt);
     MOZ_ASSERT(stmt->isNestedScope);
     MOZ_ASSERT(stmt->isBlockScope == !(stmt->type == STMT_WITH));
     uint32_t blockScopeIndex = stmt->blockScopeIndex;
 
 #ifdef DEBUG
-    MOZ_ASSERT(bce->blockScopeList.list[blockScopeIndex].length == 0);
-    uint32_t blockObjIndex = bce->blockScopeList.list[blockScopeIndex].index;
-    ObjectBox *blockObjBox = bce->objectList.find(blockObjIndex);
+    MOZ_ASSERT(blockScopeList.list[blockScopeIndex].length == 0);
+    uint32_t blockObjIndex = blockScopeList.list[blockScopeIndex].index;
+    ObjectBox *blockObjBox = objectList.find(blockObjIndex);
     NestedScopeObject *staticScope = &blockObjBox->object->as<NestedScopeObject>();
     MOZ_ASSERT(stmt->staticScope == staticScope);
-    MOZ_ASSERT(staticScope == bce->staticScope);
+    MOZ_ASSERT(staticScope == this->staticScope);
     MOZ_ASSERT_IF(!stmt->isBlockScope, staticScope->is<StaticWithObject>());
 #endif
 
-    bce->popStatement();
-
-    if (!bce->emit1(stmt->isBlockScope ? JSOP_DEBUGLEAVEBLOCK : JSOP_LEAVEWITH))
-        return false;
-
-    bce->blockScopeList.recordEnd(blockScopeIndex, bce->offset());
+    popStatement();
+
+    if (!emit1(stmt->isBlockScope ? JSOP_DEBUGLEAVEBLOCK : JSOP_LEAVEWITH))
+        return false;
+
+    blockScopeList.recordEnd(blockScopeIndex, offset());
 
     if (stmt->isBlockScope && stmt->staticScope->as<StaticBlockObject>().needsClone()) {
-        if (!bce->emit1(JSOP_POPBLOCKSCOPE))
+        if (!emit1(JSOP_POPBLOCKSCOPE))
             return false;
     }
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitIndex32(JSOp op, uint32_t index)
 {
-    MOZ_ASSERT(CheckStrictOrSloppy(this, op));
+    MOZ_ASSERT(checkStrictOrSloppy(op));
 
     const size_t len = 1 + UINT32_INDEX_LEN;
     MOZ_ASSERT(len == size_t(js_CodeSpec[op].length));
 
     ptrdiff_t offset = emitCheck(len);
     if (offset < 0)
         return false;
 
@@ -1025,17 +1003,17 @@ BytecodeEmitter::emitIndex32(JSOp op, ui
     updateDepth(offset);
     checkTypeSet(op);
     return true;
 }
 
 bool
 BytecodeEmitter::emitIndexOp(JSOp op, uint32_t index)
 {
-    MOZ_ASSERT(CheckStrictOrSloppy(this, op));
+    MOZ_ASSERT(checkStrictOrSloppy(op));
 
     const size_t len = js_CodeSpec[op].length;
     MOZ_ASSERT(len >= 1 + UINT32_INDEX_LEN);
 
     ptrdiff_t offset = emitCheck(len);
     if (offset < 0)
         return false;
 
@@ -1175,41 +1153,39 @@ BytecodeEmitter::emitAliasedVarOp(JSOp o
         MOZ_ASSERT(op != JSOP_INITALIASEDLEXICAL);
         if (!emitScopeCoordOp(JSOP_CHECKALIASEDLEXICAL, sc))
             return false;
     }
 
     return emitScopeCoordOp(op, sc);
 }
 
-// Compute the number of nested scope objects that will actually be on the scope
-// chain at runtime, given the BCE's current staticScope.
-static unsigned
-DynamicNestedScopeDepth(BytecodeEmitter *bce)
+unsigned
+BytecodeEmitter::dynamicNestedScopeDepth()
 {
     unsigned depth = 0;
-    for (NestedScopeObject *b = bce->staticScope; b; b = b->enclosingNestedScope()) {
+    for (NestedScopeObject *b = staticScope; b; b = b->enclosingNestedScope()) {
         if (!b->is<StaticBlockObject>() || b->as<StaticBlockObject>().needsClone())
             ++depth;
     }
 
     return depth;
 }
 
-static bool
-LookupAliasedName(BytecodeEmitter *bce, HandleScript script, PropertyName *name, uint32_t *pslot,
-                  ParseNode *pn = nullptr)
+bool
+BytecodeEmitter::lookupAliasedName(HandleScript script, PropertyName *name, uint32_t *pslot,
+                                   ParseNode *pn)
 {
     LazyScript::FreeVariable *freeVariables = nullptr;
     uint32_t lexicalBegin = 0;
     uint32_t numFreeVariables = 0;
-    if (bce->emitterMode == BytecodeEmitter::LazyFunction) {
-        freeVariables = bce->lazyScript->freeVariables();
+    if (emitterMode == BytecodeEmitter::LazyFunction) {
+        freeVariables = lazyScript->freeVariables();
         lexicalBegin = script->bindings.lexicalBegin();
-        numFreeVariables = bce->lazyScript->numFreeVariables();
+        numFreeVariables = lazyScript->numFreeVariables();
     }
 
     /*
      * Beware: BindingIter may contain more than one Binding for a given name
      * (in the case of |function f(x,x) {}|) but only one will be aliased.
      */
     uint32_t bindingIndex = 0;
     uint32_t slot = CallObject::RESERVED_SLOTS;
@@ -1238,37 +1214,32 @@ LookupAliasedName(BytecodeEmitter *bce, 
             }
             slot++;
         }
         bindingIndex++;
     }
     return false;
 }
 
-static bool
-LookupAliasedNameSlot(BytecodeEmitter *bce, HandleScript script, PropertyName *name,
-                      ScopeCoordinate *sc)
+bool
+BytecodeEmitter::lookupAliasedNameSlot(PropertyName *name, ScopeCoordinate *sc)
 {
     uint32_t slot;
-    if (!LookupAliasedName(bce, script, name, &slot))
+    if (!lookupAliasedName(script, name, &slot))
         return false;
 
     sc->setSlot(slot);
     return true;
 }
 
-/*
- * Use this function instead of assigning directly to 'hops' to guard for
- * uint8_t overflows.
- */
-static bool
-AssignHops(BytecodeEmitter *bce, ParseNode *pn, unsigned src, ScopeCoordinate *dst)
+bool
+BytecodeEmitter::assignHops(ParseNode *pn, unsigned src, ScopeCoordinate *dst)
 {
     if (src > UINT8_MAX) {
-        bce->reportError(pn, JSMSG_TOO_DEEP, js_function_str);
+        reportError(pn, JSMSG_TOO_DEEP, js_function_str);
         return false;
     }
 
     dst->setHops(src);
     return true;
 }
 
 static inline MaybeCheckLexical
@@ -1293,60 +1264,60 @@ BytecodeEmitter::emitAliasedVarOp(JSOp o
     BytecodeEmitter *bceOfDef = this;
     if (pn->isUsed()) {
         /*
          * As explained in bindNameToSlot, the 'level' of a use indicates how
          * many function scopes (i.e., BytecodeEmitters) to skip to find the
          * enclosing function scope of the definition being accessed.
          */
         for (unsigned i = pn->pn_cookie.level(); i; i--) {
-            skippedScopes += DynamicNestedScopeDepth(bceOfDef);
+            skippedScopes += bceOfDef->dynamicNestedScopeDepth();
             FunctionBox *funbox = bceOfDef->sc->asFunctionBox();
             if (funbox->isHeavyweight()) {
                 skippedScopes++;
                 if (funbox->function()->isNamedLambda())
                     skippedScopes++;
             }
             bceOfDef = bceOfDef->parent;
         }
     } else {
         MOZ_ASSERT(pn->isDefn());
         MOZ_ASSERT(pn->pn_cookie.level() == script->staticLevel());
     }
 
     /*
      * The final part of the skippedScopes computation depends on the type of
      * variable. An arg or local variable is at the outer scope of a function
-     * and so includes the full DynamicNestedScopeDepth. A let/catch-binding
+     * and so includes the full dynamicNestedScopeDepth. A let/catch-binding
      * requires a search of the block chain to see how many (dynamic) block
      * objects to skip.
      */
     ScopeCoordinate sc;
     if (IsArgOp(pn->getOp())) {
-        if (!AssignHops(this, pn, skippedScopes + DynamicNestedScopeDepth(bceOfDef), &sc))
-            return false;
-        JS_ALWAYS_TRUE(LookupAliasedNameSlot(bceOfDef, bceOfDef->script, pn->name(), &sc));
+        if (!assignHops(pn, skippedScopes + bceOfDef->dynamicNestedScopeDepth(), &sc))
+            return false;
+        JS_ALWAYS_TRUE(bceOfDef->lookupAliasedNameSlot(pn->name(), &sc));
     } else {
         MOZ_ASSERT(IsLocalOp(pn->getOp()) || pn->isKind(PNK_FUNCTION));
         uint32_t local = pn->pn_cookie.slot();
         if (local < bceOfDef->script->bindings.numBodyLevelLocals()) {
-            if (!AssignHops(this, pn, skippedScopes + DynamicNestedScopeDepth(bceOfDef), &sc))
-                return false;
-            JS_ALWAYS_TRUE(LookupAliasedNameSlot(bceOfDef, bceOfDef->script, pn->name(), &sc));
+            if (!assignHops(pn, skippedScopes + bceOfDef->dynamicNestedScopeDepth(), &sc))
+                return false;
+            JS_ALWAYS_TRUE(bceOfDef->lookupAliasedNameSlot(pn->name(), &sc));
         } else {
             MOZ_ASSERT_IF(this->sc->isFunctionBox(), local <= bceOfDef->script->bindings.numLocals());
             MOZ_ASSERT(bceOfDef->staticScope->is<StaticBlockObject>());
             Rooted<StaticBlockObject*> b(cx, &bceOfDef->staticScope->as<StaticBlockObject>());
             local = bceOfDef->localsToFrameSlots_[local];
             while (local < b->localOffset()) {
                 if (b->needsClone())
                     skippedScopes++;
                 b = &b->enclosingNestedScope()->as<StaticBlockObject>();
             }
-            if (!AssignHops(this, pn, skippedScopes, &sc))
+            if (!assignHops(pn, skippedScopes, &sc))
                 return false;
             sc.setSlot(b->localIndexToSlot(local));
         }
     }
 
     return emitAliasedVarOp(op, sc, NodeNeedsCheckLexical(pn));
 }
 
@@ -1477,117 +1448,117 @@ BytecodeEmitter::isAliasedName(ParseNode
       case Definition::PLACEHOLDER:
       case Definition::NAMED_LAMBDA:
       case Definition::MISSING:
         MOZ_CRASH("unexpected dn->kind");
     }
     return false;
 }
 
-static JSOp
-StrictifySetNameOp(JSOp op, BytecodeEmitter *bce)
+JSOp
+BytecodeEmitter::strictifySetNameOp(JSOp op)
 {
     switch (op) {
       case JSOP_SETNAME:
-        if (bce->sc->strict())
+        if (sc->strict())
             op = JSOP_STRICTSETNAME;
         break;
       case JSOP_SETGNAME:
-        if (bce->sc->strict())
+        if (sc->strict())
             op = JSOP_STRICTSETGNAME;
         break;
         default:;
     }
     return op;
 }
 
-static void
-StrictifySetNameNode(ParseNode *pn, BytecodeEmitter *bce)
-{
-    pn->setOp(StrictifySetNameOp(pn->getOp(), bce));
+void
+BytecodeEmitter::strictifySetNameNode(ParseNode *pn)
+{
+    pn->setOp(strictifySetNameOp(pn->getOp()));
 }
 
 /*
  * Try to convert a *NAME op with a free name to a more specialized GNAME,
  * INTRINSIC or ALIASEDVAR op, which optimize accesses on that name.
  * Return true if a conversion was made.
  */
-static bool
-TryConvertFreeName(BytecodeEmitter *bce, ParseNode *pn)
+bool
+BytecodeEmitter::tryConvertFreeName(ParseNode *pn)
 {
     /*
      * In self-hosting mode, JSOP_*NAME is unconditionally converted to
      * JSOP_*INTRINSIC. This causes lookups to be redirected to the special
      * intrinsics holder in the global object, into which any missing values are
      * cloned lazily upon first access.
      */
-    if (bce->emitterMode == BytecodeEmitter::SelfHosting) {
+    if (emitterMode == BytecodeEmitter::SelfHosting) {
         JSOp op;
         switch (pn->getOp()) {
           case JSOP_GETNAME:  op = JSOP_GETINTRINSIC; break;
           case JSOP_SETNAME:  op = JSOP_SETINTRINSIC; break;
           /* Other *NAME ops aren't (yet) supported in self-hosted code. */
           default: MOZ_CRASH("intrinsic");
         }
         pn->setOp(op);
         return true;
     }
 
     /*
      * When parsing inner functions lazily, parse nodes for outer functions no
      * longer exist and only the function's scope chain is available for
      * resolving upvar accesses within the inner function.
      */
-    if (bce->emitterMode == BytecodeEmitter::LazyFunction) {
+    if (emitterMode == BytecodeEmitter::LazyFunction) {
         // The only statements within a lazy function which can push lexical
         // scopes are try/catch blocks. Use generic ops in this case.
-        for (StmtInfoBCE *stmt = bce->topStmt; stmt; stmt = stmt->down) {
+        for (StmtInfoBCE *stmt = topStmt; stmt; stmt = stmt->down) {
             if (stmt->type == STMT_CATCH)
                 return true;
         }
 
         size_t hops = 0;
-        FunctionBox *funbox = bce->sc->asFunctionBox();
+        FunctionBox *funbox = sc->asFunctionBox();
         if (funbox->hasExtensibleScope())
             return false;
         if (funbox->function()->isNamedLambda() && funbox->function()->atom() == pn->pn_atom)
             return false;
         if (funbox->isHeavyweight()) {
             hops++;
             if (funbox->function()->isNamedLambda())
                 hops++;
         }
-        if (bce->script->directlyInsideEval())
-            return false;
-        RootedObject outerScope(bce->cx, bce->script->enclosingStaticScope());
-        for (StaticScopeIter<CanGC> ssi(bce->cx, outerScope); !ssi.done(); ssi++) {
+        if (script->directlyInsideEval())
+            return false;
+        RootedObject outerScope(cx, script->enclosingStaticScope());
+        for (StaticScopeIter<CanGC> ssi(cx, outerScope); !ssi.done(); ssi++) {
             if (ssi.type() != StaticScopeIter<CanGC>::Function) {
                 if (ssi.type() == StaticScopeIter<CanGC>::Block) {
                     // Use generic ops if a catch block is encountered.
                     return false;
                 }
                 if (ssi.hasDynamicScopeObject())
                     hops++;
                 continue;
             }
-            RootedScript script(bce->cx, ssi.funScript());
+            RootedScript script(cx, ssi.funScript());
             if (script->functionNonDelazifying()->atom() == pn->pn_atom)
                 return false;
             if (ssi.hasDynamicScopeObject()) {
                 uint32_t slot;
-                if (LookupAliasedName(bce, script, pn->pn_atom->asPropertyName(), &slot, pn)) {
+                if (lookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot, pn)) {
                     JSOp op;
                     switch (pn->getOp()) {
                       case JSOP_GETNAME: op = JSOP_GETALIASEDVAR; break;
                       case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break;
                       default: return false;
                     }
 
                     pn->setOp(op);
-                    JS_ALWAYS_TRUE(pn->pn_cookie.set(bce->parser->tokenStream, hops, slot));
+                    JS_ALWAYS_TRUE(pn->pn_cookie.set(parser->tokenStream, hops, slot));
                     return true;
                 }
                 hops++;
             }
 
             // If this walk up and check for directlyInsideEval is ever removed,
             // we'll need to adjust CompileLazyFunction to better communicate
             // whether we're inside eval to the BytecodeEmitter.  For now, this
@@ -1595,33 +1566,33 @@ TryConvertFreeName(BytecodeEmitter *bce,
             // eval.
             if (script->funHasExtensibleScope() || script->directlyInsideEval())
                 return false;
         }
     }
 
     // Unbound names aren't recognizable global-property references if the
     // script is inside a non-global eval call.
-    if (bce->insideNonGlobalEval)
+    if (insideNonGlobalEval)
         return false;
 
     // Skip trying to use GNAME ops if we know our script has a polluted
     // global scope, since they'll just get treated as NAME ops anyway.
-    if (bce->script->hasPollutedGlobalScope())
+    if (script->hasPollutedGlobalScope())
         return false;
 
     // Deoptimized names also aren't necessarily globals.
     if (pn->isDeoptimized())
         return false;
 
-    if (bce->sc->isFunctionBox()) {
+    if (sc->isFunctionBox()) {
         // Unbound names in function code may not be globals if new locals can
         // be added to this function (or an enclosing one) to alias a global
         // reference.
-        FunctionBox *funbox = bce->sc->asFunctionBox();
+        FunctionBox *funbox = sc->asFunctionBox();
         if (funbox->mightAliasLocals())
             return false;
     }
 
     // If this is eval code, being evaluated inside strict mode eval code,
     // an "unbound" name might be a binding local to that outer eval:
     //
     //   var x = "GLOBAL";
@@ -1633,51 +1604,51 @@ TryConvertFreeName(BytecodeEmitter *bce,
     // readily available now), we could exactly check global-ness, but it's not
     // worth the trouble for doubly-nested eval code.  So we conservatively
     // approximate.  If the outer eval code is strict, then this eval code will
     // be: thus, don't optimize if we're compiling strict code inside an eval.
     //
     // Though actually, we don't even need an inner eval.  We could just as well
     // have a lambda inside that outer strict mode eval and it would run into
     // the same issue.
-    if (bce->insideEval && bce->sc->strict())
+    if (insideEval && sc->strict())
         return false;
 
     JSOp op;
     switch (pn->getOp()) {
       case JSOP_GETNAME:  op = JSOP_GETGNAME; break;
-      case JSOP_SETNAME:  op = StrictifySetNameOp(JSOP_SETGNAME, bce); break;
+      case JSOP_SETNAME:  op = strictifySetNameOp(JSOP_SETGNAME); break;
       case JSOP_SETCONST:
         // Not supported.
         return false;
       default: MOZ_CRASH("gname");
     }
     pn->setOp(op);
     return true;
 }
 
 /*
  * BindNameToSlotHelper attempts to optimize name gets and sets to stack slot
- * loads and stores, given the compile-time information in bce and a PNK_NAME
+ * loads and stores, given the compile-time information in |this| and a PNK_NAME
  * node pn.  It returns false on error, true on success.
  *
  * The caller can test pn->pn_cookie.isFree() to tell whether optimization
- * occurred, in which case BindNameToSlotHelper also updated pn->pn_op.  If
+ * occurred, in which case bindNameToSlotHelper also updated pn->pn_op.  If
  * pn->pn_cookie.isFree() is still true on return, pn->pn_op still may have
  * been optimized, e.g., from JSOP_GETNAME to JSOP_CALLEE.  Whether or not
  * pn->pn_op was modified, if this function finds an argument or local variable
  * name, PND_CONST will be set in pn_dflags for read-only properties after a
  * successful return.
  *
  * NB: if you add more opcodes specialized from JSOP_GETNAME, etc., don't forget
  * to update the special cases in EmitFor (for-in) and emitAssignment (= and
  * op=, e.g. +=).
  */
-static bool
-BindNameToSlotHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+bool
+BytecodeEmitter::bindNameToSlotHelper(ParseNode *pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_NAME));
 
     /* Don't attempt if 'pn' is already bound or deoptimized or a function. */
     if (pn->isBound() || pn->isDeoptimized())
         return true;
 
     /* JSOP_CALLEE is pre-bound by definition. */
@@ -1706,50 +1677,50 @@ BindNameToSlotHelper(ExclusiveContext *c
       case JSOP_GETNAME:
       case JSOP_SETCONST:
         break;
       default:
         if (pn->isConst()) {
             JSAutoByteString name;
             if (!AtomToPrintableString(cx, pn->pn_atom, &name))
                 return false;
-            bce->reportError(pn, JSMSG_BAD_CONST_ASSIGN, name.ptr());
+            reportError(pn, JSMSG_BAD_CONST_ASSIGN, name.ptr());
             return false;
         }
     }
 
     if (dn->pn_cookie.isFree()) {
-        if (HandleScript caller = bce->evalCaller) {
-            MOZ_ASSERT(bce->script->compileAndGo());
+        if (evalCaller) {
+            MOZ_ASSERT(script->compileAndGo());
 
             /*
              * Don't generate upvars on the left side of a for loop. See
              * bug 470758.
              */
-            if (bce->emittingForInit)
+            if (emittingForInit)
                 return true;
 
             /*
              * If this is an eval in the global scope, then unbound variables
              * must be globals, so try to use GNAME ops.
              */
-            if (!caller->functionOrCallerFunction() && TryConvertFreeName(bce, pn)) {
+            if (!evalCaller->functionOrCallerFunction() && tryConvertFreeName(pn)) {
                 pn->pn_dflags |= PND_BOUND;
                 return true;
             }
 
             /*
              * Out of tricks, so we must rely on PICs to optimize named
              * accesses from direct eval called from function code.
              */
             return true;
         }
 
         /* Optimize accesses to undeclared globals. */
-        if (!TryConvertFreeName(bce, pn))
+        if (!tryConvertFreeName(pn))
             return true;
 
         pn->pn_dflags |= PND_BOUND;
         return true;
     }
 
     /*
      * At this point, we are only dealing with uses that have already been
@@ -1807,25 +1778,25 @@ BindNameToSlotHelper(ExclusiveContext *c
       case Definition::NAMED_LAMBDA: {
         MOZ_ASSERT(dn->isOp(JSOP_CALLEE));
         MOZ_ASSERT(op != JSOP_CALLEE);
 
         /*
          * Currently, the ALIASEDVAR ops do not support accessing the
          * callee of a DeclEnvObject, so use NAME.
          */
-        if (dn->pn_cookie.level() != bce->script->staticLevel())
+        if (dn->pn_cookie.level() != script->staticLevel())
             return true;
 
-        DebugOnly<JSFunction *> fun = bce->sc->asFunctionBox()->function();
+        DebugOnly<JSFunction *> fun = sc->asFunctionBox()->function();
         MOZ_ASSERT(fun->isLambda());
         MOZ_ASSERT(pn->pn_atom == fun->atom());
 
         /*
-         * Leave pn->isOp(JSOP_GETNAME) if bce->fun is heavyweight to
+         * Leave pn->isOp(JSOP_GETNAME) if this->fun is heavyweight to
          * address two cases: a new binding introduced by eval, and
          * assignment to the name in strict mode.
          *
          *   var fun = (function f(s) { eval(s); return f; });
          *   assertEq(fun("var f = 42"), 42);
          *
          * ECMAScript specifies that a function expression's name is bound
          * in a lexical environment distinct from that used to bind its
@@ -1838,17 +1809,17 @@ BindNameToSlotHelper(ExclusiveContext *c
          *
          * Outside strict mode, assignment to a function expression's name
          * has no effect.  But in strict mode, this attempt to mutate an
          * immutable binding must throw a TypeError.  We implement this by
          * not optimizing such assignments and by marking such functions as
          * heavyweight, ensuring that the function name is represented in
          * the scope chain so that assignment will throw a TypeError.
          */
-        if (!bce->sc->asFunctionBox()->isHeavyweight()) {
+        if (!sc->asFunctionBox()->isHeavyweight()) {
             op = JSOP_CALLEE;
             pn->pn_dflags |= PND_CONST;
         }
 
         pn->setOp(op);
         pn->pn_dflags |= PND_BOUND;
         return true;
       }
@@ -1860,80 +1831,68 @@ BindNameToSlotHelper(ExclusiveContext *c
         MOZ_CRASH("missing");
     }
 
     /*
      * The difference between the current static level and the static level of
      * the definition is the number of function scopes between the current
      * scope and dn's scope.
      */
-    unsigned skip = bce->script->staticLevel() - dn->pn_cookie.level();
+    unsigned skip = script->staticLevel() - dn->pn_cookie.level();
     MOZ_ASSERT_IF(skip, dn->isClosed());
 
     /*
      * Explicitly disallow accessing var/let bindings in global scope from
      * nested functions. The reason for this limitation is that, since the
      * global script is not included in the static scope chain (1. because it
      * has no object to stand in the static scope chain, 2. to minimize memory
      * bloat where a single live function keeps its whole global script
      * alive.), ScopeCoordinateToTypeSet is not able to find the var/let's
      * associated TypeSet.
      */
     if (skip) {
-        BytecodeEmitter *bceSkipped = bce;
+        BytecodeEmitter *bceSkipped = this;
         for (unsigned i = 0; i < skip; i++)
             bceSkipped = bceSkipped->parent;
         if (!bceSkipped->sc->isFunctionBox())
             return true;
     }
 
     MOZ_ASSERT(!pn->isOp(op));
     pn->setOp(op);
-    if (!pn->pn_cookie.set(bce->parser->tokenStream, skip, dn->pn_cookie.slot()))
+    if (!pn->pn_cookie.set(parser->tokenStream, skip, dn->pn_cookie.slot()))
         return false;
 
     pn->pn_dflags |= PND_BOUND;
     return true;
 }
 
 /*
  * Attempts to bind the name, then checks that no dynamic scope lookup ops are
  * emitted in self-hosting mode. NAME ops do lookups off current scope chain,
  * and we do not want to allow self-hosted code to use the dynamic scope.
  */
 bool
 BytecodeEmitter::bindNameToSlot(ParseNode *pn)
 {
-    if (!BindNameToSlotHelper(cx, this, pn))
-        return false;
-
-    StrictifySetNameNode(pn, this);
+    if (!bindNameToSlotHelper(pn))
+        return false;
+
+    strictifySetNameNode(pn);
 
     if (emitterMode == BytecodeEmitter::SelfHosting && !pn->isBound()) {
         reportError(pn, JSMSG_SELFHOSTED_UNBOUND_NAME);
         return false;
     }
 
     return true;
 }
 
-/*
- * 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.
- */
-static bool
-CheckSideEffects(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool *answer)
+bool
+BytecodeEmitter::checkSideEffects(ParseNode *pn, bool *answer)
 {
     if (!pn || *answer)
         return true;
 
     switch (pn->getArity()) {
       case PN_CODE:
         /*
          * A named function, contrary to ES3, is no longer useful, because we
@@ -1949,17 +1908,17 @@ CheckSideEffects(ExclusiveContext *cx, B
         if (pn->isOp(JSOP_NOP) || pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) ||
             pn->isOp(JSOP_STRICTEQ) || pn->isOp(JSOP_STRICTNE)) {
             /*
              * Non-operators along with ||, &&, ===, and !== never invoke
              * toString or valueOf.
              */
             bool ok = true;
             for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
-                ok &= CheckSideEffects(cx, bce, pn2, answer);
+                ok &= checkSideEffects(pn2, answer);
             return ok;
         }
 
         if (pn->isKind(PNK_GENEXP)) {
             /* Generator-expressions are harmless if the result is ignored. */
             MOZ_ASSERT(*answer == false);
             return true;
         }
@@ -1977,19 +1936,19 @@ CheckSideEffects(ExclusiveContext *cx, B
          * setters (the __defineSetter__ built-in, and writable __proto__
          * on Array.prototype create this hazard). Initialiser list nodes
          * have JSOP_NEWINIT in their pn_op.
          */
         *answer = true;
         return true;
 
       case PN_TERNARY:
-        return CheckSideEffects(cx, bce, pn->pn_kid1, answer) &&
-               CheckSideEffects(cx, bce, pn->pn_kid2, answer) &&
-               CheckSideEffects(cx, bce, pn->pn_kid3, answer);
+        return checkSideEffects(pn->pn_kid1, answer) &&
+               checkSideEffects(pn->pn_kid2, answer) &&
+               checkSideEffects(pn->pn_kid3, answer);
 
       case PN_BINARY:
       case PN_BINARY_OBJ:
         if (pn->isAssignment()) {
             /*
              * Assignment is presumed to be useful, even if the next operation
              * is another assignment overwriting this one's ostensible effect,
              * because the left operand may be a property with a setter that
@@ -1997,19 +1956,19 @@ CheckSideEffects(ExclusiveContext *cx, B
              *
              * The only exception is assignment of a useless value to a const
              * declared in the function currently being compiled.
              */
             ParseNode *pn2 = pn->pn_left;
             if (!pn2->isKind(PNK_NAME)) {
                 *answer = true;
             } else {
-                if (!bce->bindNameToSlot(pn2))
+                if (!bindNameToSlot(pn2))
                     return false;
-                if (!CheckSideEffects(cx, bce, pn->pn_right, answer))
+                if (!checkSideEffects(pn->pn_right, answer))
                     return false;
                 if (!*answer && (!pn->isOp(JSOP_NOP) || !pn2->isConst()))
                     *answer = true;
             }
             return true;
         }
 
         MOZ_ASSERT(!pn->isOp(JSOP_OR), "|| produces a list now");
@@ -2026,42 +1985,42 @@ CheckSideEffects(ExclusiveContext *cx, B
 
       case PN_UNARY:
         switch (pn->getKind()) {
           case PNK_DELETE:
           {
             ParseNode *pn2 = pn->pn_kid;
             switch (pn2->getKind()) {
               case PNK_NAME:
-                if (!bce->bindNameToSlot(pn2))
+                if (!bindNameToSlot(pn2))
                     return false;
                 if (pn2->isConst()) {
                     MOZ_ASSERT(*answer == false);
                     return true;
                 }
                 /* FALL THROUGH */
               case PNK_DOT:
               case PNK_CALL:
               case PNK_ELEM:
                 /* All these delete addressing modes have effects too. */
                 *answer = true;
                 return true;
               default:
-                return CheckSideEffects(cx, bce, pn2, answer);
+                return checkSideEffects(pn2, answer);
             }
             MOZ_CRASH("We have a returning default case");
           }
 
           case PNK_TYPEOF:
           case PNK_VOID:
           case PNK_NOT:
           case PNK_BITNOT:
             if (pn->isOp(JSOP_NOT)) {
                 /* ! does not convert its operand via toString or valueOf. */
-                return CheckSideEffects(cx, bce, pn->pn_kid, answer);
+                return checkSideEffects(pn->pn_kid, answer);
             }
             /* FALL THROUGH */
 
           default:
             /*
              * All of PNK_INC, PNK_DEC and PNK_THROW have direct effects. Of
              * the remaining unary-arity node types, we can't easily prove that
              * the operand never denotes an object with a toString or valueOf
@@ -2074,17 +2033,17 @@ CheckSideEffects(ExclusiveContext *cx, B
 
       case PN_NAME:
         /*
          * Take care to avoid trying to bind a label name (labels, both for
          * statements and property values in object initialisers, have pn_op
          * defaulted to JSOP_NOP).
          */
         if (pn->isKind(PNK_NAME) && !pn->isOp(JSOP_NOP)) {
-            if (!bce->bindNameToSlot(pn))
+            if (!bindNameToSlot(pn))
                 return false;
             if (!pn->isOp(JSOP_CALLEE) && pn->pn_cookie.isFree()) {
                 /*
                  * Not a use of an unshadowed named function expression's given
                  * name, so this expression could invoke a getter that has side
                  * effects.
                  */
                 *answer = true;
@@ -2095,17 +2054,17 @@ CheckSideEffects(ExclusiveContext *cx, B
             // Hoisted uses of lexical bindings throw on access.
             *answer = true;
         }
 
         if (pn->isKind(PNK_DOT)) {
             /* Dotted property references in general can call getters. */
             *answer = true;
         }
-        return CheckSideEffects(cx, bce, pn->maybeExpr(), answer);
+        return checkSideEffects(pn->maybeExpr(), answer);
 
       case PN_NULLARY:
         if (pn->isKind(PNK_DEBUGGER))
             *answer = true;
         return true;
     }
     return true;
 }
@@ -2215,54 +2174,53 @@ BytecodeEmitter::emitNewInit(JSProtoKey 
     code[2] = 0;
     code[3] = 0;
     code[4] = 0;
     updateDepth(offset);
     checkTypeSet(JSOP_NEWINIT);
     return true;
 }
 
-static bool
-IteratorResultShape(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned *shape)
-{
-    RootedPlainObject obj(cx);
+bool
+BytecodeEmitter::iteratorResultShape(unsigned *shape)
+{
     // No need to do any guessing for the object kind, since we know exactly how
     // many properties we plan to have.
     gc::AllocKind kind = gc::GetGCObjectKind(2);
-    obj = NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject);
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
     if (!obj)
         return false;
 
     Rooted<jsid> value_id(cx, AtomToId(cx->names().value));
     Rooted<jsid> done_id(cx, AtomToId(cx->names().done));
     if (!NativeDefineProperty(cx, obj, value_id, UndefinedHandleValue, nullptr, nullptr,
                               JSPROP_ENUMERATE))
     {
         return false;
     }
     if (!NativeDefineProperty(cx, obj, done_id, UndefinedHandleValue, nullptr, nullptr,
                               JSPROP_ENUMERATE))
     {
         return false;
     }
 
-    ObjectBox *objbox = bce->parser->newObjectBox(obj);
+    ObjectBox *objbox = parser->newObjectBox(obj);
     if (!objbox)
         return false;
 
-    *shape = bce->objectList.add(objbox);
+    *shape = objectList.add(objbox);
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitPrepareIteratorResult()
 {
     unsigned shape;
-    if (!IteratorResultShape(cx, this, &shape))
+    if (!iteratorResultShape(&shape))
         return false;
     return emitIndex32(JSOP_NEWOBJECT, shape);
 }
 
 bool
 BytecodeEmitter::emitFinishIteratorResult(bool done)
 {
     jsatomid value_id;
@@ -2450,17 +2408,17 @@ BytecodeEmitter::emitNameIncDec(ParseNod
 
     if (post) {
         if (!emit2(JSOP_PICK, (jsbytecode)2))  // N? N+1 OBJ
             return false;
         if (!emit1(JSOP_SWAP))                 // N? OBJ N+1
             return false;
     }
 
-    JSOp setOp = StrictifySetNameOp(global ? JSOP_SETGNAME : JSOP_SETNAME, this);
+    JSOp setOp = strictifySetNameOp(global ? JSOP_SETGNAME : JSOP_SETNAME);
     if (!emitAtomOp(pn->pn_kid, setOp))        // N? N+1
         return false;
     if (post && !emit1(JSOP_POP))              // RESULT
         return false;
 
     return true;
 }
 
@@ -2587,70 +2545,71 @@ BytecodeEmitter::emitNumberOp(double dva
 }
 
 void
 BytecodeEmitter::setJumpOffsetAt(ptrdiff_t off)
 {
     SET_JUMP_OFFSET(code(off), offset() - off);
 }
 
-static bool
-PushInitialConstants(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, unsigned n)
+bool
+BytecodeEmitter::pushInitialConstants(JSOp op, unsigned n)
 {
     MOZ_ASSERT(op == JSOP_UNDEFINED || op == JSOP_UNINITIALIZED);
+
     for (unsigned i = 0; i < n; ++i) {
-        if (!bce->emit1(op))
-            return false;
-    }
+        if (!emit1(op))
+            return false;
+    }
+
     return true;
 }
 
-static bool
-InitializeBlockScopedLocalsFromStack(ExclusiveContext *cx, BytecodeEmitter *bce,
-                                     Handle<StaticBlockObject *> blockObj)
+bool
+BytecodeEmitter::initializeBlockScopedLocalsFromStack(Handle<StaticBlockObject *> blockObj)
 {
     for (unsigned i = blockObj->numVariables(); i > 0; --i) {
         if (blockObj->isAliased(i - 1)) {
             ScopeCoordinate sc;
             sc.setHops(0);
             sc.setSlot(BlockObject::RESERVED_SLOTS + i - 1);
-            if (!bce->emitAliasedVarOp(JSOP_INITALIASEDLEXICAL, sc, DontCheckLexical))
+            if (!emitAliasedVarOp(JSOP_INITALIASEDLEXICAL, sc, DontCheckLexical))
                 return false;
         } else {
             // blockIndexToLocalIndex returns the slot index after the unaliased
             // locals stored in the frame. EmitUnaliasedVarOp expects the slot index
             // to include both unaliased and aliased locals, so we have to add the
             // number of aliased locals.
-            uint32_t numAliased = bce->script->bindings.numAliasedBodyLevelLocals();
+            uint32_t numAliased = script->bindings.numAliasedBodyLevelLocals();
             unsigned local = blockObj->blockIndexToLocalIndex(i - 1) + numAliased;
-            if (!bce->emitUnaliasedVarOp(JSOP_INITLEXICAL, local, DontCheckLexical))
+            if (!emitUnaliasedVarOp(JSOP_INITLEXICAL, local, DontCheckLexical))
                 return false;
         }
-        if (!bce->emit1(JSOP_POP))
+        if (!emit1(JSOP_POP))
             return false;
     }
     return true;
 }
 
-static bool
-EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmtInfo,
-                ObjectBox *objbox, JSOp initialValueOp, unsigned alreadyPushed = 0)
+bool
+BytecodeEmitter::enterBlockScope(StmtInfoBCE *stmtInfo, ObjectBox *objbox, JSOp initialValueOp,
+                                 unsigned alreadyPushed)
 {
     // Initial values for block-scoped locals. Whether it is undefined or the
     // JS_UNINITIALIZED_LEXICAL magic value depends on the context. The
     // current way we emit for-in and for-of heads means its let bindings will
     // always be initialized, so we can initialize them to undefined.
     Rooted<StaticBlockObject *> blockObj(cx, &objbox->object->as<StaticBlockObject>());
-    if (!PushInitialConstants(cx, bce, initialValueOp, blockObj->numVariables() - alreadyPushed))
-        return false;
-
-    if (!EnterNestedScope(cx, bce, stmtInfo, objbox, STMT_BLOCK))
-        return false;
-
-    if (!InitializeBlockScopedLocalsFromStack(cx, bce, blockObj))
+    if (!pushInitialConstants(initialValueOp, blockObj->numVariables() - alreadyPushed))
+        return false;
+
+    if (!enterNestedScope(stmtInfo, objbox, STMT_BLOCK))
+        return false;
+
+    if (!initializeBlockScopedLocalsFromStack(blockObj))
         return false;
 
     return true;
 }
 
 /*
  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047.
  * LLVM is deciding to inline this function which uses a lot of stack space
@@ -2677,17 +2636,17 @@ BytecodeEmitter::emitSwitch(ParseNode *p
     MOZ_ASSERT(pn2->isKind(PNK_LEXICALSCOPE) || pn2->isKind(PNK_STATEMENTLIST));
 
     /* Push the discriminant. */
     if (!emitTree(pn->pn_left))
         return false;
 
     StmtInfoBCE stmtInfo(cx);
     if (pn2->isKind(PNK_LEXICALSCOPE)) {
-        if (!EnterBlockScope(cx, this, &stmtInfo, pn2->pn_objbox, JSOP_UNINITIALIZED, 0))
+        if (!enterBlockScope(&stmtInfo, pn2->pn_objbox, JSOP_UNINITIALIZED, 0))
             return false;
 
         stmtInfo.type = STMT_SWITCH;
         stmtInfo.update = top = offset();
         /* Advance pn2 to refer to the switch case list. */
         pn2 = pn2->expr();
     } else {
         MOZ_ASSERT(pn2->isKind(PNK_STATEMENTLIST));
@@ -2804,23 +2763,23 @@ BytecodeEmitter::emitSwitch(ParseNode *p
 
     /*
      * The note has one or two offsets: first tells total switch code length;
      * second (if condswitch) tells offset to first JSOP_CASE.
      */
     if (switchOp == JSOP_CONDSWITCH) {
         /* 0 bytes of immediate for unoptimized switch. */
         switchSize = 0;
-        noteIndex = NewSrcNote3(cx, this, SRC_CONDSWITCH, 0, 0);
+        noteIndex = newSrcNote3(SRC_CONDSWITCH, 0, 0);
     } else {
         MOZ_ASSERT(switchOp == JSOP_TABLESWITCH);
 
         /* 3 offsets (len, low, high) before the table, 1 per entry. */
         switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength));
-        noteIndex = NewSrcNote2(cx, this, SRC_TABLESWITCH, 0);
+        noteIndex = newSrcNote2(SRC_TABLESWITCH, 0);
     }
     if (noteIndex < 0)
         return false;
 
     /* Emit switchOp followed by switchSize bytes of jump or lookup table. */
     if (emitN(switchOp, switchSize) < 0)
         return false;
 
@@ -2838,17 +2797,17 @@ BytecodeEmitter::emitSwitch(ParseNode *p
                 /* off is the previous JSOP_CASE's bytecode offset. */
                 if (!setSrcNoteOffset(unsigned(caseNoteIndex), 0, offset() - off))
                     return false;
             }
             if (!pn4) {
                 MOZ_ASSERT(pn3->isKind(PNK_DEFAULT));
                 continue;
             }
-            caseNoteIndex = NewSrcNote2(cx, this, SRC_NEXTCASE, 0);
+            caseNoteIndex = newSrcNote2(SRC_NEXTCASE, 0);
             if (caseNoteIndex < 0)
                 return false;
             off = emitJump(JSOP_CASE, 0);
             if (off < 0)
                 return false;
             pn3->pn_offset = off;
             if (beforeCases) {
                 unsigned noteCount, noteCountDelta;
@@ -2963,17 +2922,17 @@ BytecodeEmitter::emitSwitch(ParseNode *p
             pn3 = table[i];
             off = pn3 ? pn3->pn_offset - top : 0;
             SET_JUMP_OFFSET(pc, off);
             pc += JUMP_OFFSET_LEN;
         }
     }
 
     if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) {
-        if (!LeaveNestedScope(cx, this, &stmtInfo))
+        if (!leaveNestedScope(&stmtInfo))
             return false;
     } else {
         popStatement();
     }
 
     return true;
 }
 
@@ -3018,145 +2977,142 @@ BytecodeEmitter::emitYieldOp(JSOp op)
 
     if (!yieldOffsetList.append(offset()))
         return false;
 
     return emit1(JSOP_DEBUGAFTERYIELD);
 }
 
 bool
-frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *body)
-{
-    if (!bce->updateLocalsToFrameSlots())
+BytecodeEmitter::emitFunctionScript(ParseNode *body)
+{
+    if (!updateLocalsToFrameSlots())
         return false;
 
     /*
      * IonBuilder has assumptions about what may occur immediately after
      * script->main (e.g., in the case of destructuring params). Thus, put the
      * following ops into the range [script->code, script->main). Note:
      * execution starts from script->code, so this has no semantic effect.
      */
 
-    FunctionBox *funbox = bce->sc->asFunctionBox();
+    FunctionBox *funbox = sc->asFunctionBox();
     if (funbox->argumentsHasLocalBinding()) {
-        MOZ_ASSERT(bce->offset() == 0);  /* See JSScript::argumentsBytecode. */
-        bce->switchToProlog();
-        if (!bce->emit1(JSOP_ARGUMENTS))
-            return false;
-        InternalBindingsHandle bindings(bce->script, &bce->script->bindings);
+        MOZ_ASSERT(offset() == 0);  /* See JSScript::argumentsBytecode. */
+        switchToProlog();
+        if (!emit1(JSOP_ARGUMENTS))
+            return false;
+        InternalBindingsHandle bindings(script, &script->bindings);
         BindingIter bi = Bindings::argumentsBinding(cx, bindings);
-        if (bce->script->bindingIsAliased(bi)) {
+        if (script->bindingIsAliased(bi)) {
             ScopeCoordinate sc;
             sc.setHops(0);
             sc.setSlot(0);  // initialize to silence GCC warning
-            JS_ALWAYS_TRUE(LookupAliasedNameSlot(bce, bce->script, cx->names().arguments, &sc));
-            if (!bce->emitAliasedVarOp(JSOP_SETALIASEDVAR, sc, DontCheckLexical))
+            JS_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().arguments, &sc));
+            if (!emitAliasedVarOp(JSOP_SETALIASEDVAR, sc, DontCheckLexical))
                 return false;
         } else {
-            if (!bce->emitUnaliasedVarOp(JSOP_SETLOCAL, bi.localIndex(), DontCheckLexical))
+            if (!emitUnaliasedVarOp(JSOP_SETLOCAL, bi.localIndex(), DontCheckLexical))
                 return false;
         }
-        if (!bce->emit1(JSOP_POP))
-            return false;
-        bce->switchToMain();
+        if (!emit1(JSOP_POP))
+            return false;
+        switchToMain();
     }
 
     /*
      * Emit a prologue for run-once scripts which will deoptimize JIT code if
      * the script ends up running multiple times via foo.caller related
      * shenanigans.
      */
-    bool runOnce = bce->isRunOnceLambda();
+    bool runOnce = isRunOnceLambda();
     if (runOnce) {
-        bce->switchToProlog();
-        if (!bce->emit1(JSOP_RUNONCE))
-            return false;
-        bce->switchToMain();
-    }
-
-    if (!bce->emitTree(body))
-        return false;
-
-    if (bce->sc->isFunctionBox()) {
-        if (bce->sc->asFunctionBox()->isGenerator()) {
+        switchToProlog();
+        if (!emit1(JSOP_RUNONCE))
+            return false;
+        switchToMain();
+    }
+
+    if (!emitTree(body))
+        return false;
+
+    if (sc->isFunctionBox()) {
+        if (sc->asFunctionBox()->isGenerator()) {
             // If we fall off the end of a generator, do a final yield.
-            if (bce->sc->asFunctionBox()->isStarGenerator() && !bce->emitPrepareIteratorResult())
-                return false;
-
-            if (!bce->emit1(JSOP_UNDEFINED))
-                return false;
-
-            if (bce->sc->asFunctionBox()->isStarGenerator() &&
-                !bce->emitFinishIteratorResult(true))
-            {
-                return false;
-            }
-
-            if (!bce->emit1(JSOP_SETRVAL))
+            if (sc->asFunctionBox()->isStarGenerator() && !emitPrepareIteratorResult())
+                return false;
+
+            if (!emit1(JSOP_UNDEFINED))
+                return false;
+
+            if (sc->asFunctionBox()->isStarGenerator() && !emitFinishIteratorResult(true))
+                return false;
+
+            if (!emit1(JSOP_SETRVAL))
                 return false;
 
             ScopeCoordinate sc;
             // We know that .generator is on the top scope chain node, as we are
             // at the function end.
             sc.setHops(0);
-            MOZ_ALWAYS_TRUE(LookupAliasedNameSlot(bce, bce->script, cx->names().dotGenerator, &sc));
-            if (!bce->emitAliasedVarOp(JSOP_GETALIASEDVAR, sc, DontCheckLexical))
+            MOZ_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().dotGenerator, &sc));
+            if (!emitAliasedVarOp(JSOP_GETALIASEDVAR, sc, DontCheckLexical))
                 return false;
 
             // No need to check for finally blocks, etc as in EmitReturn.
-            if (!bce->emitYieldOp(JSOP_FINALYIELDRVAL))
+            if (!emitYieldOp(JSOP_FINALYIELDRVAL))
                 return false;
         } else {
             // Non-generator functions just return |undefined|. The JSOP_RETRVAL
             // emitted below will do that, except if the script has a finally
             // block: there can be a non-undefined value in the return value
             // slot. We just emit an explicit return in this case.
-            if (bce->hasTryFinally) {
-                if (!bce->emit1(JSOP_UNDEFINED))
+            if (hasTryFinally) {
+                if (!emit1(JSOP_UNDEFINED))
                     return false;
-                if (!bce->emit1(JSOP_RETURN))
+                if (!emit1(JSOP_RETURN))
                     return false;
             }
         }
     }
 
     // Always end the script with a JSOP_RETRVAL. Some other parts of the codebase
     // depend on this opcode, e.g. InterpreterRegs::setToEndOfScript.
-    if (!bce->emit1(JSOP_RETRVAL))
+    if (!emit1(JSOP_RETRVAL))
         return false;
 
     // If all locals are aliased, the frame's block slots won't be used, so we
     // can set numBlockScoped = 0. This is nice for generators as it ensures
     // nfixed == 0, so we don't have to initialize any local slots when resuming
     // a generator.
-    if (bce->sc->allLocalsAliased())
-        bce->script->bindings.setAllLocalsAliased();
-
-    if (!JSScript::fullyInitFromEmitter(cx, bce->script, bce))
+    if (sc->allLocalsAliased())
+        script->bindings.setAllLocalsAliased();
+
+    if (!JSScript::fullyInitFromEmitter(cx, script, this))
         return false;
 
     /*
      * If this function is only expected to run once, mark the script so that
      * initializers created within it may be given more precise types.
      */
     if (runOnce) {
-        bce->script->setTreatAsRunOnce();
-        MOZ_ASSERT(!bce->script->hasRunOnce());
+        script->setTreatAsRunOnce();
+        MOZ_ASSERT(!script->hasRunOnce());
     }
 
     /* Initialize fun->script() so that the debugger has a valid fun->script(). */
-    RootedFunction fun(cx, bce->script->functionNonDelazifying());
+    RootedFunction fun(cx, script->functionNonDelazifying());
     MOZ_ASSERT(fun->isInterpreted());
 
     if (fun->isInterpretedLazy())
-        fun->setUnlazifiedScript(bce->script);
+        fun->setUnlazifiedScript(script);
     else
-        fun->setScript(bce->script);
-
-    bce->tellDebuggerAboutCompiledScript(cx);
+        fun->setScript(script);
+
+    tellDebuggerAboutCompiledScript(cx);
 
     return true;
 }
 
 bool
 BytecodeEmitter::maybeEmitVarDecl(JSOp prologOp, ParseNode *pn, jsatomid *result)
 {
     jsatomid atomIndex;
@@ -3179,40 +3135,36 @@ BytecodeEmitter::maybeEmitVarDecl(JSOp p
         switchToMain();
     }
 
     if (result)
         *result = atomIndex;
     return true;
 }
 
-typedef bool
-(*DestructuringDeclEmitter)(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn);
-
-template <DestructuringDeclEmitter EmitName>
-static bool
-EmitDestructuringDeclsWithEmitter(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp,
-                                  ParseNode *pattern)
+template <BytecodeEmitter::DestructuringDeclEmitter EmitName>
+bool
+BytecodeEmitter::emitDestructuringDeclsWithEmitter(JSOp prologOp, ParseNode *pattern)
 {
     if (pattern->isKind(PNK_ARRAY)) {
         for (ParseNode *element = pattern->pn_head; element; element = element->pn_next) {
             if (element->isKind(PNK_ELISION))
                 continue;
             ParseNode *target = element;
             if (element->isKind(PNK_SPREAD)) {
                 MOZ_ASSERT(element->pn_kid->isKind(PNK_NAME));
                 target = element->pn_kid;
             }
             if (target->isKind(PNK_ASSIGN))
                 target = target->pn_left;
             if (target->isKind(PNK_NAME)) {
-                if (!EmitName(cx, bce, prologOp, target))
+                if (!EmitName(this, prologOp, target))
                     return false;
             } else {
-                if (!EmitDestructuringDeclsWithEmitter<EmitName>(cx, bce, prologOp, target))
+                if (!emitDestructuringDeclsWithEmitter<EmitName>(prologOp, target))
                     return false;
             }
         }
         return true;
     }
 
     MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
     for (ParseNode *member = pattern->pn_head; member; member = member->pn_next) {
@@ -3220,85 +3172,75 @@ EmitDestructuringDeclsWithEmitter(Exclus
                    member->isKind(PNK_COLON) ||
                    member->isKind(PNK_SHORTHAND));
 
         ParseNode *target = member->isKind(PNK_MUTATEPROTO) ? member->pn_kid : member->pn_right;
 
         if (target->isKind(PNK_ASSIGN))
             target = target->pn_left;
         if (target->isKind(PNK_NAME)) {
-            if (!EmitName(cx, bce, prologOp, target))
+            if (!EmitName(this, prologOp, target))
                 return false;
         } else {
-            if (!EmitDestructuringDeclsWithEmitter<EmitName>(cx, bce, prologOp, target))
+            if (!emitDestructuringDeclsWithEmitter<EmitName>(prologOp, target))
                 return false;
         }
     }
     return true;
 }
 
-bool
-EmitDestructuringDecl(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
+static bool
+EmitDestructuringDecl(BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_NAME));
     if (!bce->bindNameToSlot(pn))
         return false;
 
     MOZ_ASSERT(!pn->isOp(JSOP_CALLEE));
     return bce->maybeEmitVarDecl(prologOp, pn, nullptr);
 }
 
-static inline bool
-EmitDestructuringDecls(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp,
-                       ParseNode *pattern)
-{
-    return EmitDestructuringDeclsWithEmitter<EmitDestructuringDecl>(cx, bce, prologOp, pattern);
-}
-
 bool
-EmitInitializeDestructuringDecl(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp,
-                                ParseNode *pn)
+BytecodeEmitter::emitDestructuringDecls(JSOp prologOp, ParseNode *pattern)
+{
+    return emitDestructuringDeclsWithEmitter<EmitDestructuringDecl>(prologOp, pattern);
+}
+
+static bool
+EmitInitializeDestructuringDecl(BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_NAME));
     MOZ_ASSERT(pn->isBound());
     return bce->emitVarOp(pn, pn->getOp());
 }
 
-// Emit code to initialize all destructured names to the value on the top of
-// the stack.
-static inline bool
-EmitInitializeDestructuringDecls(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp,
-                                 ParseNode *pattern)
-{
-    return EmitDestructuringDeclsWithEmitter<EmitInitializeDestructuringDecl>(cx, bce,
-                                                                              prologOp, pattern);
-}
-
-static bool
-EmitDestructuringOpsHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pattern,
-                           VarEmitOption emitOption);
+bool
+BytecodeEmitter::emitInitializeDestructuringDecls(JSOp prologOp, ParseNode *pattern)
+{
+    return emitDestructuringDeclsWithEmitter<EmitInitializeDestructuringDecl>(prologOp, pattern);
+}
 
 bool
 BytecodeEmitter::emitDestructuringLHS(ParseNode *target, VarEmitOption emitOption)
 {
     MOZ_ASSERT(emitOption != DefineVars);
 
     // Now emit the lvalue opcode sequence. If the lvalue is a nested
     // destructuring initialiser-form, call ourselves to handle it, then pop
     // the matched value. Otherwise emit an lvalue bytecode sequence followed
     // by an assignment op.
     if (target->isKind(PNK_SPREAD))
         target = target->pn_kid;
     else if (target->isKind(PNK_ASSIGN))
         target = target->pn_left;
     if (target->isKind(PNK_ARRAY) || target->isKind(PNK_OBJECT)) {
-        if (!EmitDestructuringOpsHelper(cx, this, target, emitOption))
+        if (!emitDestructuringOpsHelper(target, emitOption))
             return false;
         if (emitOption == InitializeVars) {
-            // Per its post-condition, EmitDestructuringOpsHelper has left the
+            // Per its post-condition, emitDestructuringOpsHelper has left the
             // to-be-destructured value on top of the stack.
             if (!emit1(JSOP_POP))
                 return false;
         }
     } else if (emitOption == PushInitialValues) {
         // The lhs is a simple name so the to-be-destructured value is
         // its initial value and there is nothing to do.
         MOZ_ASSERT(target->getOp() == JSOP_SETLOCAL || target->getOp() == JSOP_INITLEXICAL);
@@ -3436,46 +3378,45 @@ BytecodeEmitter::emitDefault(ParseNode *
 {
     if (!emit1(JSOP_DUP))                                 // VALUE VALUE
         return false;
     if (!emit1(JSOP_UNDEFINED))                           // VALUE VALUE UNDEFINED
         return false;
     if (!emit1(JSOP_STRICTEQ))                            // VALUE EQL?
         return false;
     // Emit source note to enable ion compilation.
-    if (NewSrcNote(cx, this, SRC_IF) < 0)
+    if (newSrcNote(SRC_IF) < 0)
         return false;
     ptrdiff_t jump = emitJump(JSOP_IFEQ, 0);              // VALUE
     if (jump < 0)
         return false;
     if (!emit1(JSOP_POP))                                 // .
         return false;
     if (!emitTree(defaultExpr))                           // DEFAULTVALUE
         return false;
     setJumpOffsetAt(jump);
     return true;
 }
 
-static bool
-EmitDestructuringOpsArrayHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pattern,
-                                VarEmitOption emitOption)
+bool
+BytecodeEmitter::emitDestructuringOpsArrayHelper(ParseNode *pattern, VarEmitOption emitOption)
 {
     MOZ_ASSERT(pattern->isKind(PNK_ARRAY));
     MOZ_ASSERT(pattern->isArity(PN_LIST));
-    MOZ_ASSERT(bce->stackDepth != 0);
+    MOZ_ASSERT(this->stackDepth != 0);
 
     /*
      * Use an iterator to destructure the RHS, instead of index lookup.
      * InitializeVars expects us to leave the *original* value on the stack.
      */
     if (emitOption == InitializeVars) {
-        if (!bce->emit1(JSOP_DUP))                                     // ... OBJ OBJ
-            return false;
-    }
-    if (!bce->emitIterator())                                          // ... OBJ? ITER
+        if (!emit1(JSOP_DUP))                                     // ... OBJ OBJ
+            return false;
+    }
+    if (!emitIterator())                                          // ... OBJ? ITER
         return false;
     bool needToPopIterator = true;
 
     for (ParseNode *member = pattern->pn_head; member; member = member->pn_next) {
         /*
          * Now push the property name currently being matched, which is the
          * current property name "label" on the left of a colon in the object
          * initializer.
@@ -3484,191 +3425,190 @@ EmitDestructuringOpsArrayHelper(Exclusiv
         ParseNode *elem = member;
         if (elem->isKind(PNK_ASSIGN)) {
             pndefault = elem->pn_right;
             elem = elem->pn_left;
         }
 
         if (elem->isKind(PNK_SPREAD)) {
             /* Create a new array with the rest of the iterator */
-            ptrdiff_t off = bce->emitN(JSOP_NEWARRAY, 3);              // ... OBJ? ITER ARRAY
+            ptrdiff_t off = emitN(JSOP_NEWARRAY, 3);              // ... OBJ? ITER ARRAY
             if (off < 0)
                 return false;
-            bce->checkTypeSet(JSOP_NEWARRAY);
-            jsbytecode *pc = bce->code(off);
+            checkTypeSet(JSOP_NEWARRAY);
+            jsbytecode *pc = code(off);
             SET_UINT24(pc, 0);
 
-            if (!bce->emitNumberOp(0))                                 // ... OBJ? ITER ARRAY INDEX
-                return false;
-            if (!bce->emitSpread())                                    // ... OBJ? ARRAY INDEX
-                return false;
-            if (!bce->emit1(JSOP_POP))                                 // ... OBJ? ARRAY
+            if (!emitNumberOp(0))                                 // ... OBJ? ITER ARRAY INDEX
+                return false;
+            if (!emitSpread())                                    // ... OBJ? ARRAY INDEX
+                return false;
+            if (!emit1(JSOP_POP))                                 // ... OBJ? ARRAY
                 return false;
             needToPopIterator = false;
         } else {
-            if (!bce->emit1(JSOP_DUP))                                 // ... OBJ? ITER ITER
-                return false;
-            if (!bce->emitIteratorNext(pattern))                       // ... OBJ? ITER RESULT
-                return false;
-            if (!bce->emit1(JSOP_DUP))                                 // ... OBJ? ITER RESULT RESULT
-                return false;
-            if (!bce->emitAtomOp(cx->names().done, JSOP_GETPROP))      // ... OBJ? ITER RESULT DONE?
+            if (!emit1(JSOP_DUP))                                 // ... OBJ? ITER ITER
+                return false;
+            if (!emitIteratorNext(pattern))                       // ... OBJ? ITER RESULT
+                return false;
+            if (!emit1(JSOP_DUP))                                 // ... OBJ? ITER RESULT RESULT
+                return false;
+            if (!emitAtomOp(cx->names().done, JSOP_GETPROP))      // ... OBJ? ITER RESULT DONE?
                 return false;
 
             // Emit (result.done ? undefined : result.value)
             // This is mostly copied from EmitConditionalExpression, except that this code
             // does not push new values onto the stack.
-            ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_COND);
+            ptrdiff_t noteIndex = newSrcNote(SRC_COND);
             if (noteIndex < 0)
                 return false;
-            ptrdiff_t beq = bce->emitJump(JSOP_IFEQ, 0);
+            ptrdiff_t beq = emitJump(JSOP_IFEQ, 0);
             if (beq < 0)
                 return false;
 
-            if (!bce->emit1(JSOP_POP))                                 // ... OBJ? ITER
-                return false;
-            if (!bce->emit1(JSOP_UNDEFINED))                           // ... OBJ? ITER UNDEFINED
+            if (!emit1(JSOP_POP))                                 // ... OBJ? ITER
+                return false;
+            if (!emit1(JSOP_UNDEFINED))                           // ... OBJ? ITER UNDEFINED
                 return false;
 
             /* Jump around else, fixup the branch, emit else, fixup jump. */
-            ptrdiff_t jmp = bce->emitJump(JSOP_GOTO, 0);
+            ptrdiff_t jmp = emitJump(JSOP_GOTO, 0);
             if (jmp < 0)
                 return false;
-            bce->setJumpOffsetAt(beq);
-
-            if (!bce->emitAtomOp(cx->names().value, JSOP_GETPROP))     // ... OBJ? ITER VALUE
-                return false;
-
-            bce->setJumpOffsetAt(jmp);
-            if (!bce->setSrcNoteOffset(noteIndex, 0, jmp - beq))
+            setJumpOffsetAt(beq);
+
+            if (!emitAtomOp(cx->names().value, JSOP_GETPROP))     // ... OBJ? ITER VALUE
+                return false;
+
+            setJumpOffsetAt(jmp);
+            if (!setSrcNoteOffset(noteIndex, 0, jmp - beq))
                 return false;
         }
 
-        if (pndefault && !bce->emitDefault(pndefault))
+        if (pndefault && !emitDefault(pndefault))
             return false;
 
         // Destructure into the pattern the element contains.
         ParseNode *subpattern = elem;
         if (subpattern->isKind(PNK_ELISION)) {
             // The value destructuring into an elision just gets ignored.
-            if (!bce->emit1(JSOP_POP))                             // ... OBJ? ITER
+            if (!emit1(JSOP_POP))                                 // ... OBJ? ITER
                 return false;
             continue;
         }
 
-        int32_t depthBefore = bce->stackDepth;
-        if (!bce->emitDestructuringLHS(subpattern, emitOption))
+        int32_t depthBefore = this->stackDepth;
+        if (!emitDestructuringLHS(subpattern, emitOption))
             return false;
 
         if (emitOption == PushInitialValues && needToPopIterator) {
             /*
              * After '[x,y]' in 'let ([[x,y], z] = o)', the stack is
              *   | to-be-destructured-value | x | y |
              * The goal is:
              *   | x | y | z |
              * so emit a pick to produce the intermediate state
              *   | x | y | to-be-destructured-value |
              * before destructuring z. This gives the loop invariant that
              * the to-be-destructured-value is always on top of the stack.
              */
-            MOZ_ASSERT((bce->stackDepth - bce->stackDepth) >= -1);
-            uint32_t pickDistance = (uint32_t)((bce->stackDepth + 1) - depthBefore);
+            MOZ_ASSERT((this->stackDepth - this->stackDepth) >= -1);
+            uint32_t pickDistance = uint32_t((this->stackDepth + 1) - depthBefore);
             if (pickDistance > 0) {
                 if (pickDistance > UINT8_MAX) {
-                    bce->reportError(subpattern, JSMSG_TOO_MANY_LOCALS);
+                    reportError(subpattern, JSMSG_TOO_MANY_LOCALS);
                     return false;
                 }
-                if (!bce->emit2(JSOP_PICK, (jsbytecode)pickDistance))
+                if (!emit2(JSOP_PICK, (jsbytecode)pickDistance))
                     return false;
             }
         }
     }
 
-    if (needToPopIterator && !bce->emit1(JSOP_POP))
+    if (needToPopIterator && !emit1(JSOP_POP))
         return false;
 
     return true;
 }
 
-static bool
-EmitDestructuringOpsObjectHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pattern,
-                                 VarEmitOption emitOption)
+bool
+BytecodeEmitter::emitDestructuringOpsObjectHelper(ParseNode *pattern, VarEmitOption emitOption)
 {
     MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
     MOZ_ASSERT(pattern->isArity(PN_LIST));
 
-    MOZ_ASSERT(bce->stackDepth != 0);                                  // ... OBJ
+    MOZ_ASSERT(this->stackDepth != 0);                            // ... OBJ
 
     for (ParseNode *member = pattern->pn_head; member; member = member->pn_next) {
         // Duplicate the value being destructured to use as a reference base.
-        if (!bce->emit1(JSOP_DUP))                                     // ... OBJ OBJ
+        if (!emit1(JSOP_DUP))                                     // ... OBJ OBJ
             return false;
 
         // Now push the property name currently being matched, which is the
         // current property name "label" on the left of a colon in the object
         // initialiser.
         bool needsGetElem = true;
 
         ParseNode *subpattern;
         if (member->isKind(PNK_MUTATEPROTO)) {
-            if (!bce->emitAtomOp(cx->names().proto, JSOP_GETPROP))     // ... OBJ PROP
+            if (!emitAtomOp(cx->names().proto, JSOP_GETPROP))     // ... OBJ PROP
                 return false;
             needsGetElem = false;
             subpattern = member->pn_kid;
         } else {
             MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
 
             ParseNode *key = member->pn_left;
             if (key->isKind(PNK_NUMBER)) {
-                if (!bce->emitNumberOp(key->pn_dval))                  // ... OBJ OBJ KEY
+                if (!emitNumberOp(key->pn_dval))                  // ... OBJ OBJ KEY
                     return false;
             } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
                 PropertyName *name = key->pn_atom->asPropertyName();
 
                 // The parser already checked for atoms representing indexes and
                 // used PNK_NUMBER instead, but also watch for ids which TI treats
                 // as indexes for simplification of downstream analysis.
                 jsid id = NameToId(name);
                 if (id != IdToTypeId(id)) {
-                    if (!bce->emitTree(key))                           // ... OBJ OBJ KEY
+                    if (!emitTree(key))                           // ... OBJ OBJ KEY
                         return false;
                 } else {
-                    if (!bce->emitAtomOp(name, JSOP_GETPROP))          // ...OBJ PROP
+                    if (!emitAtomOp(name, JSOP_GETPROP))          // ...OBJ PROP
                         return false;
                     needsGetElem = false;
                 }
             } else {
                 MOZ_ASSERT(key->isKind(PNK_COMPUTED_NAME));
-                if (!bce->emitTree(key->pn_kid))                       // ... OBJ OBJ KEY
+                if (!emitTree(key->pn_kid))                       // ... OBJ OBJ KEY
                     return false;
             }
 
             subpattern = member->pn_right;
         }
 
         // Get the property value if not done already.
-        if (needsGetElem && !bce->emitElemOpBase(JSOP_GETELEM))        // ... OBJ PROP
+        if (needsGetElem && !emitElemOpBase(JSOP_GETELEM))        // ... OBJ PROP
             return false;
 
         if (subpattern->isKind(PNK_ASSIGN)) {
-            if (!bce->emitDefault(subpattern->pn_right))
+            if (!emitDefault(subpattern->pn_right))
                 return false;
             subpattern = subpattern->pn_left;
         }
 
         // Destructure PROP per this member's subpattern.
-        int32_t depthBefore = bce->stackDepth;
-        if (!bce->emitDestructuringLHS(subpattern, emitOption))
+        int32_t depthBefore = this->stackDepth;
+        if (!emitDestructuringLHS(subpattern, emitOption))
             return false;
 
         // If emitOption is InitializeVars, destructuring initialized each
         // target in the subpattern's LHS as it went, then popped PROP.  We've
         // correctly returned to the loop-entry stack, and we continue to the
         // next member.
-        if (emitOption == InitializeVars)                              // ... OBJ
+        if (emitOption == InitializeVars)                         // ... OBJ
             continue;
 
         MOZ_ASSERT(emitOption == PushInitialValues);
 
         // emitDestructuringLHS removed PROP, and it pushed a value per target
         // name in LHS (for |emitOption == PushInitialValues| only makes sense
         // when multiple values need to be pushed onto the stack to initialize
         // a single lexical scope). It also preserved OBJ deep in the stack as
@@ -3686,73 +3626,71 @@ EmitDestructuringOpsObjectHelper(Exclusi
         //
         // (where of course x = obj.arr[0] and y = obj.arr[1], and []-indexing
         // is really iteration-indexing). We want to have:
         //
         //     ... x y OBJ
         //
         // so that we can continue, ready to destruct z from OBJ. Pick OBJ out
         // of the stack, moving it to the top, to accomplish this.
-        MOZ_ASSERT((bce->stackDepth - bce->stackDepth) >= -1);
-        uint32_t pickDistance = (uint32_t)((bce->stackDepth + 1) - depthBefore);
+        MOZ_ASSERT((this->stackDepth - this->stackDepth) >= -1);
+        uint32_t pickDistance = uint32_t((this->stackDepth + 1) - depthBefore);
         if (pickDistance > 0) {
             if (pickDistance > UINT8_MAX) {
-                bce->reportError(subpattern, JSMSG_TOO_MANY_LOCALS);
+                reportError(subpattern, JSMSG_TOO_MANY_LOCALS);
                 return false;
             }
-            if (!bce->emit2(JSOP_PICK, (jsbytecode)pickDistance))
+            if (!emit2(JSOP_PICK, (jsbytecode)pickDistance))
                 return false;
         }
     }
 
     if (emitOption == PushInitialValues) {
         // Per the above loop invariant, the value being destructured into this
         // object pattern is atop the stack.  Pop it to achieve the
         // post-condition.
-        if (!bce->emit1(JSOP_POP))                                 // ... <pattern's target name values, seriatim>
+        if (!emit1(JSOP_POP))                                 // ... <pattern's target name values, seriatim>
             return false;
     }
 
     return true;
 }
 
 /*
- * Recursive helper for EmitDestructuringOps.
+ * Recursive helper for emitDestructuringOps.
  * EmitDestructuringOpsHelper assumes the to-be-destructured value has been
  * pushed on the stack and emits code to destructure each part of a [] or {}
  * lhs expression.
  *
  * If emitOption is InitializeVars, the initial to-be-destructured value is
  * left untouched on the stack and the overall depth is not changed.
  *
  * 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 emitDestructuringLHS)
  */
-static bool
-EmitDestructuringOpsHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pattern,
-                           VarEmitOption emitOption)
+bool
+BytecodeEmitter::emitDestructuringOpsHelper(ParseNode *pattern, VarEmitOption emitOption)
 {
     MOZ_ASSERT(emitOption != DefineVars);
 
     if (pattern->isKind(PNK_ARRAY))
-        return EmitDestructuringOpsArrayHelper(cx, bce, pattern, emitOption);
-    return EmitDestructuringOpsObjectHelper(cx, bce, pattern, emitOption);
-}
-
-static bool
-EmitDestructuringOps(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pattern,
-                     bool isLet = false)
+        return emitDestructuringOpsArrayHelper(pattern, emitOption);
+    return emitDestructuringOpsObjectHelper(pattern, emitOption);
+}
+
+bool
+BytecodeEmitter::emitDestructuringOps(ParseNode *pattern, bool isLet)
 {
     /*
      * Call our recursive helper to emit the destructuring assignments and
      * related stack manipulations.
      */
     VarEmitOption emitOption = isLet ? PushInitialValues : InitializeVars;
-    return EmitDestructuringOpsHelper(cx, bce, pattern, emitOption);
+    return emitDestructuringOpsHelper(pattern, emitOption);
 }
 
 bool
 BytecodeEmitter::emitTemplateString(ParseNode *pn)
 {
     MOZ_ASSERT(pn->isArity(PN_LIST));
 
     for (ParseNode *pn2 = pn->pn_head; pn2 != NULL; pn2 = pn2->pn_next) {
@@ -3807,26 +3745,26 @@ BytecodeEmitter::emitVariables(ParseNode
                 // ended. Thus, each iteration's assignment is responsible for
                 // initializing, and nothing needs to be done here.
                 //
                 // Otherwise this is emitting destructuring let binding
                 // initialization for a legacy comprehension expression. See
                 // emitForInOrOfVariables.
                 MOZ_ASSERT(pn->pn_count == 1);
                 if (emitOption == DefineVars) {
-                    if (!EmitDestructuringDecls(cx, this, pn->getOp(), pn2))
+                    if (!emitDestructuringDecls(pn->getOp(), pn2))
                         return false;
                 } else {
                     // Lexical bindings cannot be used before they are
                     // initialized. Similar to the JSOP_INITLEXICAL case below.
                     MOZ_ASSERT(emitOption != DefineVars);
                     MOZ_ASSERT_IF(emitOption == InitializeVars, pn->pn_xflags & PNX_POPVAR);
                     if (!emit1(JSOP_UNDEFINED))
                         return false;
-                    if (!EmitInitializeDestructuringDecls(cx, this, pn->getOp(), pn2))
+                    if (!emitInitializeDestructuringDecls(pn->getOp(), pn2))
                         return false;
                 }
                 break;
             }
 
             /*
              * A destructuring initialiser assignment preceded by var will
              * never occur to the left of 'in' in a for-in loop.  As with 'for
@@ -3844,23 +3782,23 @@ BytecodeEmitter::emitVariables(ParseNode
              */
             if (pn2->pn_left->isKind(PNK_NAME)) {
                 pn3 = pn2->pn_right;
                 pn2 = pn2->pn_left;
                 goto do_name;
             }
 
             pn3 = pn2->pn_left;
-            if (!EmitDestructuringDecls(cx, this, pn->getOp(), pn3))
+            if (!emitDestructuringDecls(pn->getOp(), pn3))
                 return false;
 
             if (!emitTree(pn2->pn_right))
                 return false;
 
-            if (!EmitDestructuringOps(cx, this, pn3, isLetExpr))
+            if (!emitDestructuringOps(pn3, isLetExpr))
                 return false;
 
             /* If we are not initializing, nothing to pop. */
             if (emitOption != InitializeVars) {
                 if (next)
                     continue;
                 break;
             }
@@ -4104,17 +4042,17 @@ BytecodeEmitter::emitAssignment(ParseNod
     /* If += etc., emit the binary operator with a source note. */
     if (op != JSOP_NOP) {
         /*
          * Take care to avoid SRC_ASSIGNOP if the left-hand side is a const
          * declared in the current compilation unit, as in this case (just
          * a bit further below) we will avoid emitting the assignment op.
          */
         if (!lhs->isKind(PNK_NAME) || !lhs->isConst()) {
-            if (NewSrcNote(cx, this, SRC_ASSIGNOP) < 0)
+            if (newSrcNote(SRC_ASSIGNOP) < 0)
                 return false;
         }
         if (!emit1(op))
             return false;
     }
 
     /* Finally, emit the specialized assignment bytecode. */
     switch (lhs->getKind()) {
@@ -4142,17 +4080,17 @@ BytecodeEmitter::emitAssignment(ParseNod
       {
         JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
         if (!emit1(setOp))
             return false;
         break;
       }
       case PNK_ARRAY:
       case PNK_OBJECT:
-        if (!EmitDestructuringOps(cx, this, lhs))
+        if (!emitDestructuringOps(lhs))
             return false;
         break;
       default:
         MOZ_ASSERT(0);
     }
     return true;
 }
 
@@ -4361,17 +4299,17 @@ BytecodeEmitter::emitCatch(ParseNode *pn
      */
     if (pn->pn_kid2 && !emit1(JSOP_DUP))
         return false;
 
     ParseNode *pn2 = pn->pn_kid1;
     switch (pn2->getKind()) {
       case PNK_ARRAY:
       case PNK_OBJECT:
-        if (!EmitDestructuringOps(cx, this, pn2))
+        if (!emitDestructuringOps(pn2))
             return false;
         if (!emit1(JSOP_POP))
             return false;
         break;
 
       case PNK_NAME:
         /* Inline and specialize bindNameToSlot for pn2. */
         MOZ_ASSERT(!pn2->pn_cookie.isFree());
@@ -4394,17 +4332,17 @@ BytecodeEmitter::emitCatch(ParseNode *pn
         // If the guard expression is false, fall through, pop the block scope,
         // and jump to the next catch block.  Otherwise jump over that code and
         // pop the dupped exception.
         ptrdiff_t guardCheck = emitJump(JSOP_IFNE, 0);
         if (guardCheck < 0)
             return false;
 
         {
-            NonLocalExitScope nle(cx, this);
+            NonLocalExitScope nle(this);
 
             // Move exception back to cx->exception to prepare for
             // the next catch.
             if (!emit1(JSOP_THROWING))
                 return false;
 
             // Leave the scope for this catch block.
             if (!nle.prepareForNonLocalJump(stmt))
@@ -4452,17 +4390,17 @@ BytecodeEmitter::emitTry(ParseNode *pn)
     //
     // For that we store in a try note associated with the catch or
     // finally block the stack depth upon the try entry. The interpreter
     // uses this depth to properly unwind the stack and the scope chain.
     //
     int depth = stackDepth;
 
     // Record the try location, then emit the try block.
-    ptrdiff_t noteIndex = NewSrcNote(cx, this, SRC_TRY);
+    ptrdiff_t noteIndex = newSrcNote(SRC_TRY);
     if (noteIndex < 0 || !emit1(JSOP_TRY))
         return false;
     ptrdiff_t tryStart = offset();
     if (!emitTree(pn->pn_kid1))
         return false;
     MOZ_ASSERT(depth == stackDepth);
 
     // GOSUB to finally, if present.
@@ -4625,17 +4563,17 @@ BytecodeEmitter::emitIf(ParseNode *pn)
         stmtInfo.type = STMT_IF;
         stmtInfo.update = top;
         if (!setSrcNoteOffset(noteIndex, 0, jmp - beq))
             return false;
     }
 
     /* Emit an annotated branch-if-false around the then part. */
     ParseNode *pn3 = pn->pn_kid3;
-    noteIndex = NewSrcNote(cx, this, pn3 ? SRC_IF_ELSE : SRC_IF);
+    noteIndex = newSrcNote(pn3 ? SRC_IF_ELSE : SRC_IF);
     if (noteIndex < 0)
         return false;
     beq = emitJump(JSOP_IFEQ, 0);
     if (beq < 0)
         return false;
 
     /* Emit code for the then and optional else parts. */
     if (!emitTree(pn->pn_kid2))
@@ -4709,80 +4647,78 @@ BytecodeEmitter::emitIf(ParseNode *pn)
  *  popblockscope (if needed)
  *
  * Note that, since pushblockscope simply changes fp->scopeChain and does not
  * otherwise touch the stack, evaluation of the let-var initializers must leave
  * the initial value in the let-var's future slot.
  */
 /*
  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
- * the comment on EmitSwitch.
+ * the comment on emitSwitch.
  */
-MOZ_NEVER_INLINE static bool
-EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
+MOZ_NEVER_INLINE bool
+BytecodeEmitter::emitLet(ParseNode *pnLet)
 {
     MOZ_ASSERT(pnLet->isArity(PN_BINARY));
     ParseNode *varList = pnLet->pn_left;
     MOZ_ASSERT(varList->isArity(PN_LIST));
     ParseNode *letBody = pnLet->pn_right;
     MOZ_ASSERT(letBody->isLexical() && letBody->isKind(PNK_LEXICALSCOPE));
 
-    int letHeadDepth = bce->stackDepth;
-
-    if (!bce->emitVariables(varList, PushInitialValues, true))
+    int letHeadDepth = this->stackDepth;
+
+    if (!emitVariables(varList, PushInitialValues, true))
         return false;
 
     /* Push storage for hoisted let decls (e.g. 'let (x) { let y }'). */
-    uint32_t valuesPushed = bce->stackDepth - letHeadDepth;
+    uint32_t valuesPushed = this->stackDepth - letHeadDepth;
     StmtInfoBCE stmtInfo(cx);
-    if (!EnterBlockScope(cx, bce, &stmtInfo, letBody->pn_objbox, JSOP_UNINITIALIZED, valuesPushed))
-        return false;
-
-    if (!bce->emitTree(letBody->pn_expr))
-        return false;
-
-    if (!LeaveNestedScope(cx, bce, &stmtInfo))
+    if (!enterBlockScope(&stmtInfo, letBody->pn_objbox, JSOP_UNINITIALIZED, valuesPushed))
+        return false;
+
+    if (!emitTree(letBody->pn_expr))
+        return false;
+
+    if (!leaveNestedScope(&stmtInfo))
         return false;
 
     return true;
 }
 
-/*
- * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
- * the comment on EmitSwitch.
- */
-MOZ_NEVER_INLINE static bool
-EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
+// the comment on emitSwitch.
+MOZ_NEVER_INLINE bool
+BytecodeEmitter::emitLexicalScope(ParseNode *pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
 
     StmtInfoBCE stmtInfo(cx);
-    if (!EnterBlockScope(cx, bce, &stmtInfo, pn->pn_objbox, JSOP_UNINITIALIZED, 0))
-        return false;
-
-    if (!bce->emitTree(pn->pn_expr))
-        return false;
-
-    if (!LeaveNestedScope(cx, bce, &stmtInfo))
+    if (!enterBlockScope(&stmtInfo, pn->pn_objbox, JSOP_UNINITIALIZED, 0))
+        return false;
+
+    if (!emitTree(pn->pn_expr))
+        return false;
+
+    if (!leaveNestedScope(&stmtInfo))
         return false;
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitWith(ParseNode *pn)
 {
     StmtInfoBCE stmtInfo(cx);
     if (!emitTree(pn->pn_left))
         return false;
-    if (!EnterNestedScope(cx, this, &stmtInfo, pn->pn_binary_obj, STMT_WITH))
+    if (!enterNestedScope(&stmtInfo, pn->pn_binary_obj, STMT_WITH))
         return false;
     if (!emitTree(pn->pn_right))
         return false;
-    if (!LeaveNestedScope(cx, this, &stmtInfo))
+    if (!leaveNestedScope(&stmtInfo))
         return false;
     return true;
 }
 
 bool
 BytecodeEmitter::emitIterator()
 {
     // Convert iterable to iterator.
@@ -4802,17 +4738,17 @@ BytecodeEmitter::emitIterator()
 
 bool
 BytecodeEmitter::emitForInOrOfVariables(ParseNode *pn, bool *letDecl)
 {
     *letDecl = pn->isKind(PNK_LEXICALSCOPE);
     MOZ_ASSERT_IF(*letDecl, pn->isLexical());
 
     // If the left part is 'var x', emit code to define x if necessary using a
-    // prolog opcode, but do not emit a pop. If it is 'let x', EnterBlockScope
+    // prolog opcode, but do not emit a pop. If it is 'let x', enterBlockScope
     // will initialize let bindings in emitForOf and emitForIn with
     // undefineds.
     //
     // Due to the horror of legacy comprehensions, there is a third case where
     // we have PNK_LET without a lexical scope, because those expressions are
     // parsed with single lexical scope for the entire comprehension. In this
     // case we must initialize the lets to not trigger dead zone checks via
     // InitializeVars.
@@ -4864,27 +4800,27 @@ BytecodeEmitter::emitForOf(StmtType type
             return false;
     }
 
     // Enter the block before the loop body, after evaluating the obj.
     // Initialize let bindings with undefined when entering, as the name
     // assigned to is a plain assignment.
     StmtInfoBCE letStmt(cx);
     if (letDecl) {
-        if (!EnterBlockScope(cx, this, &letStmt, pn1->pn_objbox, JSOP_UNDEFINED, 0))
+        if (!enterBlockScope(&letStmt, pn1->pn_objbox, JSOP_UNDEFINED, 0))
             return false;
     }
 
     LoopStmtInfo stmtInfo(cx);
-    PushLoopStatement(this, &stmtInfo, type, top);
+    pushLoopStatement(&stmtInfo, type, top);
 
     // Jump down to the loop condition to minimize overhead assuming at least
     // one iteration, as the other loop forms do.  Annotate so IonMonkey can
     // find the loop-closing jump.
-    int noteIndex = NewSrcNote(cx, this, SRC_FOR_OF);
+    int noteIndex = newSrcNote(SRC_FOR_OF);
     if (noteIndex < 0)
         return false;
     ptrdiff_t jmp = emitJump(JSOP_GOTO, 0);
     if (jmp < 0)
         return false;
 
     top = offset();
     SET_STATEMENT_TOP(&stmtInfo, top);
@@ -4966,17 +4902,17 @@ BytecodeEmitter::emitForOf(StmtType type
     // Fixup breaks and continues.
     // For STMT_SPREAD, just pop pc->topStmt.
     popStatement();
 
     if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top, offset()))
         return false;
 
     if (letDecl) {
-        if (!LeaveNestedScope(cx, this, &letStmt))
+        if (!leaveNestedScope(&letStmt))
             return false;
     }
 
     if (type == STMT_SPREAD) {
         if (!emit2(JSOP_PICK, (jsbytecode)3))      // ARR I RESULT ITER
             return false;
     }
 
@@ -5013,25 +4949,25 @@ BytecodeEmitter::emitForIn(ParseNode *pn
     if (!emit1(JSOP_UNDEFINED))
         return false;
 
     // Enter the block before the loop body, after evaluating the obj.
     // Initialize let bindings with undefined when entering, as the name
     // assigned to is a plain assignment.
     StmtInfoBCE letStmt(cx);
     if (letDecl) {
-        if (!EnterBlockScope(cx, this, &letStmt, pn1->pn_objbox, JSOP_UNDEFINED, 0))
+        if (!enterBlockScope(&letStmt, pn1->pn_objbox, JSOP_UNDEFINED, 0))
             return false;
     }
 
     LoopStmtInfo stmtInfo(cx);
-    PushLoopStatement(this, &stmtInfo, STMT_FOR_IN_LOOP, top);
+    pushLoopStatement(&stmtInfo, STMT_FOR_IN_LOOP, top);
 
     /* Annotate so IonMonkey can find the loop-closing jump. */
-    int noteIndex = NewSrcNote(cx, this, SRC_FOR_IN);
+    int noteIndex = newSrcNote(SRC_FOR_IN);
     if (noteIndex < 0)
         return false;
 
     /*
      * Jump down to the loop condition to minimize overhead assuming at
      * least one iteration, as the other loop forms do.
      */
     ptrdiff_t jmp = emitJump(JSOP_GOTO, 0);
@@ -5093,28 +5029,28 @@ BytecodeEmitter::emitForIn(ParseNode *pn
         return false;
 
     if (!tryNoteList.append(JSTRY_FOR_IN, this->stackDepth, top, offset()))
         return false;
     if (!emit1(JSOP_ENDITER))
         return false;
 
     if (letDecl) {
-        if (!LeaveNestedScope(cx, this, &letStmt))
+        if (!leaveNestedScope(&letStmt))
             return false;
     }
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitNormalFor(ParseNode *pn, ptrdiff_t top)
 {
     LoopStmtInfo stmtInfo(cx);
-    PushLoopStatement(this, &stmtInfo, STMT_FOR_LOOP, top);
+    pushLoopStatement(&stmtInfo, STMT_FOR_LOOP, top);
 
     ParseNode *forHead = pn->pn_left;
     ParseNode *forBody = pn->pn_right;
 
     /* C-style for (init; cond; update) ... loop. */
     JSOp op = JSOP_POP;
     ParseNode *pn3 = forHead->pn_kid1;
     if (!pn3) {
@@ -5131,17 +5067,17 @@ BytecodeEmitter::emitNormalFor(ParseNode
     }
 
     /*
      * NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH).
      * Use tmp to hold the biased srcnote "top" offset, which differs
      * from the top local variable by the length of the JSOP_GOTO
      * emitted in between tmp and top if this loop has a condition.
      */
-    int noteIndex = NewSrcNote(cx, this, SRC_FOR);
+    int noteIndex = newSrcNote(SRC_FOR);
     if (noteIndex < 0 || !emit1(op))
         return false;
     ptrdiff_t tmp = offset();
 
     ptrdiff_t jmp = -1;
     if (forHead->pn_kid2) {
         /* Goto the loop condition, which branches back to iterate. */
         jmp = emitJump(JSOP_GOTO, 0);
@@ -5184,17 +5120,17 @@ BytecodeEmitter::emitNormalFor(ParseNode
 
         /* Always emit the POP or NOP to help IonBuilder. */
         if (!emit1(op))
             return false;
 
         /* Restore the absolute line number for source note readers. */
         uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.end);
         if (currentLine() != lineNum) {
-            if (NewSrcNote2(cx, this, SRC_SETLINE, ptrdiff_t(lineNum)) < 0)
+            if (newSrcNote2(SRC_SETLINE, ptrdiff_t(lineNum)) < 0)
                 return false;
             current->currentLine = lineNum;
             current->lastColumn = 0;
         }
     }
 
     ptrdiff_t tmp3 = offset();
 
@@ -5239,187 +5175,186 @@ BytecodeEmitter::emitFor(ParseNode *pn, 
 
     if (pn->pn_left->isKind(PNK_FOROF))
         return emitForOf(STMT_FOR_OF_LOOP, pn, top);
 
     MOZ_ASSERT(pn->pn_left->isKind(PNK_FORHEAD));
     return emitNormalFor(pn, top);
 }
 
-static MOZ_NEVER_INLINE bool
-EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool needsProto = false)
+MOZ_NEVER_INLINE bool
+BytecodeEmitter::emitFunction(ParseNode *pn, bool needsProto)
 {
     FunctionBox *funbox = pn->pn_funbox;
     RootedFunction fun(cx, funbox->function());
     MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
 
     /*
      * Set the EMITTEDFUNCTION flag in function definitions once they have been
      * emitted. Function definitions that need hoisting to the top of the
-     * function will be seen by EmitFunc in two places.
+     * function will be seen by emitFunction in two places.
      */
     if (pn->pn_dflags & PND_EMITTEDFUNCTION) {
         MOZ_ASSERT_IF(fun->hasScript(), fun->nonLazyScript());
         MOZ_ASSERT(pn->functionIsHoisted());
-        MOZ_ASSERT(bce->sc->isFunctionBox());
+        MOZ_ASSERT(sc->isFunctionBox());
         return true;
     }
 
     pn->pn_dflags |= PND_EMITTEDFUNCTION;
 
     /*
      * Mark as singletons any function which will only be executed once, or
      * which is inner to a lambda we only expect to run once. In the latter
      * case, if the lambda runs multiple times then CloneFunctionObject will
      * make a deep clone of its contents.
      */
     if (fun->isInterpreted()) {
         bool singleton =
-            bce->script->compileAndGo() &&
+            script->compileAndGo() &&
             fun->isInterpreted() &&
-            (bce->checkSingletonContext() ||
-             (!bce->isInLoop() && bce->isRunOnceLambda()));
+            (checkSingletonContext() || (!isInLoop() && isRunOnceLambda()));
         if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
             return false;
 
         if (fun->isInterpretedLazy()) {
             if (!fun->lazyScript()->sourceObject()) {
-                JSObject *scope = EnclosingStaticScope(bce);
-                JSObject *source = bce->script->sourceObject();
+                JSObject *scope = enclosingStaticScope();
+                JSObject *source = script->sourceObject();
                 fun->lazyScript()->setParent(scope, &source->as<ScriptSourceObject>());
             }
-            if (bce->emittingRunOnceLambda)
+            if (emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
-            SharedContext *outersc = bce->sc;
+            SharedContext *outersc = sc;
 
             if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
                 funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
             // Inherit most things (principals, version, etc) from the parent.
-            Rooted<JSScript*> parent(cx, bce->script);
-            CompileOptions options(cx, bce->parser->options());
+            Rooted<JSScript*> parent(cx, script);
+            CompileOptions options(cx, parser->options());
             options.setMutedErrors(parent->mutedErrors())
                    .setCompileAndGo(parent->compileAndGo())
                    .setHasPollutedScope(parent->hasPollutedGlobalScope())
                    .setSelfHostingMode(parent->selfHosted())
                    .setNoScriptRval(false)
                    .setForEval(false)
                    .setVersion(parent->getVersion());
 
-            Rooted<JSObject*> enclosingScope(cx, EnclosingStaticScope(bce));
-            Rooted<JSObject*> sourceObject(cx, bce->script->sourceObject());
+            Rooted<JSObject*> enclosingScope(cx, enclosingStaticScope());
+            Rooted<JSObject*> sourceObject(cx, script->sourceObject());
             Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
                                                           parent->staticLevel() + 1,
                                                           sourceObject,
                                                           funbox->bufStart, funbox->bufEnd));
             if (!script)
                 return false;
 
             script->bindings = funbox->bindings;
 
-            uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
-            BytecodeEmitter bce2(bce, bce->parser, funbox, script, /* lazyScript = */ js::NullPtr(),
-                                 bce->insideEval, bce->evalCaller,
+            uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
+            BytecodeEmitter bce2(this, parser, funbox, script, /* lazyScript = */ js::NullPtr(),
+                                 insideEval, evalCaller,
                                  /* evalStaticScope = */ js::NullPtr(),
-                                 bce->insideNonGlobalEval, lineNum, bce->emitterMode);
+                                 insideNonGlobalEval, lineNum, emitterMode);
             if (!bce2.init())
                 return false;
 
             /* We measured the max scope depth when we parsed the function. */
-            if (!EmitFunctionScript(cx, &bce2, pn->pn_body))
+            if (!bce2.emitFunctionScript(pn->pn_body))
                 return false;
 
             if (funbox->usesArguments && funbox->usesApply && funbox->usesThis)
                 script->setUsesArgumentsApplyAndThis();
         }
     } else {
         MOZ_ASSERT(IsAsmJSModuleNative(fun->native()));
     }
 
     /* Make the function object a literal in the outer script's pool. */
-    unsigned index = bce->objectList.add(pn->pn_funbox);
+    unsigned index = objectList.add(pn->pn_funbox);
 
     /* Non-hoisted functions simply emit their respective op. */
     if (!pn->functionIsHoisted()) {
         /* JSOP_LAMBDA_ARROW is always preceded by JSOP_THIS. */
         MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW));
-        if (fun->isArrow() && !bce->emit1(JSOP_THIS))
+        if (fun->isArrow() && !emit1(JSOP_THIS))
             return false;
         if (needsProto) {
             MOZ_ASSERT(pn->getOp() == JSOP_LAMBDA);
             pn->setOp(JSOP_FUNWITHPROTO);
         }
-        return bce->emitIndex32(pn->getOp(), index);
+        return emitIndex32(pn->getOp(), index);
     }
 
     MOZ_ASSERT(!needsProto);
 
     /*
      * For a script we emit the code as we parse. Thus the bytecode for
      * top-level functions should go in the prolog to predefine their
      * names in the variable object before the already-generated main code
      * is executed. This extra work for top-level scripts is not necessary
      * when we emit the code for a function. It is fully parsed prior to
      * invocation of the emitter and calls to emitTree for function
      * definitions can be scheduled before generating the rest of code.
      */
-    if (!bce->sc->isFunctionBox()) {
+    if (!sc->isFunctionBox()) {
         MOZ_ASSERT(pn->pn_cookie.isFree());
         MOZ_ASSERT(pn->getOp() == JSOP_NOP);
-        MOZ_ASSERT(!bce->topStmt);
-        bce->switchToProlog();
-        if (!bce->emitIndex32(JSOP_DEFFUN, index))
-            return false;
-        if (!bce->updateSourceCoordNotes(pn->pn_pos.begin))
-            return false;
-        bce->switchToMain();
+        MOZ_ASSERT(!topStmt);
+        switchToProlog();
+        if (!emitIndex32(JSOP_DEFFUN, index))
+            return false;
+        if (!updateSourceCoordNotes(pn->pn_pos.begin))
+            return false;
+        switchToMain();
     } else {
 #ifdef DEBUG
-        BindingIter bi(bce->script);
+        BindingIter bi(script);
         while (bi->name() != fun->atom())
             bi++;
         MOZ_ASSERT(bi->kind() == Binding::VARIABLE || bi->kind() == Binding::CONSTANT ||
                    bi->kind() == Binding::ARGUMENT);
         MOZ_ASSERT(bi.argOrLocalIndex() < JS_BIT(20));
 #endif
         pn->pn_index = index;
-        if (!bce->emitIndexOp(JSOP_LAMBDA, index))
+        if (!emitIndexOp(JSOP_LAMBDA, index))
             return false;
         MOZ_ASSERT(pn->getOp() == JSOP_GETLOCAL || pn->getOp() == JSOP_GETARG);
         JSOp setOp = pn->getOp() == JSOP_GETLOCAL ? JSOP_SETLOCAL : JSOP_SETARG;
-        if (!bce->emitVarOp(pn, setOp))
-            return false;
-        if (!bce->emit1(JSOP_POP))
+        if (!emitVarOp(pn, setOp))
+            return false;
+        if (!emit1(JSOP_POP))
             return false;
     }
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitDo(ParseNode *pn)
 {
     /* Emit an annotated nop so IonBuilder can recognize the 'do' loop. */
-    ptrdiff_t noteIndex = NewSrcNote(cx, this, SRC_WHILE);
+    ptrdiff_t noteIndex = newSrcNote(SRC_WHILE);
     if (noteIndex < 0 || !emit1(JSOP_NOP))
         return false;
 
-    ptrdiff_t noteIndex2 = NewSrcNote(cx, this, SRC_WHILE);
+    ptrdiff_t noteIndex2 = newSrcNote(SRC_WHILE);
     if (noteIndex2 < 0)
         return false;
 
     /* Compile the loop body. */
     ptrdiff_t top = offset();
     if (!emitLoopHead(pn->pn_left))
         return false;
 
     LoopStmtInfo stmtInfo(cx);
-    PushLoopStatement(this, &stmtInfo, STMT_DO_LOOP, top);
+    pushLoopStatement(&stmtInfo, STMT_DO_LOOP, top);
 
     if (!emitLoopEntry(nullptr))
         return false;
 
     if (!emitTree(pn->pn_left))
         return false;
 
     /* Set loop and enclosing label update offsets, for continue. */
@@ -5468,19 +5403,19 @@ BytecodeEmitter::emitWhile(ParseNode *pn
      *  =    ===============                 ==================
      *  0    ifeq-pass                       goto; ifne-fail
      *  1    ifeq-fail; goto; ifne-pass      goto; ifne-pass; ifne-fail
      *  2    2*(ifeq-fail; goto); ifeq-pass  goto; 2*ifne-pass; ifne-fail
      *  . . .
      *  N    N*(ifeq-fail; goto); ifeq-pass  goto; N*ifne-pass; ifne-fail
      */
     LoopStmtInfo stmtInfo(cx);
-    PushLoopStatement(this, &stmtInfo, STMT_WHILE_LOOP, top);
-
-    ptrdiff_t noteIndex = NewSrcNote(cx, this, SRC_WHILE);
+    pushLoopStatement(&stmtInfo, STMT_WHILE_LOOP, top);
+
+    ptrdiff_t noteIndex = newSrcNote(SRC_WHILE);
     if (noteIndex < 0)
         return false;
 
     ptrdiff_t jmp = emitJump(JSOP_GOTO, 0);
     if (jmp < 0)
         return false;
 
     top = offset();
@@ -5544,20 +5479,20 @@ BytecodeEmitter::emitContinue(PropertyNa
     } else {
         while (!stmt->isLoop())
             stmt = stmt->down;
     }
 
     return emitGoto(stmt, &stmt->continues, SRC_CONTINUE) >= 0;
 }
 
-static bool
-InTryBlockWithFinally(BytecodeEmitter *bce)
-{
-    for (StmtInfoBCE *stmt = bce->topStmt; stmt; stmt = stmt->down) {
+bool
+BytecodeEmitter::inTryBlockWithFinally()
+{
+    for (StmtInfoBCE *stmt = topStmt; stmt; stmt = stmt->down) {
         if (stmt->type == STMT_FINALLY)
             return true;
     }
     return false;
 }
 
 bool
 BytecodeEmitter::emitReturn(ParseNode *pn)
@@ -5596,17 +5531,17 @@ BytecodeEmitter::emitReturn(ParseNode *p
      * In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an
      * extra JSOP_RETRVAL after the fixups.
      */
     ptrdiff_t top = offset();
 
     bool isGenerator = sc->isFunctionBox() && sc->asFunctionBox()->isGenerator();
     bool useGenRVal = false;
     if (isGenerator) {
-        if (sc->asFunctionBox()->isStarGenerator() && InTryBlockWithFinally(this)) {
+        if (sc->asFunctionBox()->isStarGenerator() && inTryBlockWithFinally()) {
             // Emit JSOP_SETALIASEDVAR .genrval to store the return value on the
             // scope chain, so it's not lost when we yield in a finally block.
             useGenRVal = true;
             MOZ_ASSERT(pn->pn_right);
             if (!emitTree(pn->pn_right))
                 return false;
             if (!emit1(JSOP_POP))
                 return false;
@@ -5614,35 +5549,35 @@ BytecodeEmitter::emitReturn(ParseNode *p
             if (!emit1(JSOP_SETRVAL))
                 return false;
         }
     } else {
         if (!emit1(JSOP_RETURN))
             return false;
     }
 
-    NonLocalExitScope nle(cx, this);
+    NonLocalExitScope nle(this);
 
     if (!nle.prepareForNonLocalJump(nullptr))
         return false;
 
     if (isGenerator) {
         ScopeCoordinate sc;
         // We know that .generator and .genrval are on the top scope chain node,
         // as we just exited nested scopes.
         sc.setHops(0);
         if (useGenRVal) {
-            MOZ_ALWAYS_TRUE(LookupAliasedNameSlot(this, script, cx->names().dotGenRVal, &sc));
+            MOZ_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().dotGenRVal, &sc));
             if (!emitAliasedVarOp(JSOP_GETALIASEDVAR, sc, DontCheckLexical))
                 return false;
             if (!emit1(JSOP_SETRVAL))
                 return false;
         }
 
-        MOZ_ALWAYS_TRUE(LookupAliasedNameSlot(this, script, cx->names().dotGenerator, &sc));
+        MOZ_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().dotGenerator, &sc));
         if (!emitAliasedVarOp(JSOP_GETALIASEDVAR, sc, DontCheckLexical))
             return false;
         if (!emitYieldOp(JSOP_FINALYIELDRVAL))
             return false;
     } else if (top + static_cast<ptrdiff_t>(JSOP_RETURN_LENGTH) != offset()) {
         code()[top] = JSOP_SETRVAL;
         if (!emit1(JSOP_RETRVAL))
             return false;
@@ -5708,17 +5643,17 @@ BytecodeEmitter::emitYieldStar(ParseNode
 
     ptrdiff_t initialSend = -1;
     if (!emitBackPatchOp(&initialSend))                          // goto initialSend
         return false;
 
     // Try prologue.                                             // ITER RESULT
     StmtInfoBCE stmtInfo(cx);
     pushStatement(&stmtInfo, STMT_TRY, offset());
-    ptrdiff_t noteIndex = NewSrcNote(cx, this, SRC_TRY);
+    ptrdiff_t noteIndex = newSrcNote(SRC_TRY);
     ptrdiff_t tryStart = offset();                               // tryStart:
     if (noteIndex < 0 || !emit1(JSOP_TRY))
         return false;
     MOZ_ASSERT(this->stackDepth == depth);
 
     // Load the generator object.
     if (!emitTree(gen))                                          // ITER RESULT GENOBJ
         return false;
@@ -5885,17 +5820,17 @@ BytecodeEmitter::emitStatement(ParseNode
     bool useful = false;
     if (sc->isFunctionBox())
         MOZ_ASSERT(!script->noScriptRval());
     else
         useful = wantval = !script->noScriptRval();
 
     /* Don't eliminate expressions with side effects. */
     if (!useful) {
-        if (!CheckSideEffects(cx, this, pn2, &useful))
+        if (!checkSideEffects(pn2, &useful))
             return false;
 
         /*
          * Don't eliminate apparently useless expressions if they are
          * labeled expression statements.  The pc->topStmt->update test
          * catches the case where we are nesting in emitTree for a labeled
          * compound statement.
          */
@@ -5982,17 +5917,17 @@ BytecodeEmitter::emitDelete(ParseNode *p
       }
       default:
       {
         /*
          * If useless, just emit JSOP_TRUE; otherwise convert delete foo()
          * to foo(), true (a comma expression).
          */
         bool useful = false;
-        if (!CheckSideEffects(cx, this, pn2, &useful))
+        if (!checkSideEffects(pn2, &useful))
             return false;
 
         if (useful) {
             MOZ_ASSERT_IF(pn2->isKind(PNK_CALL), !(pn2->pn_xflags & PNX_SETCALL));
             if (!emitTree(pn2))
                 return false;
             if (!emit1(JSOP_POP))
                 return false;
@@ -6356,73 +6291,77 @@ BytecodeEmitter::emitIncOrDec(ParseNode 
                 if (!emit1(binop))
                     return false;
             }
         }
     }
     return true;
 }
 
-/*
- * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
- * the comment on EmitSwitch.
- */
-MOZ_NEVER_INLINE static bool
-EmitLabeledStatement(ExclusiveContext *cx, BytecodeEmitter *bce, const LabeledStatement *pn)
+// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
+// the comment on emitSwitch.
+MOZ_NEVER_INLINE bool
+BytecodeEmitter::emitLabeledStatement(const LabeledStatement *pn)
 {
     /*
      * Emit a JSOP_LABEL instruction. The argument is the offset to the statement
      * following the labeled statement.
      */
     jsatomid index;
-    if (!bce->makeAtomIndex(pn->label(), &index))
-        return false;
-
-    ptrdiff_t top = bce->emitJump(JSOP_LABEL, 0);
+    if (!makeAtomIndex(pn->label(), &index))
+        return false;
+
+    ptrdiff_t top = emitJump(JSOP_LABEL, 0);
     if (top < 0)
         return false;
 
     /* Emit code for the labeled statement. */
     StmtInfoBCE stmtInfo(cx);
-    bce->pushStatement(&stmtInfo, STMT_LABEL, bce->offset());
+    pushStatement(&stmtInfo, STMT_LABEL, offset());
     stmtInfo.label = pn->label();
-    if (!bce->emitTree(pn->statement()))
-        return false;
-    bce->popStatement();
+
+    if (!emitTree(pn->statement()))
+        return false;
+
+    popStatement();
 
     /* Patch the JSOP_LABEL offset. */
-    bce->setJumpOffsetAt(top);
+    setJumpOffsetAt(top);
     return true;
 }
 
-static bool
-EmitSyntheticStatements(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
+bool
+BytecodeEmitter::emitSyntheticStatements(ParseNode *pn, ptrdiff_t top)
 {
     MOZ_ASSERT(pn->isArity(PN_LIST));
+
     StmtInfoBCE stmtInfo(cx);
-    bce->pushStatement(&stmtInfo, STMT_SEQ, top);
+    pushStatement(&stmtInfo, STMT_SEQ, top);
+
     ParseNode *pn2 = pn->pn_head;
     if (pn->pn_xflags & PNX_DESTRUCT)
         pn2 = pn2->pn_next;
+
     for (; pn2; pn2 = pn2->pn_next) {
-        if (!bce->emitTree(pn2))
-            return false;
-    }
-    bce->popStatement();
+        if (!emitTree(pn2))
+            return false;
+    }
+
+    popStatement();
     return true;
 }
 
 bool
 BytecodeEmitter::emitConditionalExpression(ConditionalExpression &conditional)
 {
     /* Emit the condition, then branch if false to the else part. */
     if (!emitTree(&conditional.condition()))
         return false;
 
-    ptrdiff_t noteIndex = NewSrcNote(cx, this, SRC_COND);
+    ptrdiff_t noteIndex = newSrcNote(SRC_COND);
     if (noteIndex < 0)
         return false;
 
     ptrdiff_t beq = emitJump(JSOP_IFEQ, 0);
     if (beq < 0 || !emitTree(&conditional.thenExpression()))
         return false;
 
     /* Jump around else, fixup the branch, emit else, fixup jump. */
@@ -6751,17 +6690,17 @@ BytecodeEmitter::emitDefaults(ParseNode 
             return false;
         if (!emitVarOp(arg, JSOP_GETARG))
             return false;
         if (!emit1(JSOP_UNDEFINED))
             return false;
         if (!emit1(JSOP_STRICTEQ))
             return false;
         // Emit source note to enable ion compilation.
-        if (NewSrcNote(cx, this, SRC_IF) < 0)
+        if (newSrcNote(SRC_IF) < 0)
             return false;
         ptrdiff_t jump = emitJump(JSOP_IFEQ, 0);
         if (jump < 0)
             return false;
         if (!emitTree(arg->expr()))
             return false;
         if (!emitVarOp(arg, JSOP_SETARG))
             return false;
@@ -6836,28 +6775,28 @@ BytecodeEmitter::emitClass(ParseNode *pn
         }
     }
     MOZ_ASSERT(constructor, "For now, no default constructors");
 
     bool savedStrictness = sc->setLocalStrictMode(true);
 
     StmtInfoBCE stmtInfo(cx);
     if (names) {
-        if (!EnterBlockScope(cx, this, &stmtInfo, classNode.scopeObject(), JSOP_UNINITIALIZED))
+        if (!enterBlockScope(&stmtInfo, classNode.scopeObject(), JSOP_UNINITIALIZED))
             return false;
     }
 
     if (heritageExpression) {
         if (!emitTree(heritageExpression))
             return false;
         if (!emit1(JSOP_CLASSHERITAGE))
             return false;
     }
 
-    if (!EmitFunc(cx, this, constructor, !!heritageExpression))
+    if (!emitFunction(constructor, !!heritageExpression))
         return false;
 
     if (heritageExpression) {
         // JSOP_CLASSHERITAGE leaves both prototypes on the stack. After
         // creating the constructor, trickly it to the bottom to make the object.
         if (!emit1(JSOP_SWAP))
             return false;
         if (!emit1(JSOP_OBJWITHPROTO))
@@ -6882,17 +6821,17 @@ BytecodeEmitter::emitClass(ParseNode *pn
         return false;
 
     if (names) {
         // That DEFCONST is never gonna be used, but use it here for logical consistency.
         ParseNode *innerName = names->innerBinding();
         if (!emitLexicalInitialization(innerName, JSOP_DEFCONST))
             return false;
 
-        if (!LeaveNestedScope(cx, this, &stmtInfo))
+        if (!leaveNestedScope(&stmtInfo))
             return false;
 
         ParseNode *outerName = names->outerBinding();
         if (outerName) {
             if (!emitLexicalInitialization(outerName, JSOP_DEFVAR))
                 return false;
             // Only class statements make outer bindings, and they do not leave
             // themselves on the stack.
@@ -6918,17 +6857,17 @@ BytecodeEmitter::emitTree(ParseNode *pn)
     pn->pn_offset = top;
 
     /* Emit notes to tell the current bytecode's source line number. */
     if (!updateLineNumberNotes(pn->pn_pos.begin))
         return false;
 
     switch (pn->getKind()) {
       case PNK_FUNCTION:
-        ok = EmitFunc(cx, this, pn);
+        ok = emitFunction(pn);
         break;
 
       case PNK_ARGSBODY:
       {
         RootedFunction fun(cx, sc->asFunctionBox()->function());
         ParseNode *pnlast = pn->last();
 
         // Carefully emit everything in the right order:
@@ -7095,25 +7034,25 @@ BytecodeEmitter::emitTree(ParseNode *pn)
         ok = emitYield(pn);
         break;
 
       case PNK_STATEMENTLIST:
         ok = emitStatementList(pn, top);
         break;
 
       case PNK_SEQ:
-        ok = EmitSyntheticStatements(cx, this, pn, top);
+        ok = emitSyntheticStatements(pn, top);
         break;
 
       case PNK_SEMI:
         ok = emitStatement(pn);
         break;
 
       case PNK_LABEL:
-        ok = EmitLabeledStatement(cx, this, &pn->as<LabeledStatement>());
+        ok = emitLabeledStatement(&pn->as<LabeledStatement>());
         break;
 
       case PNK_COMMA:
       {
         for (ParseNode *pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
             if (!updateSourceCoordNotes(pn2->pn_pos.begin))
                 return false;
             if (!emitTree(pn2))
@@ -7219,22 +7158,22 @@ BytecodeEmitter::emitTree(ParseNode *pn)
       case PNK_NEW:
       case PNK_TAGGED_TEMPLATE:
       case PNK_CALL:
       case PNK_GENEXP:
         ok = emitCallOrNew(pn);
         break;
 
       case PNK_LEXICALSCOPE:
-        ok = EmitLexicalScope(cx, this, pn);
+        ok = emitLexicalScope(pn);
         break;
 
       case PNK_LETBLOCK:
       case PNK_LETEXPR:
-        ok = EmitLet(cx, this, pn);
+        ok = emitLet(pn);
         break;
 
       case PNK_CONST:
       case PNK_LET:
         ok = emitVariables(pn, InitializeVars);
         break;
 
       case PNK_IMPORT:
@@ -7382,32 +7321,30 @@ AllocSrcNote(ExclusiveContext *cx, SrcNo
     if (!notes.append(dummy)) {
         ReportOutOfMemory(cx);
         return -1;
     }
     return notes.length() - 1;
 }
 
 int
-frontend::NewSrcNote(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type)
-{
-    SrcNotesVector &notes = bce->notes();
-    int index;
-
-    index = AllocSrcNote(cx, notes);
+BytecodeEmitter::newSrcNote(SrcNoteType type)
+{
+    SrcNotesVector &notes = this->notes();
+    int index = AllocSrcNote(cx, notes);
     if (index < 0)
         return -1;
 
     /*
      * Compute delta from the last annotated bytecode's offset.  If it's too
      * big to fit in sn, allocate one or more xdelta notes and reset sn.
      */
-    ptrdiff_t offset = bce->offset();
-    ptrdiff_t delta = offset - bce->lastNoteOffset();
-    bce->current->lastNoteOffset = offset;
+    ptrdiff_t offset = this->offset();
+    ptrdiff_t delta = offset - lastNoteOffset();
+    current->lastNoteOffset = offset;
     if (delta >= SN_DELTA_LIMIT) {
         do {
             ptrdiff_t xdelta = Min(delta, SN_XDELTA_MASK);
             SN_MAKE_XDELTA(&notes[index], xdelta);
             delta -= xdelta;
             index = AllocSrcNote(cx, notes);
             if (index < 0)
                 return -1;
@@ -7416,70 +7353,65 @@ frontend::NewSrcNote(ExclusiveContext *c
 
     /*
      * Initialize type and delta, then allocate the minimum number of notes
      * needed for type's arity.  Usually, we won't need more, but if an offset
      * does take two bytes, setSrcNoteOffset will grow notes.
      */
     SN_MAKE_NOTE(&notes[index], type, delta);
     for (int n = (int)js_SrcNoteSpec[type].arity; n > 0; n--) {
-        if (NewSrcNote(cx, bce, SRC_NULL) < 0)
+        if (newSrcNote(SRC_NULL) < 0)
             return -1;
     }
     return index;
 }
 
 int
-frontend::NewSrcNote2(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset)
-{
-    int index;
-
-    index = NewSrcNote(cx, bce, type);
+BytecodeEmitter::newSrcNote2(SrcNoteType type, ptrdiff_t offset)
+{
+    int index = newSrcNote(type);
     if (index >= 0) {
-        if (!bce->setSrcNoteOffset(index, 0, offset))
+        if (!setSrcNoteOffset(index, 0, offset))
             return -1;
     }
     return index;
 }
 
 int
-frontend::NewSrcNote3(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1,
-            ptrdiff_t offset2)
-{
-    int index;
-
-    index = NewSrcNote(cx, bce, type);
+BytecodeEmitter::newSrcNote3(SrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2)
+{
+    int index = newSrcNote(type);
     if (index >= 0) {
-        if (!bce->setSrcNoteOffset(index, 0, offset1))
+        if (!setSrcNoteOffset(index, 0, offset1))
             return -1;
-        if (!bce->setSrcNoteOffset(index, 1, offset2))
+        if (!setSrcNoteOffset(index, 1, offset2))
             return -1;
     }
     return index;
 }
 
 bool
-frontend::AddToSrcNoteDelta(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta)
+BytecodeEmitter::addToSrcNoteDelta(jssrcnote *sn, ptrdiff_t delta)
 {
     /*
-     * Called only from FinishTakingSrcNotes to add to main script note
+     * Called only from finishTakingSrcNotes to add to main script note
      * deltas, and only by a small positive amount.
      */
-    MOZ_ASSERT(bce->current == &bce->main);
+    MOZ_ASSERT(current == &main);
     MOZ_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);
 
     ptrdiff_t base = SN_DELTA(sn);
     ptrdiff_t limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
     ptrdiff_t newdelta = base + delta;
     if (newdelta < limit) {
         SN_SET_DELTA(sn, newdelta);
     } else {
         jssrcnote xdelta;
         SN_MAKE_XDELTA(&xdelta, delta);
-        if (!(sn = bce->main.notes.insert(sn, xdelta)))
+        if (!main.notes.insert(sn, xdelta))
             return false;
     }
     return true;
 }
 
 bool
 BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offset)
 {
@@ -7520,78 +7452,74 @@ BytecodeEmitter::setSrcNoteOffset(unsign
         *sn++ = (jssrcnote)(SN_4BYTE_OFFSET_FLAG | (offset >> 24));
         *sn++ = (jssrcnote)(offset >> 16);
         *sn++ = (jssrcnote)(offset >> 8);
     }
     *sn = (jssrcnote)offset;
     return true;
 }
 
-/*
- * Finish taking source notes in cx's notePool.
- * If successful, the final source note count is stored in the out outparam.
- */
 bool
-frontend::FinishTakingSrcNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t *out)
-{
-    MOZ_ASSERT(bce->current == &bce->main);
-
-    unsigned prologCount = bce->prolog.notes.length();
-    if (prologCount && bce->prolog.currentLine != bce->firstLine) {
-        bce->switchToProlog();
-        if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)bce->firstLine) < 0)
-            return false;
-        bce->switchToMain();
+BytecodeEmitter::finishTakingSrcNotes(uint32_t *out)
+{
+    MOZ_ASSERT(current == &main);
+
+    unsigned prologCount = prolog.notes.length();
+    if (prologCount && prolog.currentLine != firstLine) {
+        switchToProlog();
+        if (newSrcNote2(SRC_SETLINE, ptrdiff_t(firstLine)) < 0)
+            return false;
+        switchToMain();
     } else {
         /*
          * Either no prolog srcnotes, or no line number change over prolog.
          * We don't need a SRC_SETLINE, but we may need to adjust the offset
          * of the first main note, by adding to its delta and possibly even
          * prepending SRC_XDELTA notes to it to account for prolog bytecodes
          * that came at and after the last annotated bytecode.
          */
-        ptrdiff_t offset = bce->prologOffset() - bce->prolog.lastNoteOffset;
+        ptrdiff_t offset = prologOffset() - prolog.lastNoteOffset;
         MOZ_ASSERT(offset >= 0);
-        if (offset > 0 && bce->main.notes.length() != 0) {
+        if (offset > 0 && main.notes.length() != 0) {
             /* NB: Use as much of the first main note's delta as we can. */
-            jssrcnote *sn = bce->main.notes.begin();
+            jssrcnote *sn = main.notes.begin();
             ptrdiff_t delta = SN_IS_XDELTA(sn)
                             ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
                             : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
             if (offset < delta)
                 delta = offset;
             for (;;) {
-                if (!AddToSrcNoteDelta(cx, bce, sn, delta))
+                if (!addToSrcNoteDelta(sn, delta))
                     return false;
                 offset -= delta;
                 if (offset == 0)
                     break;
                 delta = Min(offset, SN_XDELTA_MASK);
-                sn = bce->main.notes.begin();
+                sn = main.notes.begin();
             }
         }
     }
 
     // The prolog count might have changed, so we can't reuse prologCount.
     // The + 1 is to account for the final SN_MAKE_TERMINATOR that is appended
     // when the notes are copied to their final destination by CopySrcNotes.
-    *out = bce->prolog.notes.length() + bce->main.notes.length() + 1;
+    *out = prolog.notes.length() + main.notes.length() + 1;
     return true;
 }
 
 void
-frontend::CopySrcNotes(BytecodeEmitter *bce, jssrcnote *destination, uint32_t nsrcnotes)
-{
-    unsigned prologCount = bce->prolog.notes.length();
-    unsigned mainCount = bce->main.notes.length();
+BytecodeEmitter::copySrcNotes(jssrcnote *destination, uint32_t nsrcnotes)
+{
+    unsigned prologCount = prolog.notes.length();
+    unsigned mainCount = main.notes.length();
     unsigned totalCount = prologCount + mainCount;
     MOZ_ASSERT(totalCount == nsrcnotes - 1);
     if (prologCount)
-        PodCopy(destination, bce->prolog.notes.begin(), prologCount);
-    PodCopy(destination + prologCount, bce->main.notes.begin(), mainCount);
+        PodCopy(destination, prolog.notes.begin(), prologCount);
+    PodCopy(destination + prologCount, main.notes.begin(), mainCount);
     SN_MAKE_TERMINATOR(&destination[totalCount]);
 }
 
 void
 CGConstList::finish(ConstArray *array)
 {
     MOZ_ASSERT(length() == array->length);
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -78,16 +78,17 @@ 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 prologLength);
 };
 
+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 EmitVariables and the destructuring functions how emit the
@@ -264,36 +265,110 @@ struct BytecodeEmitter
     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);
+
+    bool inTryBlockWithFinally();
+
+#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. Return -1 if out of
+    // memory.
+    int newSrcNote(SrcNoteType type);
+    int newSrcNote2(SrcNoteType type, ptrdiff_t offset);
+    int newSrcNote3(SrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2);
+
+    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);
 
     // Emit code for the tree rooted at pn.
     bool emitTree(ParseNode *pn);
 
+    // Emit function code for the tree rooted at body.
+    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);
     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);
+
+    // Return the enclosing lexical scope, which is the innermost enclosing static
+    // block object or compiler created function.
+    JSObject *enclosingStaticScope();
+
+    // Compute the number of nested scope objects that will actually be on the
+    // scope chain at runtime, given the current staticScope.
+    unsigned dynamicNestedScopeDepth();
+
+    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<StaticBlockObject *> blockObj);
+
+    bool lookupAliasedName(HandleScript script, PropertyName *name, uint32_t *pslot,
+                           ParseNode *pn = nullptr);
+    bool lookupAliasedNameSlot(PropertyName *name, ScopeCoordinate *sc);
+
+    // Use this function instead of assigning directly to 'hops' to guard for
+    // uint8_t overflows.
+    bool assignHops(ParseNode *pn, unsigned src, ScopeCoordinate *dst);
+
+    // 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<StaticBlockObject *> blockObj);
 
     bool flushPops(int *npops);
 
     ptrdiff_t emitCheck(ptrdiff_t delta);
 
     // Emit one bytecode.
     bool emit1(JSOp op);
 
@@ -348,16 +423,17 @@ struct BytecodeEmitter
     bool emitArray(ParseNode *pn, uint32_t count);
     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 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
@@ -378,16 +454,17 @@ struct BytecodeEmitter
     bool maybeEmitVarDecl(JSOp prologOp, ParseNode *pn, jsatomid *result);
     bool emitVariables(ParseNode *pn, VarEmitOption emitOption, bool isLetExpr = false);
 
     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, JSOp op);
     bool emitPropOp(ParseNode *pn, JSOp op);
     bool emitPropIncDec(ParseNode *pn);
@@ -400,31 +477,51 @@ struct BytecodeEmitter
     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 emitLet(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 prologOp, ParseNode *pn);
+
+    template <DestructuringDeclEmitter EmitName>
+    bool emitDestructuringDeclsWithEmitter(JSOp prologOp, ParseNode *pattern);
+
+    bool emitDestructuringDecls(JSOp prologOp, ParseNode *pattern);
+
+    // Emit code to initialize all destructured names to the value on the top of
+    // the stack.
+    bool emitInitializeDestructuringDecls(JSOp prologOp, ParseNode *pattern);
+
     // 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);
 
@@ -434,16 +531,17 @@ struct BytecodeEmitter
 
     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, ptrdiff_t top);
+    bool emitSyntheticStatements(ParseNode *pn, ptrdiff_t top);
 
     bool emitDelete(ParseNode *pn);
     bool emitLogical(ParseNode *pn);
     bool emitUnary(ParseNode *pn);
 
     MOZ_NEVER_INLINE bool emitIncOrDec(ParseNode *pn);
 
     bool emitConditionalExpression(ConditionalExpression &conditional);
@@ -461,16 +559,19 @@ struct BytecodeEmitter
     bool emitWhile(ParseNode *pn, ptrdiff_t top);
 
     bool emitBreak(PropertyName *label);
     bool emitContinue(PropertyName *label);
 
     bool emitDefaults(ParseNode *pn);
     bool emitLexicalInitialization(ParseNode *pn, JSOp globalDefOp);
 
+    bool pushInitialConstants(JSOp op, unsigned n);
+    bool initializeBlockScopedLocalsFromStack(Handle<StaticBlockObject *> blockObj);
+
     // 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();
 
@@ -482,44 +583,12 @@ struct BytecodeEmitter
     //
     // Please refer the comment above emitSpread for additional information about
     // stack convention.
     bool emitForOf(StmtType type, ParseNode *pn, ptrdiff_t top);
 
     bool emitClass(ParseNode *pn);
 };
 
-/*
- * Emit function code using bce for the tree rooted at body.
- */
-bool
-EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *body);
-
-/*
- * Append a new source note of the given type (and therefore size) to bce's
- * notes dynamic array, updating bce->noteCount. Return the new note's index
- * within the array pointed at by bce->current->notes. Return -1 if out of
- * memory.
- */
-int
-NewSrcNote(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type);
-
-int
-NewSrcNote2(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset);
-
-int
-NewSrcNote3(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1,
-               ptrdiff_t offset2);
-
-/* NB: this function can add at most one extra extended delta note. */
-bool
-AddToSrcNoteDelta(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta);
-
-bool
-FinishTakingSrcNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t *out);
-
-void
-CopySrcNotes(BytecodeEmitter *bce, jssrcnote *destination, uint32_t nsrcnotes);
-
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_BytecodeEmitter_h */
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2561,17 +2561,17 @@ JSScript::fullyInitFromEmitter(Exclusive
     /* The counts of indexed things must be checked during code generation. */
     MOZ_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT);
     MOZ_ASSERT(bce->objectList.length <= INDEX_LIMIT);
     MOZ_ASSERT(bce->regexpList.length <= INDEX_LIMIT);
 
     uint32_t mainLength = bce->offset();
     uint32_t prologLength = bce->prologOffset();
     uint32_t nsrcnotes;
-    if (!FinishTakingSrcNotes(cx, bce, &nsrcnotes))
+    if (!bce->finishTakingSrcNotes(&nsrcnotes))
         return false;
     uint32_t natoms = bce->atomIndices->count();
     if (!partiallyInit(cx, script,
                        bce->constList.length(), bce->objectList.length, bce->regexpList.length,
                        bce->tryNoteList.length(), bce->blockScopeList.length(),
                        bce->yieldOffsetList.length(), bce->typesetCount))
     {
         return false;
@@ -2586,17 +2586,17 @@ JSScript::fullyInitFromEmitter(Exclusive
     script->natoms_ = natoms;
     SharedScriptData *ssd = SharedScriptData::new_(cx, script->length(), nsrcnotes, natoms);
     if (!ssd)
         return false;
 
     jsbytecode *code = ssd->data;
     PodCopy<jsbytecode>(code, bce->prolog.code.begin(), prologLength);
     PodCopy<jsbytecode>(code + prologLength, bce->code().begin(), mainLength);
-    CopySrcNotes(bce, (jssrcnote *)(code + script->length()), nsrcnotes);
+    bce->copySrcNotes((jssrcnote *)(code + script->length()), nsrcnotes);
     InitAtomMap(bce->atomIndices.getMap(), ssd->atoms());
 
     if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes))
         return false;
 
     FunctionBox *funbox = bce->sc->isFunctionBox() ? bce->sc->asFunctionBox() : nullptr;
 
     if (bce->constList.length() != 0)