js/src/frontend/BytecodeEmitter.cpp
author yulia <ystartsev@mozilla.com>
Thu, 02 Apr 2020 20:08:15 +0000
changeset 521996 d06d14647f6fe993990383a12b185a3a54bf4872
parent 521974 1b8fcc1836cea6e60b1278c830287a52a2c8adc1
child 522699 2003cee11727ffbf1f798eb3265799733e30d41c
permissions -rw-r--r--
Bug 1625116 - add templateStringListExpr to list of optionalexprs; r=jorendorff Differential Revision: https://phabricator.services.mozilla.com/D69078

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * JS bytecode generation.
 */

#include "frontend/BytecodeEmitter.h"

#include "mozilla/ArrayUtils.h"  // mozilla::ArrayLength
#include "mozilla/Casting.h"     // mozilla::AssertedCast
#include "mozilla/DebugOnly.h"   // mozilla::DebugOnly
#include "mozilla/FloatingPoint.h"  // mozilla::NumberEqualsInt32, mozilla::NumberIsInt32
#include "mozilla/Maybe.h"          // mozilla::{Maybe,Nothing,Some}
#include "mozilla/PodOperations.h"  // mozilla::PodCopy
#include "mozilla/Sprintf.h"        // SprintfLiteral
#include "mozilla/Unused.h"         // mozilla::Unused
#include "mozilla/Variant.h"        // mozilla::AsVariant

#include <algorithm>
#include <string.h>

#include "jstypes.h"  // JS_BIT

#include "ds/Nestable.h"  // Nestable
#include "frontend/AbstractScopePtr.h"
#include "frontend/BCEScriptStencil.h"           // BCEScriptStencil
#include "frontend/BytecodeControlStructures.h"  // NestableControl, BreakableControl, LabelControl, LoopControl, TryFinallyControl
#include "frontend/CallOrNewEmitter.h"           // CallOrNewEmitter
#include "frontend/CForEmitter.h"                // CForEmitter
#include "frontend/DefaultEmitter.h"             // DefaultEmitter
#include "frontend/DoWhileEmitter.h"             // DoWhileEmitter
#include "frontend/ElemOpEmitter.h"              // ElemOpEmitter
#include "frontend/EmitterScope.h"               // EmitterScope
#include "frontend/ExpressionStatementEmitter.h"  // ExpressionStatementEmitter
#include "frontend/ForInEmitter.h"                // ForInEmitter
#include "frontend/ForOfEmitter.h"                // ForOfEmitter
#include "frontend/ForOfLoopControl.h"            // ForOfLoopControl
#include "frontend/FunctionEmitter.h"  // FunctionEmitter, FunctionScriptEmitter, FunctionParamsEmitter
#include "frontend/IfEmitter.h"     // IfEmitter, InternalIfEmitter, CondEmitter
#include "frontend/LabelEmitter.h"  // LabelEmitter
#include "frontend/LexicalScopeEmitter.h"  // LexicalScopeEmitter
#include "frontend/ModuleSharedContext.h"  // ModuleSharedContext
#include "frontend/NameFunctions.h"        // NameFunctions
#include "frontend/NameOpEmitter.h"        // NameOpEmitter
#include "frontend/ObjectEmitter.h"  // PropertyEmitter, ObjectEmitter, ClassEmitter
#include "frontend/OptionalEmitter.h"  // OptionalEmitter
#include "frontend/ParseNode.h"      // ParseNodeKind, ParseNode and subclasses
#include "frontend/Parser.h"         // Parser
#include "frontend/PropOpEmitter.h"  // PropOpEmitter
#include "frontend/SourceNotes.h"    // SrcNote, SrcNoteType, SrcNoteWriter
#include "frontend/SwitchEmitter.h"  // SwitchEmitter
#include "frontend/TDZCheckCache.h"  // TDZCheckCache
#include "frontend/TryEmitter.h"     // TryEmitter
#include "frontend/WhileEmitter.h"   // WhileEmitter
#include "js/CompileOptions.h"       // TransitiveCompileOptions, CompileOptions
#include "vm/AsyncFunctionResolveKind.h"  // AsyncFunctionResolveKind
#include "vm/BytecodeUtil.h"  // JOF_*, IsArgOp, IsLocalOp, SET_UINT24, SET_ICINDEX, BytecodeFallsThrough, BytecodeIsJumpTarget
#include "vm/FunctionPrefixKind.h"  // FunctionPrefixKind
#include "vm/GeneratorObject.h"     // AbstractGeneratorObject
#include "vm/JSAtom.h"              // JSAtom, js_*_str
#include "vm/JSContext.h"           // JSContext
#include "vm/JSFunction.h"          // JSFunction,
#include "vm/JSScript.h"  // JSScript, ScriptSourceObject, FieldInitializers, BaseScript
#include "vm/Opcodes.h"        // JSOp, JSOpLength_*
#include "vm/SharedStencil.h"  // ScopeNote
#include "vm/ThrowMsgKind.h"   // ThrowMsgKind
#include "wasm/AsmJS.h"        // IsAsmJSModule

#include "vm/JSObject-inl.h"  // JSObject

using namespace js;
using namespace js::frontend;

using mozilla::ArrayLength;
using mozilla::AssertedCast;
using mozilla::AsVariant;
using mozilla::DebugOnly;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::NumberEqualsInt32;
using mozilla::NumberIsInt32;
using mozilla::PodCopy;
using mozilla::Some;
using mozilla::Unused;

static bool ParseNodeRequiresSpecialLineNumberNotes(ParseNode* pn) {
  // The few node types listed below are exceptions to the usual
  // location-source-note-emitting code in BytecodeEmitter::emitTree().
  // Single-line `while` loops and C-style `for` loops require careful
  // handling to avoid strange stepping behavior.
  // Functions usually shouldn't have location information (bug 1431202).

  ParseNodeKind kind = pn->getKind();
  return kind == ParseNodeKind::WhileStmt || kind == ParseNodeKind::ForStmt ||
         kind == ParseNodeKind::Function;
}

BytecodeEmitter::BytecodeEmitter(
    BytecodeEmitter* parent, SharedContext* sc, HandleScript script,
    Handle<BaseScript*> lazyScript, uint32_t line, uint32_t column,
    CompilationInfo& compilationInfo, EmitterMode emitterMode,
    FieldInitializers fieldInitializers /* = FieldInitializers::Invalid() */)
    : sc(sc),
      cx(sc->cx_),
      parent(parent),
      script(cx, script),
      lazyScript(cx, lazyScript),
      bytecodeSection_(cx, line),
      perScriptData_(cx, compilationInfo),
      fieldInitializers_(fieldInitializers),
      compilationInfo(compilationInfo),
      firstLine(line),
      firstColumn(column),
      emitterMode(emitterMode) {
  MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript);

  if (IsTypeInferenceEnabled() && sc->isFunctionBox()) {
    // Functions have IC entries for type monitoring |this| and arguments.
    bytecodeSection().setNumICEntries(sc->asFunctionBox()->nargs() + 1);
  }
}

BytecodeEmitter::BytecodeEmitter(
    BytecodeEmitter* parent, BCEParserHandle* handle, SharedContext* sc,
    HandleScript script, Handle<BaseScript*> lazyScript, uint32_t line,
    uint32_t column, CompilationInfo& compilationInfo, EmitterMode emitterMode,
    FieldInitializers fieldInitializers)
    : BytecodeEmitter(parent, sc, script, lazyScript, line, column,
                      compilationInfo, emitterMode, fieldInitializers) {
  parser = handle;
  instrumentationKinds = parser->options().instrumentationKinds;
}

BytecodeEmitter::BytecodeEmitter(
    BytecodeEmitter* parent, const EitherParser& parser, SharedContext* sc,
    HandleScript script, Handle<BaseScript*> lazyScript, uint32_t line,
    uint32_t column, CompilationInfo& compilationInfo, EmitterMode emitterMode,
    FieldInitializers fieldInitializers)
    : BytecodeEmitter(parent, sc, script, lazyScript, line, column,
                      compilationInfo, emitterMode, fieldInitializers) {
  ep_.emplace(parser);
  this->parser = ep_.ptr();
  instrumentationKinds = this->parser->options().instrumentationKinds;
}

void BytecodeEmitter::initFromBodyPosition(TokenPos bodyPosition) {
  setScriptStartOffsetIfUnset(bodyPosition.begin);
  setFunctionBodyEndPos(bodyPosition.end);
}

bool BytecodeEmitter::init() { return perScriptData_.init(cx); }

bool BytecodeEmitter::init(TokenPos bodyPosition) {
  initFromBodyPosition(bodyPosition);
  return init();
}

template <typename T>
T* BytecodeEmitter::findInnermostNestableControl() const {
  return NestableControl::findNearest<T>(innermostNestableControl);
}

template <typename T, typename Predicate /* (T*) -> bool */>
T* BytecodeEmitter::findInnermostNestableControl(Predicate predicate) const {
  return NestableControl::findNearest<T>(innermostNestableControl, predicate);
}

NameLocation BytecodeEmitter::lookupName(JSAtom* name) {
  return innermostEmitterScope()->lookup(this, name);
}

Maybe<NameLocation> BytecodeEmitter::locationOfNameBoundInScope(
    JSAtom* name, EmitterScope* target) {
  return innermostEmitterScope()->locationBoundInScope(name, target);
}

Maybe<NameLocation> BytecodeEmitter::locationOfNameBoundInFunctionScope(
    JSAtom* name, EmitterScope* source) {
  EmitterScope* funScope = source;
  while (!funScope->scope(this).is<FunctionScope>()) {
    funScope = funScope->enclosingInFrame();
  }
  return source->locationBoundInScope(name, funScope);
}

bool BytecodeEmitter::markStepBreakpoint() {
  if (skipBreakpointSrcNotes()) {
    return true;
  }

  if (!emitInstrumentation(InstrumentationKind::Breakpoint)) {
    return false;
  }

  if (!newSrcNote(SrcNoteType::StepSep)) {
    return false;
  }

  if (!newSrcNote(SrcNoteType::Breakpoint)) {
    return false;
  }

  // We track the location of the most recent separator for use in
  // markSimpleBreakpoint. Note that this means that the position must already
  // be set before markStepBreakpoint is called.
  bytecodeSection().updateSeparatorPosition();

  return true;
}

bool BytecodeEmitter::markSimpleBreakpoint() {
  if (skipBreakpointSrcNotes()) {
    return true;
  }

  // If a breakable call ends up being the same location as the most recent
  // expression start, we need to skip marking it breakable in order to avoid
  // having two breakpoints with the same line/column position.
  // Note: This assumes that the position for the call has already been set.
  if (!bytecodeSection().isDuplicateLocation()) {
    if (!emitInstrumentation(InstrumentationKind::Breakpoint)) {
      return false;
    }

    if (!newSrcNote(SrcNoteType::Breakpoint)) {
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitCheck(JSOp op, ptrdiff_t delta,
                                BytecodeOffset* offset) {
  size_t oldLength = bytecodeSection().code().length();
  *offset = BytecodeOffset(oldLength);

  size_t newLength = oldLength + size_t(delta);
  if (MOZ_UNLIKELY(newLength > MaxBytecodeLength)) {
    ReportAllocationOverflow(cx);
    return false;
  }

  if (!bytecodeSection().code().growByUninitialized(delta)) {
    return false;
  }

  if (BytecodeOpHasTypeSet(op)) {
    bytecodeSection().incrementNumTypeSets();
  }

  if (BytecodeOpHasIC(op)) {
    // Even if every bytecode op is a JOF_IC op and the function has ARGC_LIMIT
    // arguments, numICEntries cannot overflow.
    static_assert(MaxBytecodeLength + 1 /* this */ + ARGC_LIMIT <= UINT32_MAX,
                  "numICEntries must not overflow");
    bytecodeSection().incrementNumICEntries();
  }

  return true;
}

#ifdef DEBUG
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(op));

  BytecodeOffset offset;
  if (!emitCheck(op, 1, &offset)) {
    return false;
  }

  jsbytecode* code = bytecodeSection().code(offset);
  code[0] = jsbytecode(op);
  bytecodeSection().updateDepth(offset);
  return true;
}

bool BytecodeEmitter::emit2(JSOp op, uint8_t op1) {
  MOZ_ASSERT(checkStrictOrSloppy(op));

  BytecodeOffset offset;
  if (!emitCheck(op, 2, &offset)) {
    return false;
  }

  jsbytecode* code = bytecodeSection().code(offset);
  code[0] = jsbytecode(op);
  code[1] = jsbytecode(op1);
  bytecodeSection().updateDepth(offset);
  return true;
}

bool BytecodeEmitter::emit3(JSOp op, jsbytecode op1, jsbytecode op2) {
  MOZ_ASSERT(checkStrictOrSloppy(op));

  /* These should filter through emitVarOp. */
  MOZ_ASSERT(!IsArgOp(op));
  MOZ_ASSERT(!IsLocalOp(op));

  BytecodeOffset offset;
  if (!emitCheck(op, 3, &offset)) {
    return false;
  }

  jsbytecode* code = bytecodeSection().code(offset);
  code[0] = jsbytecode(op);
  code[1] = op1;
  code[2] = op2;
  bytecodeSection().updateDepth(offset);
  return true;
}

bool BytecodeEmitter::emitN(JSOp op, size_t extra, BytecodeOffset* offset) {
  MOZ_ASSERT(checkStrictOrSloppy(op));
  ptrdiff_t length = 1 + ptrdiff_t(extra);

  BytecodeOffset off;
  if (!emitCheck(op, length, &off)) {
    return false;
  }

  jsbytecode* code = bytecodeSection().code(off);
  code[0] = jsbytecode(op);
  /* The remaining |extra| bytes are set by the caller */

  /*
   * Don't updateDepth if op's use-count comes from the immediate
   * operand yet to be stored in the extra bytes after op.
   */
  if (CodeSpec(op).nuses >= 0) {
    bytecodeSection().updateDepth(off);
  }

  if (offset) {
    *offset = off;
  }
  return true;
}

bool BytecodeEmitter::emitJumpTargetOp(JSOp op, BytecodeOffset* off) {
  MOZ_ASSERT(BytecodeIsJumpTarget(op));

  // Record the current IC-entry index at start of this op.
  uint32_t numEntries = bytecodeSection().numICEntries();

  size_t n = GetOpLength(op) - 1;
  MOZ_ASSERT(GetOpLength(op) >= 1 + UINT32_INDEX_LEN);

  if (!emitN(op, n, off)) {
    return false;
  }

  SET_ICINDEX(bytecodeSection().code(*off), numEntries);
  return true;
}

bool BytecodeEmitter::emitJumpTarget(JumpTarget* target) {
  BytecodeOffset off = bytecodeSection().offset();

  // Alias consecutive jump targets.
  if (bytecodeSection().lastTargetOffset().valid() &&
      off == bytecodeSection().lastTargetOffset() +
                 BytecodeOffsetDiff(JSOpLength_JumpTarget)) {
    target->offset = bytecodeSection().lastTargetOffset();
    return true;
  }

  target->offset = off;
  bytecodeSection().setLastTargetOffset(off);

  BytecodeOffset opOff;
  return emitJumpTargetOp(JSOp::JumpTarget, &opOff);
}

bool BytecodeEmitter::emitJumpNoFallthrough(JSOp op, JumpList* jump) {
  BytecodeOffset offset;
  if (!emitCheck(op, 5, &offset)) {
    return false;
  }

  jsbytecode* code = bytecodeSection().code(offset);
  code[0] = jsbytecode(op);
  MOZ_ASSERT(!jump->offset.valid() ||
             (0 <= jump->offset.value() && jump->offset < offset));
  jump->push(bytecodeSection().code(BytecodeOffset(0)), offset);
  bytecodeSection().updateDepth(offset);
  return true;
}

bool BytecodeEmitter::emitJump(JSOp op, JumpList* jump) {
  if (!emitJumpNoFallthrough(op, jump)) {
    return false;
  }
  if (BytecodeFallsThrough(op)) {
    JumpTarget fallthrough;
    if (!emitJumpTarget(&fallthrough)) {
      return false;
    }
  }
  return true;
}

void BytecodeEmitter::patchJumpsToTarget(JumpList jump, JumpTarget target) {
  MOZ_ASSERT(
      !jump.offset.valid() ||
      (0 <= jump.offset.value() && jump.offset <= bytecodeSection().offset()));
  MOZ_ASSERT(0 <= target.offset.value() &&
             target.offset <= bytecodeSection().offset());
  MOZ_ASSERT_IF(
      jump.offset.valid() &&
          target.offset + BytecodeOffsetDiff(4) <= bytecodeSection().offset(),
      BytecodeIsJumpTarget(JSOp(*bytecodeSection().code(target.offset))));
  jump.patchAll(bytecodeSection().code(BytecodeOffset(0)), target);
}

bool BytecodeEmitter::emitJumpTargetAndPatch(JumpList jump) {
  if (!jump.offset.valid()) {
    return true;
  }
  JumpTarget target;
  if (!emitJumpTarget(&target)) {
    return false;
  }
  patchJumpsToTarget(jump, target);
  return true;
}

bool BytecodeEmitter::emitCall(JSOp op, uint16_t argc,
                               const Maybe<uint32_t>& sourceCoordOffset) {
  if (sourceCoordOffset.isSome()) {
    if (!updateSourceCoordNotes(*sourceCoordOffset)) {
      return false;
    }
  }
  return emit3(op, ARGC_LO(argc), ARGC_HI(argc));
}

bool BytecodeEmitter::emitCall(JSOp op, uint16_t argc, ParseNode* pn) {
  return emitCall(op, argc, pn ? Some(pn->pn_pos.begin) : Nothing());
}

bool BytecodeEmitter::emitDupAt(unsigned slotFromTop, unsigned count) {
  MOZ_ASSERT(slotFromTop < unsigned(bytecodeSection().stackDepth()));
  MOZ_ASSERT(slotFromTop + 1 >= count);

  if (slotFromTop == 0 && count == 1) {
    return emit1(JSOp::Dup);
  }

  if (slotFromTop == 1 && count == 2) {
    return emit1(JSOp::Dup2);
  }

  if (slotFromTop >= Bit(24)) {
    reportError(nullptr, JSMSG_TOO_MANY_LOCALS);
    return false;
  }

  for (unsigned i = 0; i < count; i++) {
    BytecodeOffset off;
    if (!emitN(JSOp::DupAt, 3, &off)) {
      return false;
    }

    jsbytecode* pc = bytecodeSection().code(off);
    SET_UINT24(pc, slotFromTop);
  }

  return true;
}

bool BytecodeEmitter::emitPopN(unsigned n) {
  MOZ_ASSERT(n != 0);

  if (n == 1) {
    return emit1(JSOp::Pop);
  }

  // 2 JSOp::Pop instructions (2 bytes) are shorter than JSOp::PopN (3 bytes).
  if (n == 2) {
    return emit1(JSOp::Pop) && emit1(JSOp::Pop);
  }

  return emitUint16Operand(JSOp::PopN, n);
}

bool BytecodeEmitter::emitCheckIsObj(CheckIsObjectKind kind) {
  return emit2(JSOp::CheckIsObj, uint8_t(kind));
}

bool BytecodeEmitter::emitCheckIsCallable(CheckIsCallableKind kind) {
  return emit2(JSOp::CheckIsCallable, uint8_t(kind));
}

/* Updates line number notes, not column notes. */
bool BytecodeEmitter::updateLineNumberNotes(uint32_t offset) {
  if (skipLocationSrcNotes()) {
    return true;
  }

  ErrorReporter* er = &parser->errorReporter();
  bool onThisLine;
  if (!er->isOnThisLine(offset, bytecodeSection().currentLine(), &onThisLine)) {
    er->errorNoOffset(JSMSG_OUT_OF_MEMORY);
    return false;
  }

  if (!onThisLine) {
    unsigned line = er->lineAt(offset);
    unsigned delta = line - bytecodeSection().currentLine();

    /*
     * Encode any change in the current source line number by using
     * either several SrcNoteType::NewLine notes or just one
     * SrcNoteType::SetLine note, whichever consumes less space.
     *
     * NB: We handle backward line number deltas (possible with for
     * 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
     * SrcNoteType::SetLine.
     */
    bytecodeSection().setCurrentLine(line, offset);
    if (delta >= SrcNote::SetLine::lengthFor(line)) {
      if (!newSrcNote2(SrcNoteType::SetLine,
                       SrcNote::SetLine::toOperand(line))) {
        return false;
      }
    } else {
      do {
        if (!newSrcNote(SrcNoteType::NewLine)) {
          return false;
        }
      } while (--delta != 0);
    }

    bytecodeSection().updateSeparatorPositionIfPresent();
  }
  return true;
}

/* Updates the line number and column number information in the source notes. */
bool BytecodeEmitter::updateSourceCoordNotes(uint32_t offset) {
  if (!updateLineNumberNotes(offset)) {
    return false;
  }

  if (skipLocationSrcNotes()) {
    return true;
  }

  uint32_t columnIndex = parser->errorReporter().columnAt(offset);
  ptrdiff_t colspan =
      ptrdiff_t(columnIndex) - ptrdiff_t(bytecodeSection().lastColumn());
  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 (!SrcNote::ColSpan::isRepresentable(colspan)) {
      return true;
    }
    if (!newSrcNote2(SrcNoteType::ColSpan,
                     SrcNote::ColSpan::toOperand(colspan))) {
      return false;
    }
    bytecodeSection().setLastColumn(columnIndex, offset);
    bytecodeSection().updateSeparatorPositionIfPresent();
  }
  return true;
}

Maybe<uint32_t> BytecodeEmitter::getOffsetForLoop(ParseNode* nextpn) {
  if (!nextpn) {
    return Nothing();
  }

  // Try to give the JSOp::LoopHead the same line number as the next
  // instruction. nextpn is often a block, in which case the next instruction
  // typically comes from the first statement inside.
  if (nextpn->is<LexicalScopeNode>()) {
    nextpn = nextpn->as<LexicalScopeNode>().scopeBody();
  }
  if (nextpn->isKind(ParseNodeKind::StatementList)) {
    if (ParseNode* firstStatement = nextpn->as<ListNode>().head()) {
      nextpn = firstStatement;
    }
  }

  return Some(nextpn->pn_pos.begin);
}

bool BytecodeEmitter::emitUint16Operand(JSOp op, uint32_t operand) {
  MOZ_ASSERT(operand <= UINT16_MAX);
  if (!emit3(op, UINT16_LO(operand), UINT16_HI(operand))) {
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitUint32Operand(JSOp op, uint32_t operand) {
  BytecodeOffset off;
  if (!emitN(op, 4, &off)) {
    return false;
  }
  SET_UINT32(bytecodeSection().code(off), operand);
  return true;
}

namespace {

class NonLocalExitControl {
 public:
  enum Kind {
    // IteratorClose is handled especially inside the exception unwinder.
    Throw,

    // A 'continue' statement does not call IteratorClose for the loop it
    // is continuing, i.e. excluding the target loop.
    Continue,

    // A 'break' or 'return' statement does call IteratorClose for the
    // loop it is breaking out of or returning from, i.e. including the
    // target loop.
    Break,
    Return
  };

 private:
  BytecodeEmitter* bce_;
  const uint32_t savedScopeNoteIndex_;
  const int savedDepth_;
  uint32_t openScopeNoteIndex_;
  Kind kind_;

  NonLocalExitControl(const NonLocalExitControl&) = delete;

  MOZ_MUST_USE bool leaveScope(EmitterScope* scope);

 public:
  NonLocalExitControl(BytecodeEmitter* bce, Kind kind)
      : bce_(bce),
        savedScopeNoteIndex_(bce->bytecodeSection().scopeNoteList().length()),
        savedDepth_(bce->bytecodeSection().stackDepth()),
        openScopeNoteIndex_(bce->innermostEmitterScope()->noteIndex()),
        kind_(kind) {}

  ~NonLocalExitControl() {
    for (uint32_t n = savedScopeNoteIndex_;
         n < bce_->bytecodeSection().scopeNoteList().length(); n++) {
      bce_->bytecodeSection().scopeNoteList().recordEnd(
          n, bce_->bytecodeSection().offset());
    }
    bce_->bytecodeSection().setStackDepth(savedDepth_);
  }

  MOZ_MUST_USE bool prepareForNonLocalJump(NestableControl* target);

  MOZ_MUST_USE bool prepareForNonLocalJumpToOutermost() {
    return prepareForNonLocalJump(nullptr);
  }
};

bool NonLocalExitControl::leaveScope(EmitterScope* es) {
  if (!es->leave(bce_, /* nonLocal = */ true)) {
    return false;
  }

  // As we pop each scope due to the non-local jump, emit notes that
  // record the extent of the enclosing scope. These notes will have
  // their ends recorded in ~NonLocalExitControl().
  uint32_t enclosingScopeIndex = ScopeNote::NoScopeIndex;
  if (es->enclosingInFrame()) {
    enclosingScopeIndex = es->enclosingInFrame()->index();
  }
  if (!bce_->bytecodeSection().scopeNoteList().append(
          enclosingScopeIndex, bce_->bytecodeSection().offset(),
          openScopeNoteIndex_)) {
    return false;
  }
  openScopeNoteIndex_ = bce_->bytecodeSection().scopeNoteList().length() - 1;

  return true;
}

/*
 * Emit additional bytecode(s) for non-local jumps.
 */
bool NonLocalExitControl::prepareForNonLocalJump(NestableControl* target) {
  EmitterScope* es = bce_->innermostEmitterScope();
  int npops = 0;

  AutoCheckUnstableEmitterScope cues(bce_);

  // For 'continue', 'break', and 'return' statements, emit IteratorClose
  // bytecode inline. 'continue' statements do not call IteratorClose for
  // the loop they are continuing.
  bool emitIteratorClose =
      kind_ == Continue || kind_ == Break || kind_ == Return;
  bool emitIteratorCloseAtTarget = emitIteratorClose && kind_ != Continue;

  auto flushPops = [&npops](BytecodeEmitter* bce) {
    if (npops && !bce->emitPopN(npops)) {
      return false;
    }
    npops = 0;
    return true;
  };

  // If we are closing multiple for-of loops, the resulting FOR_OF_ITERCLOSE
  // trynotes must be appropriately nested. Each FOR_OF_ITERCLOSE starts when
  // we close the corresponding for-of iterator, and continues until the
  // actual jump.
  Vector<BytecodeOffset, 4> forOfIterCloseScopeStarts(bce_->cx);

  // Walk the nestable control stack and patch jumps.
  for (NestableControl* control = bce_->innermostNestableControl;
       control != target; control = control->enclosing()) {
    // Walk the scope stack and leave the scopes we entered. Leaving a scope
    // may emit administrative ops like JSOp::PopLexicalEnv but never anything
    // that manipulates the stack.
    for (; es != control->emitterScope(); es = es->enclosingInFrame()) {
      if (!leaveScope(es)) {
        return false;
      }
    }

    switch (control->kind()) {
      case StatementKind::Finally: {
        TryFinallyControl& finallyControl = control->as<TryFinallyControl>();
        if (finallyControl.emittingSubroutine()) {
          /*
           * There's a [exception or hole, retsub pc-index] pair and the
           * possible return value on the stack that we need to pop.
           */
          npops += 3;
        } else {
          if (!flushPops(bce_)) {
            return false;
          }
          if (!bce_->emitGoSub(&finallyControl.gosubs)) {
            //      [stack] ...
            return false;
          }
        }
        break;
      }

      case StatementKind::ForOfLoop:
        if (emitIteratorClose) {
          if (!flushPops(bce_)) {
            return false;
          }
          BytecodeOffset tryNoteStart;
          ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>();
          if (!loopinfo.emitPrepareForNonLocalJumpFromScope(
                  bce_, *es,
                  /* isTarget = */ false, &tryNoteStart)) {
            //      [stack] ...
            return false;
          }
          if (!forOfIterCloseScopeStarts.append(tryNoteStart)) {
            return false;
          }
        } else {
          // The iterator next method, the iterator, and the current
          // value are on the stack.
          npops += 3;
        }
        break;

      case StatementKind::ForInLoop:
        if (!flushPops(bce_)) {
          return false;
        }

        // The iterator and the current value are on the stack.
        if (!bce_->emit1(JSOp::EndIter)) {
          //        [stack] ...
          return false;
        }
        break;

      default:
        break;
    }
  }

  if (!flushPops(bce_)) {
    return false;
  }

  if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) {
    BytecodeOffset tryNoteStart;
    ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>();
    if (!loopinfo.emitPrepareForNonLocalJumpFromScope(bce_, *es,
                                                      /* isTarget = */ true,
                                                      &tryNoteStart)) {
      //            [stack] ... UNDEF UNDEF UNDEF
      return false;
    }
    if (!forOfIterCloseScopeStarts.append(tryNoteStart)) {
      return false;
    }
  }

  EmitterScope* targetEmitterScope =
      target ? target->emitterScope() : bce_->varEmitterScope;
  for (; es != targetEmitterScope; es = es->enclosingInFrame()) {
    if (!leaveScope(es)) {
      return false;
    }
  }

  // Close FOR_OF_ITERCLOSE trynotes.
  BytecodeOffset end = bce_->bytecodeSection().offset();
  for (BytecodeOffset start : forOfIterCloseScopeStarts) {
    if (!bce_->addTryNote(TryNoteKind::ForOfIterClose, 0, start, end)) {
      return false;
    }
  }

  return true;
}

}  // anonymous namespace

bool BytecodeEmitter::emitGoto(NestableControl* target, JumpList* jumplist,
                               GotoKind kind) {
  NonLocalExitControl nle(this, kind == GotoKind::Continue
                                    ? NonLocalExitControl::Continue
                                    : NonLocalExitControl::Break);

  if (!nle.prepareForNonLocalJump(target)) {
    return false;
  }

  return emitJump(JSOp::Goto, jumplist);
}

AbstractScopePtr BytecodeEmitter::innermostScope() const {
  return innermostEmitterScope()->scope(this);
}

bool BytecodeEmitter::emitIndexOp(JSOp op, uint32_t index) {
  MOZ_ASSERT(checkStrictOrSloppy(op));

  constexpr size_t OpLength = 1 + UINT32_INDEX_LEN;
  MOZ_ASSERT(GetOpLength(op) == OpLength);

  BytecodeOffset offset;
  if (!emitCheck(op, OpLength, &offset)) {
    return false;
  }

  jsbytecode* code = bytecodeSection().code(offset);
  code[0] = jsbytecode(op);
  SET_UINT32_INDEX(code, index);
  bytecodeSection().updateDepth(offset);
  return true;
}

bool BytecodeEmitter::emitAtomOp(JSOp op, JSAtom* atom,
                                 ShouldInstrument shouldInstrument) {
  MOZ_ASSERT(atom);

  // .generator lookups should be emitted as JSOp::GetAliasedVar instead of
  // JSOp::GetName etc, to bypass |with| objects on the scope chain.
  // It's safe to emit .this lookups though because |with| objects skip
  // those.
  MOZ_ASSERT_IF(op == JSOp::GetName || op == JSOp::GetGName,
                atom != cx->names().dotGenerator);

  if (op == JSOp::GetProp && atom == cx->names().length) {
    /* Specialize length accesses for the interpreter. */
    op = JSOp::Length;
  }

  uint32_t index;
  if (!makeAtomIndex(atom, &index)) {
    return false;
  }

  return emitAtomOp(op, index, shouldInstrument);
}

bool BytecodeEmitter::emitAtomOp(JSOp op, uint32_t atomIndex,
                                 ShouldInstrument shouldInstrument) {
  MOZ_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);

  if (shouldInstrument != ShouldInstrument::No &&
      !emitInstrumentationForOpcode(op, atomIndex)) {
    return false;
  }

  return emitIndexOp(op, atomIndex);
}

bool BytecodeEmitter::emitInternedScopeOp(uint32_t index, JSOp op) {
  MOZ_ASSERT(JOF_OPTYPE(op) == JOF_SCOPE);
  MOZ_ASSERT(index < perScriptData().gcThingList().length());
  return emitIndexOp(op, index);
}

bool BytecodeEmitter::emitInternedObjectOp(uint32_t index, JSOp op) {
  MOZ_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT);
  MOZ_ASSERT(index < perScriptData().gcThingList().length());
  return emitIndexOp(op, index);
}

bool BytecodeEmitter::emitObjectPairOp(uint32_t index1, uint32_t index2,
                                       JSOp op) {
  MOZ_ASSERT(index1 + 1 == index2, "object pair indices must be adjacent");
  return emitInternedObjectOp(index1, op);
}

bool BytecodeEmitter::emitRegExp(uint32_t index) {
  return emitIndexOp(JSOp::RegExp, index);
}

bool BytecodeEmitter::emitLocalOp(JSOp op, uint32_t slot) {
  MOZ_ASSERT(JOF_OPTYPE(op) != JOF_ENVCOORD);
  MOZ_ASSERT(IsLocalOp(op));

  BytecodeOffset off;
  if (!emitN(op, LOCALNO_LEN, &off)) {
    return false;
  }

  SET_LOCALNO(bytecodeSection().code(off), slot);
  return true;
}

bool BytecodeEmitter::emitArgOp(JSOp op, uint16_t slot) {
  MOZ_ASSERT(IsArgOp(op));
  BytecodeOffset off;
  if (!emitN(op, ARGNO_LEN, &off)) {
    return false;
  }

  SET_ARGNO(bytecodeSection().code(off), slot);
  return true;
}

bool BytecodeEmitter::emitEnvCoordOp(JSOp op, EnvironmentCoordinate ec) {
  MOZ_ASSERT(JOF_OPTYPE(op) == JOF_ENVCOORD);

  constexpr size_t N = ENVCOORD_HOPS_LEN + ENVCOORD_SLOT_LEN;
  MOZ_ASSERT(GetOpLength(op) == 1 + N);

  BytecodeOffset off;
  if (!emitN(op, N, &off)) {
    return false;
  }

  jsbytecode* pc = bytecodeSection().code(off);
  SET_ENVCOORD_HOPS(pc, ec.hops());
  pc += ENVCOORD_HOPS_LEN;
  SET_ENVCOORD_SLOT(pc, ec.slot());
  pc += ENVCOORD_SLOT_LEN;
  return true;
}

JSOp BytecodeEmitter::strictifySetNameOp(JSOp op) {
  switch (op) {
    case JSOp::SetName:
      if (sc->strict()) {
        op = JSOp::StrictSetName;
      }
      break;
    case JSOp::SetGName:
      if (sc->strict()) {
        op = JSOp::StrictSetGName;
      }
      break;
    default:;
  }
  return op;
}

bool BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) {
  if (!CheckRecursionLimit(cx)) {
    return false;
  }

restart:

  switch (pn->getKind()) {
    // Trivial cases with no side effects.
    case ParseNodeKind::EmptyStmt:
    case ParseNodeKind::TrueExpr:
    case ParseNodeKind::FalseExpr:
    case ParseNodeKind::NullExpr:
    case ParseNodeKind::RawUndefinedExpr:
    case ParseNodeKind::Elision:
    case ParseNodeKind::Generator:
      MOZ_ASSERT(pn->is<NullaryNode>());
      *answer = false;
      return true;

    case ParseNodeKind::ObjectPropertyName:
    case ParseNodeKind::PrivateName:  // no side effects, unlike
                                      // ParseNodeKind::Name
    case ParseNodeKind::StringExpr:
    case ParseNodeKind::TemplateStringExpr:
      MOZ_ASSERT(pn->is<NameNode>());
      *answer = false;
      return true;

    case ParseNodeKind::RegExpExpr:
      MOZ_ASSERT(pn->is<RegExpLiteral>());
      *answer = false;
      return true;

    case ParseNodeKind::NumberExpr:
      MOZ_ASSERT(pn->is<NumericLiteral>());
      *answer = false;
      return true;

    case ParseNodeKind::BigIntExpr:
      MOZ_ASSERT(pn->is<BigIntLiteral>());
      *answer = false;
      return true;

    // |this| can throw in derived class constructors, including nested arrow
    // functions or eval.
    case ParseNodeKind::ThisExpr:
      MOZ_ASSERT(pn->is<UnaryNode>());
      *answer = sc->needsThisTDZChecks();
      return true;

    // Trivial binary nodes with more token pos holders.
    case ParseNodeKind::NewTargetExpr:
    case ParseNodeKind::ImportMetaExpr: {
      MOZ_ASSERT(pn->as<BinaryNode>().left()->isKind(ParseNodeKind::PosHolder));
      MOZ_ASSERT(
          pn->as<BinaryNode>().right()->isKind(ParseNodeKind::PosHolder));
      *answer = false;
      return true;
    }

    case ParseNodeKind::BreakStmt:
      MOZ_ASSERT(pn->is<BreakStatement>());
      *answer = true;
      return true;

    case ParseNodeKind::ContinueStmt:
      MOZ_ASSERT(pn->is<ContinueStatement>());
      *answer = true;
      return true;

    case ParseNodeKind::DebuggerStmt:
      MOZ_ASSERT(pn->is<DebuggerStatement>());
      *answer = true;
      return true;

    // Watch out for getters!
    case ParseNodeKind::OptionalDotExpr:
    case ParseNodeKind::DotExpr:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    // Unary cases with side effects only if the child has them.
    case ParseNodeKind::TypeOfExpr:
    case ParseNodeKind::VoidExpr:
    case ParseNodeKind::NotExpr:
      return checkSideEffects(pn->as<UnaryNode>().kid(), answer);

    // Even if the name expression is effect-free, performing ToPropertyKey on
    // it might not be effect-free:
    //
    //   RegExp.prototype.toString = () => { throw 42; };
    //   ({ [/regex/]: 0 }); // ToPropertyKey(/regex/) throws 42
    //
    //   function Q() {
    //     ({ [new.target]: 0 });
    //   }
    //   Q.toString = () => { throw 17; };
    //   new Q; // new.target will be Q, ToPropertyKey(Q) throws 17
    case ParseNodeKind::ComputedName:
      MOZ_ASSERT(pn->is<UnaryNode>());
      *answer = true;
      return true;

    // Looking up or evaluating the associated name could throw.
    case ParseNodeKind::TypeOfNameExpr:
      MOZ_ASSERT(pn->is<UnaryNode>());
      *answer = true;
      return true;

    // This unary case has side effects on the enclosing object, sure.  But
    // that's not the question this function answers: it's whether the
    // operation may have a side effect on something *other* than the result
    // of the overall operation in which it's embedded.  The answer to that
    // is no, because an object literal having a mutated prototype only
    // produces a value, without affecting anything else.
    case ParseNodeKind::MutateProto:
      return checkSideEffects(pn->as<UnaryNode>().kid(), answer);

    // Unary cases with obvious side effects.
    case ParseNodeKind::PreIncrementExpr:
    case ParseNodeKind::PostIncrementExpr:
    case ParseNodeKind::PreDecrementExpr:
    case ParseNodeKind::PostDecrementExpr:
    case ParseNodeKind::ThrowStmt:
      MOZ_ASSERT(pn->is<UnaryNode>());
      *answer = true;
      return true;

    // These might invoke valueOf/toString, even with a subexpression without
    // side effects!  Consider |+{ valueOf: null, toString: null }|.
    case ParseNodeKind::BitNotExpr:
    case ParseNodeKind::PosExpr:
    case ParseNodeKind::NegExpr:
      MOZ_ASSERT(pn->is<UnaryNode>());
      *answer = true;
      return true;

    // This invokes the (user-controllable) iterator protocol.
    case ParseNodeKind::Spread:
      MOZ_ASSERT(pn->is<UnaryNode>());
      *answer = true;
      return true;

    case ParseNodeKind::InitialYield:
    case ParseNodeKind::YieldStarExpr:
    case ParseNodeKind::YieldExpr:
    case ParseNodeKind::AwaitExpr:
      MOZ_ASSERT(pn->is<UnaryNode>());
      *answer = true;
      return true;

    // Deletion generally has side effects, even if isolated cases have none.
    case ParseNodeKind::DeleteNameExpr:
    case ParseNodeKind::DeletePropExpr:
    case ParseNodeKind::DeleteElemExpr:
    case ParseNodeKind::DeleteOptionalChainExpr:
      MOZ_ASSERT(pn->is<UnaryNode>());
      *answer = true;
      return true;

    // Deletion of a non-Reference expression has side effects only through
    // evaluating the expression.
    case ParseNodeKind::DeleteExpr: {
      ParseNode* expr = pn->as<UnaryNode>().kid();
      return checkSideEffects(expr, answer);
    }

    case ParseNodeKind::ExpressionStmt:
      return checkSideEffects(pn->as<UnaryNode>().kid(), answer);

    // Binary cases with obvious side effects.
    case ParseNodeKind::InitExpr:
      *answer = true;
      return true;

    case ParseNodeKind::AssignExpr:
    case ParseNodeKind::AddAssignExpr:
    case ParseNodeKind::SubAssignExpr:
    case ParseNodeKind::BitOrAssignExpr:
    case ParseNodeKind::BitXorAssignExpr:
    case ParseNodeKind::BitAndAssignExpr:
    case ParseNodeKind::LshAssignExpr:
    case ParseNodeKind::RshAssignExpr:
    case ParseNodeKind::UrshAssignExpr:
    case ParseNodeKind::MulAssignExpr:
    case ParseNodeKind::DivAssignExpr:
    case ParseNodeKind::ModAssignExpr:
    case ParseNodeKind::PowAssignExpr:
      MOZ_ASSERT(pn->is<AssignmentNode>());
      *answer = true;
      return true;

    case ParseNodeKind::SetThis:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    case ParseNodeKind::StatementList:
    // Strict equality operations and short circuit operators are well-behaved
    // and perform no conversions.
    case ParseNodeKind::CoalesceExpr:
    case ParseNodeKind::OrExpr:
    case ParseNodeKind::AndExpr:
    case ParseNodeKind::StrictEqExpr:
    case ParseNodeKind::StrictNeExpr:
    // Any subexpression of a comma expression could be effectful.
    case ParseNodeKind::CommaExpr:
      MOZ_ASSERT(!pn->as<ListNode>().empty());
      [[fallthrough]];
    // Subcomponents of a literal may be effectful.
    case ParseNodeKind::ArrayExpr:
    case ParseNodeKind::ObjectExpr:
      for (ParseNode* item : pn->as<ListNode>().contents()) {
        if (!checkSideEffects(item, answer)) {
          return false;
        }
        if (*answer) {
          return true;
        }
      }
      return true;

    // Most other binary operations (parsed as lists in SpiderMonkey) may
    // perform conversions triggering side effects.  Math operations perform
    // ToNumber and may fail invoking invalid user-defined toString/valueOf:
    // |5 < { toString: null }|.  |instanceof| throws if provided a
    // non-object constructor: |null instanceof null|.  |in| throws if given
    // a non-object RHS: |5 in null|.
    case ParseNodeKind::BitOrExpr:
    case ParseNodeKind::BitXorExpr:
    case ParseNodeKind::BitAndExpr:
    case ParseNodeKind::EqExpr:
    case ParseNodeKind::NeExpr:
    case ParseNodeKind::LtExpr:
    case ParseNodeKind::LeExpr:
    case ParseNodeKind::GtExpr:
    case ParseNodeKind::GeExpr:
    case ParseNodeKind::InstanceOfExpr:
    case ParseNodeKind::InExpr:
    case ParseNodeKind::LshExpr:
    case ParseNodeKind::RshExpr:
    case ParseNodeKind::UrshExpr:
    case ParseNodeKind::AddExpr:
    case ParseNodeKind::SubExpr:
    case ParseNodeKind::MulExpr:
    case ParseNodeKind::DivExpr:
    case ParseNodeKind::ModExpr:
    case ParseNodeKind::PowExpr:
      MOZ_ASSERT(pn->as<ListNode>().count() >= 2);
      *answer = true;
      return true;

    case ParseNodeKind::PropertyDefinition:
    case ParseNodeKind::Case: {
      BinaryNode* node = &pn->as<BinaryNode>();
      if (!checkSideEffects(node->left(), answer)) {
        return false;
      }
      if (*answer) {
        return true;
      }
      return checkSideEffects(node->right(), answer);
    }

    // More getters.
    case ParseNodeKind::OptionalElemExpr:
    case ParseNodeKind::ElemExpr:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    // These affect visible names in this code, or in other code.
    case ParseNodeKind::ImportDecl:
    case ParseNodeKind::ExportFromStmt:
    case ParseNodeKind::ExportDefaultStmt:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    // Likewise.
    case ParseNodeKind::ExportStmt:
      MOZ_ASSERT(pn->is<UnaryNode>());
      *answer = true;
      return true;

    case ParseNodeKind::CallImportExpr:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    // Every part of a loop might be effect-free, but looping infinitely *is*
    // an effect.  (Language lawyer trivia: C++ says threads can be assumed
    // to exit or have side effects, C++14 [intro.multithread]p27, so a C++
    // implementation's equivalent of the below could set |*answer = false;|
    // if all loop sub-nodes set |*answer = false|!)
    case ParseNodeKind::DoWhileStmt:
    case ParseNodeKind::WhileStmt:
    case ParseNodeKind::ForStmt:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    // Declarations affect the name set of the relevant scope.
    case ParseNodeKind::VarStmt:
    case ParseNodeKind::ConstDecl:
    case ParseNodeKind::LetDecl:
      MOZ_ASSERT(pn->is<ListNode>());
      *answer = true;
      return true;

    case ParseNodeKind::IfStmt:
    case ParseNodeKind::ConditionalExpr: {
      TernaryNode* node = &pn->as<TernaryNode>();
      if (!checkSideEffects(node->kid1(), answer)) {
        return false;
      }
      if (*answer) {
        return true;
      }
      if (!checkSideEffects(node->kid2(), answer)) {
        return false;
      }
      if (*answer) {
        return true;
      }
      if ((pn = node->kid3())) {
        goto restart;
      }
      return true;
    }

    // Function calls can invoke non-local code.
    case ParseNodeKind::NewExpr:
    case ParseNodeKind::CallExpr:
    case ParseNodeKind::OptionalCallExpr:
    case ParseNodeKind::TaggedTemplateExpr:
    case ParseNodeKind::SuperCallExpr:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    // Function arg lists can contain arbitrary expressions. Technically
    // this only causes side-effects if one of the arguments does, but since
    // the call being made will always trigger side-effects, it isn't needed.
    case ParseNodeKind::Arguments:
      MOZ_ASSERT(pn->is<ListNode>());
      *answer = true;
      return true;

    case ParseNodeKind::OptionalChain:
      MOZ_ASSERT(pn->is<UnaryNode>());
      *answer = true;
      return true;

    case ParseNodeKind::PipelineExpr:
      MOZ_ASSERT(pn->as<ListNode>().count() >= 2);
      *answer = true;
      return true;

    // Classes typically introduce names.  Even if no name is introduced,
    // the heritage and/or class body (through computed property names)
    // usually have effects.
    case ParseNodeKind::ClassDecl:
      MOZ_ASSERT(pn->is<ClassNode>());
      *answer = true;
      return true;

    // |with| calls |ToObject| on its expression and so throws if that value
    // is null/undefined.
    case ParseNodeKind::WithStmt:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    case ParseNodeKind::ReturnStmt:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    case ParseNodeKind::Name:
      MOZ_ASSERT(pn->is<NameNode>());
      *answer = true;
      return true;

    // Shorthands could trigger getters: the |x| in the object literal in
    // |with ({ get x() { throw 42; } }) ({ x });|, for example, triggers
    // one.  (Of course, it isn't necessary to use |with| for a shorthand to
    // trigger a getter.)
    case ParseNodeKind::Shorthand:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    case ParseNodeKind::Function:
      MOZ_ASSERT(pn->is<FunctionNode>());
      /*
       * A named function, contrary to ES3, is no longer effectful, because
       * we bind its name lexically (using JSOp::Callee) instead of creating
       * an Object instance and binding a readonly, permanent property in it
       * (the object and binding can be detected and hijacked or captured).
       * This is a bug fix to ES3; it is fixed in ES3.1 drafts.
       */
      *answer = false;
      return true;

    case ParseNodeKind::Module:
      *answer = false;
      return true;

    case ParseNodeKind::TryStmt: {
      TryNode* tryNode = &pn->as<TryNode>();
      if (!checkSideEffects(tryNode->body(), answer)) {
        return false;
      }
      if (*answer) {
        return true;
      }
      if (LexicalScopeNode* catchScope = tryNode->catchScope()) {
        if (!checkSideEffects(catchScope, answer)) {
          return false;
        }
        if (*answer) {
          return true;
        }
      }
      if (ParseNode* finallyBlock = tryNode->finallyBlock()) {
        if (!checkSideEffects(finallyBlock, answer)) {
          return false;
        }
      }
      return true;
    }

    case ParseNodeKind::Catch: {
      BinaryNode* catchClause = &pn->as<BinaryNode>();
      if (ParseNode* name = catchClause->left()) {
        if (!checkSideEffects(name, answer)) {
          return false;
        }
        if (*answer) {
          return true;
        }
      }
      return checkSideEffects(catchClause->right(), answer);
    }

    case ParseNodeKind::SwitchStmt: {
      SwitchStatement* switchStmt = &pn->as<SwitchStatement>();
      if (!checkSideEffects(&switchStmt->discriminant(), answer)) {
        return false;
      }
      return *answer ||
             checkSideEffects(&switchStmt->lexicalForCaseList(), answer);
    }

    case ParseNodeKind::LabelStmt:
      return checkSideEffects(pn->as<LabeledStatement>().statement(), answer);

    case ParseNodeKind::LexicalScope:
      return checkSideEffects(pn->as<LexicalScopeNode>().scopeBody(), answer);

    // We could methodically check every interpolated expression, but it's
    // probably not worth the trouble.  Treat template strings as effect-free
    // only if they don't contain any substitutions.
    case ParseNodeKind::TemplateStringListExpr: {
      ListNode* list = &pn->as<ListNode>();
      MOZ_ASSERT(!list->empty());
      MOZ_ASSERT((list->count() % 2) == 1,
                 "template strings must alternate template and substitution "
                 "parts");
      *answer = list->count() > 1;
      return true;
    }

    // This should be unreachable but is left as-is for now.
    case ParseNodeKind::ParamsBody:
      *answer = true;
      return true;

    case ParseNodeKind::ForIn:                // by ParseNodeKind::For
    case ParseNodeKind::ForOf:                // by ParseNodeKind::For
    case ParseNodeKind::ForHead:              // by ParseNodeKind::For
    case ParseNodeKind::ClassMethod:          // by ParseNodeKind::ClassDecl
    case ParseNodeKind::ClassField:           // by ParseNodeKind::ClassDecl
    case ParseNodeKind::ClassNames:           // by ParseNodeKind::ClassDecl
    case ParseNodeKind::ClassMemberList:      // by ParseNodeKind::ClassDecl
    case ParseNodeKind::ImportSpecList:       // by ParseNodeKind::Import
    case ParseNodeKind::ImportSpec:           // by ParseNodeKind::Import
    case ParseNodeKind::ExportBatchSpecStmt:  // by ParseNodeKind::Export
    case ParseNodeKind::ExportSpecList:       // by ParseNodeKind::Export
    case ParseNodeKind::ExportSpec:           // by ParseNodeKind::Export
    case ParseNodeKind::CallSiteObj:       // by ParseNodeKind::TaggedTemplate
    case ParseNodeKind::PosHolder:         // by ParseNodeKind::NewTarget
    case ParseNodeKind::SuperBase:         // by ParseNodeKind::Elem and others
    case ParseNodeKind::PropertyNameExpr:  // by ParseNodeKind::Dot
      MOZ_CRASH("handled by parent nodes");

    case ParseNodeKind::LastUnused:
    case ParseNodeKind::Limit:
      MOZ_CRASH("invalid node kind");
  }

  MOZ_CRASH(
      "invalid, unenumerated ParseNodeKind value encountered in "
      "BytecodeEmitter::checkSideEffects");
}

bool BytecodeEmitter::isInLoop() {
  return findInnermostNestableControl<LoopControl>();
}

bool BytecodeEmitter::checkSingletonContext() {
  return script->treatAsRunOnce() && !sc->isFunctionBox() && !isInLoop();
}

bool BytecodeEmitter::checkRunOnceContext() {
  return checkSingletonContext() || (!isInLoop() && isRunOnceLambda());
}

bool BytecodeEmitter::needsImplicitThis() {
  // Short-circuit if there is an enclosing 'with' scope.
  if (sc->inWith()) {
    return true;
  }

  // Otherwise see if the current point is under a 'with'.
  for (EmitterScope* es = innermostEmitterScope(); es;
       es = es->enclosingInFrame()) {
    if (es->scope(this).kind() == ScopeKind::With) {
      return true;
    }
  }

  return false;
}

bool BytecodeEmitter::emitThisEnvironmentCallee() {
  // Get the innermost enclosing function that has a |this| binding.

  // Directly load callee from the frame if possible.
  if (sc->isFunctionBox() && !sc->asFunctionBox()->isArrow()) {
    return emit1(JSOp::Callee);
  }

  // We have to load the callee from the environment chain.
  unsigned numHops = 0;
  for (AbstractScopePtrIter si(innermostScope()); si; si++) {
    if (si.hasSyntacticEnvironment() &&
        si.abstractScopePtr().is<FunctionScope>()) {
      if (!si.abstractScopePtr().isArrow()) {
        break;
      }
    }
    if (si.abstractScopePtr().hasEnvironment()) {
      numHops++;
    }
  }

  static_assert(
      ENVCOORD_HOPS_LIMIT - 1 <= UINT8_MAX,
      "JSOp::EnvCallee operand size should match ENVCOORD_HOPS_LIMIT");

  // Note: we need to check numHops here because we don't call
  // checkEnvironmentChainLength in all cases (like 'eval').
  if (numHops >= ENVCOORD_HOPS_LIMIT - 1) {
    reportError(nullptr, JSMSG_TOO_DEEP, js_function_str);
    return false;
  }

  return emit2(JSOp::EnvCallee, numHops);
}

bool BytecodeEmitter::emitSuperBase() {
  if (!emitThisEnvironmentCallee()) {
    return false;
  }

  return emit1(JSOp::SuperBase);
}

void BytecodeEmitter::reportNeedMoreArgsError(ParseNode* pn,
                                              const char* errorName,
                                              const char* requiredArgs,
                                              const char* pluralizer,
                                              const ListNode* argsList) {
  char actualArgsStr[40];
  SprintfLiteral(actualArgsStr, "%u", argsList->count());
  reportError(pn, JSMSG_MORE_ARGS_NEEDED, errorName, requiredArgs, pluralizer,
              actualArgsStr);
}

void BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...) {
  uint32_t offset = pn ? pn->pn_pos.begin : *scriptStartOffset;

  va_list args;
  va_start(args, errorNumber);

  parser->errorReporter().errorWithNotesAtVA(nullptr, AsVariant(offset),
                                             errorNumber, &args);

  va_end(args);
}

void BytecodeEmitter::reportError(const Maybe<uint32_t>& maybeOffset,
                                  unsigned errorNumber, ...) {
  uint32_t offset = maybeOffset ? *maybeOffset : *scriptStartOffset;

  va_list args;
  va_start(args, errorNumber);

  parser->errorReporter().errorWithNotesAtVA(nullptr, AsVariant(offset),
                                             errorNumber, &args);

  va_end(args);
}

bool BytecodeEmitter::iteratorResultShape(uint32_t* shape) {
  // Use |NoValues| to keep the flags consistent with their usage for normal
  // object literal creation, where |NoValues| is always used in conjunction
  // with |NewObject|.
  ObjLiteralFlags flags{ObjLiteralFlag::NoValues};

  ObjLiteralCreationData data(cx);
  data.writer().beginObject(flags);

  for (auto name : {&JSAtomState::value, &JSAtomState::done}) {
    HandlePropertyName propName = cx->names().*name;

    uint32_t propNameIndex = 0;
    if (!data.addAtom(propName, &propNameIndex)) {
      return false;
    }
    data.writer().setPropName(propNameIndex);

    if (!data.writer().propWithUndefinedValue()) {
      return false;
    }
  }

  return perScriptData().gcThingList().append(std::move(data), shape);
}

bool BytecodeEmitter::emitPrepareIteratorResult() {
  uint32_t shape;
  if (!iteratorResultShape(&shape)) {
    return false;
  }
  return emitIndexOp(JSOp::NewObject, shape);
}

bool BytecodeEmitter::emitFinishIteratorResult(bool done) {
  if (!emitAtomOp(JSOp::InitProp, cx->names().value)) {
    return false;
  }
  if (!emit1(done ? JSOp::True : JSOp::False)) {
    return false;
  }
  if (!emitAtomOp(JSOp::InitProp, cx->names().done)) {
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitGetNameAtLocation(Handle<JSAtom*> name,
                                            const NameLocation& loc) {
  NameOpEmitter noe(this, name, loc, NameOpEmitter::Kind::Get);
  if (!noe.emitGet()) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitGetName(NameNode* name) {
  RootedAtom nameAtom(cx, name->name());
  return emitGetName(nameAtom);
}

bool BytecodeEmitter::emitTDZCheckIfNeeded(HandleAtom name,
                                           const NameLocation& loc,
                                           ValueIsOnStack isOnStack) {
  // Dynamic accesses have TDZ checks built into their VM code and should
  // never emit explicit TDZ checks.
  MOZ_ASSERT(loc.hasKnownSlot());
  MOZ_ASSERT(loc.isLexical());

  Maybe<MaybeCheckTDZ> check =
      innermostTDZCheckCache->needsTDZCheck(this, name);
  if (!check) {
    return false;
  }

  // We've already emitted a check in this basic block.
  if (*check == DontCheckTDZ) {
    return true;
  }

  // If the value is not on the stack, we have to load it first.
  if (isOnStack == ValueIsOnStack::No) {
    if (loc.kind() == NameLocation::Kind::FrameSlot) {
      if (!emitLocalOp(JSOp::GetLocal, loc.frameSlot())) {
        return false;
      }
    } else {
      if (!emitEnvCoordOp(JSOp::GetAliasedVar, loc.environmentCoordinate())) {
        return false;
      }
    }
  }

  // Emit the lexical check.
  if (loc.kind() == NameLocation::Kind::FrameSlot) {
    if (!emitLocalOp(JSOp::CheckLexical, loc.frameSlot())) {
      return false;
    }
  } else {
    if (!emitEnvCoordOp(JSOp::CheckAliasedLexical,
                        loc.environmentCoordinate())) {
      return false;
    }
  }

  // Pop the value if needed.
  if (isOnStack == ValueIsOnStack::No) {
    if (!emit1(JSOp::Pop)) {
      return false;
    }
  }

  return innermostTDZCheckCache->noteTDZCheck(this, name, DontCheckTDZ);
}

bool BytecodeEmitter::emitPropLHS(PropertyAccess* prop) {
  MOZ_ASSERT(!prop->isSuper());

  ParseNode* expr = &prop->expression();

  if (!expr->is<PropertyAccess>() || expr->as<PropertyAccess>().isSuper()) {
    // The non-optimized case.
    return emitTree(expr);
  }

  // If the object operand is also a dotted property reference, reverse the
  // list linked via expression() temporarily so we can iterate over it from
  // the bottom up (reversing again as we go), to avoid excessive recursion.
  PropertyAccess* pndot = &expr->as<PropertyAccess>();
  ParseNode* pnup = nullptr;
  ParseNode* pndown;
  for (;;) {
    // Reverse pndot->expression() to point up, not down.
    pndown = &pndot->expression();
    pndot->setExpression(pnup);
    if (!pndown->is<PropertyAccess>() ||
        pndown->as<PropertyAccess>().isSuper()) {
      break;
    }
    pnup = pndot;
    pndot = &pndown->as<PropertyAccess>();
  }

  // pndown is a primary expression, not a dotted property reference.
  if (!emitTree(pndown)) {
    return false;
  }

  while (true) {
    // Walk back up the list, emitting annotated name ops.
    if (!emitAtomOp(JSOp::GetProp, pndot->key().atom(),
                    ShouldInstrument::Yes)) {
      return false;
    }

    // Reverse the pndot->expression() link again.
    pnup = pndot->maybeExpression();
    pndot->setExpression(pndown);
    pndown = pndot;
    if (!pnup) {
      break;
    }
    pndot = &pnup->as<PropertyAccess>();
  }
  return true;
}

bool BytecodeEmitter::emitPropIncDec(UnaryNode* incDec) {
  PropertyAccess* prop = &incDec->kid()->as<PropertyAccess>();
  bool isSuper = prop->isSuper();
  ParseNodeKind kind = incDec->getKind();
  PropOpEmitter poe(
      this,
      kind == ParseNodeKind::PostIncrementExpr
          ? PropOpEmitter::Kind::PostIncrement
          : kind == ParseNodeKind::PreIncrementExpr
                ? PropOpEmitter::Kind::PreIncrement
                : kind == ParseNodeKind::PostDecrementExpr
                      ? PropOpEmitter::Kind::PostDecrement
                      : PropOpEmitter::Kind::PreDecrement,
      isSuper ? PropOpEmitter::ObjKind::Super : PropOpEmitter::ObjKind::Other);
  if (!poe.prepareForObj()) {
    return false;
  }
  if (isSuper) {
    UnaryNode* base = &prop->expression().as<UnaryNode>();
    if (!emitGetThisForSuperBase(base)) {
      //            [stack] THIS
      return false;
    }
  } else {
    if (!emitPropLHS(prop)) {
      //            [stack] OBJ
      return false;
    }
  }
  if (!poe.emitIncDec(prop->key().atom())) {
    //              [stack] RESULT
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitNameIncDec(UnaryNode* incDec) {
  MOZ_ASSERT(incDec->kid()->isKind(ParseNodeKind::Name));

  ParseNodeKind kind = incDec->getKind();
  NameNode* name = &incDec->kid()->as<NameNode>();
  RootedAtom nameAtom(cx, name->atom());
  NameOpEmitter noe(this, nameAtom,
                    kind == ParseNodeKind::PostIncrementExpr
                        ? NameOpEmitter::Kind::PostIncrement
                        : kind == ParseNodeKind::PreIncrementExpr
                              ? NameOpEmitter::Kind::PreIncrement
                              : kind == ParseNodeKind::PostDecrementExpr
                                    ? NameOpEmitter::Kind::PostDecrement
                                    : NameOpEmitter::Kind::PreDecrement);
  if (!noe.emitIncDec()) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitElemOpBase(JSOp op,
                                     ShouldInstrument shouldInstrument) {
  if (shouldInstrument != ShouldInstrument::No &&
      !emitInstrumentationForOpcode(op, 0)) {
    return false;
  }

  if (!emit1(op)) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitElemObjAndKey(PropertyByValue* elem, bool isSuper,
                                        ElemOpEmitter& eoe) {
  if (isSuper) {
    if (!eoe.prepareForObj()) {
      //            [stack]
      return false;
    }
    UnaryNode* base = &elem->expression().as<UnaryNode>();
    if (!emitGetThisForSuperBase(base)) {
      //            [stack] THIS
      return false;
    }
    if (!eoe.prepareForKey()) {
      //            [stack] THIS
      return false;
    }
    if (!emitTree(&elem->key())) {
      //            [stack] THIS KEY
      return false;
    }

    return true;
  }

  if (!eoe.prepareForObj()) {
    //              [stack]
    return false;
  }
  if (!emitTree(&elem->expression())) {
    //              [stack] OBJ
    return false;
  }
  if (!eoe.prepareForKey()) {
    //              [stack] OBJ? OBJ
    return false;
  }
  if (!emitTree(&elem->key())) {
    //              [stack] OBJ? OBJ KEY
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitElemIncDec(UnaryNode* incDec) {
  PropertyByValue* elemExpr = &incDec->kid()->as<PropertyByValue>();
  bool isSuper = elemExpr->isSuper();
  ParseNodeKind kind = incDec->getKind();
  ElemOpEmitter eoe(
      this,
      kind == ParseNodeKind::PostIncrementExpr
          ? ElemOpEmitter::Kind::PostIncrement
          : kind == ParseNodeKind::PreIncrementExpr
                ? ElemOpEmitter::Kind::PreIncrement
                : kind == ParseNodeKind::PostDecrementExpr
                      ? ElemOpEmitter::Kind::PostDecrement
                      : ElemOpEmitter::Kind::PreDecrement,
      isSuper ? ElemOpEmitter::ObjKind::Super : ElemOpEmitter::ObjKind::Other);
  if (!emitElemObjAndKey(elemExpr, isSuper, eoe)) {
    //              [stack] # if Super
    //              [stack] THIS KEY
    //              [stack] # otherwise
    //              [stack] OBJ KEY
    return false;
  }
  if (!eoe.emitIncDec()) {
    //              [stack] RESULT
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitCallIncDec(UnaryNode* incDec) {
  MOZ_ASSERT(incDec->isKind(ParseNodeKind::PreIncrementExpr) ||
             incDec->isKind(ParseNodeKind::PostIncrementExpr) ||
             incDec->isKind(ParseNodeKind::PreDecrementExpr) ||
             incDec->isKind(ParseNodeKind::PostDecrementExpr));

  ParseNode* call = incDec->kid();
  MOZ_ASSERT(call->isKind(ParseNodeKind::CallExpr));
  if (!emitTree(call)) {
    //              [stack] CALLRESULT
    return false;
  }
  if (!emit1(JSOp::ToNumeric)) {
    //              [stack] N
    return false;
  }

  // The increment/decrement has no side effects, so proceed to throw for
  // invalid assignment target.
  return emit2(JSOp::ThrowMsg, uint8_t(ThrowMsgKind::AssignToCall));
}

bool BytecodeEmitter::emitDouble(double d) {
  BytecodeOffset offset;
  if (!emitCheck(JSOp::Double, 9, &offset)) {
    return false;
  }

  jsbytecode* code = bytecodeSection().code(offset);
  code[0] = jsbytecode(JSOp::Double);
  SET_INLINE_VALUE(code, DoubleValue(d));
  bytecodeSection().updateDepth(offset);
  return true;
}

bool BytecodeEmitter::emitNumberOp(double dval) {
  int32_t ival;
  if (NumberIsInt32(dval, &ival)) {
    if (ival == 0) {
      return emit1(JSOp::Zero);
    }
    if (ival == 1) {
      return emit1(JSOp::One);
    }
    if ((int)(int8_t)ival == ival) {
      return emit2(JSOp::Int8, uint8_t(int8_t(ival)));
    }

    uint32_t u = uint32_t(ival);
    if (u < Bit(16)) {
      if (!emitUint16Operand(JSOp::Uint16, u)) {
        return false;
      }
    } else if (u < Bit(24)) {
      BytecodeOffset off;
      if (!emitN(JSOp::Uint24, 3, &off)) {
        return false;
      }
      SET_UINT24(bytecodeSection().code(off), u);
    } else {
      BytecodeOffset off;
      if (!emitN(JSOp::Int32, 4, &off)) {
        return false;
      }
      SET_INT32(bytecodeSection().code(off), ival);
    }
    return true;
  }

  return emitDouble(dval);
}

/*
 * 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
 * into emitTree which is recursive and uses relatively little stack space.
 */
MOZ_NEVER_INLINE bool BytecodeEmitter::emitSwitch(SwitchStatement* switchStmt) {
  LexicalScopeNode& lexical = switchStmt->lexicalForCaseList();
  MOZ_ASSERT(lexical.isKind(ParseNodeKind::LexicalScope));
  ListNode* cases = &lexical.scopeBody()->as<ListNode>();
  MOZ_ASSERT(cases->isKind(ParseNodeKind::StatementList));

  SwitchEmitter se(this);
  if (!se.emitDiscriminant(Some(switchStmt->discriminant().pn_pos.begin))) {
    return false;
  }

  if (!markStepBreakpoint()) {
    return false;
  }
  if (!emitTree(&switchStmt->discriminant())) {
    return false;
  }

  // Enter the scope before pushing the switch BreakableControl since all
  // breaks are under this scope.

  if (!lexical.isEmptyScope()) {
    if (!se.emitLexical(lexical.scopeBindings())) {
      return false;
    }

    // A switch statement may contain hoisted functions inside its
    // cases. The PNX_FUNCDEFS flag is propagated from the STATEMENTLIST
    // bodies of the cases to the case list.
    if (cases->hasTopLevelFunctionDeclarations()) {
      for (ParseNode* item : cases->contents()) {
        CaseClause* caseClause = &item->as<CaseClause>();
        ListNode* statements = caseClause->statementList();
        if (statements->hasTopLevelFunctionDeclarations()) {
          if (!emitHoistedFunctionsInList(statements)) {
            return false;
          }
        }
      }
    }
  } else {
    MOZ_ASSERT(!cases->hasTopLevelFunctionDeclarations());
  }

  SwitchEmitter::TableGenerator tableGen(this);
  uint32_t caseCount = cases->count() - (switchStmt->hasDefault() ? 1 : 0);
  if (caseCount == 0) {
    tableGen.finish(0);
  } else {
    for (ParseNode* item : cases->contents()) {
      CaseClause* caseClause = &item->as<CaseClause>();
      if (caseClause->isDefault()) {
        continue;
      }

      ParseNode* caseValue = caseClause->caseExpression();

      if (caseValue->getKind() != ParseNodeKind::NumberExpr) {
        tableGen.setInvalid();
        break;
      }

      int32_t i;
      if (!NumberEqualsInt32(caseValue->as<NumericLiteral>().value(), &i)) {
        tableGen.setInvalid();
        break;
      }

      if (!tableGen.addNumber(i)) {
        return false;
      }
    }

    tableGen.finish(caseCount);
  }

  if (!se.validateCaseCount(caseCount)) {
    return false;
  }

  bool isTableSwitch = tableGen.isValid();
  if (isTableSwitch) {
    if (!se.emitTable(tableGen)) {
      return false;
    }
  } else {
    if (!se.emitCond()) {
      return false;
    }

    // Emit code for evaluating cases and jumping to case statements.
    for (ParseNode* item : cases->contents()) {
      CaseClause* caseClause = &item->as<CaseClause>();
      if (caseClause->isDefault()) {
        continue;
      }

      if (!se.prepareForCaseValue()) {
        return false;
      }

      ParseNode* caseValue = caseClause->caseExpression();
      // If the expression is a literal, suppress line number emission so
      // that debugging works more naturally.
      if (!emitTree(
              caseValue, ValueUsage::WantValue,
              caseValue->isLiteral() ? SUPPRESS_LINENOTE : EMIT_LINENOTE)) {
        return false;
      }

      if (!se.emitCaseJump()) {
        return false;
      }
    }
  }

  // Emit code for each case's statements.
  for (ParseNode* item : cases->contents()) {
    CaseClause* caseClause = &item->as<CaseClause>();
    if (caseClause->isDefault()) {
      if (!se.emitDefaultBody()) {
        return false;
      }
    } else {
      if (isTableSwitch) {
        ParseNode* caseValue = caseClause->caseExpression();
        MOZ_ASSERT(caseValue->isKind(ParseNodeKind::NumberExpr));

        NumericLiteral* literal = &caseValue->as<NumericLiteral>();
#ifdef DEBUG
        // Use NumberEqualsInt32 here because switches compare using
        // strict equality, which will equate -0 and +0.  In contrast
        // NumberIsInt32 would return false for -0.
        int32_t v;
        MOZ_ASSERT(mozilla::NumberEqualsInt32(literal->value(), &v));
#endif
        int32_t i = int32_t(literal->value());

        if (!se.emitCaseBody(i, tableGen)) {
          return false;
        }
      } else {
        if (!se.emitCaseBody()) {
          return false;
        }
      }
    }

    if (!emitTree(caseClause->statementList())) {
      return false;
    }
  }

  if (!se.emitEnd()) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::isRunOnceLambda() {
  if (lazyScript) {
    // NOTE: The TreatAsRunOnce flag on lazy BaseScript was computed without a
    // complete 'funbox' so we must compute the shouldSuppressRunOnce conditions
    // now that we have the full parse info.
    return lazyScript->treatAsRunOnce() &&
           !sc->asFunctionBox()->shouldSuppressRunOnce();
  }

  return parent && parent->emittingRunOnceLambda &&
         !sc->asFunctionBox()->shouldSuppressRunOnce();
}

bool BytecodeEmitter::allocateResumeIndex(BytecodeOffset offset,
                                          uint32_t* resumeIndex) {
  static constexpr uint32_t MaxResumeIndex = BitMask(24);

  static_assert(
      MaxResumeIndex < uint32_t(AbstractGeneratorObject::RESUME_INDEX_RUNNING),
      "resumeIndex should not include magic AbstractGeneratorObject "
      "resumeIndex values");
  static_assert(
      MaxResumeIndex <= INT32_MAX / sizeof(uintptr_t),
      "resumeIndex * sizeof(uintptr_t) must fit in an int32. JIT code relies "
      "on this when loading resume entries from BaselineScript");

  *resumeIndex = bytecodeSection().resumeOffsetList().length();
  if (*resumeIndex > MaxResumeIndex) {
    reportError(nullptr, JSMSG_TOO_MANY_RESUME_INDEXES);
    return false;
  }

  return bytecodeSection().resumeOffsetList().append(offset.value());
}

bool BytecodeEmitter::allocateResumeIndexRange(
    mozilla::Span<BytecodeOffset> offsets, uint32_t* firstResumeIndex) {
  *firstResumeIndex = 0;

  for (size_t i = 0, len = offsets.size(); i < len; i++) {
    uint32_t resumeIndex;
    if (!allocateResumeIndex(offsets[i], &resumeIndex)) {
      return false;
    }
    if (i == 0) {
      *firstResumeIndex = resumeIndex;
    }
  }

  return true;
}

bool BytecodeEmitter::emitYieldOp(JSOp op) {
  // All yield operations pop or suspend the current frame.
  if (!emitInstrumentation(InstrumentationKind::Exit)) {
    return false;
  }

  if (op == JSOp::FinalYieldRval) {
    return emit1(JSOp::FinalYieldRval);
  }

  MOZ_ASSERT(op == JSOp::InitialYield || op == JSOp::Yield ||
             op == JSOp::Await);

  BytecodeOffset off;
  if (!emitN(op, 3, &off)) {
    return false;
  }

  if (op == JSOp::InitialYield || op == JSOp::Yield) {
    bytecodeSection().addNumYields();
  }

  uint32_t resumeIndex;
  if (!allocateResumeIndex(bytecodeSection().offset(), &resumeIndex)) {
    return false;
  }

  SET_RESUMEINDEX(bytecodeSection().code(off), resumeIndex);

  if (!emitInstrumentation(InstrumentationKind::Entry)) {
    return false;
  }

  BytecodeOffset unusedOffset;
  return emitJumpTargetOp(JSOp::AfterYield, &unusedOffset);
}

bool BytecodeEmitter::emitPushResumeKind(GeneratorResumeKind kind) {
  return emit2(JSOp::ResumeKind, uint8_t(kind));
}

bool BytecodeEmitter::emitSetThis(BinaryNode* setThisNode) {
  // ParseNodeKind::SetThis is used to update |this| after a super() call
  // in a derived class constructor.

  MOZ_ASSERT(setThisNode->isKind(ParseNodeKind::SetThis));
  MOZ_ASSERT(setThisNode->left()->isKind(ParseNodeKind::Name));

  RootedAtom name(cx, setThisNode->left()->as<NameNode>().name());

  // The 'this' binding is not lexical, but due to super() semantics this
  // initialization needs to be treated as a lexical one.
  NameLocation loc = lookupName(name);
  NameLocation lexicalLoc;
  if (loc.kind() == NameLocation::Kind::FrameSlot) {
    lexicalLoc = NameLocation::FrameSlot(BindingKind::Let, loc.frameSlot());
  } else if (loc.kind() == NameLocation::Kind::EnvironmentCoordinate) {
    EnvironmentCoordinate coord = loc.environmentCoordinate();
    uint8_t hops = AssertedCast<uint8_t>(coord.hops());
    lexicalLoc = NameLocation::EnvironmentCoordinate(BindingKind::Let, hops,
                                                     coord.slot());
  } else {
    MOZ_ASSERT(loc.kind() == NameLocation::Kind::Dynamic);
    lexicalLoc = loc;
  }

  NameOpEmitter noe(this, name, lexicalLoc, NameOpEmitter::Kind::Initialize);
  if (!noe.prepareForRhs()) {
    //              [stack]
    return false;
  }

  // Emit the new |this| value.
  if (!emitTree(setThisNode->right())) {
    //              [stack] NEWTHIS
    return false;
  }

  // Get the original |this| and throw if we already initialized
  // it. Do *not* use the NameLocation argument, as that's the special
  // lexical location below to deal with super() semantics.
  if (!emitGetName(name)) {
    //              [stack] NEWTHIS THIS
    return false;
  }
  if (!emit1(JSOp::CheckThisReinit)) {
    //              [stack] NEWTHIS THIS
    return false;
  }
  if (!emit1(JSOp::Pop)) {
    //              [stack] NEWTHIS
    return false;
  }
  if (!noe.emitAssignment()) {
    //              [stack] NEWTHIS
    return false;
  }

  if (!emitInitializeInstanceFields()) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::defineHoistedTopLevelFunctions(ParseNode* body) {
  MOZ_ASSERT(inPrologue());
  MOZ_ASSERT(sc->isGlobalContext() || (sc->isEvalContext() && !sc->strict()));
  MOZ_ASSERT(body->is<LexicalScopeNode>() || body->is<ListNode>());

  if (body->is<LexicalScopeNode>()) {
    body = body->as<LexicalScopeNode>().scopeBody();
    MOZ_ASSERT(body->is<ListNode>());
  }

  if (!body->as<ListNode>().hasTopLevelFunctionDeclarations()) {
    return true;
  }

  return emitHoistedFunctionsInList(&body->as<ListNode>());
}

bool BytecodeEmitter::emitScript(ParseNode* body) {
  AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission,
                                parser->errorReporter(), body);

  setScriptStartOffsetIfUnset(body->pn_pos.begin);

  MOZ_ASSERT(inPrologue());

  TDZCheckCache tdzCache(this);
  EmitterScope emitterScope(this);
  if (sc->isGlobalContext()) {
    if (!emitterScope.enterGlobal(this, sc->asGlobalContext())) {
      return false;
    }
  } else if (sc->isEvalContext()) {
    if (!emitterScope.enterEval(this, sc->asEvalContext())) {
      return false;
    }
  } else {
    MOZ_ASSERT(sc->isModuleContext());
    if (!emitterScope.enterModule(this, sc->asModuleContext())) {
      return false;
    }
  }

  setFunctionBodyEndPos(body->pn_pos.end);

  bool isSloppyEval = sc->isEvalContext() && !sc->strict();
  if (isSloppyEval && body->is<LexicalScopeNode>() &&
      !body->as<LexicalScopeNode>().isEmptyScope()) {
    // Sloppy eval scripts may need to emit DEFFUNs in the prologue. If there is
    // an immediately enclosed lexical scope, we need to enter the lexical
    // scope in the prologue for the DEFFUNs to pick up the right
    // environment chain.
    EmitterScope lexicalEmitterScope(this);
    LexicalScopeNode* scope = &body->as<LexicalScopeNode>();

    if (!lexicalEmitterScope.enterLexical(this, ScopeKind::Lexical,
                                          scope->scopeBindings())) {
      return false;
    }

    if (!defineHoistedTopLevelFunctions(scope->scopeBody())) {
      return false;
    }

    if (!switchToMain()) {
      return false;
    }

    ParseNode* scopeBody = scope->scopeBody();
    if (!emitLexicalScopeBody(scopeBody, EMIT_LINENOTE)) {
      return false;
    }

    if (!updateSourceCoordNotes(scopeBody->pn_pos.end)) {
      return false;
    }

    if (!lexicalEmitterScope.leave(this)) {
      return false;
    }
  } else {
    if (sc->isGlobalContext() || isSloppyEval) {
      if (!defineHoistedTopLevelFunctions(body)) {
        return false;
      }
    }

    if (!switchToMain()) {
      return false;
    }

    if (!emitTree(body)) {
      return false;
    }

    if (!updateSourceCoordNotes(body->pn_pos.end)) {
      return false;
    }
  }
  if (!markSimpleBreakpoint()) {
    return false;
  }

  if (!emitReturnRval()) {
    return false;
  }

  if (!emitterScope.leave(this)) {
    return false;
  }

  if (!NameFunctions(cx, body)) {
    return false;
  }

  js::UniquePtr<ImmutableScriptData> immutableScriptData =
      createImmutableScriptData(cx);
  if (!immutableScriptData) {
    return false;
  }

  BCEScriptStencil stencil(*this, std::move(immutableScriptData));
  return JSScript::fullyInitFromStencil(cx, compilationInfo, script, stencil);
}

js::UniquePtr<ImmutableScriptData> BytecodeEmitter::createImmutableScriptData(
    JSContext* cx) {
  uint32_t nslots;
  if (!getNslots(&nslots)) {
    return nullptr;
  }

  bool isFunction = sc->isFunctionBox();
  uint16_t funLength = isFunction ? sc->asFunctionBox()->length : 0;

  return ImmutableScriptData::new_(
      cx, mainOffset(), maxFixedSlots, nslots, bodyScopeIndex,
      bytecodeSection().numICEntries(), bytecodeSection().numTypeSets(),
      isFunction, funLength, bytecodeSection().code(),
      bytecodeSection().notes(), bytecodeSection().resumeOffsetList().span(),
      bytecodeSection().scopeNoteList().span(),
      bytecodeSection().tryNoteList().span());
}

bool BytecodeEmitter::getNslots(uint32_t* nslots) {
  uint64_t nslots64 =
      maxFixedSlots + static_cast<uint64_t>(bytecodeSection().maxStackDepth());
  if (nslots64 > UINT32_MAX) {
    reportError(nullptr, JSMSG_NEED_DIET, js_script_str);
    return false;
  }
  *nslots = nslots64;
  return true;
}

bool BytecodeEmitter::emitFunctionScript(FunctionNode* funNode,
                                         TopLevelFunction isTopLevel) {
  MOZ_ASSERT(inPrologue());
  ListNode* paramsBody = &funNode->body()->as<ListNode>();
  MOZ_ASSERT(paramsBody->isKind(ParseNodeKind::ParamsBody));
  FunctionBox* funbox = sc->asFunctionBox();
  AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission,
                                parser->errorReporter(), funbox);

  MOZ_ASSERT(fieldInitializers_.valid == funbox->isClassConstructor());

  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 (!fse.initScript(getFieldInitializers())) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target,
                                              size_t* emitted) {
  *emitted = 0;

  if (target->isKind(ParseNodeKind::Spread)) {
    target = target->as<UnaryNode>().kid();
  } else if (target->isKind(ParseNodeKind::AssignExpr)) {
    target = target->as<AssignmentNode>().left();
  }

  // No need to recur into ParseNodeKind::Array and
  // ParseNodeKind::Object subpatterns here, since
  // emitSetOrInitializeDestructuring does the recursion when
  // setting or initializing value.  Getting reference doesn't recur.
  if (target->isKind(ParseNodeKind::Name) ||
      target->isKind(ParseNodeKind::ArrayExpr) ||
      target->isKind(ParseNodeKind::ObjectExpr)) {
    return true;
  }

#ifdef DEBUG
  int depth = bytecodeSection().stackDepth();
#endif

  switch (target->getKind()) {
    case ParseNodeKind::DotExpr: {
      PropertyAccess* prop = &target->as<PropertyAccess>();
      bool isSuper = prop->isSuper();
      PropOpEmitter poe(this, PropOpEmitter::Kind::SimpleAssignment,
                        isSuper ? PropOpEmitter::ObjKind::Super
                                : PropOpEmitter::ObjKind::Other);
      if (!poe.prepareForObj()) {
        return false;
      }
      if (isSuper) {
        UnaryNode* base = &prop->expression().as<UnaryNode>();
        if (!emitGetThisForSuperBase(base)) {
          //        [stack] THIS SUPERBASE
          return false;
        }
        // SUPERBASE is pushed onto THIS in poe.prepareForRhs below.
        *emitted = 2;
      } else {
        if (!emitTree(&prop->expression())) {
          //        [stack] OBJ
          return false;
        }
        *emitted = 1;
      }
      if (!poe.prepareForRhs()) {
        //          [stack] # if Super
        //          [stack] THIS SUPERBASE
        //          [stack] # otherwise
        //          [stack] OBJ
        return false;
      }
      break;
    }

    case ParseNodeKind::ElemExpr: {
      PropertyByValue* elem = &target->as<PropertyByValue>();
      bool isSuper = elem->isSuper();
      ElemOpEmitter eoe(this, ElemOpEmitter::Kind::SimpleAssignment,
                        isSuper ? ElemOpEmitter::ObjKind::Super
                                : ElemOpEmitter::ObjKind::Other);
      if (!emitElemObjAndKey(elem, isSuper, eoe)) {
        //          [stack] # if Super
        //          [stack] THIS KEY
        //          [stack] # otherwise
        //          [stack] OBJ KEY
        return false;
      }
      if (isSuper) {
        // SUPERBASE is pushed onto KEY in eoe.prepareForRhs below.
        *emitted = 3;
      } else {
        *emitted = 2;
      }
      if (!eoe.prepareForRhs()) {
        //          [stack] # if Super
        //          [stack] THIS KEY SUPERBASE
        //          [stack] # otherwise
        //          [stack] OBJ KEY
        return false;
      }
      break;
    }

    case ParseNodeKind::CallExpr:
      MOZ_ASSERT_UNREACHABLE(
          "Parser::reportIfNotValidSimpleAssignmentTarget "
          "rejects function calls as assignment "
          "targets in destructuring assignments");
      break;

    default:
      MOZ_CRASH("emitDestructuringLHSRef: bad lhs kind");
  }

  MOZ_ASSERT(bytecodeSection().stackDepth() == depth + int(*emitted));

  return true;
}

bool BytecodeEmitter::emitSetOrInitializeDestructuring(
    ParseNode* target, DestructuringFlavor flav) {
  // 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(ParseNodeKind::Spread)) {
    target = target->as<UnaryNode>().kid();
  } else if (target->isKind(ParseNodeKind::AssignExpr)) {
    target = target->as<AssignmentNode>().left();
  }
  if (target->isKind(ParseNodeKind::ArrayExpr) ||
      target->isKind(ParseNodeKind::ObjectExpr)) {
    if (!emitDestructuringOps(&target->as<ListNode>(), flav)) {
      return false;
    }
    // Per its post-condition, emitDestructuringOps has left the
    // to-be-destructured value on top of the stack.
    if (!emit1(JSOp::Pop)) {
      return false;
    }
  } else {
    switch (target->getKind()) {
      case ParseNodeKind::Name: {
        RootedAtom name(cx, target->as<NameNode>().name());
        NameLocation loc = lookupName(name);
        NameOpEmitter::Kind kind;
        switch (flav) {
          case DestructuringFlavor::Declaration:
            kind = NameOpEmitter::Kind::Initialize;
            break;

          case DestructuringFlavor::Assignment:
            kind = NameOpEmitter::Kind::SimpleAssignment;
            break;
        }

        NameOpEmitter noe(this, name, loc, kind);
        if (!noe.prepareForRhs()) {
          //        [stack] V ENV?
          return false;
        }
        if (noe.emittedBindOp()) {
          // This is like ordinary assignment, but with one difference.
          //
          // In `a = b`, we first determine a binding for `a` (using
          // JSOp::BindName or JSOp::BindGName), then we evaluate `b`, then
          // a JSOp::SetName instruction.
          //
          // In `[a] = [b]`, per spec, `b` is evaluated first, then we
          // determine a binding for `a`. Then we need to do assignment--
          // but the operands are on the stack in the wrong order for
          // JSOp::SetProp, so we have to add a JSOp::Swap.
          //
          // In the cases where we are emitting a name op, emit a swap
          // because of this.
          if (!emit1(JSOp::Swap)) {
            //      [stack] ENV V
            return false;
          }
        } else {
          // In cases of emitting a frame slot or environment slot,
          // nothing needs be done.
        }
        if (!noe.emitAssignment()) {
          //        [stack] V
          return false;
        }

        break;
      }

      case ParseNodeKind::DotExpr: {
        // The reference is already pushed by emitDestructuringLHSRef.
        //          [stack] # if Super
        //          [stack] THIS SUPERBASE VAL
        //          [stack] # otherwise
        //          [stack] OBJ VAL
        PropertyAccess* prop = &target->as<PropertyAccess>();
        bool isSuper = prop->isSuper();
        PropOpEmitter poe(this, PropOpEmitter::Kind::SimpleAssignment,
                          isSuper ? PropOpEmitter::ObjKind::Super
                                  : PropOpEmitter::ObjKind::Other);
        if (!poe.skipObjAndRhs()) {
          return false;
        }
        //          [stack] # VAL
        if (!poe.emitAssignment(prop->key().atom())) {
          return false;
        }
        break;
      }

      case ParseNodeKind::ElemExpr: {
        // The reference is already pushed by emitDestructuringLHSRef.
        //          [stack] # if Super
        //          [stack] THIS KEY SUPERBASE VAL
        //          [stack] # otherwise
        //          [stack] OBJ KEY VAL
        PropertyByValue* elem = &target->as<PropertyByValue>();
        bool isSuper = elem->isSuper();
        ElemOpEmitter eoe(this, ElemOpEmitter::Kind::SimpleAssignment,
                          isSuper ? ElemOpEmitter::ObjKind::Super
                                  : ElemOpEmitter::ObjKind::Other);
        if (!eoe.skipObjAndKeyAndRhs()) {
          return false;
        }
        if (!eoe.emitAssignment()) {
          //        [stack] VAL
          return false;
        }
        break;
      }

      case ParseNodeKind::CallExpr:
        MOZ_ASSERT_UNREACHABLE(
            "Parser::reportIfNotValidSimpleAssignmentTarget "
            "rejects function calls as assignment "
            "targets in destructuring assignments");
        break;

      default:
        MOZ_CRASH("emitSetOrInitializeDestructuring: bad lhs kind");
    }

    // Pop the assigned value.
    if (!emit1(JSOp::Pop)) {
      //            [stack] # empty
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitIteratorNext(
    const Maybe<uint32_t>& callSourceCoordOffset,
    IteratorKind iterKind /* = IteratorKind::Sync */,
    bool allowSelfHosted /* = false */) {
  MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
             ".next() iteration is prohibited in self-hosted code because it "
             "can run user-modifiable iteration code");

  //                [stack] ... NEXT ITER
  MOZ_ASSERT(bytecodeSection().stackDepth() >= 2);

  if (!emitCall(JSOp::Call, 0, callSourceCoordOffset)) {
    //              [stack] ... RESULT
    return false;
  }

  if (iterKind == IteratorKind::Async) {
    if (!emitAwaitInInnermostScope()) {
      //            [stack] ... RESULT
      return false;
    }
  }

  if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) {
    //              [stack] ... RESULT
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitPushNotUndefinedOrNull() {
  //                [stack] V
  MOZ_ASSERT(bytecodeSection().stackDepth() > 0);

  if (!emit1(JSOp::Dup)) {
    //              [stack] V V
    return false;
  }
  if (!emit1(JSOp::Undefined)) {
    //              [stack] V V UNDEFINED
    return false;
  }
  if (!emit1(JSOp::StrictNe)) {
    //              [stack] V NEQ
    return false;
  }

  JumpList undefinedOrNullJump;
  if (!emitJump(JSOp::And, &undefinedOrNullJump)) {
    //              [stack] V NEQ
    return false;
  }

  if (!emit1(JSOp::Pop)) {
    //              [stack] V
    return false;
  }
  if (!emit1(JSOp::Dup)) {
    //              [stack] V V
    return false;
  }
  if (!emit1(JSOp::Null)) {
    //              [stack] V V NULL
    return false;
  }
  if (!emit1(JSOp::StrictNe)) {
    //              [stack] V NEQ
    return false;
  }

  if (!emitJumpTargetAndPatch(undefinedOrNullJump)) {
    //              [stack] V NOT-UNDEF-OR-NULL
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitIteratorCloseInScope(
    EmitterScope& currentScope,
    IteratorKind iterKind /* = IteratorKind::Sync */,
    CompletionKind completionKind /* = CompletionKind::Normal */,
    bool allowSelfHosted /* = false */) {
  MOZ_ASSERT(
      allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
      ".close() on iterators is prohibited in self-hosted code because it "
      "can run user-modifiable iteration code");

  // Generate inline logic corresponding to IteratorClose (ES 7.4.6).
  //
  // Callers need to ensure that the iterator object is at the top of the
  // stack.

  if (!emit1(JSOp::Dup)) {
    //              [stack] ... ITER ITER
    return false;
  }

  // Step 3.
  //
  // Get the "return" method.
  if (!emitAtomOp(JSOp::CallProp, cx->names().return_)) {
    //              [stack] ... ITER RET
    return false;
  }

  // Step 4.
  //
  // Do nothing if "return" is undefined or null.
  InternalIfEmitter ifReturnMethodIsDefined(this);
  if (!emitPushNotUndefinedOrNull()) {
    //              [stack] ... ITER RET NOT-UNDEF-OR-NULL
    return false;
  }

  if (!ifReturnMethodIsDefined.emitThenElse()) {
    //              [stack] ... ITER RET
    return false;
  }

  if (completionKind == CompletionKind::Throw) {
    // 7.4.6 IteratorClose ( iterator, completion )
    //   ...
    //   3. Let return be ? GetMethod(iterator, "return").
    //   4. If return is undefined, return Completion(completion).
    //   5. Let innerResult be Call(return, iterator, « »).
    //   6. If completion.[[Type]] is throw, return Completion(completion).
    //   7. If innerResult.[[Type]] is throw, return
    //      Completion(innerResult).
    //
    // For CompletionKind::Normal case, JSOp::Call for step 5 checks if RET
    // is callable, and throws if not.  Since step 6 doesn't match and
    // error handling in step 3 and step 7 can be merged.
    //
    // For CompletionKind::Throw case, an error thrown by JSOp::Call for
    // step 5 is ignored by try-catch.  So we should check if RET is
    // callable here, outside of try-catch, and the throw immediately if
    // not.
    CheckIsCallableKind kind = CheckIsCallableKind::IteratorReturn;
    if (!emitCheckIsCallable(kind)) {
      //            [stack] ... ITER RET
      return false;
    }
  }

  // Steps 5, 8.
  //
  // Call "return" if it is not undefined or null, and check that it returns
  // an Object.
  if (!emit1(JSOp::Swap)) {
    //              [stack] ... RET ITER
    return false;
  }

  Maybe<TryEmitter> tryCatch;

  if (completionKind == CompletionKind::Throw) {
    tryCatch.emplace(this, TryEmitter::Kind::TryCatch,
                     TryEmitter::ControlKind::NonSyntactic);

    // Mutate stack to balance stack for try-catch.
    if (!emit1(JSOp::Undefined)) {
      //            [stack] ... RET ITER UNDEF
      return false;
    }
    if (!tryCatch->emitTry()) {
      //            [stack] ... RET ITER UNDEF
      return false;
    }
    if (!emitDupAt(2, 2)) {
      //            [stack] ... RET ITER UNDEF RET
      return false;
    }
  }

  if (!emitCall(JSOp::Call, 0)) {
    //              [stack] ... ... RESULT
    return false;
  }

  if (iterKind == IteratorKind::Async) {
    if (completionKind != CompletionKind::Throw) {
      // Await clobbers rval, so save the current rval.
      if (!emit1(JSOp::GetRval)) {
        //          [stack] ... ... RESULT RVAL
        return false;
      }
      if (!emit1(JSOp::Swap)) {
        //          [stack] ... ... RVAL RESULT
        return false;
      }
    }
    if (!emitAwaitInScope(currentScope)) {
      //            [stack] ... ... RVAL? RESULT
      return false;
    }
  }

  if (completionKind == CompletionKind::Throw) {
    if (!emit1(JSOp::Swap)) {
      //            [stack] ... RET ITER RESULT UNDEF
      return false;
    }
    if (!emit1(JSOp::Pop)) {
      //            [stack] ... RET ITER RESULT
      return false;
    }

    if (!tryCatch->emitCatch()) {
      //            [stack] ... RET ITER RESULT EXC
      return false;
    }

    // Just ignore the exception thrown by call and await.
    if (!emit1(JSOp::Pop)) {
      //            [stack] ... RET ITER RESULT
      return false;
    }

    if (!tryCatch->emitEnd()) {
      //            [stack] ... RET ITER RESULT
      return false;
    }

    // Restore stack.
    if (!emit2(JSOp::Unpick, 2)) {
      //            [stack] ... RESULT RET ITER
      return false;
    }
    if (!emitPopN(2)) {
      //            [stack] ... RESULT
      return false;
    }
  } else {
    if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) {
      //            [stack] ... RVAL? RESULT
      return false;
    }

    if (iterKind == IteratorKind::Async) {
      if (!emit1(JSOp::Swap)) {
        //          [stack] ... RESULT RVAL
        return false;
      }
      if (!emit1(JSOp::SetRval)) {
        //          [stack] ... RESULT
        return false;
      }
    }
  }

  if (!ifReturnMethodIsDefined.emitElse()) {
    //              [stack] ... ITER RET
    return false;
  }

  if (!emit1(JSOp::Pop)) {
    //              [stack] ... ITER
    return false;
  }

  if (!ifReturnMethodIsDefined.emitEnd()) {
    return false;
  }

  return emit1(JSOp::Pop);
  //                [stack] ...
}

template <typename InnerEmitter>
bool BytecodeEmitter::wrapWithDestructuringTryNote(int32_t iterDepth,
                                                   InnerEmitter emitter) {
  MOZ_ASSERT(bytecodeSection().stackDepth() >= iterDepth);

  // Pad a nop at the beginning of the bytecode covered by the trynote so
  // that when unwinding environments, we may unwind to the scope
  // corresponding to the pc *before* the start, in case the first bytecode
  // emitted by |emitter| is the start of an inner scope. See comment above
  // UnwindEnvironmentToTryPc.
  if (!emit1(JSOp::TryDestructuring)) {
    return false;
  }

  BytecodeOffset start = bytecodeSection().offset();
  if (!emitter(this)) {
    return false;
  }
  BytecodeOffset end = bytecodeSection().offset();
  if (start != end) {
    return addTryNote(TryNoteKind::Destructuring, iterDepth, start, end);
  }
  return true;
}

bool BytecodeEmitter::emitDefault(ParseNode* defaultExpr, ParseNode* pattern) {
  //                [stack] VALUE

  DefaultEmitter de(this);
  if (!de.prepareForDefault()) {
    //              [stack]
    return false;
  }
  if (!emitInitializer(defaultExpr, pattern)) {
    //              [stack] DEFAULTVALUE
    return false;
  }
  if (!de.emitEnd()) {
    //              [stack] VALUE/DEFAULTVALUE
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitAnonymousFunctionWithName(ParseNode* node,
                                                    HandleAtom name) {
  MOZ_ASSERT(node->isDirectRHSAnonFunction());

  if (node->is<FunctionNode>()) {
    // Function doesn't have 'name' property at this point.
    // Set function's name at compile time.
    if (!setFunName(node->as<FunctionNode>().funbox(), name)) {
      return false;
    }

    return emitTree(node);
  }

  MOZ_ASSERT(node->is<ClassNode>());

  return emitClass(&node->as<ClassNode>(), ClassNameKind::InferredName, name);
}

bool BytecodeEmitter::emitAnonymousFunctionWithComputedName(
    ParseNode* node, FunctionPrefixKind prefixKind) {
  MOZ_ASSERT(node->isDirectRHSAnonFunction());

  if (node->is<FunctionNode>()) {
    if (!emitTree(node)) {
      //            [stack] NAME FUN
      return false;
    }
    if (!emitDupAt(1)) {
      //            [stack] NAME FUN NAME
      return false;
    }
    if (!emit2(JSOp::SetFunName, uint8_t(prefixKind))) {
      //            [stack] NAME FUN
      return false;
    }
    return true;
  }

  MOZ_ASSERT(node->is<ClassNode>());
  MOZ_ASSERT(prefixKind == FunctionPrefixKind::None);

  return emitClass(&node->as<ClassNode>(), ClassNameKind::ComputedName);
}

bool BytecodeEmitter::setFunName(FunctionBox* funbox, JSAtom* name) {
  // The inferred name may already be set if this function is an interpreted
  // lazy function and we OOM'ed after we set the inferred name the first
  // time.
  if (funbox->hasInferredName()) {
    MOZ_ASSERT(!funbox->emitBytecode);
    MOZ_ASSERT(funbox->inferredName() == name);

    return true;
  }

  funbox->setInferredName(name);
  return true;
}

bool BytecodeEmitter::emitInitializer(ParseNode* initializer,
                                      ParseNode* pattern) {
  if (initializer->isDirectRHSAnonFunction()) {
    MOZ_ASSERT(!pattern->isInParens());
    RootedAtom name(cx, pattern->as<NameNode>().name());
    if (!emitAnonymousFunctionWithName(initializer, name)) {
      return false;
    }
  } else {
    if (!emitTree(initializer)) {
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern,
                                                DestructuringFlavor flav) {
  MOZ_ASSERT(pattern->isKind(ParseNodeKind::ArrayExpr));
  MOZ_ASSERT(bytecodeSection().stackDepth() != 0);

  // Here's pseudo code for |let [a, b, , c=y, ...d] = x;|
  //
  // Lines that are annotated "covered by trynote" mean that upon throwing
  // an exception, IteratorClose is called on iter only if done is false.
  //
  //   let x, y;
  //   let a, b, c, d;
  //   let iter, next, lref, result, done, value; // stack values
  //
  //   iter = x[Symbol.iterator]();
  //   next = iter.next;
  //
  //   // ==== emitted by loop for a ====
  //   lref = GetReference(a);              // covered by trynote
  //
  //   result = Call(next, iter);
  //   done = result.done;
  //
  //   if (done)
  //     value = undefined;
  //   else
  //     value = result.value;
  //
  //   SetOrInitialize(lref, value);        // covered by trynote
  //
  //   // ==== emitted by loop for b ====
  //   lref = GetReference(b);              // covered by trynote
  //
  //   if (done) {
  //     value = undefined;
  //   } else {
  //     result = Call(next, iter);
  //     done = result.done;
  //     if (done)
  //       value = undefined;
  //     else
  //       value = result.value;
  //   }
  //
  //   SetOrInitialize(lref, value);        // covered by trynote
  //
  //   // ==== emitted by loop for elision ====
  //   if (done) {
  //     value = undefined;
  //   } else {
  //     result = Call(next, iter);
  //     done = result.done;
  //     if (done)
  //       value = undefined;
  //     else
  //       value = result.value;
  //   }
  //
  //   // ==== emitted by loop for c ====
  //   lref = GetReference(c);              // covered by trynote
  //
  //   if (done) {
  //     value = undefined;
  //   } else {
  //     result = Call(next, iter);
  //     done = result.done;
  //     if (done)
  //       value = undefined;
  //     else
  //       value = result.value;
  //   }
  //
  //   if (value === undefined)
  //     value = y;                         // covered by trynote
  //
  //   SetOrInitialize(lref, value);        // covered by trynote
  //
  //   // ==== emitted by loop for d ====
  //   lref = GetReference(d);              // covered by trynote
  //
  //   if (done)
  //     value = [];
  //   else
  //     value = [...iter];
  //
  //   SetOrInitialize(lref, value);        // covered by trynote
  //
  //   // === emitted after loop ===
  //   if (!done)
  //      IteratorClose(iter);

  // Use an iterator to destructure the RHS, instead of index lookup. We
  // must leave the *original* value on the stack.
  if (!emit1(JSOp::Dup)) {
    //              [stack] ... OBJ OBJ
    return false;
  }
  if (!emitIterator()) {
    //              [stack] ... OBJ NEXT ITER
    return false;
  }

  // For an empty pattern [], call IteratorClose unconditionally. Nothing
  // else needs to be done.
  if (!pattern->head()) {
    if (!emit1(JSOp::Swap)) {
      //            [stack] ... OBJ ITER NEXT
      return false;
    }
    if (!emit1(JSOp::Pop)) {
      //            [stack] ... OBJ ITER
      return false;
    }

    return emitIteratorCloseInInnermostScope();
    //              [stack] ... OBJ
  }

  // Push an initial FALSE value for DONE.
  if (!emit1(JSOp::False)) {
    //              [stack] ... OBJ NEXT ITER FALSE
    return false;
  }

  // TryNoteKind::Destructuring expects the iterator and the done value
  // to be the second to top and the top of the stack, respectively.
  // IteratorClose is called upon exception only if done is false.
  int32_t tryNoteDepth = bytecodeSection().stackDepth();

  for (ParseNode* member : pattern->contents()) {
    bool isFirst = member == pattern->head();
    DebugOnly<bool> hasNext = !!member->pn_next;

    size_t emitted = 0;

    // Spec requires LHS reference to be evaluated first.
    ParseNode* lhsPattern = member;
    if (lhsPattern->isKind(ParseNodeKind::AssignExpr)) {
      lhsPattern = lhsPattern->as<AssignmentNode>().left();
    }

    bool isElision = lhsPattern->isKind(ParseNodeKind::Elision);
    if (!isElision) {
      auto emitLHSRef = [lhsPattern, &emitted](BytecodeEmitter* bce) {
        return bce->emitDestructuringLHSRef(lhsPattern, &emitted);
        //          [stack] ... OBJ NEXT ITER DONE LREF*
      };
      if (!wrapWithDestructuringTryNote(tryNoteDepth, emitLHSRef)) {
        return false;
      }
    }

    // Pick the DONE value to the top of the stack.
    if (emitted) {
      if (!emit2(JSOp::Pick, emitted)) {
        //          [stack] ... OBJ NEXT ITER LREF* DONE
        return false;
      }
    }

    if (isFirst) {
      // If this element is the first, DONE is always FALSE, so pop it.
      //
      // Non-first elements should emit if-else depending on the
      // member pattern, below.
      if (!emit1(JSOp::Pop)) {
        //          [stack] ... OBJ NEXT ITER LREF*
        return false;
      }
    }

    if (member->isKind(ParseNodeKind::Spread)) {
      InternalIfEmitter ifThenElse(this);
      if (!isFirst) {
        // If spread is not the first element of the pattern,
        // iterator can already be completed.
        //          [stack] ... OBJ NEXT ITER LREF* DONE

        if (!ifThenElse.emitThenElse()) {
          //        [stack] ... OBJ NEXT ITER LREF*
          return false;
        }

        if (!emitUint32Operand(JSOp::NewArray, 0)) {
          //        [stack] ... OBJ NEXT ITER LREF* ARRAY
          return false;
        }
        if (!ifThenElse.emitElse()) {
          //        [stack] ... OBJ NEXT ITER LREF*
          return false;
        }
      }

      // If iterator is not completed, create a new array with the rest
      // of the iterator.
      if (!emitDupAt(emitted + 1, 2)) {
        //          [stack] ... OBJ NEXT ITER LREF* NEXT
        return false;
      }
      if (!emitUint32Operand(JSOp::NewArray, 0)) {
        //          [stack] ... OBJ NEXT ITER LREF* NEXT ITER ARRAY
        return false;
      }
      if (!emitNumberOp(0)) {
        //          [stack] ... OBJ NEXT ITER LREF* NEXT ITER ARRAY INDEX
        return false;
      }
      if (!emitSpread()) {
        //          [stack] ... OBJ NEXT ITER LREF* ARRAY INDEX
        return false;
      }
      if (!emit1(JSOp::Pop)) {
        //          [stack] ... OBJ NEXT ITER LREF* ARRAY
        return false;
      }

      if (!isFirst) {
        if (!ifThenElse.emitEnd()) {
          return false;
        }
        MOZ_ASSERT(ifThenElse.pushed() == 1);
      }

      // At this point the iterator is done. Unpick a TRUE value for DONE above
      // ITER.
      if (!emit1(JSOp::True)) {
        //          [stack] ... OBJ NEXT ITER LREF* ARRAY TRUE
        return false;
      }
      if (!emit2(JSOp::Unpick, emitted + 1)) {
        //          [stack] ... OBJ NEXT ITER TRUE LREF* ARRAY
        return false;
      }

      auto emitAssignment = [member, flav](BytecodeEmitter* bce) {
        return bce->emitSetOrInitializeDestructuring(member, flav);
        //          [stack] ... OBJ NEXT ITER TRUE
      };
      if (!wrapWithDestructuringTryNote(tryNoteDepth, emitAssignment)) {
        return false;
      }

      MOZ_ASSERT(!hasNext);
      break;
    }

    ParseNode* pndefault = nullptr;
    if (member->isKind(ParseNodeKind::AssignExpr)) {
      pndefault = member->as<AssignmentNode>().right();
    }

    MOZ_ASSERT(!member->isKind(ParseNodeKind::Spread));

    InternalIfEmitter ifAlreadyDone(this);
    if (!isFirst) {
      //            [stack] ... OBJ NEXT ITER LREF* DONE

      if (!ifAlreadyDone.emitThenElse()) {
        //          [stack] ... OBJ NEXT ITER LREF*
        return false;
      }

      if (!emit1(JSOp::Undefined)) {
        //          [stack] ... OBJ NEXT ITER LREF* UNDEF
        return false;
      }
      if (!emit1(JSOp::NopDestructuring)) {
        //          [stack] ... OBJ NEXT ITER LREF* UNDEF
        return false;
      }

      // The iterator is done. Unpick a TRUE value for DONE above ITER.
      if (!emit1(JSOp::True)) {
        //          [stack] ... OBJ NEXT ITER LREF* UNDEF TRUE
        return false;
      }
      if (!emit2(JSOp::Unpick, emitted + 1)) {
        //          [stack] ... OBJ NEXT ITER TRUE LREF* UNDEF
        return false;
      }

      if (!ifAlreadyDone.emitElse()) {
        //          [stack] ... OBJ NEXT ITER LREF*
        return false;
      }
    }

    if (!emitDupAt(emitted + 1, 2)) {
      //            [stack] ... OBJ NEXT ITER LREF* NEXT
      return false;
    }
    if (!emitIteratorNext(Some(pattern->pn_pos.begin))) {
      //            [stack] ... OBJ NEXT ITER LREF* RESULT
      return false;
    }
    if (!emit1(JSOp::Dup)) {
      //            [stack] ... OBJ NEXT ITER LREF* RESULT RESULT
      return false;
    }
    if (!emitAtomOp(JSOp::GetProp, cx->names().done)) {
      //            [stack] ... OBJ NEXT ITER LREF* RESULT DONE
      return false;
    }

    if (!emit1(JSOp::Dup)) {
      //            [stack] ... OBJ NEXT ITER LREF* RESULT DONE DONE
      return false;
    }
    if (!emit2(JSOp::Unpick, emitted + 2)) {
      //            [stack] ... OBJ NEXT ITER DONE LREF* RESULT DONE
      return false;
    }

    InternalIfEmitter ifDone(this);
    if (!ifDone.emitThenElse()) {
      //            [stack] ... OBJ NEXT ITER DONE LREF* RESULT
      return false;
    }

    if (!emit1(JSOp::Pop)) {
      //            [stack] ... OBJ NEXT ITER DONE LREF*
      return false;
    }
    if (!emit1(JSOp::Undefined)) {
      //            [stack] ... OBJ NEXT ITER DONE LREF* UNDEF
      return false;
    }
    if (!emit1(JSOp::NopDestructuring)) {
      //            [stack] ... OBJ NEXT ITER DONE LREF* UNDEF
      return false;
    }

    if (!ifDone.emitElse()) {
      //            [stack] ... OBJ NEXT ITER DONE LREF* RESULT
      return false;
    }

    if (!emitAtomOp(JSOp::GetProp, cx->names().value)) {
      //            [stack] ... OBJ NEXT ITER DONE LREF* VALUE
      return false;
    }

    if (!ifDone.emitEnd()) {
      return false;
    }
    MOZ_ASSERT(ifDone.pushed() == 0);

    if (!isFirst) {
      if (!ifAlreadyDone.emitEnd()) {
        return false;
      }
      MOZ_ASSERT(ifAlreadyDone.pushed() == 2);
    }

    if (pndefault) {
      auto emitDefault = [pndefault, lhsPattern](BytecodeEmitter* bce) {
        return bce->emitDefault(pndefault, lhsPattern);
        //          [stack] ... OBJ NEXT ITER DONE LREF* VALUE
      };

      if (!wrapWithDestructuringTryNote(tryNoteDepth, emitDefault)) {
        return false;
      }
    }

    if (!isElision) {
      auto emitAssignment = [lhsPattern, flav](BytecodeEmitter* bce) {
        return bce->emitSetOrInitializeDestructuring(lhsPattern, flav);
        //          [stack] ... OBJ NEXT ITER DONE
      };

      if (!wrapWithDestructuringTryNote(tryNoteDepth, emitAssignment)) {
        return false;
      }
    } else {
      if (!emit1(JSOp::Pop)) {
        //          [stack] ... OBJ NEXT ITER DONE
        return false;
      }
    }
  }

  // The last DONE value is on top of the stack. If not DONE, call
  // IteratorClose.
  //                [stack] ... OBJ NEXT ITER DONE

  InternalIfEmitter ifDone(this);
  if (!ifDone.emitThenElse()) {
    //              [stack] ... OBJ NEXT ITER
    return false;
  }
  if (!emitPopN(2)) {
    //              [stack] ... OBJ
    return false;
  }
  if (!ifDone.emitElse()) {
    //              [stack] ... OBJ NEXT ITER
    return false;
  }
  if (!emit1(JSOp::Swap)) {
    //              [stack] ... OBJ ITER NEXT
    return false;
  }
  if (!emit1(JSOp::Pop)) {
    //              [stack] ... OBJ ITER
    return false;
  }
  if (!emitIteratorCloseInInnermostScope()) {
    //              [stack] ... OBJ
    return false;
  }
  if (!ifDone.emitEnd()) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitComputedPropertyName(UnaryNode* computedPropName) {
  MOZ_ASSERT(computedPropName->isKind(ParseNodeKind::ComputedName));
  return emitTree(computedPropName->kid()) && emit1(JSOp::ToId);
}

bool BytecodeEmitter::emitDestructuringOpsObject(ListNode* pattern,
                                                 DestructuringFlavor flav) {
  MOZ_ASSERT(pattern->isKind(ParseNodeKind::ObjectExpr));

  //                [stack] ... RHS
  MOZ_ASSERT(bytecodeSection().stackDepth() > 0);

  if (!emit1(JSOp::CheckObjCoercible)) {
    //              [stack] ... RHS
    return false;
  }

  bool needsRestPropertyExcludedSet =
      pattern->count() > 1 && pattern->last()->isKind(ParseNodeKind::Spread);
  if (needsRestPropertyExcludedSet) {
    if (!emitDestructuringObjRestExclusionSet(pattern)) {
      //            [stack] ... RHS SET
      return false;
    }

    if (!emit1(JSOp::Swap)) {
      //            [stack] ... SET RHS
      return false;
    }
  }

  for (ParseNode* member : pattern->contents()) {
    ParseNode* subpattern;
    if (member->isKind(ParseNodeKind::MutateProto) ||
        member->isKind(ParseNodeKind::Spread)) {
      subpattern = member->as<UnaryNode>().kid();
    } else {
      MOZ_ASSERT(member->isKind(ParseNodeKind::PropertyDefinition) ||
                 member->isKind(ParseNodeKind::Shorthand));
      subpattern = member->as<BinaryNode>().right();
    }

    ParseNode* lhs = subpattern;
    MOZ_ASSERT_IF(member->isKind(ParseNodeKind::Spread),
                  !lhs->isKind(ParseNodeKind::AssignExpr));
    if (lhs->isKind(ParseNodeKind::AssignExpr)) {
      lhs = lhs->as<AssignmentNode>().left();
    }

    size_t emitted;
    if (!emitDestructuringLHSRef(lhs, &emitted)) {
      //            [stack] ... SET? RHS LREF*
      return false;
    }

    // Duplicate the value being destructured to use as a reference base.
    if (!emitDupAt(emitted)) {
      //            [stack] ... SET? RHS LREF* RHS
      return false;
    }

    if (member->isKind(ParseNodeKind::Spread)) {
      if (!updateSourceCoordNotes(member->pn_pos.begin)) {
        return false;
      }

      if (!emit1(JSOp::NewInit)) {
        //          [stack] ... SET? RHS LREF* RHS TARGET
        return false;
      }
      if (!emit1(JSOp::Dup)) {
        //          [stack] ... SET? RHS LREF* RHS TARGET TARGET
        return false;
      }
      if (!emit2(JSOp::Pick, 2)) {
        //          [stack] ... SET? RHS LREF* TARGET TARGET RHS
        return false;
      }

      if (needsRestPropertyExcludedSet) {
        if (!emit2(JSOp::Pick, emitted + 4)) {
          //        [stack] ... RHS LREF* TARGET TARGET RHS SET
          return false;
        }
      }

      CopyOption option = needsRestPropertyExcludedSet ? CopyOption::Filtered
                                                       : CopyOption::Unfiltered;
      if (!emitCopyDataProperties(option)) {
        //          [stack] ... RHS LREF* TARGET
        return false;
      }

      // Destructure TARGET per this member's lhs.
      if (!emitSetOrInitializeDestructuring(lhs, flav)) {
        //          [stack] ... RHS
        return false;
      }

      MOZ_ASSERT(member == pattern->last(), "Rest property is always last");
      break;
    }

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

    if (member->isKind(ParseNodeKind::MutateProto)) {
      if (!emitAtomOp(JSOp::GetProp, cx->names().proto)) {
        //          [stack] ... SET? RHS LREF* PROP
        return false;
      }
      needsGetElem = false;
    } else {
      MOZ_ASSERT(member->isKind(ParseNodeKind::PropertyDefinition) ||
                 member->isKind(ParseNodeKind::Shorthand));

      ParseNode* key = member->as<BinaryNode>().left();
      if (key->isKind(ParseNodeKind::NumberExpr)) {
        if (!emitNumberOp(key->as<NumericLiteral>().value())) {
          //        [stack]... SET? RHS LREF* RHS KEY
          return false;
        }
      } else if (key->isKind(ParseNodeKind::BigIntExpr)) {
        if (!emitBigIntOp(&key->as<BigIntLiteral>())) {
          //        [stack]... SET? RHS LREF* RHS KEY
          return false;
        }
      } else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
                 key->isKind(ParseNodeKind::StringExpr)) {
        if (!emitAtomOp(JSOp::GetProp, key->as<NameNode>().atom(),
                        ShouldInstrument::Yes)) {
          //        [stack] ... SET? RHS LREF* PROP
          return false;
        }
        needsGetElem = false;
      } else {
        if (!emitComputedPropertyName(&key->as<UnaryNode>())) {
          //        [stack] ... SET? RHS LREF* RHS KEY
          return false;
        }

        // Add the computed property key to the exclusion set.
        if (needsRestPropertyExcludedSet) {
          if (!emitDupAt(emitted + 3)) {
            //      [stack] ... SET RHS LREF* RHS KEY SET
            return false;
          }
          if (!emitDupAt(1)) {
            //      [stack] ... SET RHS LREF* RHS KEY SET KEY
            return false;
          }
          if (!emit1(JSOp::Undefined)) {
            //      [stack] ... SET RHS LREF* RHS KEY SET KEY UNDEFINED
            return false;
          }
          if (!emit1(JSOp::InitElem)) {
            //      [stack] ... SET RHS LREF* RHS KEY SET
            return false;
          }
          if (!emit1(JSOp::Pop)) {
            //      [stack] ... SET RHS LREF* RHS KEY
            return false;
          }
        }
      }
    }

    // Get the property value if not done already.
    if (needsGetElem && !emitElemOpBase(JSOp::GetElem)) {
      //            [stack] ... SET? RHS LREF* PROP
      return false;
    }

    if (subpattern->isKind(ParseNodeKind::AssignExpr)) {
      if (!emitDefault(subpattern->as<AssignmentNode>().right(), lhs)) {
        //          [stack] ... SET? RHS LREF* VALUE
        return false;
      }
    }

    // Destructure PROP per this member's lhs.
    if (!emitSetOrInitializeDestructuring(subpattern, flav)) {
      //            [stack] ... SET? RHS
      return false;
    }
  }

  return true;
}

static bool IsDestructuringRestExclusionSetObjLiteralCompatible(
    ListNode* pattern) {
  int32_t propCount = 0;
  for (ParseNode* member : pattern->contents()) {
    if (member->isKind(ParseNodeKind::Spread)) {
      MOZ_ASSERT(!member->pn_next, "unexpected trailing element after spread");
      break;
    }

    propCount++;

    if (member->isKind(ParseNodeKind::MutateProto)) {
      continue;
    }

    ParseNode* key = member->as<BinaryNode>().left();
    if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
        key->isKind(ParseNodeKind::StringExpr)) {
      continue;
    }

    // Number and BigInt keys aren't yet supported. Computed property names need
    // to be added dynamically.
    MOZ_ASSERT(key->isKind(ParseNodeKind::NumberExpr) ||
               key->isKind(ParseNodeKind::BigIntExpr) ||
               key->isKind(ParseNodeKind::ComputedName));
    return false;
  }

  if (propCount >= PropertyTree::MAX_HEIGHT) {
    // JSOp::NewObject cannot accept dictionary-mode objects.
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitDestructuringObjRestExclusionSet(ListNode* pattern) {
  MOZ_ASSERT(pattern->isKind(ParseNodeKind::ObjectExpr));
  MOZ_ASSERT(pattern->last()->isKind(ParseNodeKind::Spread));

  // See if we can use ObjLiteral to construct the exclusion set object.
  if (IsDestructuringRestExclusionSetObjLiteralCompatible(pattern)) {
    if (!emitDestructuringRestExclusionSetObjLiteral(pattern)) {
      //            [stack] OBJ
      return false;
    }
  } else {
    // Take the slow but sure way and start off with a blank object.
    if (!emit1(JSOp::NewInit)) {
      //            [stack] OBJ
      return false;
    }
  }

  RootedAtom pnatom(cx);
  for (ParseNode* member : pattern->contents()) {
    if (member->isKind(ParseNodeKind::Spread)) {
      MOZ_ASSERT(!member->pn_next, "unexpected trailing element after spread");
      break;
    }

    bool isIndex = false;
    if (member->isKind(ParseNodeKind::MutateProto)) {
      pnatom.set(cx->names().proto);
    } else {
      ParseNode* key = member->as<BinaryNode>().left();
      if (key->isKind(ParseNodeKind::NumberExpr)) {
        if (!emitNumberOp(key->as<NumericLiteral>().value())) {
          return false;
        }
        isIndex = true;
      } else if (key->isKind(ParseNodeKind::BigIntExpr)) {
        if (!emitBigIntOp(&key->as<BigIntLiteral>())) {
          return false;
        }
        isIndex = true;
      } else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
                 key->isKind(ParseNodeKind::StringExpr)) {
        pnatom.set(key->as<NameNode>().atom());
      } else {
        // Otherwise this is a computed property name which needs to
        // be added dynamically.
        MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));
        continue;
      }
    }

    // Initialize elements with |undefined|.
    if (!emit1(JSOp::Undefined)) {
      return false;
    }

    if (isIndex) {
      if (!emit1(JSOp::InitElem)) {
        return false;
      }
    } else {
      if (!emitAtomOp(JSOp::InitProp, pnatom)) {
        return false;
      }
    }
  }

  return true;
}

bool BytecodeEmitter::emitDestructuringOps(ListNode* pattern,
                                           DestructuringFlavor flav) {
  if (pattern->isKind(ParseNodeKind::ArrayExpr)) {
    return emitDestructuringOpsArray(pattern, flav);
  }
  return emitDestructuringOpsObject(pattern, flav);
}

bool BytecodeEmitter::emitTemplateString(ListNode* templateString) {
  bool pushedString = false;

  for (ParseNode* item : templateString->contents()) {
    bool isString = (item->getKind() == ParseNodeKind::StringExpr ||
                     item->getKind() == ParseNodeKind::TemplateStringExpr);

    // Skip empty strings. These are very common: a template string like
    // `${a}${b}` has three empty strings and without this optimization
    // we'd emit four JSOp::Add operations instead of just one.
    if (isString && item->as<NameNode>().atom()->empty()) {
      continue;
    }

    if (!isString) {
      // We update source notes before emitting the expression
      if (!updateSourceCoordNotes(item->pn_pos.begin)) {
        return false;
      }
    }

    if (!emitTree(item)) {
      return false;
    }

    if (!isString) {
      // We need to convert the expression to a string
      if (!emit1(JSOp::ToString)) {
        return false;
      }
    }

    if (pushedString) {
      // We've pushed two strings onto the stack. Add them together, leaving
      // just one.
      if (!emit1(JSOp::Add)) {
        return false;
      }
    } else {
      pushedString = true;
    }
  }

  if (!pushedString) {
    // All strings were empty, this can happen for something like `${""}`.
    // Just push an empty string.
    if (!emitAtomOp(JSOp::String, cx->names().empty)) {
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitDeclarationList(ListNode* declList) {
  for (ParseNode* decl : declList->contents()) {
    ParseNode* pattern;
    ParseNode* initializer;
    if (decl->isKind(ParseNodeKind::Name)) {
      pattern = decl;
      initializer = nullptr;
    } else {
      AssignmentNode* assignNode = &decl->as<AssignmentNode>();
      pattern = assignNode->left();
      initializer = assignNode->right();
    }

    if (pattern->isKind(ParseNodeKind::Name)) {
      // initializer can be null here.
      if (!emitSingleDeclaration(declList, &pattern->as<NameNode>(),
                                 initializer)) {
        return false;
      }
    } else {
      MOZ_ASSERT(pattern->isKind(ParseNodeKind::ArrayExpr) ||
                 pattern->isKind(ParseNodeKind::ObjectExpr));
      MOZ_ASSERT(initializer != nullptr);

      if (!updateSourceCoordNotes(initializer->pn_pos.begin)) {
        return false;
      }
      if (!markStepBreakpoint()) {
        return false;
      }
      if (!emitTree(initializer)) {
        return false;
      }

      if (!emitDestructuringOps(&pattern->as<ListNode>(),
                                DestructuringFlavor::Declaration)) {
        return false;
      }

      if (!emit1(JSOp::Pop)) {
        return false;
      }
    }
  }
  return true;
}

bool BytecodeEmitter::emitSingleDeclaration(ListNode* declList, NameNode* decl,
                                            ParseNode* initializer) {
  MOZ_ASSERT(decl->isKind(ParseNodeKind::Name));

  // Nothing to do for initializer-less 'var' declarations, as there's no TDZ.
  if (!initializer && declList->isKind(ParseNodeKind::VarStmt)) {
    return true;
  }

  RootedAtom nameAtom(cx, decl->name());
  NameOpEmitter noe(this, nameAtom, NameOpEmitter::Kind::Initialize);
  if (!noe.prepareForRhs()) {
    //              [stack] ENV?
    return false;
  }
  if (!initializer) {
    // Lexical declarations are initialized to undefined without an
    // initializer.
    MOZ_ASSERT(declList->isKind(ParseNodeKind::LetDecl),
               "var declarations without initializers handled above, "
               "and const declarations must have initializers");
    if (!emit1(JSOp::Undefined)) {
      //            [stack] ENV? UNDEF
      return false;
    }
  } else {
    MOZ_ASSERT(initializer);

    if (!updateSourceCoordNotes(initializer->pn_pos.begin)) {
      return false;
    }
    if (!markStepBreakpoint()) {
      return false;
    }
    if (!emitInitializer(initializer, decl)) {
      //            [stack] ENV? V
      return false;
    }
  }
  if (!noe.emitAssignment()) {
    //              [stack] V
    return false;
  }
  if (!emit1(JSOp::Pop)) {
    //              [stack]
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitAssignmentRhs(ParseNode* rhs,
                                        HandleAtom anonFunctionName) {
  if (rhs->isDirectRHSAnonFunction()) {
    if (anonFunctionName) {
      return emitAnonymousFunctionWithName(rhs, anonFunctionName);
    }
    return emitAnonymousFunctionWithComputedName(rhs, FunctionPrefixKind::None);
  }
  return emitTree(rhs);
}

// The RHS value to assign is already on the stack, i.e., the next enumeration
// value in a for-in or for-of loop. Offset is the location in the stack of the
// already-emitted rhs. If we emitted a BIND[G]NAME, then the scope is on the
// top of the stack and we need to dig one deeper to get the right RHS value.
bool BytecodeEmitter::emitAssignmentRhs(uint8_t offset) {
  if (offset != 1) {
    return emit2(JSOp::Pick, offset - 1);
  }

  return true;
}

static inline JSOp CompoundAssignmentParseNodeKindToJSOp(ParseNodeKind pnk) {
  switch (pnk) {
    case ParseNodeKind::InitExpr:
      return JSOp::Nop;
    case ParseNodeKind::AssignExpr:
      return JSOp::Nop;
    case ParseNodeKind::AddAssignExpr:
      return JSOp::Add;
    case ParseNodeKind::SubAssignExpr:
      return JSOp::Sub;
    case ParseNodeKind::BitOrAssignExpr:
      return JSOp::BitOr;
    case ParseNodeKind::BitXorAssignExpr:
      return JSOp::BitXor;
    case ParseNodeKind::BitAndAssignExpr:
      return JSOp::BitAnd;
    case ParseNodeKind::LshAssignExpr:
      return JSOp::Lsh;
    case ParseNodeKind::RshAssignExpr:
      return JSOp::Rsh;
    case ParseNodeKind::UrshAssignExpr:
      return JSOp::Ursh;
    case ParseNodeKind::MulAssignExpr:
      return JSOp::Mul;
    case ParseNodeKind::DivAssignExpr:
      return JSOp::Div;
    case ParseNodeKind::ModAssignExpr:
      return JSOp::Mod;
    case ParseNodeKind::PowAssignExpr:
      return JSOp::Pow;
    default:
      MOZ_CRASH("unexpected compound assignment op");
  }
}

bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs,
                                           ParseNode* rhs) {
  JSOp compoundOp = CompoundAssignmentParseNodeKindToJSOp(kind);
  bool isCompound = compoundOp != JSOp::Nop;
  bool isInit = kind == ParseNodeKind::InitExpr;

  MOZ_ASSERT_IF(isInit, lhs->isKind(ParseNodeKind::DotExpr) ||
                            lhs->isKind(ParseNodeKind::ElemExpr));

  // Name assignments are handled separately because choosing ops and when
  // to emit BindName is involved and should avoid duplication.
  if (lhs->isKind(ParseNodeKind::Name)) {
    NameNode* nameNode = &lhs->as<NameNode>();
    RootedAtom name(cx, nameNode->name());
    NameOpEmitter noe(this, name,
                      isCompound ? NameOpEmitter::Kind::CompoundAssignment
                                 : NameOpEmitter::Kind::SimpleAssignment);
    if (!noe.prepareForRhs()) {
      //            [stack] ENV? VAL?
      return false;
    }

    if (rhs) {
      if (!emitAssignmentRhs(rhs, name)) {
        //          [stack] ENV? VAL? RHS
        return false;
      }
    } else {
      uint8_t offset = noe.emittedBindOp() ? 2 : 1;
      // Assumption: Things with pre-emitted RHS values never need to be named.
      if (!emitAssignmentRhs(offset)) {
        //          [stack] ENV? VAL? RHS
        return false;
      }
    }

    // Emit the compound assignment op if there is one.
    if (isCompound) {
      if (!emit1(compoundOp)) {
        //          [stack] ENV? VAL
        return false;
      }
    }
    if (!noe.emitAssignment()) {
      //            [stack] VAL
      return false;
    }

    return true;
  }

  Maybe<PropOpEmitter> poe;
  Maybe<ElemOpEmitter> eoe;

  // Deal with non-name assignments.
  uint8_t offset = 1;

  RootedAtom anonFunctionName(cx);
  switch (lhs->getKind()) {
    case ParseNodeKind::DotExpr: {
      PropertyAccess* prop = &lhs->as<PropertyAccess>();
      bool isSuper = prop->isSuper();
      poe.emplace(this,
                  isCompound ? PropOpEmitter::Kind::CompoundAssignment
                             : isInit ? PropOpEmitter::Kind::PropInit
                                      : PropOpEmitter::Kind::SimpleAssignment,
                  isSuper ? PropOpEmitter::ObjKind::Super
                          : PropOpEmitter::ObjKind::Other);
      if (!poe->prepareForObj()) {
        return false;
      }
      anonFunctionName = &prop->name();
      if (isSuper) {
        UnaryNode* base = &prop->expression().as<UnaryNode>();
        if (!emitGetThisForSuperBase(base)) {
          //        [stack] THIS SUPERBASE
          return false;
        }
        // SUPERBASE is pushed onto THIS later in poe->emitGet below.
        offset += 2;
      } else {
        if (!emitTree(&prop->expression())) {
          //        [stack] OBJ
          return false;
        }
        offset += 1;
      }
      break;
    }
    case ParseNodeKind::ElemExpr: {
      PropertyByValue* elem = &lhs->as<PropertyByValue>();
      bool isSuper = elem->isSuper();
      eoe.emplace(this,
                  isCompound ? ElemOpEmitter::Kind::CompoundAssignment
                             : isInit ? ElemOpEmitter::Kind::PropInit
                                      : ElemOpEmitter::Kind::SimpleAssignment,
                  isSuper ? ElemOpEmitter::ObjKind::Super
                          : ElemOpEmitter::ObjKind::Other);
      if (!emitElemObjAndKey(elem, isSuper, *eoe)) {
        //          [stack] # if Super
        //          [stack] THIS KEY
        //          [stack] # otherwise
        //          [stack] OBJ KEY
        return false;
      }
      if (isSuper) {
        // SUPERBASE is pushed onto KEY in eoe->emitGet below.
        offset += 3;
      } else {
        offset += 2;
      }
      break;
    }
    case ParseNodeKind::ArrayExpr:
    case ParseNodeKind::ObjectExpr:
      break;
    case ParseNodeKind::CallExpr:
      if (!emitTree(lhs)) {
        return false;
      }

      // Assignment to function calls is forbidden, but we have to make the
      // call first.  Now we can throw.
      if (!emit2(JSOp::ThrowMsg, uint8_t(ThrowMsgKind::AssignToCall))) {
        return false;
      }

      // Rebalance the stack to placate stack-depth assertions.
      if (!emit1(JSOp::Pop)) {
        return false;
      }
      break;
    default:
      MOZ_ASSERT(0);
  }

  if (isCompound) {
    MOZ_ASSERT(rhs);
    switch (lhs->getKind()) {
      case ParseNodeKind::DotExpr: {
        PropertyAccess* prop = &lhs->as<PropertyAccess>();
        if (!poe->emitGet(prop->key().atom())) {
          //        [stack] # if Super
          //        [stack] THIS SUPERBASE PROP
          //        [stack] # otherwise
          //        [stack] OBJ PROP
          return false;
        }
        break;
      }
      case ParseNodeKind::ElemExpr: {
        if (!eoe->emitGet()) {
          //        [stack] KEY THIS OBJ ELEM
          return false;
        }
        break;
      }
      case ParseNodeKind::CallExpr:
        // We just emitted a JSOp::ThrowMsg and popped the call's return
        // value.  Push a random value to make sure the stack depth is
        // correct.
        if (!emit1(JSOp::Null)) {
          //        [stack] NULL
          return false;
        }
        break;
      default:;
    }
  }

  switch (lhs->getKind()) {
    case ParseNodeKind::DotExpr:
      if (!poe->prepareForRhs()) {
        //          [stack] # if Simple Assignment with Super
        //          [stack] THIS SUPERBASE
        //          [stack] # if Simple Assignment with other
        //          [stack] OBJ
        //          [stack] # if Compound Assignment with Super
        //          [stack] THIS SUPERBASE PROP
        //          [stack] # if Compound Assignment with other
        //          [stack] OBJ PROP
        return false;
      }
      break;
    case ParseNodeKind::ElemExpr:
      if (!eoe->prepareForRhs()) {
        //          [stack] # if Simple Assignment with Super
        //          [stack] THIS KEY SUPERBASE
        //          [stack] # if Simple Assignment with other
        //          [stack] OBJ KEY
        //          [stack] # if Compound Assignment with Super
        //          [stack] THIS KEY SUPERBASE ELEM
        //          [stack] # if Compound Assignment with other
        //          [stack] OBJ KEY ELEM
        return false;
      }
      break;
    default:
      break;
  }

  // Purpose of anonFunctionName:
  // In normal property assignments (`obj.x = function(){}`), the anonymous
  // function does not have a computed name, and rhs->isDirectRHSAnonFunction()
  // will be false (and anonFunctionName will not be used). However, in field
  // initializers (`class C { x = function(){} }`), field initialization is
  // implemented via a property or elem assignment (where we are now), and
  // rhs->isDirectRHSAnonFunction() is set - so we'll assign the name of the
  // function.
  if (rhs) {
    if (!emitAssignmentRhs(rhs, anonFunctionName)) {
      //            [stack] ... VAL? RHS
      return false;
    }
  } else {
    // Assumption: Things with pre-emitted RHS values never need to be named.
    if (!emitAssignmentRhs(offset)) {
      //            [stack] ... VAL? RHS
      return false;
    }
  }

  /* If += etc., emit the binary operator with a source note. */
  if (isCompound) {
    if (!newSrcNote(SrcNoteType::AssignOp)) {
      return false;
    }
    if (!emit1(compoundOp)) {
      //            [stack] ... VAL
      return false;
    }
  }

  /* Finally, emit the specialized assignment bytecode. */
  switch (lhs->getKind()) {
    case ParseNodeKind::DotExpr: {
      PropertyAccess* prop = &lhs->as<PropertyAccess>();
      if (!poe->emitAssignment(prop->key().atom())) {
        //          [stack] VAL
        return false;
      }

      poe.reset();
      break;
    }
    case ParseNodeKind::CallExpr:
      // We threw above, so nothing to do here.
      break;
    case ParseNodeKind::ElemExpr: {
      if (!eoe->emitAssignment()) {
        //          [stack] VAL
        return false;
      }

      eoe.reset();
      break;
    }
    case ParseNodeKind::ArrayExpr:
    case ParseNodeKind::ObjectExpr:
      if (!emitDestructuringOps(&lhs->as<ListNode>(),
                                DestructuringFlavor::Assignment)) {
        return false;
      }
      break;
    default:
      MOZ_ASSERT(0);
  }
  return true;
}

bool BytecodeEmitter::emitCallSiteObjectArray(ListNode* cookedOrRaw,
                                              uint32_t* arrayIndex) {
  uint32_t count = cookedOrRaw->count();
  ParseNode* pn = cookedOrRaw->head();

  // The first element of a call-site node is the raw-values list. Skip over it.
  if (cookedOrRaw->isKind(ParseNodeKind::CallSiteObj)) {
    MOZ_ASSERT(pn->isKind(ParseNodeKind::ArrayExpr));
    pn = pn->pn_next;
    count--;
  } else {
    MOZ_ASSERT(cookedOrRaw->isKind(ParseNodeKind::ArrayExpr));
  }

  ObjLiteralCreationData data(cx);
  ObjLiteralFlags flags(ObjLiteralFlag::Array);
  data.writer().beginObject(flags);
  data.writer().beginDenseArrayElements();

  size_t idx;
  for (idx = 0; pn; idx++, pn = pn->pn_next) {
    MOZ_ASSERT(pn->isKind(ParseNodeKind::TemplateStringExpr) ||
               pn->isKind(ParseNodeKind::RawUndefinedExpr));

    if (!emitObjLiteralValue(&data, pn)) {
      return false;
    }
  }
  MOZ_ASSERT(idx == count);

  return perScriptData().gcThingList().append(std::move(data), arrayIndex);
}

bool BytecodeEmitter::emitCallSiteObject(CallSiteNode* callSiteObj) {
  uint32_t cookedIndex;
  if (!emitCallSiteObjectArray(callSiteObj, &cookedIndex)) {
    return false;
  }

  uint32_t rawIndex;
  if (!emitCallSiteObjectArray(callSiteObj->rawNodes(), &rawIndex)) {
    return false;
  }

  MOZ_ASSERT(sc->hasCallSiteObj());

  return emitObjectPairOp(cookedIndex, rawIndex, JSOp::CallSiteObj);
}

bool BytecodeEmitter::emitCatch(BinaryNode* catchClause) {
  // We must be nested under a try-finally statement.
  MOZ_ASSERT(innermostNestableControl->is<TryFinallyControl>());

  ParseNode* param = catchClause->left();
  if (!param) {
    // Catch parameter was omitted; just discard the exception.
    if (!emit1(JSOp::Pop)) {
      return false;
    }
  } else {
    switch (param->getKind()) {
      case ParseNodeKind::ArrayExpr:
      case ParseNodeKind::ObjectExpr:
        if (!emitDestructuringOps(&param->as<ListNode>(),
                                  DestructuringFlavor::Declaration)) {
          return false;
        }
        if (!emit1(JSOp::Pop)) {
          return false;
        }
        break;

      case ParseNodeKind::Name:
        if (!emitLexicalInitialization(&param->as<NameNode>())) {
          return false;
        }
        if (!emit1(JSOp::Pop)) {
          return false;
        }
        break;

      default:
        MOZ_ASSERT(0);
    }
  }

  /* Emit the catch body. */
  return emitTree(catchClause->right());
}

// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See the
// comment on EmitSwitch.
MOZ_NEVER_INLINE bool BytecodeEmitter::emitTry(TryNode* tryNode) {
  LexicalScopeNode* catchScope = tryNode->catchScope();
  ParseNode* finallyNode = tryNode->finallyBlock();

  TryEmitter::Kind kind;
  if (catchScope) {
    if (finallyNode) {
      kind = TryEmitter::Kind::TryCatchFinally;
    } else {
      kind = TryEmitter::Kind::TryCatch;
    }
  } else {
    MOZ_ASSERT(finallyNode);
    kind = TryEmitter::Kind::TryFinally;
  }
  TryEmitter tryCatch(this, kind, TryEmitter::ControlKind::Syntactic);

  if (!tryCatch.emitTry()) {
    return false;
  }

  if (!emitTree(tryNode->body())) {
    return false;
  }

  // If this try has a catch block, emit it.
  if (catchScope) {
    // The emitted code for a catch block looks like:
    //
    // [pushlexicalenv]             only if any local aliased
    // exception
    // setlocal 0; pop              assign or possibly destructure exception
    // < catch block contents >
    // debugleaveblock
    // [poplexicalenv]              only if any local aliased
    // if there is a finally block:
    //   gosub <finally>
    //   goto <after finally>
    if (!tryCatch.emitCatch()) {
      return false;
    }

    // Emit the lexical scope and catch body.
    if (!emitTree(catchScope)) {
      return false;
    }
  }

  // Emit the finally handler, if there is one.
  if (finallyNode) {
    if (!tryCatch.emitFinally(Some(finallyNode->pn_pos.begin))) {
      return false;
    }

    if (!emitTree(finallyNode)) {
      return false;
    }
  }

  if (!tryCatch.emitEnd()) {
    return false;
  }

  return true;
}

MOZ_MUST_USE bool BytecodeEmitter::emitGoSub(JumpList* jump) {
  // Emit the following:
  //
  //     False
  //     ResumeIndex <resumeIndex>
  //     Gosub <target>
  //   resumeOffset:
  //     JumpTarget
  //
  // The order is important: the Baseline Interpreter relies on JSOp::JumpTarget
  // setting the frame's ICEntry when resuming at resumeOffset.

  if (!emit1(JSOp::False)) {
    return false;
  }

  BytecodeOffset off;
  if (!emitN(JSOp::ResumeIndex, 3, &off)) {
    return false;
  }

  if (!emitJumpNoFallthrough(JSOp::Gosub, jump)) {
    return false;
  }

  uint32_t resumeIndex;
  if (!allocateResumeIndex(bytecodeSection().offset(), &resumeIndex)) {
    return false;
  }

  SET_RESUMEINDEX(bytecodeSection().code(off), resumeIndex);

  JumpTarget target;
  return emitJumpTarget(&target);
}

bool BytecodeEmitter::emitIf(TernaryNode* ifNode) {
  IfEmitter ifThenElse(this);

  if (!ifThenElse.emitIf(Some(ifNode->kid1()->pn_pos.begin))) {
    return false;
  }

if_again:
  if (!markStepBreakpoint()) {
    return false;
  }

  /* Emit code for the condition before pushing stmtInfo. */
  if (!emitTree(ifNode->kid1())) {
    return false;
  }

  ParseNode* elseNode = ifNode->kid3();
  if (elseNode) {
    if (!ifThenElse.emitThenElse()) {
      return false;
    }
  } else {
    if (!ifThenElse.emitThen()) {
      return false;
    }
  }

  /* Emit code for the then part. */
  if (!emitTree(ifNode->kid2())) {
    return false;
  }

  if (elseNode) {
    if (elseNode->isKind(ParseNodeKind::IfStmt)) {
      ifNode = &elseNode->as<TernaryNode>();

      if (!ifThenElse.emitElseIf(Some(ifNode->kid1()->pn_pos.begin))) {
        return false;
      }

      goto if_again;
    }

    if (!ifThenElse.emitElse()) {
      return false;
    }

    /* Emit code for the else part. */
    if (!emitTree(elseNode)) {
      return false;
    }
  }

  if (!ifThenElse.emitEnd()) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitHoistedFunctionsInList(ListNode* stmtList) {
  MOZ_ASSERT(stmtList->hasTopLevelFunctionDeclarations());

  // We can call this multiple times for sloppy eval scopes.
  if (stmtList->emittedTopLevelFunctionDeclarations()) {
    return true;
  }

  stmtList->setEmittedTopLevelFunctionDeclarations();

  for (ParseNode* stmt : stmtList->contents()) {
    ParseNode* maybeFun = stmt;

    if (!sc->strict()) {
      while (maybeFun->isKind(ParseNodeKind::LabelStmt)) {
        maybeFun = maybeFun->as<LabeledStatement>().statement();
      }
    }

    if (maybeFun->is<FunctionNode>() &&
        maybeFun->as<FunctionNode>().functionIsHoisted()) {
      if (!emitTree(maybeFun)) {
        return false;
      }
    }
  }

  return true;
}

bool BytecodeEmitter::emitLexicalScopeBody(
    ParseNode* body, EmitLineNumberNote emitLineNote /* = EMIT_LINENOTE */) {
  if (body->isKind(ParseNodeKind::StatementList) &&
      body->as<ListNode>().hasTopLevelFunctionDeclarations()) {
    // This block contains function statements whose definitions are
    // hoisted to the top of the block. Emit these as a separate pass
    // before the rest of the block.
    if (!emitHoistedFunctionsInList(&body->as<ListNode>())) {
      return false;
    }
  }

  // Line notes were updated by emitLexicalScope or emitScript.
  return emitTree(body, ValueUsage::WantValue, emitLineNote);
}

// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
// the comment on emitSwitch.
MOZ_NEVER_INLINE bool BytecodeEmitter::emitLexicalScope(
    LexicalScopeNode* lexicalScope) {
  LexicalScopeEmitter lse(this);

  ParseNode* body = lexicalScope->scopeBody();
  if (lexicalScope->isEmptyScope()) {
    if (!lse.emitEmptyScope()) {
      return false;
    }

    if (!emitLexicalScopeBody(body)) {
      return false;
    }

    if (!lse.emitEnd()) {
      return false;
    }

    return true;
  }

  // We are about to emit some bytecode for what the spec calls "declaration
  // instantiation". Assign these instructions to the opening `{` of the
  // block. (Using the location of each declaration we're instantiating is
  // too weird when stepping in the debugger.)
  if (!ParseNodeRequiresSpecialLineNumberNotes(body)) {
    if (!updateSourceCoordNotes(lexicalScope->pn_pos.begin)) {
      return false;
    }
  }

  ScopeKind kind;
  if (body->isKind(ParseNodeKind::Catch)) {
    BinaryNode* catchNode = &body->as<BinaryNode>();
    kind =
        (!catchNode->left() || catchNode->left()->isKind(ParseNodeKind::Name))
            ? ScopeKind::SimpleCatch
            : ScopeKind::Catch;
  } else {
    kind = lexicalScope->kind();
  }

  if (!lse.emitScope(kind, lexicalScope->scopeBindings())) {
    return false;
  }

  if (body->isKind(ParseNodeKind::ForStmt)) {
    // for loops need to emit {FRESHEN,RECREATE}LEXICALENV if there are
    // lexical declarations in the head. Signal this by passing a
    // non-nullptr lexical scope.
    if (!emitFor(&body->as<ForNode>(), &lse.emitterScope())) {
      return false;
    }
  } else {
    if (!emitLexicalScopeBody(body, SUPPRESS_LINENOTE)) {
      return false;
    }
  }

  if (!lse.emitEnd()) {
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitWith(BinaryNode* withNode) {
  // Ensure that the column of the 'with' is set properly.
  if (!updateSourceCoordNotes(withNode->left()->pn_pos.begin)) {
    return false;
  }

  if (!markStepBreakpoint()) {
    return false;
  }

  if (!emitTree(withNode->left())) {
    return false;
  }

  EmitterScope emitterScope(this);
  if (!emitterScope.enterWith(this)) {
    return false;
  }

  if (!emitTree(withNode->right())) {
    return false;
  }

  return emitterScope.leave(this);
}

bool BytecodeEmitter::emitCopyDataProperties(CopyOption option) {
  DebugOnly<int32_t> depth = bytecodeSection().stackDepth();

  uint32_t argc;
  if (option == CopyOption::Filtered) {
    MOZ_ASSERT(depth > 2);
    //              [stack] TARGET SOURCE SET
    argc = 3;

    if (!emitAtomOp(JSOp::GetIntrinsic, cx->names().CopyDataProperties)) {
      //            [stack] TARGET SOURCE SET COPYDATAPROPERTIES
      return false;
    }
  } else {
    MOZ_ASSERT(depth > 1);
    //              [stack] TARGET SOURCE
    argc = 2;

    if (!emitAtomOp(JSOp::GetIntrinsic,
                    cx->names().CopyDataPropertiesUnfiltered)) {
      //            [stack] TARGET SOURCE COPYDATAPROPERTIES
      return false;
    }
  }

  if (!emit1(JSOp::Undefined)) {
    //              [stack] TARGET SOURCE SET? COPYDATAPROPERTIES
    //                    UNDEFINED
    return false;
  }
  if (!emit2(JSOp::Pick, argc + 1)) {
    //              [stack] SOURCE SET? COPYDATAPROPERTIES UNDEFINED
    //                    TARGET
    return false;
  }
  if (!emit2(JSOp::Pick, argc + 1)) {
    //              [stack] SET? COPYDATAPROPERTIES UNDEFINED TARGET
    //                    SOURCE
    return false;
  }
  if (option == CopyOption::Filtered) {
    if (!emit2(JSOp::Pick, argc + 1)) {
      //            [stack] COPYDATAPROPERTIES UNDEFINED TARGET SOURCE SET
      return false;
    }
  }
  if (!emitCall(JSOp::CallIgnoresRv, argc)) {
    //              [stack] IGNORED
    return false;
  }

  if (!emit1(JSOp::Pop)) {
    //              [stack]
    return false;
  }

  MOZ_ASSERT(depth - int(argc) == bytecodeSection().stackDepth());
  return true;
}

bool BytecodeEmitter::emitBigIntOp(BigIntLiteral* bigint) {
  uint32_t index;
  if (!perScriptData().gcThingList().append(bigint, &index)) {
    return false;
  }
  return emitIndexOp(JSOp::BigInt, index);
}

bool BytecodeEmitter::emitIterator() {
  // Convert iterable to iterator.
  if (!emit1(JSOp::Dup)) {
    //              [stack] OBJ OBJ
    return false;
  }
  if (!emit2(JSOp::Symbol, uint8_t(JS::SymbolCode::iterator))) {
    //              [stack] OBJ OBJ @@ITERATOR
    return false;
  }
  if (!emitElemOpBase(JSOp::CallElem)) {
    //              [stack] OBJ ITERFN
    return false;
  }
  if (!emit1(JSOp::Swap)) {
    //              [stack] ITERFN OBJ
    return false;
  }
  if (!emitCall(JSOp::CallIter, 0)) {
    //              [stack] ITER
    return false;
  }
  if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) {
    //              [stack] ITER
    return false;
  }
  if (!emit1(JSOp::Dup)) {
    //              [stack] ITER ITER
    return false;
  }
  if (!emitAtomOp(JSOp::GetProp, cx->names().next)) {
    //              [stack] ITER NEXT
    return false;
  }
  if (!emit1(JSOp::Swap)) {
    //              [stack] NEXT ITER
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitAsyncIterator() {
  // Convert iterable to iterator.
  if (!emit1(JSOp::Dup)) {
    //              [stack] OBJ OBJ
    return false;
  }
  if (!emit2(JSOp::Symbol, uint8_t(JS::SymbolCode::asyncIterator))) {
    //              [stack] OBJ OBJ @@ASYNCITERATOR
    return false;
  }
  if (!emitElemOpBase(JSOp::CallElem)) {
    //              [stack] OBJ ITERFN
    return false;
  }

  InternalIfEmitter ifAsyncIterIsUndefined(this);
  if (!emitPushNotUndefinedOrNull()) {
    //              [stack] OBJ ITERFN !UNDEF-OR-NULL
    return false;
  }
  if (!emit1(JSOp::Not)) {
    //              [stack] OBJ ITERFN UNDEF-OR-NULL
    return false;
  }
  if (!ifAsyncIterIsUndefined.emitThenElse()) {
    //              [stack] OBJ ITERFN
    return false;
  }

  if (!emit1(JSOp::Pop)) {
    //              [stack] OBJ
    return false;
  }
  if (!emit1(JSOp::Dup)) {
    //              [stack] OBJ OBJ
    return false;
  }
  if (!emit2(JSOp::Symbol, uint8_t(JS::SymbolCode::iterator))) {
    //              [stack] OBJ OBJ @@ITERATOR
    return false;
  }
  if (!emitElemOpBase(JSOp::CallElem)) {
    //              [stack] OBJ ITERFN
    return false;
  }
  if (!emit1(JSOp::Swap)) {
    //              [stack] ITERFN OBJ
    return false;
  }
  if (!emitCall(JSOp::CallIter, 0)) {
    //              [stack] ITER
    return false;
  }
  if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) {
    //              [stack] ITER
    return false;
  }

  if (!emit1(JSOp::Dup)) {
    //              [stack] ITER ITER
    return false;
  }
  if (!emitAtomOp(JSOp::GetProp, cx->names().next)) {
    //              [stack] ITER SYNCNEXT
    return false;
  }

  if (!emit1(JSOp::ToAsyncIter)) {
    //              [stack] ITER
    return false;
  }

  if (!ifAsyncIterIsUndefined.emitElse()) {
    //              [stack] OBJ ITERFN
    return false;
  }

  if (!emit1(JSOp::Swap)) {
    //              [stack] ITERFN OBJ
    return false;
  }
  if (!emitCall(JSOp::CallIter, 0)) {
    //              [stack] ITER
    return false;
  }
  if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) {
    //              [stack] ITER
    return false;
  }

  if (!ifAsyncIterIsUndefined.emitEnd()) {
    //              [stack] ITER
    return false;
  }

  if (!emit1(JSOp::Dup)) {
    //              [stack] ITER ITER
    return false;
  }
  if (!emitAtomOp(JSOp::GetProp, cx->names().next)) {
    //              [stack] ITER NEXT
    return false;
  }
  if (!emit1(JSOp::Swap)) {
    //              [stack] NEXT ITER
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitSpread(bool allowSelfHosted) {
  LoopControl loopInfo(this, StatementKind::Spread);

  if (!loopInfo.emitLoopHead(this, Nothing())) {
    //              [stack] NEXT ITER ARR I
    return false;
  }

  {
#ifdef DEBUG
    auto loopDepth = bytecodeSection().stackDepth();
#endif

    // Spread operations can't contain |continue|, so don't bother setting loop
    // and enclosing "update" offsets, as we do with for-loops.

    if (!emitDupAt(3, 2)) {
      //            [stack] NEXT ITER ARR I NEXT
      return false;
    }
    if (!emitIteratorNext(Nothing(), IteratorKind::Sync, allowSelfHosted)) {
      //            [stack] NEXT ITER ARR I RESULT
      return false;
    }
    if (!emit1(JSOp::Dup)) {
      //            [stack] NEXT ITER ARR I RESULT RESULT
      return false;
    }
    if (!emitAtomOp(JSOp::GetProp, cx->names().done)) {
      //            [stack] NEXT ITER ARR I RESULT DONE
      return false;
    }
    if (!emitJump(JSOp::IfNe, &loopInfo.breaks)) {
      //            [stack] NEXT ITER ARR I RESULT
      return false;
    }

    // Emit code to assign result.value to the iteration variable.
    if (!emitAtomOp(JSOp::GetProp, cx->names().value)) {
      //            [stack] NEXT ITER ARR I VALUE
      return false;
    }
    if (!emit1(JSOp::InitElemInc)) {
      //            [stack] NEXT ITER ARR (I+1)
      return false;
    }

    if (!loopInfo.emitLoopEnd(this, JSOp::Goto, TryNoteKind::ForOf)) {
      //            [stack] NEXT ITER ARR (I+1)
      return false;
    }

    MOZ_ASSERT(bytecodeSection().stackDepth() == loopDepth);
  }

  // When we leave the loop body and jump to this point, the result value is
  // still on the stack. Account for that by updating the stack depth
  // manually.
  bytecodeSection().setStackDepth(bytecodeSection().stackDepth() + 1);

  // No continues should occur in spreads.
  MOZ_ASSERT(!loopInfo.continues.offset.valid());

  if (!emit2(JSOp::Pick, 4)) {
    //              [stack] ITER ARR FINAL_INDEX RESULT NEXT
    return false;
  }
  if (!emit2(JSOp::Pick, 4)) {
    //              [stack] ARR FINAL_INDEX RESULT NEXT ITER
    return false;
  }

  return emitPopN(3);
  //                [stack] ARR FINAL_INDEX
}

bool BytecodeEmitter::emitInitializeForInOrOfTarget(TernaryNode* forHead) {
  MOZ_ASSERT(forHead->isKind(ParseNodeKind::ForIn) ||
             forHead->isKind(ParseNodeKind::ForOf));

  MOZ_ASSERT(bytecodeSection().stackDepth() >= 1,
             "must have a per-iteration value for initializing");

  ParseNode* target = forHead->kid1();
  MOZ_ASSERT(!forHead->kid2());

  // If the for-in/of loop didn't have a variable declaration, per-loop
  // initialization is just assigning the iteration value to a target
  // expression.
  if (!parser->astGenerator().isDeclarationList(target)) {
    return emitAssignmentOrInit(ParseNodeKind::AssignExpr, target, nullptr);
    //              [stack] ... ITERVAL
  }

  // Otherwise, per-loop initialization is (possibly) declaration
  // initialization.  If the declaration is a lexical declaration, it must be
  // initialized.  If the declaration is a variable declaration, an
  // assignment to that name (which does *not* necessarily assign to the
  // variable!) must be generated.

  if (!updateSourceCoordNotes(target->pn_pos.begin)) {
    return false;
  }

  MOZ_ASSERT(target->isForLoopDeclaration());
  target = parser->astGenerator().singleBindingFromDeclaration(
      &target->as<ListNode>());

  NameNode* nameNode = nullptr;
  if (target->isKind(ParseNodeKind::Name)) {
    nameNode = &target->as<NameNode>();
  } else if (target->isKind(ParseNodeKind::AssignExpr) ||
             target->isKind(ParseNodeKind::InitExpr)) {
    BinaryNode* assignNode = &target->as<BinaryNode>();
    if (assignNode->left()->is<NameNode>()) {
      nameNode = &assignNode->left()->as<NameNode>();
    }
  }

  if (nameNode) {
    RootedAtom nameAtom(cx, nameNode->name());
    NameOpEmitter noe(this, nameAtom, NameOpEmitter::Kind::Initialize);
    if (!noe.prepareForRhs()) {
      return false;
    }
    if (noe.emittedBindOp()) {
      // Per-iteration initialization in for-in/of loops computes the
      // iteration value *before* initializing.  Thus the initializing
      // value may be buried under a bind-specific value on the stack.
      // Swap it to the top of the stack.
      MOZ_ASSERT(bytecodeSection().stackDepth() >= 2);
      if (!emit1(JSOp::Swap)) {
        return false;
      }
    } else {
      // In cases of emitting a frame slot or environment slot,
      // nothing needs be done.
      MOZ_ASSERT(bytecodeSection().stackDepth() >= 1);
    }
    if (!noe.emitAssignment()) {
      return false;
    }

    // The caller handles removing the iteration value from the stack.
    return true;
  }

  MOZ_ASSERT(
      !target->isKind(ParseNodeKind::AssignExpr) &&
          !target->isKind(ParseNodeKind::InitExpr),
      "for-in/of loop destructuring declarations can't have initializers");

  MOZ_ASSERT(target->isKind(ParseNodeKind::ArrayExpr) ||
             target->isKind(ParseNodeKind::ObjectExpr));
  return emitDestructuringOps(&target->as<ListNode>(),
                              DestructuringFlavor::Declaration);
}

bool BytecodeEmitter::emitForOf(ForNode* forOfLoop,
                                const EmitterScope* headLexicalEmitterScope) {
  MOZ_ASSERT(forOfLoop->isKind(ParseNodeKind::ForStmt));

  TernaryNode* forOfHead = forOfLoop->head();
  MOZ_ASSERT(forOfHead->isKind(ParseNodeKind::ForOf));

  unsigned iflags = forOfLoop->iflags();
  IteratorKind iterKind =
      (iflags & JSITER_FORAWAITOF) ? IteratorKind::Async : IteratorKind::Sync;
  MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox());
  MOZ_ASSERT_IF(iterKind == IteratorKind::Async,
                sc->asFunctionBox()->isAsync());

  ParseNode* forHeadExpr = forOfHead->kid3();

  // Certain builtins (e.g. Array.from) are implemented in self-hosting
  // as for-of loops.
  bool allowSelfHostedIter = false;
  if (emitterMode == BytecodeEmitter::SelfHosting &&
      forHeadExpr->isKind(ParseNodeKind::CallExpr) &&
      forHeadExpr->as<BinaryNode>().left()->isName(
          cx->names().allowContentIter)) {
    allowSelfHostedIter = true;
  }

  ForOfEmitter forOf(this, headLexicalEmitterScope, allowSelfHostedIter,
                     iterKind);

  if (!forOf.emitIterated()) {
    //              [stack]
    return false;
  }

  if (!updateSourceCoordNotes(forHeadExpr->pn_pos.begin)) {
    return false;
  }
  if (!markStepBreakpoint()) {
    return false;
  }
  if (!emitTree(forHeadExpr)) {
    //              [stack] ITERABLE
    return false;
  }

  if (headLexicalEmitterScope) {
    DebugOnly<ParseNode*> forOfTarget = forOfHead->kid1();
    MOZ_ASSERT(forOfTarget->isKind(ParseNodeKind::LetDecl) ||
               forOfTarget->isKind(ParseNodeKind::ConstDecl));
  }

  if (!forOf.emitInitialize(Some(forOfHead->pn_pos.begin))) {
    //              [stack] NEXT ITER VALUE
    return false;
  }

  if (!emitInitializeForInOrOfTarget(forOfHead)) {
    //              [stack] NEXT ITER VALUE
    return false;
  }

  if (!forOf.emitBody()) {
    //              [stack] NEXT ITER UNDEF
    return false;
  }

  // Perform the loop body.
  ParseNode* forBody = forOfLoop->body();
  if (!emitTree(forBody)) {
    //              [stack] NEXT ITER UNDEF
    return false;
  }

  if (!forOf.emitEnd(Some(forHeadExpr->pn_pos.begin))) {
    //              [stack]
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitForIn(ForNode* forInLoop,
                                const EmitterScope* headLexicalEmitterScope) {
  TernaryNode* forInHead = forInLoop->head();
  MOZ_ASSERT(forInHead->isKind(ParseNodeKind::ForIn));

  ForInEmitter forIn(this, headLexicalEmitterScope);

  // Annex B: Evaluate the var-initializer expression if present.
  // |for (var i = initializer in expr) { ... }|
  ParseNode* forInTarget = forInHead->kid1();
  if (parser->astGenerator().isDeclarationList(forInTarget)) {
    ParseNode* decl = parser->astGenerator().singleBindingFromDeclaration(
        &forInTarget->as<ListNode>());
    if (decl->isKind(ParseNodeKind::AssignExpr) ||
        decl->isKind(ParseNodeKind::InitExpr)) {
      BinaryNode* assignNode = &decl->as<BinaryNode>();
      if (assignNode->left()->is<NameNode>()) {
        NameNode* nameNode = &assignNode->left()->as<NameNode>();
        ParseNode* initializer = assignNode->right();
        MOZ_ASSERT(
            forInTarget->isKind(ParseNodeKind::VarStmt),
            "for-in initializers are only permitted for |var| declarations");

        if (!updateSourceCoordNotes(decl->pn_pos.begin)) {
          return false;
        }

        RootedAtom nameAtom(cx, nameNode->name());
        NameOpEmitter noe(this, nameAtom, NameOpEmitter::Kind::Initialize);
        if (!noe.prepareForRhs()) {
          return false;
        }
        if (!emitInitializer(initializer, nameNode)) {
          return false;
        }
        if (!noe.emitAssignment()) {
          return false;
        }

        // Pop the initializer.
        if (!emit1(JSOp::Pop)) {
          return false;
        }
      }
    }
  }

  if (!forIn.emitIterated()) {
    //              [stack]
    return false;
  }

  // Evaluate the expression being iterated.
  ParseNode* expr = forInHead->kid3();

  if (!updateSourceCoordNotes(expr->pn_pos.begin)) {
    return false;
  }
  if (!markStepBreakpoint()) {
    return false;
  }
  if (!emitTree(expr)) {
    //              [stack] EXPR
    return false;
  }

  MOZ_ASSERT(forInLoop->iflags() == 0);

  MOZ_ASSERT_IF(headLexicalEmitterScope,
                forInTarget->isKind(ParseNodeKind::LetDecl) ||
                    forInTarget->isKind(ParseNodeKind::ConstDecl));

  if (!forIn.emitInitialize()) {
    //              [stack] ITER ITERVAL
    return false;
  }

  if (!emitInitializeForInOrOfTarget(forInHead)) {
    //              [stack] ITER ITERVAL
    return false;
  }

  if (!forIn.emitBody()) {
    //              [stack] ITER ITERVAL
    return false;
  }

  // Perform the loop body.
  ParseNode* forBody = forInLoop->body();
  if (!emitTree(forBody)) {
    //              [stack] ITER ITERVAL
    return false;
  }

  if (!forIn.emitEnd(Some(forInHead->pn_pos.begin))) {
    //              [stack]
    return false;
  }

  return true;
}

/* C-style `for (init; cond; update) ...` loop. */
bool BytecodeEmitter::emitCStyleFor(
    ForNode* forNode, const EmitterScope* headLexicalEmitterScope) {
  TernaryNode* forHead = forNode->head();
  ParseNode* forBody = forNode->body();
  ParseNode* init = forHead->kid1();
  ParseNode* cond = forHead->kid2();
  ParseNode* update = forHead->kid3();
  bool isLet = init && init->isKind(ParseNodeKind::LetDecl);

  CForEmitter cfor(this, isLet ? headLexicalEmitterScope : nullptr);

  if (!cfor.emitInit(init ? Some(init->pn_pos.begin) : Nothing())) {
    //              [stack]
    return false;
  }

  // If the head of this for-loop declared any lexical variables, the parser
  // wrapped this ParseNodeKind::For node in a ParseNodeKind::LexicalScope
  // representing the implicit scope of those variables. By the time we get
  // here, we have already entered that scope. So far, so good.
  if (init) {
    // Emit the `init` clause, whether it's an expression or a variable
    // declaration. (The loop variables were hoisted into an enclosing
    // scope, but we still need to emit code for the initializers.)
    if (init->isForLoopDeclaration()) {
      if (!emitTree(init)) {
        //          [stack]
        return false;
      }
    } else {
      if (!updateSourceCoordNotes(init->pn_pos.begin)) {
        return false;
      }
      if (!markStepBreakpoint()) {
        return false;
      }

      // 'init' is an expression, not a declaration. emitTree left its
      // value on the stack.
      if (!emitTree(init, ValueUsage::IgnoreValue)) {
        //          [stack] VAL
        return false;
      }
      if (!emit1(JSOp::Pop)) {
        //          [stack]
        return false;
      }
    }
  }

  if (!cfor.emitCond(cond ? Some(cond->pn_pos.begin) : Nothing())) {
    //              [stack]
    return false;
  }

  if (cond) {
    if (!updateSourceCoordNotes(cond->pn_pos.begin)) {
      return false;
    }
    if (!markStepBreakpoint()) {
      return false;
    }
    if (!emitTree(cond)) {
      //            [stack] VAL
      return false;
    }
  }

  if (!cfor.emitBody(cond ? CForEmitter::Cond::Present
                          : CForEmitter::Cond::Missing)) {
    //              [stack]
    return false;
  }

  if (!emitTree(forBody)) {
    //              [stack]
    return false;
  }

  if (!cfor.emitUpdate(
          update ? CForEmitter::Update::Present : CForEmitter::Update::Missing,
          update ? Some(update->pn_pos.begin) : Nothing())) {
    //              [stack]
    return false;
  }

  // Check for update code to do before the condition (if any).
  if (update) {
    if (!updateSourceCoordNotes(update->pn_pos.begin)) {
      return false;
    }
    if (!markStepBreakpoint()) {
      return false;
    }
    if (!emitTree(update, ValueUsage::IgnoreValue)) {
      //            [stack] VAL
      return false;
    }
  }

  if (!cfor.emitEnd(Some(forNode->pn_pos.begin))) {
    //              [stack]
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitFor(ForNode* forNode,
                              const EmitterScope* headLexicalEmitterScope) {
  if (forNode->head()->isKind(ParseNodeKind::ForHead)) {
    return emitCStyleFor(forNode, headLexicalEmitterScope);
  }

  if (!updateLineNumberNotes(forNode->pn_pos.begin)) {
    return false;
  }

  if (forNode->head()->isKind(ParseNodeKind::ForIn)) {
    return emitForIn(forNode, headLexicalEmitterScope);
  }

  MOZ_ASSERT(forNode->head()->isKind(ParseNodeKind::ForOf));
  return emitForOf(forNode, headLexicalEmitterScope);
}

MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(
    FunctionNode* funNode, bool needsProto /* = false */,
    ListNode* classContentsIfConstructor /* = nullptr */) {
  FunctionBox* funbox = funNode->funbox();

  MOZ_ASSERT((classContentsIfConstructor != nullptr) ==
             funbox->isClassConstructor());

  //                [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) {
    if (!fe.emitAgain()) {
      //            [stack]
      return false;
    }
    MOZ_ASSERT(funNode->functionIsHoisted());
    return true;
  }

  if (funbox->isInterpreted()) {
    if (!funbox->emitBytecode) {
      if (!fe.emitLazy()) {
        //          [stack] FUN?
        return false;
      }

      if (classContentsIfConstructor) {
        mozilla::Maybe<FieldInitializers> fieldInitializers =
            setupFieldInitializers(classContentsIfConstructor,
                                   FieldPlacement::Instance);
        if (!fieldInitializers) {
          ReportAllocationOverflow(cx);
          return false;
        }
        funbox->setFieldInitializers(*fieldInitializers);
      }

      return true;
    }

    if (!fe.prepareForNonLazy()) {
      //            [stack]
      return false;
    }

    // Only propagate transitive compiler options (principals, version, etc)
    // from the parent. The remaining values will use their defaults.
    MOZ_ASSERT(script->mutedErrors() == parser->options().mutedErrors());
    const JS::TransitiveCompileOptions& transitiveOptions = parser->options();

    RootedFunction fun(cx, funbox->function());
    RootedScript innerScript(
        cx, JSScript::Create(
                cx, fun, compilationInfo.sourceObject, funbox->extent,
                ImmutableScriptFlags::fromCompileOptions(transitiveOptions)));
    if (!innerScript) {
      return false;
    }

    EmitterMode nestedMode = emitterMode;
    if (nestedMode == BytecodeEmitter::LazyFunction) {
      MOZ_ASSERT(lazyScript->isBinAST());
      nestedMode = BytecodeEmitter::Normal;
    }

    mozilla::Maybe<FieldInitializers> fieldInitializers;
    if (classContentsIfConstructor) {
      fieldInitializers = setupFieldInitializers(classContentsIfConstructor,
                                                 FieldPlacement::Instance);
      if (!fieldInitializers) {
        ReportAllocationOverflow(cx);
        return false;
      }
    } else {
      // The BCE requires passing some value even if not used.
      fieldInitializers = Some(FieldInitializers::Invalid());
    }

    BytecodeEmitter bce2(this, parser, funbox, innerScript,
                         /* lazyScript = */ nullptr, funbox->extent.lineno,
                         funbox->extent.column, compilationInfo, nestedMode,
                         *fieldInitializers);
    if (!bce2.init(funNode->pn_pos)) {
      return false;
    }

    /* We measured the max scope depth when we parsed the function. */
    if (!bce2.emitFunctionScript(funNode, TopLevelFunction::No)) {
      return false;
    }

    // fieldInitializers are copied to the JSScript inside BytecodeEmitter

    if (funbox->isLikelyConstructorWrapper()) {
      innerScript->setIsLikelyConstructorWrapper();
    }

    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();

  DoWhileEmitter doWhile(this);
  if (!doWhile.emitBody(Some(doNode->pn_pos.begin),
                        getOffsetForLoop(bodyNode))) {
    return false;
  }

  if (!emitTree(bodyNode)) {
    return false;
  }

  if (!doWhile.emitCond()) {
    return false;
  }

  ParseNode* condNode = doNode->right();
  if (!updateSourceCoordNotes(condNode->pn_pos.begin)) {
    return false;
  }
  if (!markStepBreakpoint()) {
    return false;
  }
  if (!emitTree(condNode)) {
    return false;
  }

  if (!doWhile.emitEnd()) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitWhile(BinaryNode* whileNode) {
  ParseNode* bodyNode = whileNode->right();

  WhileEmitter wh(this);

  ParseNode* condNode = whileNode->left();
  if (!wh.emitCond(Some(whileNode->pn_pos.begin), getOffsetForLoop(condNode),
                   Some(whileNode->pn_pos.end))) {
    return false;
  }

  if (!updateSourceCoordNotes(condNode->pn_pos.begin)) {
    return false;
  }
  if (!markStepBreakpoint()) {
    return false;
  }
  if (!emitTree(condNode)) {
    return false;
  }

  if (!wh.emitBody()) {
    return false;
  }
  if (!emitTree(bodyNode)) {
    return false;
  }

  if (!wh.emitEnd()) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitBreak(PropertyName* label) {
  BreakableControl* target;
  if (label) {
    // Any statement with the matching label may be the break target.
    auto hasSameLabel = [label](LabelControl* labelControl) {
      return labelControl->label() == label;
    };
    target = findInnermostNestableControl<LabelControl>(hasSameLabel);
  } else {
    auto isNotLabel = [](BreakableControl* control) {
      return !control->is<LabelControl>();
    };
    target = findInnermostNestableControl<BreakableControl>(isNotLabel);
  }

  return emitGoto(target, &target->breaks, GotoKind::Break);
}

bool BytecodeEmitter::emitContinue(PropertyName* label) {
  LoopControl* target = nullptr;
  if (label) {
    // Find the loop statement enclosed by the matching label.
    NestableControl* control = innermostNestableControl;
    while (!control->is<LabelControl>() ||
           control->as<LabelControl>().label() != label) {
      if (control->is<LoopControl>()) {
        target = &control->as<LoopControl>();
      }
      control = control->enclosing();
    }
  } else {
    target = findInnermostNestableControl<LoopControl>();
  }
  return emitGoto(target, &target->continues, GotoKind::Continue);
}

bool BytecodeEmitter::emitGetFunctionThis(NameNode* thisName) {
  MOZ_ASSERT(sc->thisBinding() == ThisBinding::Function);
  MOZ_ASSERT(thisName->isName(cx->names().dotThis));

  return emitGetFunctionThis(Some(thisName->pn_pos.begin));
}

bool BytecodeEmitter::emitGetFunctionThis(
    const mozilla::Maybe<uint32_t>& offset) {
  if (offset) {
    if (!updateLineNumberNotes(*offset)) {
      return false;
    }
  }

  if (!emitGetName(cx->names().dotThis)) {
    //              [stack] THIS
    return false;
  }
  if (sc->needsThisTDZChecks()) {
    if (!emit1(JSOp::CheckThis)) {
      //            [stack] THIS
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitGetThisForSuperBase(UnaryNode* superBase) {
  MOZ_ASSERT(superBase->isKind(ParseNodeKind::SuperBase));
  NameNode* nameNode = &superBase->kid()->as<NameNode>();
  return emitGetFunctionThis(nameNode);
  //                [stack] THIS
}

bool BytecodeEmitter::emitThisLiteral(ThisLiteral* pn) {
  if (ParseNode* kid = pn->kid()) {
    NameNode* thisName = &kid->as<NameNode>();
    return emitGetFunctionThis(thisName);
    //              [stack] THIS
  }

  if (sc->thisBinding() == ThisBinding::Module) {
    return emit1(JSOp::Undefined);
    //              [stack] UNDEF
  }

  MOZ_ASSERT(sc->thisBinding() == ThisBinding::Global);
  return emit1(JSOp::GlobalThis);
  //                [stack] THIS
}

bool BytecodeEmitter::emitCheckDerivedClassConstructorReturn() {
  MOZ_ASSERT(lookupName(cx->names().dotThis).hasKnownSlot());
  if (!emitGetName(cx->names().dotThis)) {
    return false;
  }
  if (!emit1(JSOp::CheckReturn)) {
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitReturn(UnaryNode* returnNode) {
  if (!updateSourceCoordNotes(returnNode->pn_pos.begin)) {
    return false;
  }

  bool needsIteratorResult =
      sc->isFunctionBox() && sc->asFunctionBox()->needsIteratorResult();
  if (needsIteratorResult) {
    if (!emitPrepareIteratorResult()) {
      return false;
    }
  }

  if (!updateSourceCoordNotes(returnNode->pn_pos.begin)) {
    return false;
  }
  if (!markStepBreakpoint()) {
    return false;
  }

  /* Push a return value */
  if (ParseNode* expr = returnNode->kid()) {
    if (!emitTree(expr)) {
      return false;
    }

    if (sc->asFunctionBox()->isAsync() && sc->asFunctionBox()->isGenerator()) {
      if (!emitAwaitInInnermostScope()) {
        return false;
      }
    }
  } else {
    /* No explicit return value provided */
    if (!emit1(JSOp::Undefined)) {
      return false;
    }
  }

  if (needsIteratorResult) {
    if (!emitFinishIteratorResult(true)) {
      return false;
    }
  }

  // We know functionBodyEndPos is set because "return" is only
  // valid in a function, and so we've passed through
  // emitFunctionScript.
  if (!updateSourceCoordNotes(*functionBodyEndPos)) {
    return false;
  }

  /*
   * EmitNonLocalJumpFixup may add fixup bytecode to close open try
   * blocks having finally clauses and to exit intermingled let blocks.
   * We can't simply transfer control flow to our caller in that case,
   * because we must gosub to those finally clauses from inner to outer,
   * with the correct stack pointer (i.e., after popping any with,
   * for/in, etc., slots nested inside the finally's try).
   *
   * In this case we mutate JSOp::Return into JSOp::SetRval and add an
   * extra JSOp::RetRval after the fixups.
   */
  BytecodeOffset top = bytecodeSection().offset();

  bool needsFinalYield =
      sc->isFunctionBox() && sc->asFunctionBox()->needsFinalYield();
  bool isDerivedClassConstructor =
      sc->isFunctionBox() && sc->asFunctionBox()->isDerivedClassConstructor();

  if (!emit1((needsFinalYield || isDerivedClassConstructor) ? JSOp::SetRval
                                                            : JSOp::Return)) {
    return false;
  }

  // Make sure that we emit this before popping the blocks in
  // prepareForNonLocalJump, to ensure that the error is thrown while the
  // scope-chain is still intact.
  if (isDerivedClassConstructor) {
    if (!emitCheckDerivedClassConstructorReturn()) {
      return false;
    }
  }

  NonLocalExitControl nle(this, NonLocalExitControl::Return);

  if (!nle.prepareForNonLocalJumpToOutermost()) {
    return false;
  }

  if (needsFinalYield) {
    // We know that .generator is on the function scope, as we just exited
    // all nested scopes.
    NameLocation loc = *locationOfNameBoundInFunctionScope(
        cx->names().dotGenerator, varEmitterScope);

    // Resolve the return value before emitting the final yield.
    if (sc->asFunctionBox()->needsPromiseResult()) {
      if (!emit1(JSOp::GetRval)) {
        //          [stack] RVAL
        return false;
      }
      if (!emitGetNameAtLocation(cx->names().dotGenerator, loc)) {
        //          [stack] RVAL GEN
        return false;
      }
      if (!emit2(JSOp::AsyncResolve,
                 uint8_t(AsyncFunctionResolveKind::Fulfill))) {
        //          [stack] PROMISE
        return false;
      }
      if (!emit1(JSOp::SetRval)) {
        //          [stack]
        return false;
      }
    }

    if (!emitGetNameAtLocation(cx->names().dotGenerator, loc)) {
      return false;
    }
    if (!emitYieldOp(JSOp::FinalYieldRval)) {
      return false;
    }
  } else if (isDerivedClassConstructor) {
    MOZ_ASSERT(JSOp(bytecodeSection().code()[top.value()]) == JSOp::SetRval);
    if (!emitReturnRval()) {
      return false;
    }
  } else if (top + BytecodeOffsetDiff(JSOpLength_Return) !=
                 bytecodeSection().offset() ||
             // If we are instrumenting, make sure we use RetRval and add any
             // instrumentation for the frame exit.
             instrumentationKinds) {
    bytecodeSection().code()[top.value()] = jsbytecode(JSOp::SetRval);
    if (!emitReturnRval()) {
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitGetDotGeneratorInScope(EmitterScope& currentScope) {
  NameLocation loc = *locationOfNameBoundInFunctionScope(
      cx->names().dotGenerator, &currentScope);
  return emitGetNameAtLocation(cx->names().dotGenerator, loc);
}

bool BytecodeEmitter::emitInitialYield(UnaryNode* yieldNode) {
  if (!emitTree(yieldNode->kid())) {
    return false;
  }

  if (!emitYieldOp(JSOp::InitialYield)) {
    //              [stack] RVAL GENERATOR RESUMEKIND
    return false;
  }
  if (!emit1(JSOp::CheckResumeKind)) {
    //              [stack] RVAL
    return false;
  }
  if (!emit1(JSOp::Pop)) {
    //              [stack]
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitYield(UnaryNode* yieldNode) {
  MOZ_ASSERT(sc->isFunctionBox());
  MOZ_ASSERT(sc->asFunctionBox()->isGenerator());
  MOZ_ASSERT(yieldNode->isKind(ParseNodeKind::YieldExpr));

  bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult();
  if (needsIteratorResult) {
    if (!emitPrepareIteratorResult()) {
      //            [stack] ITEROBJ
      return false;
    }
  }
  if (ParseNode* expr = yieldNode->kid()) {
    if (!emitTree(expr)) {
      //            [stack] ITEROBJ? VAL
      return false;
    }
  } else {
    if (!emit1(JSOp::Undefined)) {
      //            [stack] ITEROBJ? UNDEFINED
      return false;
    }
  }

  // 25.5.3.7 AsyncGeneratorYield step 5.
  if (sc->asFunctionBox()->isAsync()) {
    MOZ_ASSERT(!needsIteratorResult);
    if (!emitAwaitInInnermostScope()) {
      //            [stack] RESULT
      return false;
    }
  }

  if (needsIteratorResult) {
    if (!emitFinishIteratorResult(false)) {
      //            [stack] ITEROBJ
      return false;
    }
  }

  if (!emitGetDotGeneratorInInnermostScope()) {
    //              [stack] # if needsIteratorResult
    //              [stack] ITEROBJ .GENERATOR
    //              [stack] # else
    //              [stack] RESULT .GENERATOR
    return false;
  }

  if (!emitYieldOp(JSOp::Yield)) {
    //              [stack] YIELDRESULT GENERATOR RESUMEKIND
    return false;
  }

  if (!emit1(JSOp::CheckResumeKind)) {
    //              [stack] YIELDRESULT
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitAwaitInInnermostScope(UnaryNode* awaitNode) {
  MOZ_ASSERT(sc->isFunctionBox());
  MOZ_ASSERT(awaitNode->isKind(ParseNodeKind::AwaitExpr));

  if (!emitTree(awaitNode->kid())) {
    return false;
  }
  return emitAwaitInInnermostScope();
}

bool BytecodeEmitter::emitAwaitInScope(EmitterScope& currentScope) {
  if (!emit1(JSOp::TrySkipAwait)) {
    //              [stack] VALUE_OR_RESOLVED CANSKIP
    return false;
  }

  if (!emit1(JSOp::Not)) {
    //              [stack] VALUE_OR_RESOLVED !CANSKIP
    return false;
  }

  InternalIfEmitter ifCanSkip(this);
  if (!ifCanSkip.emitThen()) {
    //              [stack] VALUE_OR_RESOLVED
    return false;
  }

  if (sc->asFunctionBox()->needsPromiseResult()) {
    if (!emitGetDotGeneratorInScope(currentScope)) {
      //            [stack] VALUE GENERATOR
      return false;
    }
    if (!emit1(JSOp::AsyncAwait)) {
      //            [stack] PROMISE
      return false;
    }
  }

  if (!emitGetDotGeneratorInScope(currentScope)) {
    //              [stack] VALUE|PROMISE GENERATOR
    return false;
  }
  if (!emitYieldOp(JSOp::Await)) {
    //              [stack] RESOLVED GENERATOR RESUMEKIND
    return false;
  }
  if (!emit1(JSOp::CheckResumeKind)) {
    //              [stack] RESOLVED
    return false;
  }

  if (!ifCanSkip.emitEnd()) {
    return false;
  }

  MOZ_ASSERT(ifCanSkip.popped() == 0);

  return true;
}

// ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c
// 14.4.14 Runtime Semantics: Evaluation
// YieldExpression : yield* AssignmentExpression
bool BytecodeEmitter::emitYieldStar(ParseNode* iter) {
  MOZ_ASSERT(sc->isFunctionBox());
  MOZ_ASSERT(sc->asFunctionBox()->isGenerator());

  // Step 1.
  IteratorKind iterKind =
      sc->asFunctionBox()->isAsync() ? IteratorKind::Async : IteratorKind::Sync;
  bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult();

  // Steps 2-5.
  if (!emitTree(iter)) {
    //              [stack] ITERABLE
    return false;
  }
  if (iterKind == IteratorKind::Async) {
    if (!emitAsyncIterator()) {
      //            [stack] NEXT ITER
      return false;
    }
  } else {
    if (!emitIterator()) {
      //            [stack] NEXT ITER
      return false;
    }
  }

  // Step 6.
  // Start with NormalCompletion(undefined).
  if (!emit1(JSOp::Undefined)) {
    //              [stack] NEXT ITER RECEIVED
    return false;
  }
  if (!emitPushResumeKind(GeneratorResumeKind::Next)) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND
    return false;
  }

  const int32_t startDepth = bytecodeSection().stackDepth();
  MOZ_ASSERT(startDepth >= 4);

  // Step 7 is a loop.
  LoopControl loopInfo(this, StatementKind::YieldStar);
  if (!loopInfo.emitLoopHead(this, Nothing())) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND
    return false;
  }

  // Step 7.a. Check for Normal completion.
  if (!emit1(JSOp::Dup)) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND RESUMEKIND
    return false;
  }
  if (!emitPushResumeKind(GeneratorResumeKind::Next)) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND RESUMEKIND NORMAL
    return false;
  }
  if (!emit1(JSOp::StrictEq)) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND IS_NORMAL
    return false;
  }

  InternalIfEmitter ifKind(this);
  if (!ifKind.emitThenElse()) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND
    return false;
  }
  {
    if (!emit1(JSOp::Pop)) {
      //            [stack] NEXT ITER RECEIVED
      return false;
    }

    // Step 7.a.i.
    // result = iter.next(received)
    if (!emit2(JSOp::Unpick, 2)) {
      //            [stack] RECEIVED NEXT ITER
      return false;
    }
    if (!emit1(JSOp::Dup2)) {
      //            [stack] RECEIVED NEXT ITER NEXT ITER
      return false;
    }
    if (!emit2(JSOp::Pick, 4)) {
      //            [stack] NEXT ITER NEXT ITER RECEIVED
      return false;
    }
    if (!emitCall(JSOp::Call, 1, iter)) {
      //            [stack] NEXT ITER RESULT
      return false;
    }

    // Step 7.a.ii.
    if (iterKind == IteratorKind::Async) {
      if (!emitAwaitInInnermostScope()) {
        //          [stack] NEXT ITER RESULT
        return false;
      }
    }

    // Step 7.a.iii.
    if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) {
      //            [stack] NEXT ITER RESULT
      return false;
    }

    // Bytecode for steps 7.a.iv-vii is emitted after the ifKind if-else because
    // it's shared with other branches.
  }

  // Step 7.b. Check for Throw completion.
  if (!ifKind.emitElseIf(Nothing())) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND
    return false;
  }
  if (!emit1(JSOp::Dup)) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND RESUMEKIND
    return false;
  }
  if (!emitPushResumeKind(GeneratorResumeKind::Throw)) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND RESUMEKIND THROW
    return false;
  }
  if (!emit1(JSOp::StrictEq)) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND IS_THROW
    return false;
  }
  if (!ifKind.emitThenElse()) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND
    return false;
  }
  {
    if (!emit1(JSOp::Pop)) {
      //            [stack] NEXT ITER RECEIVED
      return false;
    }
    // Step 7.b.i.
    if (!emitDupAt(1)) {
      //            [stack] NEXT ITER RECEIVED ITER
      return false;
    }
    if (!emit1(JSOp::Dup)) {
      //            [stack] NEXT ITER RECEIVED ITER ITER
      return false;
    }
    if (!emitAtomOp(JSOp::CallProp, cx->names().throw_)) {
      //            [stack] NEXT ITER RECEIVED ITER THROW
      return false;
    }

    // Step 7.b.ii.
    InternalIfEmitter ifThrowMethodIsNotDefined(this);
    if (!emitPushNotUndefinedOrNull()) {
      //            [stack] NEXT ITER RECEIVED ITER THROW
      //            [stack]   NOT-UNDEF-OR_NULL
      return false;
    }

    if (!ifThrowMethodIsNotDefined.emitThenElse()) {
      //            [stack] NEXT ITER RECEIVED ITER THROW
      return false;
    }

    // Step 7.b.ii.1.
    // RESULT = ITER.throw(EXCEPTION)
    if (!emit1(JSOp::Swap)) {
      //            [stack] NEXT ITER RECEIVED THROW ITER
      return false;
    }
    if (!emit2(JSOp::Pick, 2)) {
      //            [stack] NEXT ITER THROW ITER RECEIVED
      return false;
    }
    if (!emitCall(JSOp::Call, 1, iter)) {
      //            [stack] NEXT ITER RESULT
      return false;
    }

    // Step 7.b.ii.2.
    if (iterKind == IteratorKind::Async) {
      if (!emitAwaitInInnermostScope()) {
        //          [stack] NEXT ITER RESULT
        return false;
      }
    }

    // Step 7.b.ii.4.
    if (!emitCheckIsObj(CheckIsObjectKind::IteratorThrow)) {
      //            [stack] NEXT ITER RESULT
      return false;
    }

    // Bytecode for steps 7.b.ii.5-8 is emitted after the ifKind if-else because
    // it's shared with other branches.

    // Step 7.b.iii.
    if (!ifThrowMethodIsNotDefined.emitElse()) {
      //            [stack] NEXT ITER RECEIVED ITER THROW
      return false;
    }
    if (!emit1(JSOp::Pop)) {
      //            [stack] NEXT ITER RECEIVED ITER
      return false;
    }

    // Steps 7.b.iii.1-4.
    //
    // If the iterator does not have a "throw" method, it calls IteratorClose
    // and then throws a TypeError.
    if (!emitIteratorCloseInInnermostScope(iterKind)) {
      //            [stack] NEXT ITER RECEIVED ITER
      return false;
    }
    // Steps 7.b.iii.5-6.
    if (!emit2(JSOp::ThrowMsg, uint8_t(ThrowMsgKind::IteratorNoThrow))) {
      //            [stack] NEXT ITER RECEIVED ITER
      //            [stack] # throw
      return false;
    }

    if (!ifThrowMethodIsNotDefined.emitEnd()) {
      return false;
    }
  }

  // Step 7.c. It must be a Return completion.
  if (!ifKind.emitElse()) {
    //              [stack] NEXT ITER RECEIVED RESUMEKIND
    return false;
  }
  {
    if (!emit1(JSOp::Pop))