Bug 1473796 - Part 4: Add FunctionEmitter, FunctionScriptEmitter, and FunctionParamsEmitter. r=jorendorff
authorTooru Fujisawa <arai_a@mac.com>
Tue, 12 Mar 2019 06:15:23 +0000
changeset 521491 2429f6898afa
parent 521490 7e1e3f35d7cf
child 521492 f3448180f267
push id10866
push usernerli@mozilla.com
push dateTue, 12 Mar 2019 18:59:09 +0000
treeherdermozilla-beta@445c24a51727 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1473796
milestone67.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 1473796 - Part 4: Add FunctionEmitter, FunctionScriptEmitter, and FunctionParamsEmitter. r=jorendorff FunctionEmitter is for function declaration/expression/method/etc in the enclosing script. FunctionScriptEmitter is for function script, including body and class fields, excluding parameters. FunctionParamsEmitter is for function parameters. Differential Revision: https://phabricator.services.mozilla.com/D19622
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FunctionEmitter.cpp
js/src/frontend/FunctionEmitter.h
js/src/frontend/moz.build
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -32,16 +32,17 @@
 #include "frontend/DefaultEmitter.h"  // DefaultEmitter
 #include "frontend/DoWhileEmitter.h"
 #include "frontend/ElemOpEmitter.h"
 #include "frontend/EmitterScope.h"
 #include "frontend/ExpressionStatementEmitter.h"
 #include "frontend/ForInEmitter.h"
 #include "frontend/ForOfEmitter.h"
 #include "frontend/ForOfLoopControl.h"
+#include "frontend/FunctionEmitter.h"  // FunctionEmitter, FunctionScriptEmitter, FunctionParamsEmitter
 #include "frontend/IfEmitter.h"
 #include "frontend/LabelEmitter.h"         // LabelEmitter
 #include "frontend/LexicalScopeEmitter.h"  // LexicalScopeEmitter
 #include "frontend/ModuleSharedContext.h"
 #include "frontend/NameOpEmitter.h"
 #include "frontend/ObjectEmitter.h"  // PropertyEmitter, ObjectEmitter, ClassEmitter
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
@@ -2447,146 +2448,65 @@ bool BytecodeEmitter::emitScript(ParseNo
     return false;
   }
 
   tellDebuggerAboutCompiledScript(cx);
 
   return true;
 }
 
-bool BytecodeEmitter::emitInitializeInstanceFields() {
-  MOZ_ASSERT(fieldInitializers_.valid);
-  size_t numFields = fieldInitializers_.numFieldInitializers;
-
-  if (numFields == 0) {
-    return true;
-  }
-
-  if (!emitGetName(cx->names().dotInitializers)) {
-    //              [stack] ARRAY
-    return false;
-  }
-
-  for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
-    if (fieldIndex < numFields - 1) {
-      // We DUP to keep the array around (it is consumed in the bytecode below)
-      // for next iterations of this loop, except for the last iteration, which
-      // avoids an extra POP at the end of the loop.
-      if (!emit1(JSOP_DUP)) {
-        //          [stack] ARRAY ARRAY
-        return false;
-      }
-    }
-
-    if (!emitNumberOp(fieldIndex)) {
-      //            [stack] ARRAY? ARRAY INDEX
-      return false;
-    }
-
-    if (!emit1(JSOP_CALLELEM)) {
-      //            [stack] ARRAY? FUNC
-      return false;
-    }
-
-    // This is guaranteed to run after super(), so we don't need TDZ checks.
-    if (!emitGetName(cx->names().dotThis)) {
-      //            [stack] ARRAY? FUNC THIS
-      return false;
-    }
-
-    if (!emitCall(JSOP_CALL_IGNORES_RV, 0)) {
-      //            [stack] ARRAY? RVAL
-      return false;
-    }
-
-    if (!emit1(JSOP_POP)) {
-      //            [stack] ARRAY?
-      return false;
-    }
-  }
-
-  return true;
-}
-
 bool BytecodeEmitter::emitFunctionScript(FunctionNode* funNode,
                                          TopLevelFunction isTopLevel) {
   MOZ_ASSERT(inPrologue());
-  ParseNode* body = funNode->body();
-  MOZ_ASSERT(body->isKind(ParseNodeKind::ParamsBody));
+  ListNode* paramsBody = &funNode->body()->as<ListNode>();
+  MOZ_ASSERT(paramsBody->isKind(ParseNodeKind::ParamsBody));
   FunctionBox* funbox = sc->asFunctionBox();
   AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission,
                                 parser->errorReporter(), funbox);
 
-  setScriptStartOffsetIfUnset(body->pn_pos.begin);
-
-  // The ordering of these EmitterScopes is important. The named lambda
-  // scope needs to enclose the function scope needs to enclose the extra
-  // var scope.
-
-  Maybe<EmitterScope> namedLambdaEmitterScope;
-  if (funbox->namedLambdaBindings()) {
-    namedLambdaEmitterScope.emplace(this);
-    if (!namedLambdaEmitterScope->enterNamedLambda(this, funbox)) {
-      return false;
-    }
-  }
-
-  /*
-   * Mark the script so that initializers created within it may be given more
-   * precise types.
-   */
-  if (isRunOnceLambda()) {
-    script->setTreatAsRunOnce();
-    MOZ_ASSERT(!script->hasRunOnce());
-  }
-
-  setFunctionBodyEndPos(body->pn_pos.end);
-  if (!emitTree(body)) {
-    return false;
-  }
-
-  if (!updateSourceCoordNotes(body->pn_pos.end)) {
-    return false;
-  }
-
-  // We only want to mark the end of a function as a breakable position if
-  // there is token there that the user can easily associate with the function
-  // as a whole. Since arrow function single-expression bodies have no closing
-  // curly bracket, we do not place a breakpoint at their end position.
-  if (!funbox->hasExprBody()) {
-    if (!markSimpleBreakpoint()) {
-      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 (!emit1(JSOP_RETRVAL)) {
-    return false;
-  }
-
-  if (namedLambdaEmitterScope) {
-    if (!namedLambdaEmitterScope->leave(this)) {
-      return false;
-    }
-    namedLambdaEmitterScope.reset();
+  setScriptStartOffsetIfUnset(paramsBody->pn_pos.begin);
+
+  //                [stack]
+
+  FunctionScriptEmitter fse(this, funbox, Some(paramsBody->pn_pos.begin),
+                            Some(paramsBody->pn_pos.end));
+  if (!fse.prepareForParameters()) {
+    //              [stack]
+    return false;
+  }
+
+  if (!emitFunctionFormalParameters(paramsBody)) {
+    //              [stack]
+    return false;
+  }
+
+  if (!fse.prepareForBody()) {
+    //              [stack]
+    return false;
+  }
+
+  if (!emitTree(paramsBody->last())) {
+    //              [stack]
+    return false;
+  }
+
+  if (!fse.emitEndBody()) {
+    //              [stack]
+    return false;
   }
 
   if (isTopLevel == TopLevelFunction::Yes) {
     if (!NameFunctions(cx, funNode)) {
       return false;
     }
   }
 
-  if (!JSScript::fullyInitFromEmitter(cx, script, this)) {
-    return false;
-  }
-
-  tellDebuggerAboutCompiledScript(cx);
+  if (!fse.initScript()) {
+    return false;
+  }
 
   return true;
 }
 
 bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target,
                                               size_t* emitted) {
   *emitted = 0;
 
@@ -5665,228 +5585,100 @@ bool BytecodeEmitter::emitFor(ForNode* f
   MOZ_ASSERT(forNode->head()->isKind(ParseNodeKind::ForOf));
   return emitForOf(forNode, headLexicalEmitterScope);
 }
 
 MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(FunctionNode* funNode,
                                                     bool needsProto) {
   FunctionBox* funbox = funNode->funbox();
   RootedFunction fun(cx, funbox->function());
-  RootedAtom name(cx, fun->explicitName());
-  MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
+
+  //                [stack]
+
+  FunctionEmitter fe(this, funbox, funNode->syntaxKind(),
+                     funNode->functionIsHoisted()
+                         ? FunctionEmitter::IsHoisted::Yes
+                         : FunctionEmitter::IsHoisted::No);
 
   // Set the |wasEmitted| flag in the funbox once the function has been
   // emitted. Function definitions that need hoisting to the top of the
   // function will be seen by emitFunction in two places.
   if (funbox->wasEmitted) {
-    // Annex B block-scoped functions are hoisted like any other
-    // block-scoped function to the top of their scope. When their
-    // definitions are seen for the second time, we need to emit the
-    // assignment that assigns the function to the outer 'var' binding.
-    if (funbox->isAnnexB) {
-      // Get the location of the 'var' binding in the body scope. The
-      // name must be found, else there is a bug in the Annex B handling
-      // in Parser.
-      //
-      // In sloppy eval contexts, this location is dynamic.
-      Maybe<NameLocation> lhsLoc =
-          locationOfNameBoundInScope(name, varEmitterScope);
-
-      // If there are parameter expressions, the var name could be a
-      // parameter.
-      if (!lhsLoc && sc->isFunctionBox() &&
-          sc->asFunctionBox()->hasExtraBodyVarScope()) {
-        lhsLoc = locationOfNameBoundInScope(
-            name, varEmitterScope->enclosingInFrame());
-      }
-
-      if (!lhsLoc) {
-        lhsLoc = Some(NameLocation::DynamicAnnexBVar());
-      } else {
-        MOZ_ASSERT(lhsLoc->bindingKind() == BindingKind::Var ||
-                   lhsLoc->bindingKind() == BindingKind::FormalParameter ||
-                   (lhsLoc->bindingKind() == BindingKind::Let &&
-                    sc->asFunctionBox()->hasParameterExprs));
-      }
-
-      NameOpEmitter noe(this, name, *lhsLoc,
-                        NameOpEmitter::Kind::SimpleAssignment);
-      if (!noe.prepareForRhs()) {
-        return false;
-      }
-      if (!emitGetName(name)) {
-        return false;
-      }
-      if (!noe.emitAssignment()) {
-        return false;
-      }
-      if (!emit1(JSOP_POP)) {
-        return false;
-      }
-    }
-
-    MOZ_ASSERT_IF(fun->hasScript(), fun->nonLazyScript());
+    if (!fe.emitAgain()) {
+      //            [stack]
+      return false;
+    }
     MOZ_ASSERT(funNode->functionIsHoisted());
     return true;
   }
 
-  funbox->wasEmitted = true;
-
-  // 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 = checkRunOnceContext();
-    if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton)) {
-      return false;
-    }
-
-    SharedContext* outersc = sc;
     if (fun->isInterpretedLazy()) {
-      funbox->setEnclosingScopeForInnerLazyFunction(innermostScope());
-      if (emittingRunOnceLambda) {
-        fun->lazyScript()->setTreatAsRunOnce();
-      }
-    } else {
-      MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
-
-      // Inherit most things (principals, version, etc) from the
-      // parent.  Use default values for the rest.
-      Rooted<JSScript*> parent(cx, script);
-      MOZ_ASSERT(parent->mutedErrors() == parser->options().mutedErrors());
-      const JS::TransitiveCompileOptions& transitiveOptions = parser->options();
-      JS::CompileOptions options(cx, transitiveOptions);
-
-      Rooted<ScriptSourceObject*> sourceObject(cx, script->sourceObject());
-      Rooted<JSScript*> script(
-          cx, JSScript::Create(cx, options, sourceObject, funbox->bufStart,
-                               funbox->bufEnd, funbox->toStringStart,
-                               funbox->toStringEnd));
-      if (!script) {
-        return false;
-      }
-
-      EmitterMode nestedMode = emitterMode;
-      if (nestedMode == BytecodeEmitter::LazyFunction) {
-        MOZ_ASSERT(lazyScript->isBinAST());
-        nestedMode = BytecodeEmitter::Normal;
-      }
-
-      BytecodeEmitter bce2(this, parser, funbox, script,
-                           /* lazyScript = */ nullptr, funNode->pn_pos,
-                           nestedMode);
-      if (!bce2.init()) {
-        return false;
-      }
-
-      /* We measured the max scope depth when we parsed the function. */
-      if (!bce2.emitFunctionScript(funNode, TopLevelFunction::No)) {
-        return false;
-      }
-
-      if (funbox->isLikelyConstructorWrapper()) {
-        script->setLikelyConstructorWrapper();
-      }
-    }
-
-    if (outersc->isFunctionBox()) {
-      outersc->asFunctionBox()->setHasInnerFunctions();
-    }
-  } else {
-    MOZ_ASSERT(IsAsmJSModule(fun));
-  }
-
-  // Make the function object a literal in the outer script's pool.
-  unsigned index = objectList.add(funNode->funbox());
-
-  // Non-hoisted functions simply emit their respective op.
-  if (!funNode->functionIsHoisted()) {
-    // JSOP_LAMBDA_ARROW is always preceded by a new.target
-    MOZ_ASSERT(fun->isArrow() ==
-               (funNode->syntaxKind() == FunctionSyntaxKind::Arrow));
-
-    if (fun->isArrow()) {
-      if (sc->allowNewTarget()) {
-        if (!emit1(JSOP_NEWTARGET)) {
-          return false;
-        }
-      } else {
-        if (!emit1(JSOP_NULL)) {
-          return false;
-        }
-      }
-    }
-
-    if (needsProto) {
-      MOZ_ASSERT(funNode->syntaxKind() ==
-                 FunctionSyntaxKind::DerivedClassConstructor);
-      return emitIndex32(JSOP_FUNWITHPROTO, index);
-    }
-
-    // This is a FunctionExpression, ArrowFunctionExpression, or class
-    // constructor. Emit the single instruction (without location info).
-    JSOp op = funNode->syntaxKind() == FunctionSyntaxKind::Arrow
-                  ? JSOP_LAMBDA_ARROW
-                  : JSOP_LAMBDA;
-    return emitIndex32(op, index);
-  }
-
-  MOZ_ASSERT(!needsProto);
-
-  bool topLevelFunction;
-  if (sc->isFunctionBox() || (sc->isEvalContext() && sc->strict())) {
-    // No nested functions inside other functions are top-level.
-    topLevelFunction = false;
-  } else {
-    // In sloppy eval scripts, top-level functions in are accessed
-    // dynamically. In global and module scripts, top-level functions are
-    // those bound in the var scope.
-    NameLocation loc = lookupName(name);
-    topLevelFunction = loc.kind() == NameLocation::Kind::Dynamic ||
-                       loc.bindingKind() == BindingKind::Var;
-  }
-
-  if (topLevelFunction) {
-    if (sc->isModuleContext()) {
-      // For modules, we record the function and instantiate the binding
-      // during ModuleInstantiate(), before the script is run.
-
-      RootedModuleObject module(cx, sc->asModuleContext()->module());
-      if (!module->noteFunctionDeclaration(cx, name, fun)) {
-        return false;
-      }
-    } else {
-      MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext());
-      MOZ_ASSERT(funNode->syntaxKind() == FunctionSyntaxKind::Statement);
-      MOZ_ASSERT(inPrologue());
-      if (!emitIndex32(JSOP_LAMBDA, index)) {
-        return false;
-      }
-      if (!emit1(JSOP_DEFFUN)) {
-        return false;
-      }
-    }
-  } else {
-    // For functions nested within functions and blocks, make a lambda and
-    // initialize the binding name of the function in the current scope.
-
-    NameOpEmitter noe(this, name, NameOpEmitter::Kind::Initialize);
-    if (!noe.prepareForRhs()) {
-      return false;
-    }
-    if (!emitIndexOp(JSOP_LAMBDA, index)) {
-      return false;
-    }
-    if (!noe.emitAssignment()) {
-      return false;
-    }
-    if (!emit1(JSOP_POP)) {
-      return false;
-    }
+      if (!fe.emitLazy()) {
+        //          [stack] FUN?
+        return false;
+      }
+
+      return true;
+    }
+
+    if (!fe.prepareForNonLazy()) {
+      //            [stack]
+      return false;
+    }
+
+    // Inherit most things (principals, version, etc) from the
+    // parent.  Use default values for the rest.
+    Rooted<JSScript*> parent(cx, script);
+    MOZ_ASSERT(parent->mutedErrors() == parser->options().mutedErrors());
+    const JS::TransitiveCompileOptions& transitiveOptions = parser->options();
+    JS::CompileOptions options(cx, transitiveOptions);
+
+    Rooted<ScriptSourceObject*> sourceObject(cx, script->sourceObject());
+    Rooted<JSScript*> script(
+        cx, JSScript::Create(cx, options, sourceObject, funbox->bufStart,
+                             funbox->bufEnd, funbox->toStringStart,
+                             funbox->toStringEnd));
+    if (!script) {
+      return false;
+    }
+
+    EmitterMode nestedMode = emitterMode;
+    if (nestedMode == BytecodeEmitter::LazyFunction) {
+      MOZ_ASSERT(lazyScript->isBinAST());
+      nestedMode = BytecodeEmitter::Normal;
+    }
+
+    BytecodeEmitter bce2(this, parser, funbox, script,
+                         /* lazyScript = */ nullptr, funNode->pn_pos,
+                         nestedMode);
+    if (!bce2.init()) {
+      return false;
+    }
+
+    /* We measured the max scope depth when we parsed the function. */
+    if (!bce2.emitFunctionScript(funNode, TopLevelFunction::No)) {
+      return false;
+    }
+
+    if (funbox->isLikelyConstructorWrapper()) {
+      script->setLikelyConstructorWrapper();
+    }
+
+    if (!fe.emitNonLazyEnd()) {
+      //            [stack] FUN?
+      return false;
+    }
+
+    return true;
+  }
+
+  if (!fe.emitAsmJSModule()) {
+    //              [stack]
+    return false;
   }
 
   return true;
 }
 
 bool BytecodeEmitter::emitDo(BinaryNode* doNode) {
   ParseNode* bodyNode = doNode->left();
 
@@ -8307,307 +8099,195 @@ bool BytecodeEmitter::emitTypeof(UnaryNo
 
   if (!emitTree(typeofNode->kid())) {
     return false;
   }
 
   return emit1(op);
 }
 
-bool BytecodeEmitter::emitFunctionFormalParametersAndBody(
-    ListNode* paramsBody) {
-  MOZ_ASSERT(paramsBody->isKind(ParseNodeKind::ParamsBody));
-  MOZ_ASSERT(inPrologue());
-
-  ParseNode* funBody = paramsBody->last();
-  FunctionBox* funbox = sc->asFunctionBox();
-
-  TDZCheckCache tdzCache(this);
-
-  if (funbox->hasParameterExprs) {
-    switchToMain();
-
-    EmitterScope funEmitterScope(this);
-    if (!funEmitterScope.enterFunction(this, funbox)) {
-      return false;
-    }
-
-    if (!emitInitializeFunctionSpecialNames()) {
-      return false;
-    }
-
-    if (!emitFunctionFormalParameters(paramsBody)) {
-      return false;
-    }
-
-    {
-      Maybe<EmitterScope> extraVarEmitterScope;
-
-      if (funbox->hasExtraBodyVarScope()) {
-        extraVarEmitterScope.emplace(this);
-        if (!extraVarEmitterScope->enterFunctionExtraBodyVar(this, funbox)) {
-          return false;
-        }
-
-        // After emitting expressions for all parameters, copy over any
-        // formal parameters which have been redeclared as vars. For
-        // example, in the following, the var y in the body scope is 42:
-        //
-        //   function f(x, y = 42) { var y; }
-        //
-        RootedAtom name(cx);
-        if (funbox->extraVarScopeBindings() &&
-            funbox->functionScopeBindings()) {
-          for (BindingIter bi(*funbox->functionScopeBindings(), true); bi;
-               bi++) {
-            name = bi.name();
-
-            // There may not be a var binding of the same name.
-            if (!locationOfNameBoundInScope(name, extraVarEmitterScope.ptr())) {
-              continue;
-            }
-
-            // The '.this' and '.generator' function special
-            // bindings should never appear in the extra var
-            // scope. 'arguments', however, may.
-            MOZ_ASSERT(name != cx->names().dotThis &&
-                       name != cx->names().dotGenerator);
-
-            NameOpEmitter noe(this, name, NameOpEmitter::Kind::Initialize);
-            if (!noe.prepareForRhs()) {
-              return false;
-            }
-
-            NameLocation paramLoc =
-                *locationOfNameBoundInScope(name, &funEmitterScope);
-            if (!emitGetNameAtLocation(name, paramLoc)) {
-              return false;
-            }
-            if (!noe.emitAssignment()) {
-              return false;
-            }
-            if (!emit1(JSOP_POP)) {
-              return false;
-            }
-          }
-        }
-      }
-
-      if (!emitFunctionBody(funBody)) {
-        return false;
-      }
-
-      if (extraVarEmitterScope && !extraVarEmitterScope->leave(this)) {
-        return false;
-      }
-    }
-
-    return funEmitterScope.leave(this);
-  }
-
-  // No parameter expressions. Enter the function body scope and emit
-  // everything.
-  //
-  // One caveat is that Debugger considers ops in the prologue to be
-  // unreachable (i.e. cannot set a breakpoint on it). If there are no
-  // parameter exprs, any unobservable environment ops (like pushing the
-  // call object, setting '.this', etc) need to go in the prologue, else it
-  // messes up breakpoint tests.
-  EmitterScope emitterScope(this);
-
-  if (!emitterScope.enterFunction(this, funbox)) {
-    return false;
-  }
-
-  if (!emitInitializeFunctionSpecialNames()) {
-    return false;
-  }
-  switchToMain();
-
-  if (!emitFunctionFormalParameters(paramsBody)) {
-    return false;
-  }
-
-  if (!emitFunctionBody(funBody)) {
-    return false;
-  }
-
-  return emitterScope.leave(this);
-}
-
 bool BytecodeEmitter::emitFunctionFormalParameters(ListNode* paramsBody) {
   ParseNode* funBody = paramsBody->last();
   FunctionBox* funbox = sc->asFunctionBox();
-  EmitterScope* funScope = innermostEmitterScope();
-
-  bool hasParameterExprs = funbox->hasParameterExprs;
+
   bool hasRest = funbox->hasRest();
 
-  // Parameters can't reuse the reject try-catch block from the function body,
-  // because the body may have pushed an additional var-environment. This
-  // messes up scope resolution for the |.generator| variable, because we'd
-  // need different hops to reach |.generator| depending on whether the error
-  // was thrown from the parameters or the function body.
-  Maybe<TryEmitter> rejectTryCatch;
-  if (hasParameterExprs && funbox->needsPromiseResult()) {
-    if (!emitAsyncFunctionRejectPrologue(rejectTryCatch)) {
-      return false;
-    }
-  }
-
-  uint16_t argSlot = 0;
+  FunctionParamsEmitter fpe(this, funbox);
   for (ParseNode* arg = paramsBody->head(); arg != funBody;
-       arg = arg->pn_next, argSlot++) {
+       arg = arg->pn_next) {
     ParseNode* bindingElement = arg;
     ParseNode* initializer = nullptr;
     if (arg->isKind(ParseNodeKind::AssignExpr)) {
       bindingElement = arg->as<AssignmentNode>().left();
       initializer = arg->as<AssignmentNode>().right();
     }
+    bool hasInitializer = !!initializer;
+    bool isRest = hasRest && arg->pn_next == funBody;
+    bool isDestructuring = !bindingElement->isKind(ParseNodeKind::Name);
 
     // Left-hand sides are either simple names or destructuring patterns.
     MOZ_ASSERT(bindingElement->isKind(ParseNodeKind::Name) ||
                bindingElement->isKind(ParseNodeKind::ArrayExpr) ||
                bindingElement->isKind(ParseNodeKind::ObjectExpr));
 
-    // The rest parameter doesn't have an initializer.
-    bool isRest = hasRest && arg->pn_next == funBody;
-    MOZ_ASSERT_IF(isRest, !initializer);
-
-    bool isDestructuring = !bindingElement->isKind(ParseNodeKind::Name);
-
-    // ES 14.1.19 says if BindingElement contains an expression in the
-    // production FormalParameter : BindingElement, it is evaluated in a
-    // new var environment. This is needed to prevent vars from escaping
-    // direct eval in parameter expressions.
-    Maybe<EmitterScope> paramExprVarScope;
-    if (funbox->hasDirectEvalInParameterExpr &&
-        (isDestructuring || initializer)) {
-      paramExprVarScope.emplace(this);
-      if (!paramExprVarScope->enterParameterExpressionVar(this)) {
-        return false;
-      }
-    }
-
-    // First push the RHS if there is a default expression or if it is
-    // rest.
-
-    if (initializer) {
-      // If we have an initializer, emit the initializer and assign it
-      // to the argument slot. TDZ is taken care of afterwards.
-      MOZ_ASSERT(hasParameterExprs);
-
-      if (!emitArgOp(JSOP_GETARG, argSlot)) {
-        //          [stack] ARG
-        return false;
-      }
-
-      if (!emitDefault(initializer, bindingElement)) {
-        //          [stack] ARG/DEFAULT
-        return false;
-      }
-    } else if (isRest) {
-      if (!emit1(JSOP_REST)) {
-        return false;
-      }
-    }
-
-    // Initialize the parameter name.
-
-    if (isDestructuring) {
-      // If we had an initializer or the rest parameter, the value is
-      // already on the stack.
-      if (!initializer && !isRest && !emitArgOp(JSOP_GETARG, argSlot)) {
-        return false;
-      }
+    auto emitDefaultInitializer = [this, &initializer, &bindingElement]() {
+      //            [stack]
+
+      if (!this->emitInitializer(initializer, bindingElement)) {
+        //          [stack] DEFAULT
+        return false;
+      }
+      return true;
+    };
+
+    auto emitDestructuring = [this, &fpe, &bindingElement]() {
+      //            [stack] ARG
 
       // If there's an parameter expression var scope, the destructuring
       // declaration needs to initialize the name in the function scope,
       // which is not the innermost scope.
-      if (!emitDestructuringOps(
-              &bindingElement->as<ListNode>(),
-              paramExprVarScope ? DestructuringFlavor::FormalParameterInVarScope
-                                : DestructuringFlavor::Declaration)) {
-        return false;
-      }
-
-      if (!emit1(JSOP_POP)) {
-        return false;
-      }
-    } else if (hasParameterExprs || isRest) {
+      if (!this->emitDestructuringOps(&bindingElement->as<ListNode>(),
+                                      fpe.getDestructuringFlavor())) {
+        //          [stack] ARG
+        return false;
+      }
+
+      return true;
+    };
+
+    if (isRest) {
+      if (isDestructuring) {
+        if (!fpe.prepareForDestructuringRest()) {
+          //        [stack]
+          return false;
+        }
+        if (!emitDestructuring()) {
+          //        [stack]
+          return false;
+        }
+        if (!fpe.emitDestructuringRestEnd()) {
+          //        [stack]
+          return false;
+        }
+      } else {
+        RootedAtom paramName(cx, bindingElement->as<NameNode>().name());
+        if (!fpe.emitRest(paramName)) {
+          //        [stack]
+          return false;
+        }
+      }
+
+      continue;
+    }
+
+    if (isDestructuring) {
+      if (hasInitializer) {
+        if (!fpe.prepareForDestructuringDefaultInitializer()) {
+          //        [stack]
+          return false;
+        }
+        if (!emitDefaultInitializer()) {
+          //        [stack]
+          return false;
+        }
+        if (!fpe.prepareForDestructuringDefault()) {
+          //        [stack]
+          return false;
+        }
+        if (!emitDestructuring()) {
+          //        [stack]
+          return false;
+        }
+        if (!fpe.emitDestructuringDefaultEnd()) {
+          //        [stack]
+          return false;
+        }
+      } else {
+        if (!fpe.prepareForDestructuring()) {
+          //        [stack]
+          return false;
+        }
+        if (!emitDestructuring()) {
+          //        [stack]
+          return false;
+        }
+        if (!fpe.emitDestructuringEnd()) {
+          //        [stack]
+          return false;
+        }
+      }
+
+      continue;
+    }
+
+    if (hasInitializer) {
+      if (!fpe.prepareForDefault()) {
+        //          [stack]
+        return false;
+      }
+      if (!emitDefaultInitializer()) {
+        //          [stack]
+        return false;
+      }
       RootedAtom paramName(cx, bindingElement->as<NameNode>().name());
-      NameLocation paramLoc = *locationOfNameBoundInScope(paramName, funScope);
-      NameOpEmitter noe(this, paramName, paramLoc,
-                        NameOpEmitter::Kind::Initialize);
-      if (!noe.prepareForRhs()) {
-        return false;
-      }
-      if (hasParameterExprs) {
-        // If we had an initializer or a rest parameter, the value is
-        // already on the stack.
-        if (!initializer && !isRest) {
-          if (!emitArgOp(JSOP_GETARG, argSlot)) {
-            return false;
-          }
-        }
-      }
-      if (!noe.emitAssignment()) {
-        return false;
-      }
-      if (!emit1(JSOP_POP)) {
-        return false;
-      }
-    }
-
-    if (paramExprVarScope) {
-      if (!paramExprVarScope->leave(this)) {
-        return false;
-      }
-    }
-  }
-
-  if (rejectTryCatch) {
-    if (!emitAsyncFunctionRejectEpilogue(*rejectTryCatch)) {
+      if (!fpe.emitDefaultEnd(paramName)) {
+        //          [stack]
+        return false;
+      }
+
+      continue;
+    }
+
+    RootedAtom paramName(cx, bindingElement->as<NameNode>().name());
+    if (!fpe.emitSimple(paramName)) {
+      //            [stack]
       return false;
     }
   }
 
   return true;
 }
 
 bool BytecodeEmitter::emitInitializeFunctionSpecialNames() {
   FunctionBox* funbox = sc->asFunctionBox();
 
+  //                [stack]
+
   auto emitInitializeFunctionSpecialName =
       [](BytecodeEmitter* bce, HandlePropertyName name, JSOp op) {
         // A special name must be slotful, either on the frame or on the
         // call environment.
         MOZ_ASSERT(bce->lookupName(name).hasKnownSlot());
 
         NameOpEmitter noe(bce, name, NameOpEmitter::Kind::Initialize);
         if (!noe.prepareForRhs()) {
+          //        [stack]
           return false;
         }
         if (!bce->emit1(op)) {
+          //        [stack] THIS/ARGUMENTS
           return false;
         }
         if (!noe.emitAssignment()) {
+          //        [stack] THIS/ARGUMENTS
           return false;
         }
         if (!bce->emit1(JSOP_POP)) {
+          //        [stack]
           return false;
         }
 
         return true;
       };
 
   // Do nothing if the function doesn't have an arguments binding.
   if (funbox->argumentsHasLocalBinding()) {
     if (!emitInitializeFunctionSpecialName(this, cx->names().arguments,
                                            JSOP_ARGUMENTS)) {
+      //            [stack]
       return false;
     }
   }
 
   // Do nothing if the function doesn't have a this-binding (this
   // happens for instance if it doesn't use this/eval or if it's an
   // arrow function).
   if (funbox->hasThisBinding()) {
@@ -8616,116 +8296,20 @@ bool BytecodeEmitter::emitInitializeFunc
       return false;
     }
   }
 
   // Do nothing if the function doesn't implicitly return a promise result.
   if (funbox->needsPromiseResult()) {
     if (!emitInitializeFunctionSpecialName(this, cx->names().dotGenerator,
                                            JSOP_GENERATOR)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-bool BytecodeEmitter::emitFunctionBody(ParseNode* funBody) {
-  FunctionBox* funbox = sc->asFunctionBox();
-
-  Maybe<TryEmitter> rejectTryCatch;
-  if (funbox->needsPromiseResult()) {
-    if (!emitAsyncFunctionRejectPrologue(rejectTryCatch)) {
-      return false;
-    }
-  }
-
-  if (funbox->function()->kind() ==
-      JSFunction::FunctionKind::ClassConstructor) {
-    if (!emitInitializeInstanceFields()) {
-      return false;
-    }
-  }
-
-  if (!emitTree(funBody)) {
-    return false;
-  }
-
-  if (funbox->needsFinalYield()) {
-    // If we fall off the end of a generator, do a final yield.
-    bool needsIteratorResult = funbox->needsIteratorResult();
-    if (needsIteratorResult) {
-      if (!emitPrepareIteratorResult()) {
-        return false;
-      }
-    }
-
-    if (!emit1(JSOP_UNDEFINED)) {
-      return false;
-    }
-
-    if (needsIteratorResult) {
-      if (!emitFinishIteratorResult(true)) {
-        return false;
-      }
-    }
-
-    if (funbox->needsPromiseResult()) {
-      if (!emitGetDotGeneratorInInnermostScope()) {
-        //          [stack] RVAL GEN
-        return false;
-      }
-
-      if (!emit2(JSOP_ASYNCRESOLVE,
-                 uint8_t(AsyncFunctionResolveKind::Fulfill))) {
-        //          [stack] PROMISE
-        return false;
-      }
-    }
-
-    if (!emit1(JSOP_SETRVAL)) {
-      return false;
-    }
-
-    if (!emitGetDotGeneratorInInnermostScope()) {
-      return false;
-    }
-
-    // No need to check for finally blocks, etc as in EmitReturn.
-    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. Make sure the return value
-    // is |undefined|.
-    if (hasTryFinally) {
-      if (!emit1(JSOP_UNDEFINED)) {
-        return false;
-      }
-      if (!emit1(JSOP_SETRVAL)) {
-        return false;
-      }
-    }
-  }
-
-  if (funbox->isDerivedClassConstructor()) {
-    if (!emitCheckDerivedClassConstructorReturn()) {
-      return false;
-    }
-  }
-
-  if (rejectTryCatch) {
-    if (!emitAsyncFunctionRejectEpilogue(*rejectTryCatch)) {
-      return false;
-    }
-  }
-
+      //            [stack]
+      return false;
+    }
+  }
   return true;
 }
 
 bool BytecodeEmitter::emitLexicalInitialization(NameNode* name) {
   return emitLexicalInitialization(name->name());
 }
 
 bool BytecodeEmitter::emitLexicalInitialization(JSAtom* name) {
@@ -8741,56 +8325,16 @@ bool BytecodeEmitter::emitLexicalInitial
 
   if (!noe.emitAssignment()) {
     return false;
   }
 
   return true;
 }
 
-bool BytecodeEmitter::emitAsyncFunctionRejectPrologue(
-    Maybe<TryEmitter>& tryCatch) {
-  tryCatch.emplace(this, TryEmitter::Kind::TryCatch,
-                   TryEmitter::ControlKind::NonSyntactic);
-  return tryCatch->emitTry();
-}
-
-bool BytecodeEmitter::emitAsyncFunctionRejectEpilogue(TryEmitter& tryCatch) {
-  if (!tryCatch.emitCatch()) {
-    return false;
-  }
-
-  if (!emit1(JSOP_EXCEPTION)) {
-    //              [stack] EXC
-    return false;
-  }
-  if (!emitGetDotGeneratorInInnermostScope()) {
-    //              [stack] EXC GEN
-    return false;
-  }
-  if (!emit2(JSOP_ASYNCRESOLVE, uint8_t(AsyncFunctionResolveKind::Reject))) {
-    //              [stack] PROMISE
-    return false;
-  }
-  if (!emit1(JSOP_SETRVAL)) {
-    //              [stack]
-    return false;
-  }
-  if (!emitGetDotGeneratorInInnermostScope()) {
-    //              [stack] GEN
-    return false;
-  }
-  if (!emit1(JSOP_FINALYIELDRVAL)) {
-    //              [stack]
-    return false;
-  }
-
-  return tryCatch.emitEnd();
-}
-
 static MOZ_ALWAYS_INLINE FunctionNode* FindConstructor(JSContext* cx,
                                                        ListNode* classMethods) {
   for (ParseNode* mn : classMethods->contents()) {
     if (mn->is<ClassMethod>()) {
       ClassMethod& method = mn->as<ClassMethod>();
       ParseNode& methodName = method.name();
       if (!method.isStatic() &&
           (methodName.isKind(ParseNodeKind::ObjectPropertyName) ||
@@ -8980,19 +8524,18 @@ bool BytecodeEmitter::emitTree(
   switch (pn->getKind()) {
     case ParseNodeKind::Function:
       if (!emitFunction(&pn->as<FunctionNode>())) {
         return false;
       }
       break;
 
     case ParseNodeKind::ParamsBody:
-      if (!emitFunctionFormalParametersAndBody(&pn->as<ListNode>())) {
-        return false;
-      }
+      MOZ_ASSERT_UNREACHABLE(
+          "ParamsBody should be handled in emitFunctionScript.");
       break;
 
     case ParseNodeKind::IfStmt:
       if (!emitIf(&pn->as<TernaryNode>())) {
         return false;
       }
       break;
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -488,18 +488,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
   MOZ_MUST_USE bool emitTree(ParseNode* pn,
                              ValueUsage valueUsage = ValueUsage::WantValue,
                              EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
 
   // Emit global, eval, or module code for tree rooted at body. Always
   // encompasses the entire source.
   MOZ_MUST_USE bool emitScript(ParseNode* body);
 
-  MOZ_MUST_USE bool emitInitializeInstanceFields();
-
   // Emit function code for the tree rooted at body.
   enum class TopLevelFunction { No, Yes };
   MOZ_MUST_USE bool emitFunctionScript(FunctionNode* funNode,
                                        TopLevelFunction isTopLevel);
 
   void updateDepth(ptrdiff_t target);
   MOZ_MUST_USE bool markStepBreakpoint();
   MOZ_MUST_USE bool markSimpleBreakpoint();
@@ -830,29 +828,21 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
   MOZ_MUST_USE bool emitForOf(ForNode* forNode,
                               const EmitterScope* headLexicalEmitterScope);
 
   MOZ_MUST_USE bool emitInitializeForInOrOfTarget(TernaryNode* forHead);
 
   MOZ_MUST_USE bool emitBreak(PropertyName* label);
   MOZ_MUST_USE bool emitContinue(PropertyName* label);
 
-  MOZ_MUST_USE bool emitFunctionFormalParametersAndBody(ListNode* paramsBody);
   MOZ_MUST_USE bool emitFunctionFormalParameters(ListNode* paramsBody);
   MOZ_MUST_USE bool emitInitializeFunctionSpecialNames();
-  MOZ_MUST_USE bool emitFunctionBody(ParseNode* funBody);
   MOZ_MUST_USE bool emitLexicalInitialization(NameNode* name);
   MOZ_MUST_USE bool emitLexicalInitialization(JSAtom* name);
 
-  // Async functions have implicit try-catch blocks to convert exceptions
-  // into promise rejections.
-  MOZ_MUST_USE bool emitAsyncFunctionRejectPrologue(
-      mozilla::Maybe<TryEmitter>& tryCatch);
-  MOZ_MUST_USE bool emitAsyncFunctionRejectEpilogue(TryEmitter& tryCatch);
-
   // Emit bytecode for the spread operator.
   //
   // emitSpread expects the current index (I) of the array, the array itself
   // and the iterator to be on the stack in that order (iterator on the bottom).
   // It will pop the iterator and I, then iterate over the iterator by calling
   // |.next()| and put the results into the I-th element of array with
   // incrementing I, then push the result I (it will be original I +
   // iteration count). The stack after iteration will look like |ARRAY INDEX|.
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/FunctionEmitter.cpp
@@ -0,0 +1,1143 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "frontend/FunctionEmitter.h"
+
+#include "mozilla/Assertions.h"  // MOZ_ASSERT
+
+#include "builtin/ModuleObject.h"          // ModuleObject
+#include "frontend/BytecodeEmitter.h"      // BytecodeEmitter
+#include "frontend/ModuleSharedContext.h"  // ModuleSharedContext
+#include "frontend/NameAnalysisTypes.h"    // NameLocation
+#include "frontend/NameOpEmitter.h"        // NameOpEmitter
+#include "frontend/ParseContext.h"         // BindingIter
+#include "frontend/PropOpEmitter.h"        // PropOpEmitter
+#include "frontend/SharedContext.h"        // SharedContext
+#include "vm/AsyncFunction.h"              // AsyncFunctionResolveKind
+#include "vm/JSScript.h"                   // JSScript
+#include "vm/Opcodes.h"                    // JSOP_*
+#include "vm/Scope.h"                      // BindingKind
+#include "wasm/AsmJS.h"                    // IsAsmJSModule
+
+using namespace js;
+using namespace js::frontend;
+
+using mozilla::Maybe;
+using mozilla::Some;
+
+FunctionEmitter::FunctionEmitter(BytecodeEmitter* bce, FunctionBox* funbox,
+                                 FunctionSyntaxKind syntaxKind,
+                                 IsHoisted isHoisted)
+    : bce_(bce),
+      funbox_(funbox),
+      fun_(bce_->cx, funbox_->function()),
+      name_(bce_->cx, fun_->explicitName()),
+      syntaxKind_(syntaxKind),
+      isHoisted_(isHoisted) {
+  MOZ_ASSERT_IF(fun_->isInterpretedLazy(), fun_->lazyScript());
+}
+
+bool FunctionEmitter::interpretedCommon() {
+  // 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.
+  bool singleton = bce_->checkRunOnceContext();
+  if (!JSFunction::setTypeForScriptedFunction(bce_->cx, fun_, singleton)) {
+    return false;
+  }
+
+  SharedContext* outersc = bce_->sc;
+  if (outersc->isFunctionBox()) {
+    outersc->asFunctionBox()->setHasInnerFunctions();
+  }
+
+  return true;
+}
+
+bool FunctionEmitter::prepareForNonLazy() {
+  MOZ_ASSERT(state_ == State::Start);
+
+  MOZ_ASSERT(fun_->isInterpreted());
+  MOZ_ASSERT(!fun_->isInterpretedLazy());
+  MOZ_ASSERT(!funbox_->wasEmitted);
+
+  //                [stack]
+
+  funbox_->wasEmitted = true;
+
+  if (!interpretedCommon()) {
+    return false;
+  }
+
+  MOZ_ASSERT_IF(bce_->sc->strict(), funbox_->strictScript);
+
+#ifdef DEBUG
+  state_ = State::NonLazy;
+#endif
+  return true;
+}
+
+bool FunctionEmitter::emitNonLazyEnd() {
+  MOZ_ASSERT(state_ == State::NonLazy);
+
+  //                [stack]
+
+  if (!emitFunction()) {
+    //              [stack] FUN?
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::End;
+#endif
+  return true;
+}
+
+bool FunctionEmitter::emitLazy() {
+  MOZ_ASSERT(state_ == State::Start);
+
+  MOZ_ASSERT(fun_->isInterpreted());
+  MOZ_ASSERT(fun_->isInterpretedLazy());
+  MOZ_ASSERT(!funbox_->wasEmitted);
+
+  //                [stack]
+
+  funbox_->wasEmitted = true;
+
+  if (!interpretedCommon()) {
+    return false;
+  }
+
+  funbox_->setEnclosingScopeForInnerLazyFunction(bce_->innermostScope());
+  if (bce_->emittingRunOnceLambda) {
+    fun_->lazyScript()->setTreatAsRunOnce();
+  }
+
+  if (!emitFunction()) {
+    //              [stack] FUN?
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::End;
+#endif
+  return true;
+}
+
+bool FunctionEmitter::emitAgain() {
+  MOZ_ASSERT(state_ == State::Start);
+  MOZ_ASSERT(funbox_->wasEmitted);
+  MOZ_ASSERT_IF(fun_->hasScript(), fun_->nonLazyScript());
+
+  //                [stack]
+
+  // Annex B block-scoped functions are hoisted like any other assignment
+  // that assigns the function to the outer 'var' binding.
+  if (!funbox_->isAnnexB) {
+#ifdef DEBUG
+    state_ = State::End;
+#endif
+    return true;
+  }
+
+  // Get the location of the 'var' binding in the body scope. The
+  // name must be found, else there is a bug in the Annex B handling
+  // in Parser.
+  //
+  // In sloppy eval contexts, this location is dynamic.
+  Maybe<NameLocation> lhsLoc =
+      bce_->locationOfNameBoundInScope(name_, bce_->varEmitterScope);
+
+  // If there are parameter expressions, the var name could be a
+  // parameter.
+  if (!lhsLoc && bce_->sc->isFunctionBox() &&
+      bce_->sc->asFunctionBox()->hasExtraBodyVarScope()) {
+    lhsLoc = bce_->locationOfNameBoundInScope(
+        name_, bce_->varEmitterScope->enclosingInFrame());
+  }
+
+  if (!lhsLoc) {
+    lhsLoc = Some(NameLocation::DynamicAnnexBVar());
+  } else {
+    MOZ_ASSERT(lhsLoc->bindingKind() == BindingKind::Var ||
+               lhsLoc->bindingKind() == BindingKind::FormalParameter ||
+               (lhsLoc->bindingKind() == BindingKind::Let &&
+                bce_->sc->asFunctionBox()->hasParameterExprs));
+  }
+
+  NameOpEmitter noe(bce_, name_, *lhsLoc,
+                    NameOpEmitter::Kind::SimpleAssignment);
+  if (!noe.prepareForRhs()) {
+    //              [stack]
+    return false;
+  }
+
+  if (!bce_->emitGetName(name_)) {
+    //              [stack] FUN
+    return false;
+  }
+
+  if (!noe.emitAssignment()) {
+    //              [stack] FUN
+    return false;
+  }
+
+  if (!bce_->emit1(JSOP_POP)) {
+    //              [stack]
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::End;
+#endif
+  return true;
+}
+
+bool FunctionEmitter::emitAsmJSModule() {
+  MOZ_ASSERT(state_ == State::Start);
+
+  MOZ_ASSERT(!funbox_->wasEmitted);
+  MOZ_ASSERT(IsAsmJSModule(fun_));
+
+  //                [stack]
+
+  funbox_->wasEmitted = true;
+
+  if (!emitFunction()) {
+    //              [stack]
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::End;
+#endif
+  return true;
+}
+
+bool FunctionEmitter::emitFunction() {
+  // Make the function object a literal in the outer script's pool.
+  unsigned index = bce_->objectList.add(funbox_);
+
+  //                [stack]
+
+  if (isHoisted_ == IsHoisted::No) {
+    return emitNonHoisted(index);
+    //              [stack] FUN?
+  }
+
+  bool topLevelFunction;
+  if (bce_->sc->isFunctionBox() ||
+      (bce_->sc->isEvalContext() && bce_->sc->strict())) {
+    // No nested functions inside other functions are top-level.
+    topLevelFunction = false;
+  } else {
+    // In sloppy eval scripts, top-level functions are accessed dynamically.
+    // In global and module scripts, top-level functions are those bound in
+    // the var scope.
+    NameLocation loc = bce_->lookupName(name_);
+    topLevelFunction = loc.kind() == NameLocation::Kind::Dynamic ||
+                       loc.bindingKind() == BindingKind::Var;
+  }
+
+  if (topLevelFunction) {
+    return emitTopLevelFunction(index);
+    //              [stack]
+  }
+
+  return emitHoisted(index);
+  //                [stack]
+}
+
+bool FunctionEmitter::emitNonHoisted(unsigned index) {
+  // Non-hoisted functions simply emit their respective op.
+
+  //                [stack]
+
+  // JSOP_LAMBDA_ARROW is always preceded by a opcode that pushes new.target.
+  // See below.
+  MOZ_ASSERT(fun_->isArrow() == (syntaxKind_ == FunctionSyntaxKind::Arrow));
+
+  if (fun_->isArrow()) {
+    if (!emitNewTargetForArrow()) {
+      //            [stack] NEW.TARGET/NULL
+      return false;
+    }
+  }
+
+  if (syntaxKind_ == FunctionSyntaxKind::DerivedClassConstructor) {
+    //              [stack] PROTO
+    if (!bce_->emitIndex32(JSOP_FUNWITHPROTO, index)) {
+      //            [stack] FUN
+      return false;
+    }
+    return true;
+  }
+
+  // This is a FunctionExpression, ArrowFunctionExpression, or class
+  // constructor. Emit the single instruction (without location info).
+  JSOp op = syntaxKind_ == FunctionSyntaxKind::Arrow ? JSOP_LAMBDA_ARROW
+                                                     : JSOP_LAMBDA;
+  if (!bce_->emitIndex32(op, index)) {
+    //              [stack] FUN
+    return false;
+  }
+
+  return true;
+}
+
+bool FunctionEmitter::emitHoisted(unsigned index) {
+  MOZ_ASSERT(syntaxKind_ == FunctionSyntaxKind::Statement);
+
+  //                [stack]
+
+  // For functions nested within functions and blocks, make a lambda and
+  // initialize the binding name of the function in the current scope.
+
+  NameOpEmitter noe(bce_, name_, NameOpEmitter::Kind::Initialize);
+  if (!noe.prepareForRhs()) {
+    //              [stack]
+    return false;
+  }
+
+  if (!bce_->emitIndexOp(JSOP_LAMBDA, index)) {
+    //              [stack] FUN
+    return false;
+  }
+
+  if (!noe.emitAssignment()) {
+    //              [stack] FUN
+    return false;
+  }
+
+  if (!bce_->emit1(JSOP_POP)) {
+    //              [stack]
+    return false;
+  }
+
+  return true;
+}
+
+bool FunctionEmitter::emitTopLevelFunction(unsigned index) {
+  //                [stack]
+
+  if (bce_->sc->isModuleContext()) {
+    // For modules, we record the function and instantiate the binding
+    // during ModuleInstantiate(), before the script is run.
+
+    JS::Rooted<ModuleObject*> module(bce_->cx,
+                                     bce_->sc->asModuleContext()->module());
+    if (!module->noteFunctionDeclaration(bce_->cx, name_, fun_)) {
+      return false;
+    }
+    return true;
+  }
+
+  MOZ_ASSERT(bce_->sc->isGlobalContext() || bce_->sc->isEvalContext());
+  MOZ_ASSERT(syntaxKind_ == FunctionSyntaxKind::Statement);
+  MOZ_ASSERT(bce_->inPrologue());
+
+  if (!bce_->emitIndex32(JSOP_LAMBDA, index)) {
+    //              [stack] FUN
+    return false;
+  }
+  if (!bce_->emit1(JSOP_DEFFUN)) {
+    //              [stack]
+    return false;
+  }
+  return true;
+}
+
+bool FunctionEmitter::emitNewTargetForArrow() {
+  //                [stack]
+
+  if (bce_->sc->allowNewTarget()) {
+    if (!bce_->emit1(JSOP_NEWTARGET)) {
+      //            [stack] NEW.TARGET
+      return false;
+    }
+  } else {
+    if (!bce_->emit1(JSOP_NULL)) {
+      //            [stack] NULL
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool FunctionScriptEmitter::prepareForParameters() {
+  MOZ_ASSERT(state_ == State::Start);
+  MOZ_ASSERT(bce_->inPrologue());
+
+  //                [stack]
+
+  if (paramStart_) {
+    bce_->setScriptStartOffsetIfUnset(*paramStart_);
+  }
+
+  // The ordering of these EmitterScopes is important. The named lambda
+  // scope needs to enclose the function scope needs to enclose the extra
+  // var scope.
+
+  if (funbox_->namedLambdaBindings()) {
+    namedLambdaEmitterScope_.emplace(bce_);
+    if (!namedLambdaEmitterScope_->enterNamedLambda(bce_, funbox_)) {
+      return false;
+    }
+  }
+
+  /*
+   * Mark the script so that initializers created within it may be given more
+   * precise types.
+   */
+  if (bce_->isRunOnceLambda()) {
+    bce_->script->setTreatAsRunOnce();
+    MOZ_ASSERT(!bce_->script->hasRunOnce());
+  }
+
+  if (bodyEnd_) {
+    bce_->setFunctionBodyEndPos(*bodyEnd_);
+  }
+
+  if (paramStart_) {
+    if (!bce_->updateLineNumberNotes(*paramStart_)) {
+      return false;
+    }
+  }
+
+  tdzCache_.emplace(bce_);
+  functionEmitterScope_.emplace(bce_);
+
+  if (funbox_->hasParameterExprs) {
+    // There's parameter exprs, emit them in the main section.
+    //
+    // One caveat is that Debugger considers ops in the prologue to be
+    // unreachable (i.e. cannot set a breakpoint on it). If there are no
+    // parameter exprs, any unobservable environment ops (like pushing the
+    // call object, setting '.this', etc) need to go in the prologue, else it
+    // messes up breakpoint tests.
+    bce_->switchToMain();
+  }
+
+  if (!functionEmitterScope_->enterFunction(bce_, funbox_)) {
+    return false;
+  }
+
+  if (!bce_->emitInitializeFunctionSpecialNames()) {
+    //              [stack]
+    return false;
+  }
+
+  if (!funbox_->hasParameterExprs) {
+    bce_->switchToMain();
+  }
+
+  // Parameters can't reuse the reject try-catch block from the function body,
+  // because the body may have pushed an additional var-environment. This
+  // messes up scope resolution for the |.generator| variable, because we'd
+  // need different hops to reach |.generator| depending on whether the error
+  // was thrown from the parameters or the function body.
+  if (funbox_->hasParameterExprs && funbox_->needsPromiseResult()) {
+    if (!emitAsyncFunctionRejectPrologue()) {
+      return false;
+    }
+  }
+
+#ifdef DEBUG
+  state_ = State::Parameters;
+#endif
+  return true;
+}
+
+bool FunctionScriptEmitter::prepareForBody() {
+  MOZ_ASSERT(state_ == State::Parameters);
+
+  //                [stack]
+
+  if (rejectTryCatch_) {
+    if (!emitAsyncFunctionRejectEpilogue()) {
+      return false;
+    }
+  }
+
+  if (!emitExtraBodyVarScope()) {
+    //              [stack]
+    return false;
+  }
+
+  if (funbox_->needsPromiseResult()) {
+    if (!emitAsyncFunctionRejectPrologue()) {
+      return false;
+    }
+  }
+
+  if (funbox_->function()->kind() ==
+      JSFunction::FunctionKind::ClassConstructor) {
+    if (!emitInitializeInstanceFields()) {
+      //            [stack]
+      return false;
+    }
+  }
+
+#ifdef DEBUG
+  state_ = State::Body;
+#endif
+  return true;
+}
+
+bool FunctionScriptEmitter::emitAsyncFunctionRejectPrologue() {
+  rejectTryCatch_.emplace(bce_, TryEmitter::Kind::TryCatch,
+                   TryEmitter::ControlKind::NonSyntactic);
+  return rejectTryCatch_->emitTry();
+}
+
+bool FunctionScriptEmitter::emitAsyncFunctionRejectEpilogue() {
+  if (!rejectTryCatch_->emitCatch()) {
+    return false;
+  }
+
+  if (!bce_->emit1(JSOP_EXCEPTION)) {
+    //              [stack] EXC
+    return false;
+  }
+  if (!bce_->emitGetDotGeneratorInInnermostScope()) {
+    //              [stack] EXC GEN
+    return false;
+  }
+  if (!bce_->emit2(JSOP_ASYNCRESOLVE, uint8_t(AsyncFunctionResolveKind::Reject))) {
+    //              [stack] PROMISE
+    return false;
+  }
+  if (!bce_->emit1(JSOP_SETRVAL)) {
+    //              [stack]
+    return false;
+  }
+  if (!bce_->emitGetDotGeneratorInInnermostScope()) {
+    //              [stack] GEN
+    return false;
+  }
+  if (!bce_->emit1(JSOP_FINALYIELDRVAL)) {
+    //              [stack]
+    return false;
+  }
+
+  if (!rejectTryCatch_->emitEnd()) {
+    return false;
+  }
+  rejectTryCatch_.reset();
+  return true;
+}
+
+bool FunctionScriptEmitter::emitExtraBodyVarScope() {
+  //                [stack]
+
+  if (!funbox_->hasExtraBodyVarScope()) {
+    return true;
+  }
+
+  extraBodyVarEmitterScope_.emplace(bce_);
+  if (!extraBodyVarEmitterScope_->enterFunctionExtraBodyVar(bce_, funbox_)) {
+    return false;
+  }
+
+  if (!funbox_->extraVarScopeBindings() || !funbox_->functionScopeBindings()) {
+    return true;
+  }
+
+  // After emitting expressions for all parameters, copy over any formal
+  // parameters which have been redeclared as vars. For example, in the
+  // following, the var y in the body scope is 42:
+  //
+  //   function f(x, y = 42) { var y; }
+  //
+  JS::Rooted<JSAtom*> name(bce_->cx);
+  for (BindingIter bi(*funbox_->functionScopeBindings(), true); bi; bi++) {
+    name = bi.name();
+
+    // There may not be a var binding of the same name.
+    if (!bce_->locationOfNameBoundInScope(name,
+                                          extraBodyVarEmitterScope_.ptr())) {
+      continue;
+    }
+
+    // The '.this' and '.generator' function special
+    // bindings should never appear in the extra var
+    // scope. 'arguments', however, may.
+    MOZ_ASSERT(name != bce_->cx->names().dotThis &&
+               name != bce_->cx->names().dotGenerator);
+
+    NameOpEmitter noe(bce_, name, NameOpEmitter::Kind::Initialize);
+    if (!noe.prepareForRhs()) {
+      //            [stack]
+      return false;
+    }
+
+    NameLocation paramLoc =
+        *bce_->locationOfNameBoundInScope(name, functionEmitterScope_.ptr());
+    if (!bce_->emitGetNameAtLocation(name, paramLoc)) {
+      //            [stack] VAL
+      return false;
+    }
+
+    if (!noe.emitAssignment()) {
+      //            [stack] VAL
+      return false;
+    }
+
+    if (!bce_->emit1(JSOP_POP)) {
+      //            [stack]
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool FunctionScriptEmitter::emitInitializeInstanceFields() {
+  MOZ_ASSERT(bce_->fieldInitializers_.valid);
+  size_t numFields = bce_->fieldInitializers_.numFieldInitializers;
+
+  if (numFields == 0) {
+    return true;
+  }
+
+  if (!bce_->emitGetName(bce_->cx->names().dotInitializers)) {
+    //              [stack] ARRAY
+    return false;
+  }
+
+  for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
+    if (fieldIndex < numFields - 1) {
+      // We DUP to keep the array around (it is consumed in the bytecode below)
+      // for next iterations of this loop, except for the last iteration, which
+      // avoids an extra POP at the end of the loop.
+      if (!bce_->emit1(JSOP_DUP)) {
+        //          [stack] ARRAY ARRAY
+        return false;
+      }
+    }
+
+    if (!bce_->emitNumberOp(fieldIndex)) {
+      //            [stack] ARRAY? ARRAY INDEX
+      return false;
+    }
+
+    if (!bce_->emit1(JSOP_CALLELEM)) {
+      //            [stack] ARRAY? FUNC
+      return false;
+    }
+
+    // This is guaranteed to run after super(), so we don't need TDZ checks.
+    if (!bce_->emitGetName(bce_->cx->names().dotThis)) {
+      //            [stack] ARRAY? FUNC THIS
+      return false;
+    }
+
+    if (!bce_->emitCall(JSOP_CALL_IGNORES_RV, 0)) {
+      //            [stack] ARRAY? RVAL
+      return false;
+    }
+
+    if (!bce_->emit1(JSOP_POP)) {
+      //            [stack] ARRAY?
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool FunctionScriptEmitter::emitEndBody() {
+  MOZ_ASSERT(state_ == State::Body);
+
+  //                [stack]
+
+  if (funbox_->needsFinalYield()) {
+    // If we fall off the end of a generator, do a final yield.
+    bool needsIteratorResult = funbox_->needsIteratorResult();
+    if (needsIteratorResult) {
+      if (!bce_->emitPrepareIteratorResult()) {
+        //          [stack] RESULT
+        return false;
+      }
+    }
+
+    if (!bce_->emit1(JSOP_UNDEFINED)) {
+      //            [stack] RESULT? UNDEF
+      return false;
+    }
+
+    if (needsIteratorResult) {
+      if (!bce_->emitFinishIteratorResult(true)) {
+        //          [stack] RESULT
+        return false;
+      }
+    }
+
+    if (funbox_->needsPromiseResult()) {
+      if (!bce_->emitGetDotGeneratorInInnermostScope()) {
+        //          [stack] RVAL GEN
+        return false;
+      }
+
+      if (!bce_->emit2(JSOP_ASYNCRESOLVE,
+                       uint8_t(AsyncFunctionResolveKind::Fulfill))) {
+        //          [stack] PROMISE
+        return false;
+      }
+    }
+
+    if (!bce_->emit1(JSOP_SETRVAL)) {
+      //            [stack]
+      return false;
+    }
+
+    if (!bce_->emitGetDotGeneratorInInnermostScope()) {
+      //            [stack] GEN
+      return false;
+    }
+
+    // No need to check for finally blocks, etc as in EmitReturn.
+    if (!bce_->emitYieldOp(JSOP_FINALYIELDRVAL)) {
+      //            [stack]
+      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. Make sure the return value
+    // is |undefined|.
+    if (bce_->hasTryFinally) {
+      if (!bce_->emit1(JSOP_UNDEFINED)) {
+        //          [stack] UNDEF
+        return false;
+      }
+      if (!bce_->emit1(JSOP_SETRVAL)) {
+        //          [stack]
+        return false;
+      }
+    }
+  }
+
+  if (funbox_->isDerivedClassConstructor()) {
+    if (!bce_->emitCheckDerivedClassConstructorReturn()) {
+      //            [stack]
+      return false;
+    }
+  }
+
+  if (rejectTryCatch_) {
+    if (!emitAsyncFunctionRejectEpilogue()) {
+      return false;
+    }
+  }
+
+  if (extraBodyVarEmitterScope_) {
+    if (!extraBodyVarEmitterScope_->leave(bce_)) {
+      return false;
+    }
+
+    extraBodyVarEmitterScope_.reset();
+  }
+
+  if (!functionEmitterScope_->leave(bce_)) {
+    return false;
+  }
+  functionEmitterScope_.reset();
+  tdzCache_.reset();
+
+  if (bodyEnd_) {
+    if (!bce_->updateSourceCoordNotes(*bodyEnd_)) {
+      return false;
+    }
+  }
+
+  // We only want to mark the end of a function as a breakable position if
+  // there is token there that the user can easily associate with the function
+  // as a whole. Since arrow function single-expression bodies have no closing
+  // curly bracket, we do not place a breakpoint at their end position.
+  if (!funbox_->hasExprBody()) {
+    if (!bce_->markSimpleBreakpoint()) {
+      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)) {
+    //              [stack]
+    return false;
+  }
+
+  if (namedLambdaEmitterScope_) {
+    if (!namedLambdaEmitterScope_->leave(bce_)) {
+      return false;
+    }
+    namedLambdaEmitterScope_.reset();
+  }
+
+#ifdef DEBUG
+  state_ = State::EndBody;
+#endif
+  return true;
+}
+
+bool FunctionScriptEmitter::initScript() {
+  MOZ_ASSERT(state_ == State::EndBody);
+
+  if (!JSScript::fullyInitFromEmitter(bce_->cx, bce_->script, bce_)) {
+    return false;
+  }
+
+  bce_->tellDebuggerAboutCompiledScript(bce_->cx);
+
+#ifdef DEBUG
+  state_ = State::End;
+#endif
+  return true;
+}
+
+FunctionParamsEmitter::FunctionParamsEmitter(BytecodeEmitter* bce,
+                                             FunctionBox* funbox)
+    : bce_(bce),
+      funbox_(funbox),
+      functionEmitterScope_(bce_->innermostEmitterScope()) {}
+
+bool FunctionParamsEmitter::emitSimple(JS::Handle<JSAtom*> paramName) {
+  MOZ_ASSERT(state_ == State::Start);
+
+  //                [stack]
+
+  if (funbox_->hasParameterExprs) {
+    if (!bce_->emitArgOp(JSOP_GETARG, argSlot_)) {
+      //            [stack] ARG
+      return false;
+    }
+
+    if (!emitAssignment(paramName)) {
+      //            [stack]
+      return false;
+    }
+  }
+
+  argSlot_++;
+  return true;
+}
+
+bool FunctionParamsEmitter::prepareForDefault() {
+  MOZ_ASSERT(state_ == State::Start);
+
+  //                [stack]
+
+  if (!enterParameterExpressionVarScope()) {
+    return false;
+  }
+
+  if (!prepareForInitializer()) {
+    //              [stack]
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::Default;
+#endif
+  return true;
+}
+
+bool FunctionParamsEmitter::emitDefaultEnd(JS::Handle<JSAtom*> paramName) {
+  MOZ_ASSERT(state_ == State::Default);
+
+  //                [stack] DEFAULT
+
+  if (!emitInitializerEnd()) {
+    //              [stack] ARG/DEFAULT
+    return false;
+  }
+  if (!emitAssignment(paramName)) {
+    //              [stack]
+    return false;
+  }
+  if (!leaveParameterExpressionVarScope()) {
+    return false;
+  }
+
+  argSlot_++;
+
+#ifdef DEBUG
+  state_ = State::Start;
+#endif
+  return true;
+}
+
+bool FunctionParamsEmitter::prepareForDestructuring() {
+  MOZ_ASSERT(state_ == State::Start);
+
+  //                [stack]
+
+  if (!enterParameterExpressionVarScope()) {
+    return false;
+  }
+
+  if (!bce_->emitArgOp(JSOP_GETARG, argSlot_)) {
+    //              [stack] ARG
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::Destructuring;
+#endif
+  return true;
+}
+
+bool FunctionParamsEmitter::emitDestructuringEnd() {
+  MOZ_ASSERT(state_ == State::Destructuring);
+
+  //                [stack] ARG
+
+  if (!bce_->emit1(JSOP_POP)) {
+    //              [stack]
+    return false;
+  }
+
+  if (!leaveParameterExpressionVarScope()) {
+    return false;
+  }
+
+  argSlot_++;
+
+#ifdef DEBUG
+  state_ = State::Start;
+#endif
+  return true;
+}
+
+bool FunctionParamsEmitter::prepareForDestructuringDefaultInitializer() {
+  MOZ_ASSERT(state_ == State::Start);
+
+  //                [stack]
+
+  if (!enterParameterExpressionVarScope()) {
+    return false;
+  }
+  if (!prepareForInitializer()) {
+    //              [stack]
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::DestructuringDefaultInitializer;
+#endif
+  return true;
+}
+
+bool FunctionParamsEmitter::prepareForDestructuringDefault() {
+  MOZ_ASSERT(state_ == State::DestructuringDefaultInitializer);
+
+  //                [stack] DEFAULT
+
+  if (!emitInitializerEnd()) {
+    //              [stack] ARG/DEFAULT
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::DestructuringDefault;
+#endif
+  return true;
+}
+
+bool FunctionParamsEmitter::emitDestructuringDefaultEnd() {
+  MOZ_ASSERT(state_ == State::DestructuringDefault);
+
+  //                [stack] ARG/DEFAULT
+
+  if (!bce_->emit1(JSOP_POP)) {
+    //              [stack]
+    return false;
+  }
+
+  if (!leaveParameterExpressionVarScope()) {
+    return false;
+  }
+
+  argSlot_++;
+
+#ifdef DEBUG
+  state_ = State::Start;
+#endif
+  return true;
+}
+
+bool FunctionParamsEmitter::emitRest(JS::Handle<JSAtom*> paramName) {
+  MOZ_ASSERT(state_ == State::Start);
+
+  //                [stack]
+
+  if (!emitRestArray()) {
+    //              [stack] REST
+    return false;
+  }
+  if (!emitAssignment(paramName)) {
+    //              [stack]
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::End;
+#endif
+  return true;
+}
+
+bool FunctionParamsEmitter::prepareForDestructuringRest() {
+  MOZ_ASSERT(state_ == State::Start);
+
+  //                [stack]
+
+  if (!enterParameterExpressionVarScope()) {
+    return false;
+  }
+  if (!emitRestArray()) {
+    //              [stack] REST
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::DestructuringRest;
+#endif
+  return true;
+}
+
+bool FunctionParamsEmitter::emitDestructuringRestEnd() {
+  MOZ_ASSERT(state_ == State::DestructuringRest);
+
+  //                [stack] REST
+
+  if (!bce_->emit1(JSOP_POP)) {
+    //              [stack]
+    return false;
+  }
+
+  if (!leaveParameterExpressionVarScope()) {
+    return false;
+  }
+
+#ifdef DEBUG
+  state_ = State::End;
+#endif
+  return true;
+}
+
+bool FunctionParamsEmitter::enterParameterExpressionVarScope() {
+  if (!funbox_->hasDirectEvalInParameterExpr) {
+    return true;
+  }
+
+  // ES 14.1.19 says if BindingElement contains an expression in the
+  // production FormalParameter : BindingElement, it is evaluated in a
+  // new var environment. This is needed to prevent vars from escaping
+  // direct eval in parameter expressions.
+  paramExprVarEmitterScope_.emplace(bce_);
+  if (!paramExprVarEmitterScope_->enterParameterExpressionVar(bce_)) {
+    return false;
+  }
+  return true;
+}
+
+bool FunctionParamsEmitter::leaveParameterExpressionVarScope() {
+  if (!paramExprVarEmitterScope_) {
+    return true;
+  }
+
+  if (!paramExprVarEmitterScope_->leave(bce_)) {
+    return false;
+  }
+  paramExprVarEmitterScope_.reset();
+
+  return true;
+}
+
+bool FunctionParamsEmitter::prepareForInitializer() {
+  //                [stack]
+
+  // If we have an initializer, emit the initializer and assign it
+  // to the argument slot. TDZ is taken care of afterwards.
+  MOZ_ASSERT(funbox_->hasParameterExprs);
+  if (!bce_->emitArgOp(JSOP_GETARG, argSlot_)) {
+    //              [stack] ARG
+    return false;
+  }
+  default_.emplace(bce_);
+  if (!default_->prepareForDefault()) {
+    //              [stack]
+    return false;
+  }
+  return true;
+}
+
+bool FunctionParamsEmitter::emitInitializerEnd() {
+  //                [stack] DEFAULT
+
+  if (!default_->emitEnd()) {
+    //              [stack] ARG/DEFAULT
+    return false;
+  }
+  default_.reset();
+  return true;
+}
+
+bool FunctionParamsEmitter::emitRestArray() {
+  //                [stack]
+
+  if (!bce_->emit1(JSOP_REST)) {
+    //              [stack] REST
+    return false;
+  }
+  return true;
+}
+
+bool FunctionParamsEmitter::emitAssignment(JS::Handle<JSAtom*> paramName) {
+  //                [stack] ARG
+
+  NameLocation paramLoc =
+      *bce_->locationOfNameBoundInScope(paramName, functionEmitterScope_);
+
+  // RHS is already pushed in the caller side.
+  // Make sure prepareForRhs doesn't touch stack.
+  MOZ_ASSERT(paramLoc.kind() == NameLocation::Kind::ArgumentSlot ||
+             paramLoc.kind() == NameLocation::Kind::FrameSlot ||
+             paramLoc.kind() == NameLocation::Kind::EnvironmentCoordinate);
+
+  NameOpEmitter noe(bce_, paramName, paramLoc, NameOpEmitter::Kind::Initialize);
+  if (!noe.prepareForRhs()) {
+    //              [stack] ARG
+    return false;
+  }
+
+  if (!noe.emitAssignment()) {
+    //              [stack] ARG
+    return false;
+  }
+
+  if (!bce_->emit1(JSOP_POP)) {
+    //              [stack]
+    return false;
+  }
+
+  return true;
+}
+
+DestructuringFlavor FunctionParamsEmitter::getDestructuringFlavor() {
+  MOZ_ASSERT(state_ == State::Destructuring ||
+             state_ == State::DestructuringDefault ||
+             state_ == State::DestructuringRest);
+
+  return funbox_->hasDirectEvalInParameterExpr
+             ? DestructuringFlavor::FormalParameterInVarScope
+             : DestructuringFlavor::Declaration;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/FunctionEmitter.h
@@ -0,0 +1,462 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef frontend_FunctionEmitter_h
+#define frontend_FunctionEmitter_h
+
+#include "mozilla/Attributes.h"  // MOZ_STACK_CLASS, MOZ_MUST_USE
+
+#include <stdint.h>  // uint16_t, uint32_t
+
+#include "frontend/DefaultEmitter.h"       // DefaultEmitter
+#include "frontend/DestructuringFlavor.h"  // DestructuringFlavor
+#include "frontend/EmitterScope.h"         // EmitterScope
+#include "frontend/SharedContext.h"        // FunctionBox
+#include "frontend/TDZCheckCache.h"        // TDZCheckCache
+#include "frontend/TryEmitter.h"           // TryEmitter
+#include "gc/Rooting.h"                    // JS::Rooted, JS::Handle
+#include "vm/BytecodeUtil.h"               // JSOp
+#include "vm/JSAtom.h"                     // JSAtom
+#include "vm/JSFunction.h"                 // JSFunction
+
+namespace js {
+namespace frontend {
+
+struct BytecodeEmitter;
+
+// Class for emitting function declaration, expression, or method etc.
+//
+// This class handles the enclosing script's part (function object creation,
+// declaration, etc). The content of the function script is handled by
+// FunctionScriptEmitter and FunctionParamsEmitter.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+//   `function f() {}`, non lazy script
+//     FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
+//                        FunctionEmitter::IsHoisted::No);
+//     fe.prepareForNonLazy();
+//
+//     // Emit script with FunctionScriptEmitter here.
+//     ...
+//
+//     fe.emitNonLazyEnd();
+//
+//   `function f() {}`, lazy script
+//     FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
+//                        FunctionEmitter::IsHoisted::No);
+//     fe.emitLazy();
+//
+//   `function f() {}`, emitting hoisted function again
+//     // See emitAgain comment for more details
+//     FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
+//                        FunctionEmitter::IsHoisted::Yes);
+//     fe.emitAgain();
+//
+//   `function f() { "use asm"; }`
+//     FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
+//                        FunctionEmitter::IsHoisted::No);
+//     fe.emitAsmJSModule();
+//
+class MOZ_STACK_CLASS FunctionEmitter {
+ public:
+  enum class IsHoisted { No, Yes };
+
+ private:
+  BytecodeEmitter* bce_;
+
+  FunctionBox* funbox_;
+
+  // Function linked from funbox_.
+  JS::Rooted<JSFunction*> fun_;
+
+  // Function's explicit name.
+  JS::Rooted<JSAtom*> name_;
+
+  FunctionSyntaxKind syntaxKind_;
+  IsHoisted isHoisted_;
+
+#ifdef DEBUG
+  // The state of this emitter.
+  //
+  // +-------+
+  // | Start |-+
+  // +-------+ |
+  //           |
+  //   +-------+
+  //   |
+  //   | [non-lazy function]
+  //   |   prepareForNonLazy  +---------+ emitNonLazyEnd     +-----+
+  //   +--------------------->| NonLazy |---------------->+->| End |
+  //   |                      +---------+                 ^  +-----+
+  //   |                                                  |
+  //   | [lazy function]                                  |
+  //   |   emitLazy                                       |
+  //   +------------------------------------------------->+
+  //   |                                                  ^
+  //   | [emitting hoisted function again]                |
+  //   |   emitAgain                                      |
+  //   +------------------------------------------------->+
+  //   |                                                  ^
+  //   | [asm.js module]                                  |
+  //   |   emitAsmJSModule                                |
+  //   +--------------------------------------------------+
+  //
+  enum class State {
+    // The initial state.
+    Start,
+
+    // After calling prepareForNonLazy.
+    NonLazy,
+
+    // After calling emitNonLazyEnd, emitLazy, emitAgain, or emitAsmJSModule.
+    End
+  };
+  State state_ = State::Start;
+#endif
+
+ public:
+  FunctionEmitter(BytecodeEmitter* bce, FunctionBox* funbox,
+                  FunctionSyntaxKind syntaxKind, IsHoisted isHoisted);
+
+  MOZ_MUST_USE bool prepareForNonLazy();
+  MOZ_MUST_USE bool emitNonLazyEnd();
+
+  MOZ_MUST_USE bool emitLazy();
+
+  MOZ_MUST_USE bool emitAgain();
+
+  MOZ_MUST_USE bool emitAsmJSModule();
+
+ private:
+  // Common code for non-lazy and lazy functions.
+  MOZ_MUST_USE bool interpretedCommon();
+
+  // Emit the function declaration, expression, method etc.
+  // This leaves function object on the stack for expression etc,
+  // and doesn't for declaration.
+  MOZ_MUST_USE bool emitFunction();
+
+  // Helper methods used by emitFunction for each case.
+  // `index` is the object index of the function.
+  MOZ_MUST_USE bool emitNonHoisted(unsigned index);
+  MOZ_MUST_USE bool emitHoisted(unsigned index);
+  MOZ_MUST_USE bool emitTopLevelFunction(unsigned index);
+  MOZ_MUST_USE bool emitNewTargetForArrow();
+};
+
+// Class for emitting function script.
+// Parameters are handled by FunctionParamsEmitter.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+//   `function f(a) { expr }`
+//     FunctionScriptEmitter fse(this, funbox_for_f,
+//                               Some(offset_of_opening_paren),
+//                               Some(offset_of_closing_brace));
+//     fse.prepareForParameters();
+//
+//     // Emit parameters with FunctionParamsEmitter here.
+//     ...
+//
+//     fse.prepareForBody();
+//     emit(expr);
+//     fse.emitEnd();
+//
+//     // Do NameFunctions operation here if needed.
+//
+//     fse.initScript();
+//
+class MOZ_STACK_CLASS FunctionScriptEmitter {
+ private:
+  BytecodeEmitter* bce_;
+
+  FunctionBox* funbox_;
+
+  // Scope for the function name for a named lambda.
+  // None for anonymous function.
+  mozilla::Maybe<EmitterScope> namedLambdaEmitterScope_;
+
+  // Scope for function body.
+  mozilla::Maybe<EmitterScope> functionEmitterScope_;
+
+  // Scope for the extra body var.
+  // None if `funbox_->hasExtraBodyVarScope() == false`.
+  mozilla::Maybe<EmitterScope> extraBodyVarEmitterScope_;
+
+  mozilla::Maybe<TDZCheckCache> tdzCache_;
+
+  // try-catch block for async function parameter and body.
+  mozilla::Maybe<TryEmitter> rejectTryCatch_;
+
+  // See the comment for constructor.
+  mozilla::Maybe<uint32_t> paramStart_;
+  mozilla::Maybe<uint32_t> bodyEnd_;
+
+#ifdef DEBUG
+  // The state of this emitter.
+  //
+  // +-------+ prepareForParameters  +------------+
+  // | Start |---------------------->| Parameters |-+
+  // +-------+                       +------------+ |
+  //                                                |
+  //   +--------------------------------------------+
+  //   |
+  //   | prepareForBody  +------+ emitEndBody  +---------+
+  //   +---------------->| Body |------------->| EndBody |-+
+  //                     +------+              +---------+ |
+  //                                                       |
+  //     +-------------------------------------------------+
+  //     |
+  //     | initScript  +-----+
+  //     +------------>| End |
+  //                   +-----+
+  enum class State {
+    // The initial state.
+    Start,
+
+    // After calling prepareForParameters.
+    Parameters,
+
+    // After calling prepareForBody.
+    Body,
+
+    // After calling emitEndBody.
+    EndBody,
+
+    // After calling initScript.
+    End
+  };
+  State state_ = State::Start;
+#endif
+
+ public:
+  // Parameters are the offset in the source code for each character below:
+  //
+  //   function f(a, b, ...c) { ... }
+  //             ^                  ^
+  //             |                  |
+  //             paramStart         bodyEnd
+  //
+  // Can be Nothing() if not available.
+  FunctionScriptEmitter(BytecodeEmitter* bce, FunctionBox* funbox,
+                        const mozilla::Maybe<uint32_t>& paramStart,
+                        const mozilla::Maybe<uint32_t>& bodyEnd)
+      : bce_(bce),
+        funbox_(funbox),
+        paramStart_(paramStart),
+        bodyEnd_(bodyEnd) {}
+
+  MOZ_MUST_USE bool prepareForParameters();
+  MOZ_MUST_USE bool prepareForBody();
+  MOZ_MUST_USE bool emitEndBody();
+
+  // Initialize JSScript for this function.
+  // WARNING: There shouldn't be any fallible operation for the function
+  //          compilation after `initScript` call.
+  //          See the comment inside JSScript::fullyInitFromEmitter for
+  //          more details.
+  MOZ_MUST_USE bool initScript();
+
+ private:
+  MOZ_MUST_USE bool emitExtraBodyVarScope();
+  MOZ_MUST_USE bool emitInitializeInstanceFields();
+
+  // Async functions have implicit try-catch blocks to convert exceptions
+  // into promise rejections.
+  MOZ_MUST_USE bool emitAsyncFunctionRejectPrologue();
+  MOZ_MUST_USE bool emitAsyncFunctionRejectEpilogue();
+};
+
+// Class for emitting function parameters.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+//   `function f(a, b=10, ...c) {}`
+//     FunctionParamsEmitter fpe(this, funbox_for_f);
+//
+//     fpe.emitSimple(atom_of_a);
+//
+//     fpe.prepareForDefault();
+//     emit(10);
+//     fpe.emitDefaultEnd(atom_of_b);
+//
+//     fpe.emitRest(atom_of_c);
+//
+//   `function f([a], [b]=[1], ...[c]) {}`
+//     FunctionParamsEmitter fpe(this, funbox_for_f);
+//
+//     fpe.prepareForDestructuring();
+//     emit(destructuring_for_[a]);
+//     fpe.emitDestructuringEnd();
+//
+//     fpe.prepareForDestructuringDefaultInitializer();
+//     emit([1]);
+//     fpe.prepareForDestructuringDefault();
+//     emit(destructuring_for_[b]);
+//     fpe.emitDestructuringDefaultEnd();
+//
+//     fpe.prepareForDestructuringRest();
+//     emit(destructuring_for_[c]);
+//     fpe.emitDestructuringRestEnd();
+//
+class MOZ_STACK_CLASS FunctionParamsEmitter {
+ private:
+  BytecodeEmitter* bce_;
+
+  FunctionBox* funbox_;
+
+  // The pointer to `FunctionScriptEmitter::functionEmitterScope_`,
+  // passed via `BytecodeEmitter::innermostEmitterScope()`.
+  EmitterScope* functionEmitterScope_;
+
+  // The slot for the current parameter.
+  // NOTE: after emitting rest parameter, this isn't incremented.
+  uint16_t argSlot_ = 0;
+
+  // DefaultEmitter for default parameter.
+  mozilla::Maybe<DefaultEmitter> default_;
+
+  // Scope for each parameter expression.
+  // Populated only when there's `eval` in parameters.
+  mozilla::Maybe<EmitterScope> paramExprVarEmitterScope_;
+
+#ifdef DEBUG
+  // The state of this emitter.
+  //
+  // +----------------------------------------------------------+
+  // |                                                          |
+  // |  +-------+                                               |
+  // +->| Start |-+                                             |
+  //    +-------+ |                                             |
+  //              |                                             |
+  // +------------+                                             |
+  // |                                                          |
+  // | [single binding, wihtout default]                        |
+  // |   emitSimple                                             |
+  // +--------------------------------------------------------->+
+  // |                                                          ^
+  // | [single binding, with default]                           |
+  // |   prepareForDefault  +---------+ emitDefaultEnd          |
+  // +--------------------->| Default |------------------------>+
+  // |                      +---------+                         ^
+  // |                                                          |
+  // | [destructuring, without default]                         |
+  // |   prepareForDestructuring  +---------------+             |
+  // +--------------------------->| Destructuring |-+           |
+  // |                            +---------------+ |           |
+  // |                                              |           |
+  // |    +-----------------------------------------+           |
+  // |    |                                                     |
+  // |    | emitDestructuringEnd                                |
+  // |    +---------------------------------------------------->+
+  // |                                                          ^
+  // | [destructuring, with default]                            |
+  // |   prepareForDestructuringDefaultInitializer              |
+  // +---------------------------------------------+            |
+  // |                                             |            |
+  // |    +----------------------------------------+            |
+  // |    |                                                     |
+  // |    |  +---------------------------------+                |
+  // |    +->| DestructuringDefaultInitializer |-+              |
+  // |       +---------------------------------+ |              |
+  // |                                           |              |
+  // |      +------------------------------------+              |
+  // |      |                                                   |
+  // |      | prepareForDestructuringDefault                    |
+  // |      +-------------------------------+                   |
+  // |                                      |                   |
+  // |        +-----------------------------+                   |
+  // |        |                                                 |
+  // |        |  +----------------------+                       |
+  // |        +->| DestructuringDefault |-+                     |
+  // |           +----------------------+ |                     |
+  // |                                    |                     |
+  // |          +-------------------------+                     |
+  // |          |                                               |
+  // |          | emitDestructuringDefaultEnd                   |
+  // |          +---------------------------------------------->+
+  // |
+  // | [single binding rest]
+  // |   emitRest                                                  +-----+
+  // +--------------------------------------------------------->+->| End |
+  // |                                                          ^  +-----+
+  // | [destructuring rest]                                     |
+  // |   prepareForDestructuringRest   +-------------------+    |
+  // +-------------------------------->| DestructuringRest |-+  |
+  //                                   +-------------------+ |  |
+  //                                                         |  |
+  //    +----------------------------------------------------+  |
+  //    |                                                       |
+  //    | emitDestructuringRestEnd                              |
+  //    +-------------------------------------------------------+
+  //
+  enum class State {
+    // The initial state, or after emitting non-rest parameter.
+    Start,
+
+    // After calling prepareForDefault.
+    Default,
+
+    // After calling prepareForDestructuring.
+    Destructuring,
+
+    // After calling prepareForDestructuringDefaultInitializer.
+    DestructuringDefaultInitializer,
+
+    // After calling prepareForDestructuringDefault.
+    DestructuringDefault,
+
+    // After calling prepareForDestructuringRest.
+    DestructuringRest,
+
+    // After calling emitRest or emitDestructuringRestEnd.
+    End,
+  };
+  State state_ = State::Start;
+#endif
+
+ public:
+  FunctionParamsEmitter(BytecodeEmitter* bce, FunctionBox* funbox);
+
+  // paramName is used only when there's at least one expression in the
+  // paramerters (funbox_->hasParameterExprs == true).
+  MOZ_MUST_USE bool emitSimple(JS::Handle<JSAtom*> paramName);
+
+  MOZ_MUST_USE bool prepareForDefault();
+  MOZ_MUST_USE bool emitDefaultEnd(JS::Handle<JSAtom*> paramName);
+
+  MOZ_MUST_USE bool prepareForDestructuring();
+  MOZ_MUST_USE bool emitDestructuringEnd();
+
+  MOZ_MUST_USE bool prepareForDestructuringDefaultInitializer();
+  MOZ_MUST_USE bool prepareForDestructuringDefault();
+  MOZ_MUST_USE bool emitDestructuringDefaultEnd();
+
+  MOZ_MUST_USE bool emitRest(JS::Handle<JSAtom*> paramName);
+
+  MOZ_MUST_USE bool prepareForDestructuringRest();
+  MOZ_MUST_USE bool emitDestructuringRestEnd();
+
+  MOZ_MUST_USE DestructuringFlavor getDestructuringFlavor();
+
+ private:
+  // Enter/leave var scope for `eval` if necessary.
+  MOZ_MUST_USE bool enterParameterExpressionVarScope();
+  MOZ_MUST_USE bool leaveParameterExpressionVarScope();
+
+  MOZ_MUST_USE bool prepareForInitializer();
+  MOZ_MUST_USE bool emitInitializerEnd();
+
+  MOZ_MUST_USE bool emitRestArray();
+
+  MOZ_MUST_USE bool emitAssignment(JS::Handle<JSAtom*> paramName);
+};
+
+} /* namespace frontend */
+} /* namespace js */
+
+#endif /* frontend_FunctionEmitter_h */
--- a/js/src/frontend/moz.build
+++ b/js/src/frontend/moz.build
@@ -33,16 +33,17 @@ UNIFIED_SOURCES += [
     'DoWhileEmitter.cpp',
     'ElemOpEmitter.cpp',
     'EmitterScope.cpp',
     'ExpressionStatementEmitter.cpp',
     'FoldConstants.cpp',
     'ForInEmitter.cpp',
     'ForOfEmitter.cpp',
     'ForOfLoopControl.cpp',
+    'FunctionEmitter.cpp',
     'IfEmitter.cpp',
     'JumpList.cpp',
     'LabelEmitter.cpp',
     'LexicalScopeEmitter.cpp',
     'NameFunctions.cpp',
     'NameOpEmitter.cpp',
     'ObjectEmitter.cpp',
     'ParseContext.cpp',