Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/frontend/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 385 kB image not shown  

SSL BytecodeEmitter.cpp

  Interaktion und
PortierbarkeitC
 

/* -*- 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/Casting.h"    // mozilla::AssertedCast
#include "mozilla/DebugOnly.h"  // mozilla::DebugOnly
#include "mozilla/FloatingPoint.h"  // mozilla::NumberEqualsInt32, mozilla::NumberIsInt32
#include "mozilla/HashTable.h"  // mozilla::HashSet
#include "mozilla/Maybe.h"      // mozilla::{Maybe,Nothing,Some}
#include "mozilla/Saturate.h"
#include "mozilla/Variant.h"  // mozilla::AsVariant

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

#include "jstypes.h"  // JS_BIT

#include "frontend/AbstractScopePtr.h"           // ScopeIndex
#include "frontend/BytecodeControlStructures.h"  // NestableControl, BreakableControl, LabelControl, LoopControl, TryFinallyControl
#include "frontend/CallOrNewEmitter.h"           // CallOrNewEmitter
#include "frontend/CForEmitter.h"                // CForEmitter
#include "frontend/DecoratorEmitter.h"           // DecoratorEmitter
#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/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/NameAnalysisTypes.h"    // PrivateNameKind
#include "frontend/NameFunctions.h"        // NameFunctions
#include "frontend/NameOpEmitter.h"        // NameOpEmitter
#include "frontend/ObjectEmitter.h"  // PropertyEmitter, ObjectEmitter, ClassEmitter
#include "frontend/OptionalEmitter.h"  // OptionalEmitter
#include "frontend/ParseContext.h"     // ParseContext::Scope
#include "frontend/ParseNode.h"   // ParseNodeKind, ParseNode and subclasses
#include "frontend/Parser.h"      // Parser
#include "frontend/ParserAtom.h"  // ParserAtomsTable, ParserAtom
#include "frontend/PrivateOpEmitter.h"  // PrivateOpEmitter
#include "frontend/PropOpEmitter.h"     // PropOpEmitter
#include "frontend/SourceNotes.h"       // SrcNote, SrcNoteType, SrcNoteWriter
#include "frontend/SwitchEmitter.h"     // SwitchEmitter
#include "frontend/TaggedParserAtomIndexHasher.h"  // TaggedParserAtomIndexHasher
#include "frontend/TDZCheckCache.h"                // TDZCheckCache
#include "frontend/TryEmitter.h"                   // TryEmitter
#include "frontend/UsingEmitter.h"                 // UsingEmitter
#include "frontend/WhileEmitter.h"                 // WhileEmitter
#include "js/ColumnNumber.h"  // JS::LimitedColumnNumberOneOrigin, JS::ColumnNumberOffset
#include "js/friend/ErrorMessages.h"  // JSMSG_*
#include "js/friend/StackLimits.h"    // AutoCheckRecursionLimit
#include "util/StringBuilder.h"       // StringBuilder
#include "vm/BytecodeUtil.h"  // JOF_*, IsArgOp, IsLocalOp, SET_UINT24, SET_ICINDEX, BytecodeFallsThrough, BytecodeIsJumpTarget
#include "vm/CompletionKind.h"      // CompletionKind
#include "vm/FunctionPrefixKind.h"  // FunctionPrefixKind
#include "vm/GeneratorObject.h"     // AbstractGeneratorObject
#include "vm/Opcodes.h"             // JSOp, JSOpLength_*
#include "vm/PropMap.h"             // SharedPropMap::MaxPropsForNonDictionary
#include "vm/Scope.h"               // GetScopeDataTrailingNames
#include "vm/SharedStencil.h"       // ScopeNote
#include "vm/ThrowMsgKind.h"        // ThrowMsgKind
#include "vm/TypeofEqOperand.h"     // TypeofEqOperand

using namespace js;
using namespace js::frontend;

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

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

static bool NeedsFieldInitializer(ParseNode* member, bool inStaticContext) {
  // For the purposes of bytecode emission, StaticClassBlocks are treated as if
  // they were static initializers.
  return (member->is<StaticClassBlock>() && inStaticContext) ||
         (member->is<ClassField>() &&
          member->as<ClassField>().isStatic() == inStaticContext);
}

static bool NeedsAccessorInitializer(ParseNode* member, bool isStatic) {
  if (isStatic) {
    return false;
  }
  return member->is<ClassMethod>() &&
         member->as<ClassMethod>().name().isKind(ParseNodeKind::PrivateName) &&
         !member->as<ClassMethod>().isStatic() &&
         member->as<ClassMethod>().accessorType() != AccessorType::None;
}

static bool ShouldSuppressBreakpointsAndSourceNotes(
    SharedContext* sc, BytecodeEmitter::EmitterMode emitterMode) {
  // Suppress for all self-hosting code.
  if (emitterMode == BytecodeEmitter::EmitterMode::SelfHosting) {
    return true;
  }

  // Suppress for synthesized class constructors.
  if (sc->isFunctionBox()) {
    FunctionBox* funbox = sc->asFunctionBox();
    return funbox->isSyntheticFunction() && funbox->isClassConstructor();
  }

  return false;
}

BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent, FrontendContext* fc,
                                 SharedContext* sc,
                                 const ErrorReporter& errorReporter,
                                 CompilationState& compilationState,
                                 EmitterMode emitterMode)
    : sc(sc),
      fc(fc),
      parent(parent),
      bytecodeSection_(fc, sc->extent().lineno,
                       JS::LimitedColumnNumberOneOrigin(sc->extent().column)),
      perScriptData_(fc, compilationState),
      errorReporter_(errorReporter),
      compilationState(compilationState),
      suppressBreakpointsAndSourceNotes(
          ShouldSuppressBreakpointsAndSourceNotes(sc, emitterMode)),
      emitterMode(emitterMode) {
  MOZ_ASSERT_IF(parent, fc == parent->fc);
}

BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent, SharedContext* sc)
    : BytecodeEmitter(parent, parent->fc, sc, parent->errorReporter_,
                      parent->compilationState, parent->emitterMode) {}

BytecodeEmitter::BytecodeEmitter(FrontendContext* fc,
                                 const EitherParser& parser, SharedContext* sc,
                                 CompilationState& compilationState,
                                 EmitterMode emitterMode)
    : BytecodeEmitter(nullptr, fc, sc, parser.errorReporter(), compilationState,
                      emitterMode) {
  ep_.emplace(parser);
}

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

bool BytecodeEmitter::init() {
  if (!parent) {
    if (!compilationState.prepareSharedDataStorage(fc)) {
      return false;
    }
  }
  return perScriptData_.init(fc);
}

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(TaggedParserAtomIndex name) {
  return innermostEmitterScope()->lookup(this, name);
}

void BytecodeEmitter::lookupPrivate(TaggedParserAtomIndex name,
                                    NameLocation& loc,
                                    Maybe<NameLocation>& brandLoc) {
  innermostEmitterScope()->lookupPrivate(this, name, loc, brandLoc);
}

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

template <typename T>
Maybe<NameLocation> BytecodeEmitter::locationOfNameBoundInScopeType(
    TaggedParserAtomIndex name, EmitterScope* source) {
  EmitterScope* aScope = source;
  while (!aScope->scope(this).is<T>()) {
    aScope = aScope->enclosingInFrame();
  }
  return source->locationBoundInScope(name, aScope);
}

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

  if (!newSrcNote(SrcNoteType::BreakpointStepSep)) {
    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 (!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(fc);
    return false;
  }

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

  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) const {
  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(op, 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(op, 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(op, 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(op, 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 + ICINDEX_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(op, 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::emitPickN(uint8_t n) {
  MOZ_ASSERT(n != 0);

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

  return emit2(JSOp::Pick, n);
}

bool BytecodeEmitter::emitUnpickN(uint8_t n) {
  MOZ_ASSERT(n != 0);

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

  return emit2(JSOp::Unpick, n);
}

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

bool BytecodeEmitter::emitBuiltinObject(BuiltinObjectKind kind) {
  return emit2(JSOp::BuiltinObject, uint8_t(kind));
}

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

  const ErrorReporter& er = errorReporter();
  std::optional<bool> onThisLineStatus =
      er.isOnThisLine(offset, bytecodeSection().currentLine());
  if (!onThisLineStatus.has_value()) {
    er.errorNoOffset(JSMSG_OUT_OF_MEMORY);
    return false;
  }

  bool onThisLine = *onThisLineStatus;

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

    // If we use a `SetLine` note below, we want it to be relative to the
    // scripts initial line number for better chance of sharing.
    unsigned initialLine = sc->extent().lineno;
    MOZ_ASSERT(line >= initialLine);

    /*
     * 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, initialLine)) {
      if (!newSrcNote2(SrcNoteType::SetLine,
                       SrcNote::SetLine::toOperand(line, initialLine))) {
        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 (skipLocationSrcNotes()) {
    return true;
  }

  if (!updateLineNumberNotes(offset)) {
    return false;
  }

  JS::LimitedColumnNumberOneOrigin columnIndex =
      errorReporter().columnAt(offset);

  // Assert colspan is always representable.
  static_assert((0 - ptrdiff_t(JS::LimitedColumnNumberOneOrigin::Limit)) >=
                SrcNote::ColSpan::MinColSpan);
  static_assert((ptrdiff_t(JS::LimitedColumnNumberOneOrigin::Limit) - 0) <=
                SrcNote::ColSpan::MaxColSpan);

  JS::ColumnNumberOffset colspan = columnIndex - bytecodeSection().lastColumn();

  if (colspan != JS::ColumnNumberOffset::zero()) {
    if (lastLineOnlySrcNoteIndex != LastSrcNoteIsNotLineOnly) {
      MOZ_ASSERT(bytecodeSection().lastColumn() ==
                 JS::LimitedColumnNumberOneOrigin());

      const SrcNotesVector& notes = bytecodeSection().notes();
      SrcNoteType type = notes[lastLineOnlySrcNoteIndex].type();
      if (type == SrcNoteType::NewLine) {
        if (!convertLastNewLineToNewLineColumn(columnIndex)) {
          return false;
        }
      } else {
        MOZ_ASSERT(type == SrcNoteType::SetLine);
        if (!convertLastSetLineToSetLineColumn(columnIndex)) {
          return false;
        }
      }
    } else {
      if (!newSrcNote2(SrcNoteType::ColSpan,
                       SrcNote::ColSpan::toOperand(colspan))) {
        return false;
      }
    }
    bytecodeSection().setLastColumn(columnIndex, offset);
    bytecodeSection().updateSeparatorPositionIfPresent();
  }
  return true;
}

bool BytecodeEmitter::updateSourceCoordNotesIfNonLiteral(ParseNode* node) {
  if (node->isLiteral()) {
    return true;
  }
  return updateSourceCoordNotes(node->pn_pos.begin);
}

uint32_t BytecodeEmitter::getOffsetForLoop(ParseNode* nextpn) const {
  // 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 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;
}

bool BytecodeEmitter::emitGoto(NestableControl* target, GotoKind kind) {
  NonLocalExitControl nle(this, kind == GotoKind::Continue
                                    ? NonLocalExitKind::Continue
                                    : NonLocalExitKind::Break);
  return nle.emitNonLocalJump(target);
}

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

ScopeIndex BytecodeEmitter::innermostScopeIndex() const {
  return *innermostEmitterScope()->scopeIndex(this);
}

bool BytecodeEmitter::emitGCIndexOp(JSOp op, GCThingIndex index) {
  MOZ_ASSERT(checkStrictOrSloppy(op));

  constexpr size_t OpLength = 1 + GCTHING_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_GCTHING_INDEX(code, index);
  bytecodeSection().updateDepth(op, offset);
  return true;
}

bool BytecodeEmitter::emitAtomOp(JSOp op, TaggedParserAtomIndex atom) {
  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 != TaggedParserAtomIndex::WellKnown::dot_generator_());

  GCThingIndex index;
  if (!makeAtomIndex(atom, ParserAtom::Atomize::Yes, &index)) {
    return false;
  }

  return emitAtomOp(op, index);
}

bool BytecodeEmitter::emitAtomOp(JSOp op, GCThingIndex atomIndex) {
  MOZ_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
#ifdef DEBUG
  auto atom = perScriptData().gcThingList().getAtom(atomIndex);
  MOZ_ASSERT(compilationState.parserAtoms.isInstantiatedAsJSAtom(atom));
#endif
  return emitGCIndexOp(op, atomIndex);
}

bool BytecodeEmitter::emitStringOp(JSOp op, TaggedParserAtomIndex atom) {
  MOZ_ASSERT(atom);
  GCThingIndex index;
  if (!makeAtomIndex(atom, ParserAtom::Atomize::No, &index)) {
    return false;
  }

  return emitStringOp(op, index);
}

bool BytecodeEmitter::emitStringOp(JSOp op, GCThingIndex atomIndex) {
  MOZ_ASSERT(JOF_OPTYPE(op) == JOF_STRING);
  return emitGCIndexOp(op, atomIndex);
}

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

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

bool BytecodeEmitter::emitRegExp(GCThingIndex index) {
  return emitGCIndexOp(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 ||
             JOF_OPTYPE(op) == JOF_DEBUGCOORD);

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

bool BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) const {
  AutoCheckRecursionLimit recursion(fc);
  if (!recursion.check(fc)) {
    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;

    // |new.target| doesn't have any side-effects.
    case ParseNodeKind::NewTargetExpr: {
      MOZ_ASSERT(pn->is<NewTargetNode>());
      *answer = false;
      return true;
    }

    // Trivial binary nodes with more token pos holders.
    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:
    case ParseNodeKind::ArgumentsLength:
      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::CoalesceAssignExpr:
    case ParseNodeKind::OrAssignExpr:
    case ParseNodeKind::AndAssignExpr:
    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;

#ifdef ENABLE_RECORD_TUPLE
    case ParseNodeKind::RecordExpr:
    case ParseNodeKind::TupleExpr:
      MOZ_CRASH("Record and Tuple are not supported yet");
#endif

#ifdef ENABLE_DECORATORS
    case ParseNodeKind::DecoratorList:
      MOZ_CRASH("Decorators are not supported yet");
#endif

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    case ParseNodeKind::UsingDecl:
    case ParseNodeKind::AwaitUsingDecl:
      MOZ_CRASH("Using declarations are not supported yet");
#endif

    // 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::PrivateInExpr:
    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::ElemExpr:
    case ParseNodeKind::OptionalElemExpr:
      MOZ_ASSERT(pn->is<BinaryNode>());
      *answer = true;
      return true;

    // Throws if the operand is not of the right class. Can also call a private
    // getter.
    case ParseNodeKind::PrivateMemberExpr:
    case ParseNodeKind::OptionalPrivateMemberExpr:
      *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:
    case ParseNodeKind::CallImportSpec:
      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;

    // 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::DefaultConstructor:   // by ParseNodeKind::ClassDecl
    case ParseNodeKind::ClassBodyScope:       // by ParseNodeKind::ClassDecl
    case ParseNodeKind::ClassMethod:          // by ParseNodeKind::ClassDecl
    case ParseNodeKind::ClassField:           // by ParseNodeKind::ClassDecl
    case ParseNodeKind::ClassNames:           // by ParseNodeKind::ClassDecl
    case ParseNodeKind::StaticClassBlock:     // by ParseNodeKind::ClassDecl
    case ParseNodeKind::ClassMemberList:      // by ParseNodeKind::ClassDecl
    case ParseNodeKind::ImportSpecList:       // by ParseNodeKind::Import
    case ParseNodeKind::ImportSpec:           // by ParseNodeKind::Import
    case ParseNodeKind::ImportNamespaceSpec:  // by ParseNodeKind::Import
    case ParseNodeKind::ImportAttribute:      // by ParseNodeKind::Import
    case ParseNodeKind::ImportAttributeList:  // by ParseNodeKind::Import
    case ParseNodeKind::ImportModuleRequest:  // by ParseNodeKind::Import
    case ParseNodeKind::ExportBatchSpecStmt:  // by ParseNodeKind::Export
    case ParseNodeKind::ExportSpecList:       // by ParseNodeKind::Export
    case ParseNodeKind::ExportSpec:           // by ParseNodeKind::Export
    case ParseNodeKind::ExportNamespaceSpec:  // 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() const {
  return findInnermostNestableControl<LoopControl>();
}

bool BytecodeEmitter::checkSingletonContext() const {
  MOZ_ASSERT_IF(sc->treatAsRunOnce(), sc->isTopLevelContext());
  return sc->treatAsRunOnce() && !isInLoop();
}

bool BytecodeEmitter::needsImplicitThis() const {
  // 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;
}

size_t BytecodeEmitter::countThisEnvironmentHops() const {
  unsigned numHops = 0;

  for (const auto* current = this; current; current = current->parent) {
    for (EmitterScope* es = current->innermostEmitterScope(); es;
         es = es->enclosingInFrame()) {
      if (es->scope(current).is<FunctionScope>()) {
        if (!es->scope(current).isArrow()) {
          // The Parser is responsible for marking the environment as either
          // closed-over or used-by-eval which ensure that is must exist.
          MOZ_ASSERT(es->scope(current).hasEnvironment());
          return numHops;
        }
      }
      if (es->scope(current).hasEnvironment()) {
        numHops++;
      }
    }
  }

  // The "this" environment exists outside of the compilation, but the
  // `ScopeContext` recorded the number of additional hops needed, so add
  // those in now.
  MOZ_ASSERT(sc->allowSuperProperty());
  numHops += compilationState.scopeContext.enclosingThisEnvironmentHops;
  return numHops;
}

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.
  size_t numHops = countThisEnvironmentHops();

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

  MOZ_ASSERT(numHops < ENVCOORD_HOPS_LIMIT - 1);

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

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

  return emit1(JSOp::SuperBase);
}

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

  va_list args;
  va_start(args, errorNumber);

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

  va_end(args);
}

void BytecodeEmitter::reportError(uint32_t offset, unsigned errorNumber,
                                  ...) const {
  va_list args;
  va_start(args, errorNumber);

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

  va_end(args);
}

bool BytecodeEmitter::addObjLiteralData(ObjLiteralWriter& writer,
                                        GCThingIndex* outIndex) {
  if (!writer.checkForDuplicatedNames(fc)) {
    return false;
  }

  size_t len = writer.getCode().size();
  auto* code = compilationState.alloc.newArrayUninitialized<uint8_t>(len);
  if (!code) {
    js::ReportOutOfMemory(fc);
    return false;
  }
  memcpy(code, writer.getCode().data(), len);

  ObjLiteralIndex objIndex(compilationState.objLiteralData.length());
  if (uint32_t(objIndex) >= TaggedScriptThingIndex::IndexLimit) {
    ReportAllocationOverflow(fc);
    return false;
  }
  if (!compilationState.objLiteralData.emplaceBack(code, len, writer.getKind(),
                                                   writer.getFlags(),
                                                   writer.getPropertyCount())) {
    js::ReportOutOfMemory(fc);
    return false;
  }

  return perScriptData().gcThingList().append(objIndex, outIndex);
}

bool BytecodeEmitter::emitPrepareIteratorResult() {
  constexpr JSOp op = JSOp::NewObject;

  ObjLiteralWriter writer;
  writer.beginShape(op);

  writer.setPropNameNoDuplicateCheck(parserAtoms(),
                                     TaggedParserAtomIndex::WellKnown::value());
  if (!writer.propWithUndefinedValue(fc)) {
    return false;
  }
  writer.setPropNameNoDuplicateCheck(parserAtoms(),
                                     TaggedParserAtomIndex::WellKnown::done());
  if (!writer.propWithUndefinedValue(fc)) {
    return false;
  }

  GCThingIndex shape;
  if (!addObjLiteralData(writer, &shape)) {
    return false;
  }

  return emitGCIndexOp(op, shape);
}

bool BytecodeEmitter::emitFinishIteratorResult(bool done) {
  if (!emitAtomOp(JSOp::InitProp, TaggedParserAtomIndex::WellKnown::value())) {
    return false;
  }
  if (!emit1(done ? JSOp::True : JSOp::False)) {
    return false;
  }
  if (!emitAtomOp(JSOp::InitProp, TaggedParserAtomIndex::WellKnown::done())) {
    return false;
  }
  return true;
}

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

  return true;
}

bool BytecodeEmitter::emitGetName(NameNode* name) {
  MOZ_ASSERT(name->isKind(ParseNodeKind::Name));

  return emitGetName(name->name());
}

bool BytecodeEmitter::emitGetPrivateName(NameNode* name) {
  MOZ_ASSERT(name->isKind(ParseNodeKind::PrivateName));
  return emitGetPrivateName(name->name());
}

bool BytecodeEmitter::emitGetPrivateName(TaggedParserAtomIndex nameAtom) {
  // The parser ensures the private name is present on the environment chain,
  // but its location can be Dynamic or Global when emitting debugger
  // eval-in-frame code.
  NameLocation location = lookupName(nameAtom);
  MOZ_ASSERT(location.kind() == NameLocation::Kind::FrameSlot ||
             location.kind() == NameLocation::Kind::EnvironmentCoordinate ||
             location.kind() == NameLocation::Kind::Dynamic ||
             location.kind() == NameLocation::Kind::Global);

  return emitGetNameAtLocation(nameAtom, location);
}

bool BytecodeEmitter::emitTDZCheckIfNeeded(TaggedParserAtomIndex 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() || loc.isPrivateMethod() || loc.isSynthetic());

  // Private names are implemented as lexical bindings, but it's just an
  // implementation detail. Per spec there's no TDZ check when using them.
  if (parserAtoms().isPrivateName(name)) {
    return true;
  }

  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())) {
      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, ValueUsage valueUsage) {
  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(), valueUsage)) {
    //              [stack] RESULT
    return false;
  }

  return true;
}

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

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

  return true;
}

bool BytecodeEmitter::emitElemObjAndKey(PropertyByValue* elem,
                                        ElemOpEmitter& eoe) {
  ParseNode* exprOrSuper = &elem->expression();
  ParseNode* key = &elem->key();

  if (!eoe.prepareForObj()) {
    //              [stack]
    return false;
  }

  if (elem->isSuper()) {
    auto* base = &exprOrSuper->as<UnaryNode>();
    if (!emitGetThisForSuperBase(base)) {
      //            [stack] THIS
      return false;
    }
  } else {
    if (!emitTree(exprOrSuper)) {
      //            [stack] OBJ
      return false;
    }
  }

  if (!eoe.prepareForKey()) {
    //              [stack] # if Super
    //              [stack] THIS? THIS
    //              [stack] # otherwise
    //              [stack] OBJ? OBJ
    return false;
  }

  if (!emitTree(key)) {
    //              [stack] # if Super
    //              [stack] THIS? THIS KEY
    //              [stack] # otherwise
    //              [stack] OBJ? OBJ KEY
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitElemOpBase(JSOp op) {
  if (!emit1(op)) {
    return false;
  }

  return true;
}

static ElemOpEmitter::Kind ConvertIncDecKind(ParseNodeKind kind) {
  switch (kind) {
    case ParseNodeKind::PostIncrementExpr:
      return ElemOpEmitter::Kind::PostIncrement;
    case ParseNodeKind::PreIncrementExpr:
      return ElemOpEmitter::Kind::PreIncrement;
    case ParseNodeKind::PostDecrementExpr:
      return ElemOpEmitter::Kind::PostDecrement;
    case ParseNodeKind::PreDecrementExpr:
      return ElemOpEmitter::Kind::PreDecrement;
    default:
      MOZ_CRASH("unexpected inc/dec node kind");
  }
}

static PrivateOpEmitter::Kind PrivateConvertIncDecKind(ParseNodeKind kind) {
  switch (kind) {
    case ParseNodeKind::PostIncrementExpr:
      return PrivateOpEmitter::Kind::PostIncrement;
    case ParseNodeKind::PreIncrementExpr:
      return PrivateOpEmitter::Kind::PreIncrement;
    case ParseNodeKind::PostDecrementExpr:
      return PrivateOpEmitter::Kind::PostDecrement;
    case ParseNodeKind::PreDecrementExpr:
      return PrivateOpEmitter::Kind::PreDecrement;
    default:
      MOZ_CRASH("unexpected inc/dec node kind");
  }
}

bool BytecodeEmitter::emitElemIncDec(UnaryNode* incDec, ValueUsage valueUsage) {
  PropertyByValue* elemExpr = &incDec->kid()->as<PropertyByValue>();
  bool isSuper = elemExpr->isSuper();
  MOZ_ASSERT(!elemExpr->key().isKind(ParseNodeKind::PrivateName));
  ParseNodeKind kind = incDec->getKind();
  ElemOpEmitter eoe(
      this, ConvertIncDecKind(kind),
      isSuper ? ElemOpEmitter::ObjKind::Super : ElemOpEmitter::ObjKind::Other);
  if (!emitElemObjAndKey(elemExpr, eoe)) {
    //              [stack] # if Super
    //              [stack] THIS KEY
    //              [stack] # otherwise
    //              [stack] OBJ KEY
    return false;
  }
  if (!eoe.emitIncDec(valueUsage)) {
    //              [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::emitPrivateIncDec(UnaryNode* incDec,
                                        ValueUsage valueUsage) {
  PrivateMemberAccess* privateExpr = &incDec->kid()->as<PrivateMemberAccess>();
  ParseNodeKind kind = incDec->getKind();
  PrivateOpEmitter xoe(this, PrivateConvertIncDecKind(kind),
                       privateExpr->privateName().name());
  if (!emitTree(&privateExpr->expression())) {
    //              [stack] OBJ
    return false;
  }
  if (!xoe.emitReference()) {
    //              [stack] OBJ NAME
    return false;
  }
  if (!xoe.emitIncDec(valueUsage)) {
    //              [stack] RESULT
    return false;
  }

  return true;
}

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(JSOp::Double, 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(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 hasTopLevelFunctionDeclarations 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::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) {
  // ParseContext::Scope::setOwnStackSlotCount should check the fixed slot
  // for the following, and it should prevent using fixed slot if there are
  // too many bindings:
  //   * generator or asyn function
  //   * module code after top-level await
  MOZ_ASSERT(innermostEmitterScopeNoCheck()->frameSlotEnd() <=
             ParseContext::Scope::FixedSlotLimit);

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

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

  auto name = 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 (!emitInitializeInstanceMembers(true)) {
    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>());
}

// For Global and sloppy-Eval scripts, this performs most of the steps of the
// spec's [GlobalDeclarationInstantiation] and [EvalDeclarationInstantiation]
// operations.
//
// Note that while strict-Eval is handled in the same part of the spec, it never
// fails for global-redeclaration checks so those scripts initialize directly in
// their bytecode.
bool BytecodeEmitter::emitDeclarationInstantiation(ParseNode* body) {
  if (sc->isModuleContext()) {
    // ES Modules have dedicated variable and lexial environments and therefore
    // do not have to perform redeclaration checks. We initialize their bindings
    // elsewhere in bytecode.
    return true;
  }

  if (sc->isEvalContext() && sc->strict()) {
    // Strict Eval has a dedicated variables (and lexical) environment and
    // therefore does not have to perform redeclaration checks. We initialize
    // their bindings elsewhere in the bytecode.
    return true;
  }

  // If we have no variables bindings, then we are done!
  if (sc->isGlobalContext()) {
    if (!sc->asGlobalContext()->bindings) {
      return true;
    }
  } else {
    MOZ_ASSERT(sc->isEvalContext());

    if (!sc->asEvalContext()->bindings) {
      return true;
    }
  }

#if DEBUG
  // There should be no emitted functions yet.
  for (const auto& thing : perScriptData().gcThingList().objects()) {
    MOZ_ASSERT(thing.isEmptyGlobalScope() || thing.isScope());
  }
#endif

  // Emit the hoisted functions to gc-things list. There is no bytecode
  // generated yet to bind them.
  if (!defineHoistedTopLevelFunctions(body)) {
    return false;
  }

  // Save the last GCThingIndex emitted. The hoisted functions are contained in
  // the gc-things list up until this point. This set of gc-things also contain
  // initial scopes (of which there must be at least one).
  MOZ_ASSERT(perScriptData().gcThingList().length() > 0);
  GCThingIndex lastFun =
      GCThingIndex(perScriptData().gcThingList().length() - 1);

#if DEBUG
  for (const auto& thing : perScriptData().gcThingList().objects()) {
    MOZ_ASSERT(thing.isEmptyGlobalScope() || thing.isScope() ||
               thing.isFunction());
  }
#endif

  // Check for declaration conflicts and initialize the bindings.
  // NOTE: The self-hosting top-level script should not populate the builtins
  //       directly on the GlobalObject (and instead uses JSOp::GetIntrinsic for
  //       lookups).
  if (emitterMode == BytecodeEmitter::EmitterMode::Normal) {
    if (!emitGCIndexOp(JSOp::GlobalOrEvalDeclInstantiation, lastFun)) {
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitScript(ParseNode* body) {
  setScriptStartOffsetIfUnset(body->pn_pos.begin);

  MOZ_ASSERT(inPrologue());

  TDZCheckCache tdzCache(this);
  EmitterScope emitterScope(this);
  Maybe<AsyncEmitter> topLevelAwait;
  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;
    }
    if (sc->asModuleContext()->isAsync()) {
      topLevelAwait.emplace(this);
    }
  }

  setFunctionBodyEndPos(body->pn_pos.end);

  bool isSloppyEval = sc->isEvalContext() && !sc->strict();
  if (isSloppyEval && body->is<LexicalScopeNode>() &&
      !body->as<LexicalScopeNode>().isEmptyScope()) {
    // Sloppy eval scripts may emit hoisted functions bindings with a
    // `JSOp::GlobalOrEvalDeclInstantiation` opcode below. If this eval needs a
    // top-level lexical environment, we must ensure that environment is created
    // before those functions are created and bound.
    //
    // This differs from the global-script case below because the global-lexical
    // environment exists outside the script itself. In the case of strict eval
    // scripts, the `emitterScope` above is already sufficient.
    EmitterScope lexicalEmitterScope(this);
    LexicalScopeNode* scope = &body->as<LexicalScopeNode>();

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

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

    switchToMain();

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

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

    if (!lexicalEmitterScope.leave(this)) {
      return false;
    }
  } else {
    if (!emitDeclarationInstantiation(body)) {
      return false;
    }
    if (topLevelAwait) {
      if (!topLevelAwait->prepareForModule()) {
        return false;
      }
    }

    switchToMain();

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    if (!emitterScope.prepareForModuleDisposableScopeBody(this)) {
      return false;
    }
#endif

    if (topLevelAwait) {
      if (!topLevelAwait->prepareForBody()) {
        return false;
      }
    }

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

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

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
  if (!emitterScope.emitModuleDisposableScopeBodyEnd(this)) {
    return false;
  }
#endif

  if (topLevelAwait) {
    if (!topLevelAwait->emitEndModule()) {
      return false;
    }
  }

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

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

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

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

  // Create a Stencil and convert it into a JSScript.
  return intoScriptStencil(CompilationStencil::TopLevelIndex);
}

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

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

  mozilla::SaturateUint8 propertyCountEstimate = propertyAdditionEstimate;

  // Add fields to the property count estimate.
  if (isFunction && sc->asFunctionBox()->useMemberInitializers()) {
    propertyCountEstimate +=
        sc->asFunctionBox()->memberInitializers().numMemberInitializers;
  }

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

#if defined(ENABLE_DECORATORS) || defined(ENABLE_EXPLICIT_RESOURCE_MANAGEMENT)
bool BytecodeEmitter::emitCheckIsCallable() {
  // This emits code to check if the value at the top of the stack is
  // callable. The value is left on the stack.
  //            [stack] VAL
  if (!emitAtomOp(JSOp::GetIntrinsic,
                  TaggedParserAtomIndex::WellKnown::IsCallable())) {
    //            [stack] VAL ISCALLABLE
    return false;
  }
  if (!emit1(JSOp::Undefined)) {
    //            [stack] VAL ISCALLABLE UNDEFINED
    return false;
  }
  if (!emitDupAt(2)) {
    //            [stack] VAL ISCALLABLE UNDEFINED VAL
    return false;
  }
  return emitCall(JSOp::Call, 1);
  //              [stack] VAL ISCALLABLE_RESULT
}
#endif

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

bool BytecodeEmitter::emitFunctionScript(FunctionNode* funNode) {
  MOZ_ASSERT(inPrologue());
  ParamsBodyNode* paramsBody = funNode->body();
  FunctionBox* funbox = sc->asFunctionBox();

  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->body())) {
    //              [stack]
    return false;
  }

  if (!fse.emitEndBody()) {
    //              [stack]
    return false;
  }

  if (funbox->index() == CompilationStencil::TopLevelIndex) {
    if (!NameFunctions(fc, parserAtoms(), funNode)) {
      return false;
    }
  }

  return fse.intoStencil();
}

class js::frontend::DestructuringLHSRef {
  struct None {
    size_t numReferenceSlots() const { return 0; }
  };

  mozilla::Variant<None, NameOpEmitter, PropOpEmitter, ElemOpEmitter,
                   PrivateOpEmitter>
      emitter_ = AsVariant(None{});

 public:
  template <typename T>
  void from(T&& emitter) {
    emitter_.emplace<T>(std::forward<T>(emitter));
  }

  template <typename T>
  T& emitter() {
    return emitter_.as<T>();
  }

  /**
   * Return the number of values pushed onto the stack.
   */

  size_t numReferenceSlots() const {
    return emitter_.match([](auto& e) { return e.numReferenceSlots(); });
  }
};

bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target,
                                              DestructuringFlavor flav,
                                              DestructuringLHSRef& lref) {
#ifdef DEBUG
  int depth = bytecodeSection().stackDepth();
#endif

  switch (target->getKind()) {
    case ParseNodeKind::ArrayExpr:
    case ParseNodeKind::ObjectExpr:
      // No need to recurse into ParseNodeKind::Array and ParseNodeKind::Object
      // subpatterns here, since emitSetOrInitializeDestructuring does the
      // recursion when setting or initializing the value.
      break;

    case ParseNodeKind::Name: {
      auto* name = &target->as<NameNode>();
      NameOpEmitter noe(this, name->atom(),
                        flav == DestructuringFlavor::Assignment
                            ? NameOpEmitter::Kind::SimpleAssignment
                            : NameOpEmitter::Kind::Initialize);
      if (!noe.prepareForRhs()) {
        return false;
      }

      lref.from(std::move(noe));
      return true;
    }

    case ParseNodeKind::ArgumentsLength:
    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;
        }
      } else {
        if (!emitTree(&prop->expression())) {
          //        [stack] OBJ
          return false;
        }
      }
      if (!poe.prepareForRhs()) {
        //          [stack] # if Super
        //          [stack] THIS SUPERBASE
        //          [stack] # otherwise
        //          [stack] OBJ
        return false;
      }

      lref.from(std::move(poe));
      break;
    }

    case ParseNodeKind::ElemExpr: {
      PropertyByValue* elem = &target->as<PropertyByValue>();
      bool isSuper = elem->isSuper();
      MOZ_ASSERT(!elem->key().isKind(ParseNodeKind::PrivateName));
      ElemOpEmitter eoe(this, ElemOpEmitter::Kind::SimpleAssignment,
                        isSuper ? ElemOpEmitter::ObjKind::Super
                                : ElemOpEmitter::ObjKind::Other);
      if (!emitElemObjAndKey(elem, eoe)) {
        //          [stack] # if Super
        //          [stack] THIS KEY
        //          [stack] # otherwise
        //          [stack] OBJ KEY
        return false;
      }
      if (!eoe.prepareForRhs()) {
        //          [stack] # if Super
        //          [stack] THIS KEY SUPERBASE
        //          [stack] # otherwise
        //          [stack] OBJ KEY
        return false;
      }

      lref.from(std::move(eoe));
      break;
    }

    case ParseNodeKind::PrivateMemberExpr: {
      PrivateMemberAccess* privateExpr = &target->as<PrivateMemberAccess>();
      PrivateOpEmitter xoe(this, PrivateOpEmitter::Kind::SimpleAssignment,
                           privateExpr->privateName().name());
      if (!emitTree(&privateExpr->expression())) {
        //          [stack] OBJ
        return false;
      }
      if (!xoe.emitReference()) {
        //          [stack] OBJ NAME
        return false;
      }

      lref.from(std::move(xoe));
      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(lref.numReferenceSlots()));

  return true;
}

bool BytecodeEmitter::emitSetOrInitializeDestructuring(
    ParseNode* target, DestructuringFlavor flav, DestructuringLHSRef& lref) {
  // 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.

  switch (target->getKind()) {
    case ParseNodeKind::ArrayExpr:
    case ParseNodeKind::ObjectExpr:
      if (!emitDestructuringOps(&target->as<ListNode>(), flav)) {
        return false;
      }
      // emitDestructuringOps leaves the assigned (to-be-destructured) value on
      // top of the stack.
      break;

    case ParseNodeKind::Name: {
      // The environment is already pushed by emitDestructuringLHSRef.
      //            [stack] ENV? VAL
      auto& noe = lref.emitter<NameOpEmitter>();

      if (!noe.emitAssignment()) {
        //          [stack] VAL
        return false;
      }
      break;
    }

    case ParseNodeKind::ArgumentsLength:
    case ParseNodeKind::DotExpr: {
      // The reference is already pushed by emitDestructuringLHSRef.
      //            [stack] # if Super
      //            [stack] THIS SUPERBASE VAL
      //            [stack] # otherwise
      //            [stack] OBJ VAL
      auto& poe = lref.emitter<PropOpEmitter>();
      auto* prop = &target->as<PropertyAccess>();

      if (!poe.emitAssignment(prop->key().atom())) {
        //          [stack] # VAL
        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
      auto& eoe = lref.emitter<ElemOpEmitter>();

      if (!eoe.emitAssignment()) {
        //          [stack] VAL
        return false;
      }
      break;
    }

    case ParseNodeKind::PrivateMemberExpr: {
      // The reference is already pushed by emitDestructuringLHSRef.
      //            [stack] OBJ NAME VAL
      auto& xoe = lref.emitter<PrivateOpEmitter>();

      if (!xoe.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;
}

JSOp BytecodeEmitter::getIterCallOp(JSOp callOp,
                                    SelfHostedIter selfHostedIter) const {
  if (emitterMode == BytecodeEmitter::SelfHosting) {
    MOZ_ASSERT(selfHostedIter != SelfHostedIter::Deny);

    switch (callOp) {
      case JSOp::Call:
        return JSOp::CallContent;
      case JSOp::CallIter:
        return JSOp::CallContentIter;
      default:
        MOZ_CRASH("Unknown iterator call op");
    }
  }

  return callOp;
}

bool BytecodeEmitter::emitIteratorNext(
    const Maybe<uint32_t>& callSourceCoordOffset,
    IteratorKind iterKind /* = IteratorKind::Sync */,
    SelfHostedIter selfHostedIter /* = SelfHostedIter::Deny */) {
  MOZ_ASSERT(selfHostedIter != SelfHostedIter::Deny ||
                 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(getIterCallOp(JSOp::Call, selfHostedIter), 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::emitIteratorCloseInScope(
    EmitterScope& currentScope,
    IteratorKind iterKind /* = IteratorKind::Sync */,
    CompletionKind completionKind /* = CompletionKind::Normal */,
    SelfHostedIter selfHostedIter /* = SelfHostedIter::Deny */) {
  MOZ_ASSERT(selfHostedIter != SelfHostedIter::Deny ||
                 emitterMode != BytecodeEmitter::SelfHosting,
             ".close() on iterators is prohibited in self-hosted code because "
             "it can run user-modifiable iteration code");

  if (iterKind == IteratorKind::Sync) {
    return emit2(JSOp::CloseIter, uint8_t(completionKind));
  }

  // Generate inline logic corresponding to IteratorClose (ES2021 7.4.6) and
  // AsyncIteratorClose (ES2021 7.4.7). Steps numbers apply to both operations.
  //
  // Callers need to ensure that the iterator object is at the top of the
  // stack.

  // For non-Throw completions, we emit the equivalent of:
  //
  // var returnMethod = GetMethod(iterator, "return");
  // if (returnMethod !== undefined) {
  //   var innerResult = [Await] Call(returnMethod, iterator);
  //   CheckIsObj(innerResult);
  // }
  //
  // Whereas for Throw completions, we emit:
  //
  // try {
  //   var returnMethod = GetMethod(iterator, "return");
  //   if (returnMethod !== undefined) {
  //     [Await] Call(returnMethod, iterator);
  //   }
  // } catch {}

  Maybe<TryEmitter> tryCatch;

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

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

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

  // Steps 1-2 are assertions, step 3 is implicit.

  // Step 4.
  //
  // Get the "return" method.
  if (!emitAtomOp(JSOp::GetProp, TaggedParserAtomIndex::WellKnown::return_())) {
    //              [stack] ... ITER RET
    return false;
  }

  // Step 5.
  //
  // Do nothing if "return" is undefined or null.
  InternalIfEmitter ifReturnMethodIsDefined(this);
  if (!emit1(JSOp::IsNullOrUndefined)) {
    //              [stack] ... ITER RET NULL-OR-UNDEF
    return false;
  }

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

  // Steps 5.c, 7.
  //
  // Call the "return" method.
  if (!emit1(JSOp::Swap)) {
    //              [stack] ... RET ITER
    return false;
  }

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

  // 7.4.7 AsyncIteratorClose, step 5.d.
  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] ... RESULT RVAL
        return false;
      }
      if (!emit1(JSOp::SetRval)) {
        //          [stack] ... RESULT
        return false;
      }
    }
  }

  // Step 6 (Handled in caller).

  // Step 8.
  if (completionKind != CompletionKind::Throw) {
    // Check that the "return" result is an object.
    if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) {
      //            [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;
  }

  if (completionKind == CompletionKind::Throw) {
    if (!tryCatch->emitCatch()) {
      //            [stack] ... ITER EXC
      return false;
    }

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

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

  // Step 9 (Handled in caller).

  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, TaggedParserAtomIndex 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.
    setFunName(node->as<FunctionNode>().funbox(), name);

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

void BytecodeEmitter::setFunName(FunctionBox* funbox,
                                 TaggedParserAtomIndex name) const {
  // 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->displayAtom() == name);
  } else {
    funbox->setInferredName(name);
  }
}

bool BytecodeEmitter::emitInitializer(ParseNode* initializer,
                                      ParseNode* pattern) {
  if (initializer->isDirectRHSAnonFunction()) {
    MOZ_ASSERT(!pattern->isInParens());
    auto name = 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(getSelfHostedIterFor(pattern) == SelfHostedIter::Deny,
             "array destructuring is prohibited in self-hosted code because it"
             "can run user-modifiable iteration code");
  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
  //
  //   // NOTE: the fast path for this example is not applicable, because of
  //   // the spread and the assignment |c=y|, but it is documented here for a
  //   // simpler example, |let [a,b] = x;|
  //   //
  //   // if (IsOptimizableArray(x)) {
  //   //   a = x[0];
  //   //   b = x[1];
  //   //   goto end: // (skip everything below)
  //   // }
  //
  //   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);
  //
  //   end:

  bool isEligibleForArrayOptimizations = true;
  for (ParseNode* member : pattern->contents()) {
    switch (member->getKind()) {
      case ParseNodeKind::Elision:
        break;
      case ParseNodeKind::Name: {
        auto name = member->as<NameNode>().name();
        NameLocation loc = lookupName(name);
        if (loc.kind() != NameLocation::Kind::ArgumentSlot &&
            loc.kind() != NameLocation::Kind::FrameSlot &&
            loc.kind() != NameLocation::Kind::EnvironmentCoordinate) {
          isEligibleForArrayOptimizations = false;
        }
        break;
      }
      default:
        // Unfortunately we can't handle any recursive destructuring,
        // because we can't guarantee that the recursed-into parts
        // won't run code which invalidates our constraints. We also
        // cannot handle ParseNodeKind::AssignExpr for similar reasons.
        isEligibleForArrayOptimizations = false;
        break;
    }
    if (!isEligibleForArrayOptimizations) {
      break;
    }
  }

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

  Maybe<InternalIfEmitter> ifArrayOptimizable;

  if (isEligibleForArrayOptimizations) {
    ifArrayOptimizable.emplace(
        this, BranchEmitterBase::LexicalKind::MayContainLexicalAccessInBranch);

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

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

    if (!ifArrayOptimizable->emitThenElse()) {
      //            [stack] OBJ OBJ
      return false;
    }

    if (!emitAtomOp(JSOp::GetProp,
                    TaggedParserAtomIndex::WellKnown::length())) {
      //            [stack] OBJ LENGTH
      return false;
    }

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

    uint32_t idx = 0;
    for (ParseNode* member : pattern->contents()) {
      if (member->isKind(ParseNodeKind::Elision)) {
        idx += 1;
        continue;
      }
      MOZ_ASSERT(member->isKind(ParseNodeKind::Name));

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

      if (!emitNumberOp(idx)) {
        //          [stack] LENGTH OBJ OBJ IDX
        return false;
      }

      if (!emit1(JSOp::Dup)) {
        //          [stack] LENGTH OBJ OBJ IDX IDX
        return false;
      }

      if (!emitDupAt(4)) {
        //          [stack] LENGTH OBJ OBJ IDX IDX LENGTH
        return false;
      }

      if (!emit1(JSOp::Lt)) {
        //          [stack] LENGTH OBJ OBJ IDX IS_IN_DENSE_BOUNDS
        return false;
      }

      InternalIfEmitter isInDenseBounds(this);
      if (!isInDenseBounds.emitThenElse()) {
        //          [stack] LENGTH OBJ OBJ IDX
        return false;
      }

      if (!emit1(JSOp::GetElem)) {
        //          [stack] LENGTH OBJ VALUE
        return false;
      }

      if (!isInDenseBounds.emitElse()) {
        //          [stack] LENGTH OBJ OBJ IDX
        return false;
      }

      if (!emitPopN(2)) {
        //          [stack] LENGTH OBJ
        return false;
      }

      if (!emit1(JSOp::Undefined)) {
        //          [stack] LENGTH OBJ UNDEFINED
        return false;
      }

      if (!isInDenseBounds.emitEnd()) {
        //          [stack] LENGTH OBJ VALUE|UNDEFINED
        return false;
      }

      DestructuringLHSRef lref;
      if (!emitDestructuringLHSRef(member, flav, lref)) {
        //          [stack] LENGTH OBJ
        return false;
      }

      if (!emitSetOrInitializeDestructuring(member, flav, lref)) {
        //          [stack] LENGTH OBJ
        return false;
      }

      idx += 1;
    }

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

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

    if (!ifArrayOptimizable->emitElse()) {
      //            [stack] OBJ OBJ
      return false;
    }
  }

  if (!emitIterator(SelfHostedIter::Deny)) {
    //              [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;
    }

    if (!emitIteratorCloseInInnermostScope()) {
      //            [stack] ... OBJ
      return false;
    }

    if (ifArrayOptimizable.isSome()) {
      if (!ifArrayOptimizable->emitEnd()) {
        //          [stack] OBJ
        return false;
      }
    }

    return true;
  }

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

    ParseNode* subpattern;
    if (member->isKind(ParseNodeKind::Spread)) {
      subpattern = member->as<UnaryNode>().kid();

      MOZ_ASSERT(!subpattern->isKind(ParseNodeKind::AssignExpr));
    } else {
      subpattern = member;
    }

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

    // Spec requires LHS reference to be evaluated first.
    DestructuringLHSRef lref;
    bool isElision = lhsPattern->isKind(ParseNodeKind::Elision);
    if (!isElision) {
      auto emitLHSRef = [lhsPattern, flav, &lref](BytecodeEmitter* bce) {
        return bce->emitDestructuringLHSRef(lhsPattern, flav, lref);
        //          [stack] ... OBJ NEXT ITER DONE LREF*
      };
      if (!wrapWithDestructuringTryNote(tryNoteDepth, emitLHSRef)) {
        return false;
      }
    }

    // Number of stack slots emitted for the LHS reference.
    size_t emitted = lref.numReferenceSlots();

    // Pick the DONE value to the top of the stack.
    if (emitted) {
      if (!emitPickN(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 ITER
        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(SelfHostedIter::Deny)) {
        //          [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 (!emitUnpickN(emitted + 1)) {
        //          [stack] ... OBJ NEXT ITER TRUE LREF* ARRAY
        return false;
      }

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

      MOZ_ASSERT(!hasNext);
      break;
    }

    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 (!emitUnpickN(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, TaggedParserAtomIndex::WellKnown::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 (!emitUnpickN(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, TaggedParserAtomIndex::WellKnown::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, &lref](BytecodeEmitter* bce) {
        return bce->emitSetOrInitializeDestructuring(lhsPattern, flav, lref);
        //          [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;
  }

  if (ifArrayOptimizable.isSome()) {
    if (!ifArrayOptimizable->emitEnd()) {
      //            [stack] OBJ
      return false;
    }
  }

  return true;
}

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

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;
    bool hasKeyOnStack = false;
    if (member->isKind(ParseNodeKind::MutateProto) ||
        member->isKind(ParseNodeKind::Spread)) {
      subpattern = member->as<UnaryNode>().kid();

      MOZ_ASSERT_IF(member->isKind(ParseNodeKind::Spread),
                    !subpattern->isKind(ParseNodeKind::AssignExpr));
    } else {
      MOZ_ASSERT(member->isKind(ParseNodeKind::PropertyDefinition) ||
                 member->isKind(ParseNodeKind::Shorthand));
      subpattern = member->as<BinaryNode>().right();

      // Computed property names are evaluated before the subpattern.
      ParseNode* key = member->as<BinaryNode>().left();
      if (key->isKind(ParseNodeKind::ComputedName)) {
        if (!emitComputedPropertyName(&key->as<UnaryNode>())) {
          //        [stack] ... SET? RHS KEY
          return false;
        }
        hasKeyOnStack = true;
      }
    }

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

    // Spec requires LHS reference to be evaluated first.
    DestructuringLHSRef lref;
    if (!emitDestructuringLHSRef(lhs, flav, lref)) {
      //            [stack] ... SET? RHS KEY? LREF*
      return false;
    }

    // Number of stack slots emitted for the LHS reference.
    size_t emitted = lref.numReferenceSlots();

    // Duplicate the value being destructured to use as a reference base.
    if (!emitDupAt(emitted + hasKeyOnStack)) {
      //            [stack] ... SET? RHS KEY? 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, lref)) {
        //          [stack] ... RHS
        return false;
      }

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

    // Now push the property value currently being matched, which is the value
    // of the current property name "label" on the left of a colon in the object
    // initialiser.
    if (member->isKind(ParseNodeKind::MutateProto)) {
      if (!emitAtomOp(JSOp::GetProp,
                      TaggedParserAtomIndex::WellKnown::proto_())) {
        //          [stack] ... SET? RHS LREF* PROP
        return false;
      }
    } else {
      MOZ_ASSERT(member->isKind(ParseNodeKind::PropertyDefinition) ||
                 member->isKind(ParseNodeKind::Shorthand));

      ParseNode* key = member->as<BinaryNode>().left();
      if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
          key->isKind(ParseNodeKind::StringExpr)) {
        if (!emitAtomOp(JSOp::GetProp, key->as<NameNode>().atom())) {
          //        [stack] ... SET? RHS LREF* PROP
          return false;
        }
      } else {
        if (key->isKind(ParseNodeKind::NumberExpr)) {
          if (!emitNumberOp(key->as<NumericLiteral>().value())) {
            //      [stack]... SET? RHS LREF* RHS KEY
            return false;
          }
        } else {
          // Otherwise this is a computed property name. BigInt keys are parsed
          // as (synthetic) computed property names, too.
          MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));
          MOZ_ASSERT(hasKeyOnStack);

          if (!emit2(JSOp::Pick, emitted + 1)) {
            //      [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 (!emitElemOpBase(JSOp::GetElem)) {
          //        [stack] ... SET? RHS LREF* PROP
          return false;
        }
      }
    }

    if (pndefault) {
      if (!emitDefault(pndefault, lhs)) {
        //          [stack] ... SET? RHS LREF* VALUE
        return false;
      }
    }

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

  return true;
}

static bool IsDestructuringRestExclusionSetObjLiteralCompatible(
    ListNode* pattern) {
  uint32_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 > SharedPropMap::MaxPropsForNonDictionary) {
    // 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;
    }
  }

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

    TaggedParserAtomIndex pnatom;
    if (member->isKind(ParseNodeKind::MutateProto)) {
      pnatom = TaggedParserAtomIndex::WellKnown::proto_();
    } else {
      ParseNode* key = member->as<BinaryNode>().left();
      if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
          key->isKind(ParseNodeKind::StringExpr)) {
        pnatom = key->as<NameNode>().atom();
      } else if (key->isKind(ParseNodeKind::NumberExpr)) {
        if (!emitNumberOp(key->as<NumericLiteral>().value())) {
          return false;
        }
      } else {
        // Otherwise this is a computed property name which needs to be added
        // dynamically. BigInt keys are parsed as (synthetic) computed property
        // names, too.
        MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));
        continue;
      }
    }

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

    if (!pnatom) {
      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() ==
                        TaggedParserAtomIndex::WellKnown::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 (!emitStringOp(JSOp::String,
                      TaggedParserAtomIndex::WellKnown::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;
  }

  auto nameAtom = 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;
    }
  }

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
  if (declList->isKind(ParseNodeKind::UsingDecl)) {
    if (!innermostEmitterScope()->prepareForDisposableAssignment(
            UsingHint::Sync)) {
      //            [stack] ENV? V
      return false;
    }
  } else if (declList->isKind(ParseNodeKind::AwaitUsingDecl)) {
    if (!innermostEmitterScope()->prepareForDisposableAssignment(
            UsingHint::Async)) {
      //            [stack] ENV? V
      return false;
    }
  }
#endif

  if (!noe.emitAssignment()) {
    //              [stack] V
    return false;
  }

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

  return true;
}

bool BytecodeEmitter::emitAssignmentRhs(
    ParseNode* rhs, TaggedParserAtomIndex 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 JSOp::BindUnqualifiedName or
// JSOp::BindUnqualifiedGName, 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 emitPickN(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;
    case ParseNodeKind::CoalesceAssignExpr:
    case ParseNodeKind::OrAssignExpr:
    case ParseNodeKind::AndAssignExpr:
      // Short-circuit assignment operators are handled elsewhere.
      [[fallthrough]];
    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;

  // We estimate the number of properties this could create
  // if used as constructor merely by counting this.foo = assignment
  // or init expressions;
  //
  // This currently doesn't handle this[x] = foo;
  if (isInit || kind == ParseNodeKind::AssignExpr) {
    if (lhs->isKind(ParseNodeKind::DotExpr)) {
      if (lhs->as<PropertyAccess>().expression().isKind(
              ParseNodeKind::ThisExpr)) {
        propertyAdditionEstimate++;
      }
    }
  }

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

  // |name| is used within NameOpEmitter, so its lifetime must surpass |noe|.
  TaggedParserAtomIndex name;

  Maybe<NameOpEmitter> noe;
  Maybe<PropOpEmitter> poe;
  Maybe<ElemOpEmitter> eoe;
  Maybe<PrivateOpEmitter> xoe;

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

  // Purpose of anonFunctionName:
  //
  // In normal name assignments (`f = function(){}`), an anonymous function gets
  // an inferred name based on the left-hand side name node.
  //
  // 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.
  TaggedParserAtomIndex anonFunctionName;

  switch (lhs->getKind()) {
    case ParseNodeKind::Name: {
      name = lhs->as<NameNode>().name();
      anonFunctionName = name;
      noe.emplace(this, name,
                  isCompound ? NameOpEmitter::Kind::CompoundAssignment
                             : NameOpEmitter::Kind::SimpleAssignment);
      break;
    }
    case ParseNodeKind::ArgumentsLength:
    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();
      MOZ_ASSERT(!elem->key().isKind(ParseNodeKind::PrivateName));
      eoe.emplace(this,
                  isCompound ? ElemOpEmitter::Kind::CompoundAssignment
                  : isInit   ? ElemOpEmitter::Kind::PropInit
                             : ElemOpEmitter::Kind::SimpleAssignment,
                  isSuper ? ElemOpEmitter::ObjKind::Super
                          : ElemOpEmitter::ObjKind::Other);
      if (!emitElemObjAndKey(elem, *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::PrivateMemberExpr: {
      PrivateMemberAccess* privateExpr = &lhs->as<PrivateMemberAccess>();
      xoe.emplace(this,
                  isCompound ? PrivateOpEmitter::Kind::CompoundAssignment
                  : isInit   ? PrivateOpEmitter::Kind::PropInit
                             : PrivateOpEmitter::Kind::SimpleAssignment,
                  privateExpr->privateName().name());
      if (!emitTree(&privateExpr->expression())) {
        //          [stack] OBJ
        return false;
      }
      if (!xoe->emitReference()) {
        //          [stack] OBJ KEY
        return false;
      }
      offset += xoe->numReferenceSlots();
      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::ArgumentsLength:
      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::PrivateMemberExpr: {
        if (!xoe->emitGet()) {
          //        [stack] OBJ KEY VALUE
          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::Name:
      if (!noe->prepareForRhs()) {
        //          [stack] ENV? VAL?
        return false;
      }
      offset += noe->emittedBindOp();
      break;
    case ParseNodeKind::ArgumentsLength:
    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;
    case ParseNodeKind::PrivateMemberExpr:
      // no stack adjustment needed
      break;
    default:
      break;
  }

  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 hint for the decompiler. */
  if (isCompound) {
    if (!emit1(compoundOp)) {
      //            [stack] ... VAL
      return false;
    }
    if (!emit1(JSOp::NopIsAssignOp)) {
      //            [stack] ... VAL
      return false;
    }
  }

  /* Finally, emit the specialized assignment bytecode. */
  switch (lhs->getKind()) {
    case ParseNodeKind::Name: {
      if (!noe->emitAssignment()) {
        //          [stack] VAL
        return false;
      }
      break;
    }
    case ParseNodeKind::ArgumentsLength:
    case ParseNodeKind::DotExpr: {
      PropertyAccess* prop = &lhs->as<PropertyAccess>();
      if (!poe->emitAssignment(prop->key().atom())) {
        //          [stack] VAL
        return false;
      }
      break;
    }
    case ParseNodeKind::CallExpr:
      // We threw above, so nothing to do here.
      break;
    case ParseNodeKind::ElemExpr: {
      if (!eoe->emitAssignment()) {
        //          [stack] VAL
        return false;
      }
      break;
    }
    case ParseNodeKind::PrivateMemberExpr:
      if (!xoe->emitAssignment()) {
        //          [stack] VAL
        return false;
      }
      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::emitShortCircuitAssignment(AssignmentNode* node) {
  TDZCheckCache tdzCache(this);

  JSOp op;
  switch (node->getKind()) {
    case ParseNodeKind::CoalesceAssignExpr:
      op = JSOp::Coalesce;
      break;
    case ParseNodeKind::OrAssignExpr:
      op = JSOp::Or;
      break;
    case ParseNodeKind::AndAssignExpr:
      op = JSOp::And;
      break;
    default:
      MOZ_CRASH("Unexpected ParseNodeKind");
  }

  ParseNode* lhs = node->left();
  ParseNode* rhs = node->right();

  // |name| is used within NameOpEmitter, so its lifetime must surpass |noe|.
  TaggedParserAtomIndex name;

  // Select the appropriate emitter based on the left-hand side.
  Maybe<NameOpEmitter> noe;
  Maybe<PropOpEmitter> poe;
  Maybe<ElemOpEmitter> eoe;
  Maybe<PrivateOpEmitter> xoe;

  int32_t depth = bytecodeSection().stackDepth();

  // Number of values pushed onto the stack in addition to the lhs value.
  int32_t numPushed;

  // Evaluate the left-hand side expression and compute any stack values needed
  // for the assignment.
  switch (lhs->getKind()) {
    case ParseNodeKind::Name: {
      name = lhs->as<NameNode>().name();
      noe.emplace(this, name, NameOpEmitter::Kind::CompoundAssignment);

      if (!noe->prepareForRhs()) {
        //          [stack] ENV? LHS
        return false;
      }

      numPushed = noe->emittedBindOp();
      break;
    }
    case ParseNodeKind::ArgumentsLength:
    case ParseNodeKind::DotExpr: {
      PropertyAccess* prop = &lhs->as<PropertyAccess>();
      bool isSuper = prop->isSuper();

      poe.emplace(this, PropOpEmitter::Kind::CompoundAssignment,
                  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;
        }
      } else {
        if (!emitTree(&prop->expression())) {
          //        [stack] OBJ
          return false;
        }
      }

      if (!poe->emitGet(prop->key().atom())) {
        //          [stack] # if Super
        //          [stack] THIS SUPERBASE LHS
        //          [stack] # otherwise
        //          [stack] OBJ LHS
        return false;
      }

      if (!poe->prepareForRhs()) {
        //          [stack] # if Super
        //          [stack] THIS SUPERBASE LHS
        //          [stack] # otherwise
        //          [stack] OBJ LHS
        return false;
      }

      numPushed = 1 + isSuper;
      break;
    }

    case ParseNodeKind::ElemExpr: {
      PropertyByValue* elem = &lhs->as<PropertyByValue>();
      bool isSuper = elem->isSuper();
      MOZ_ASSERT(!elem->key().isKind(ParseNodeKind::PrivateName));
      eoe.emplace(this, ElemOpEmitter::Kind::CompoundAssignment,
                  isSuper ? ElemOpEmitter::ObjKind::Super
                          : ElemOpEmitter::ObjKind::Other);

      if (!emitElemObjAndKey(elem, *eoe)) {
        //          [stack] # if Super
        //          [stack] THIS KEY
        //          [stack] # otherwise
        //          [stack] OBJ KEY
        return false;
      }

      if (!eoe->emitGet()) {
        //          [stack] # if Super
        //          [stack] THIS KEY SUPERBASE LHS
        //          [stack] # otherwise
        //          [stack] OBJ KEY LHS
        return false;
      }

      if (!eoe->prepareForRhs()) {
        //          [stack] # if Super
        //          [stack] THIS KEY SUPERBASE LHS
        //          [stack] # otherwise
        //          [stack] OBJ KEY LHS
        return false;
      }

      numPushed = 2 + isSuper;
      break;
    }

    case ParseNodeKind::PrivateMemberExpr: {
      PrivateMemberAccess* privateExpr = &lhs->as<PrivateMemberAccess>();
      xoe.emplace(this, PrivateOpEmitter::Kind::CompoundAssignment,
                  privateExpr->privateName().name());
      if (!emitTree(&privateExpr->expression())) {
        //          [stack] OBJ
        return false;
      }
      if (!xoe->emitReference()) {
        //          [stack] OBJ NAME
        return false;
      }
      if (!xoe->emitGet()) {
        //          [stack] OBJ NAME LHS
        return false;
      }
      numPushed = xoe->numReferenceSlots();
      break;
    }

    default:
      MOZ_CRASH();
  }

  MOZ_ASSERT(bytecodeSection().stackDepth() == depth + numPushed + 1);

  // Test for the short-circuit condition.
  JumpList jump;
  if (!emitJump(op, &jump)) {
    //              [stack] ... LHS
    return false;
  }

  // The short-circuit condition wasn't fulfilled, pop the left-hand side value
  // which was kept on the stack.
  if (!emit1(JSOp::Pop)) {
    //              [stack] ...
    return false;
  }

  if (!emitAssignmentRhs(rhs, name)) {
    //              [stack] ... RHS
    return false;
  }

  // Perform the actual assignment.
  switch (lhs->getKind()) {
    case ParseNodeKind::Name: {
      if (!noe->emitAssignment()) {
        //          [stack] RHS
        return false;
      }
      break;
    }
    case ParseNodeKind::ArgumentsLength:
    case ParseNodeKind::DotExpr: {
      PropertyAccess* prop = &lhs->as<PropertyAccess>();

      if (!poe->emitAssignment(prop->key().atom())) {
        //          [stack] RHS
        return false;
      }
      break;
    }

    case ParseNodeKind::ElemExpr: {
      if (!eoe->emitAssignment()) {
        //          [stack] RHS
        return false;
      }
      break;
    }

    case ParseNodeKind::PrivateMemberExpr:
      if (!xoe->emitAssignment()) {
        //          [stack] RHS
        return false;
      }
      break;

    default:
      MOZ_CRASH();
  }

  MOZ_ASSERT(bytecodeSection().stackDepth() == depth + 1);

  // Join with the short-circuit jump and pop anything left on the stack.
  if (numPushed > 0) {
    JumpList jumpAroundPop;
    if (!emitJump(JSOp::Goto, &jumpAroundPop)) {
      //            [stack] RHS
      return false;
    }

    if (!emitJumpTargetAndPatch(jump)) {
      //            [stack] ... LHS
      return false;
    }

    // Reconstruct the stack depth after the jump.
    bytecodeSection().setStackDepth(depth + 1 + numPushed);

    // Move the left-hand side value to the bottom and pop the rest.
    if (!emitUnpickN(numPushed)) {
      //            [stack] LHS ...
      return false;
    }
    if (!emitPopN(numPushed)) {
      //            [stack] LHS
      return false;
    }

    if (!emitJumpTargetAndPatch(jumpAroundPop)) {
      //            [stack] LHS | RHS
      return false;
    }
  } else {
    if (!emitJumpTargetAndPatch(jump)) {
      //            [stack] LHS | RHS
      return false;
    }
  }

  MOZ_ASSERT(bytecodeSection().stackDepth() == depth + 1);

  return true;
}

bool BytecodeEmitter::emitCallSiteObjectArray(ObjLiteralWriter& writer,
                                              ListNode* cookedOrRaw,
                                              ParseNode* head, uint32_t count) {
  DebugOnly<size_t> idx = 0;
  for (ParseNode* pn : cookedOrRaw->contentsFrom(head)) {
    MOZ_ASSERT(pn->isKind(ParseNodeKind::TemplateStringExpr) ||
               pn->isKind(ParseNodeKind::RawUndefinedExpr));

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

  return true;
}

bool BytecodeEmitter::emitCallSiteObject(CallSiteNode* callSiteObj) {
  constexpr JSOp op = JSOp::CallSiteObj;

  // The first element of a call-site node is the raw-values list. Skip over it.
  ListNode* raw = callSiteObj->rawNodes();
  MOZ_ASSERT(raw->isKind(ParseNodeKind::ArrayExpr));
  ParseNode* head = callSiteObj->head()->pn_next;

  uint32_t count = callSiteObj->count() - 1;
  MOZ_ASSERT(count == raw->count());

  ObjLiteralWriter writer;
  writer.beginCallSiteObj(op);
  writer.beginDenseArrayElements();

  // Write elements of the two arrays: the 'cooked' values followed by the
  // 'raw' values.
  MOZ_RELEASE_ASSERT(count < UINT32_MAX / 2,
                     "Number of elements for both arrays must fit in uint32_t");
  if (!emitCallSiteObjectArray(writer, callSiteObj, head, count)) {
    return false;
  }
  if (!emitCallSiteObjectArray(writer, raw, raw->head(), count)) {
    return false;
  }

  GCThingIndex cookedIndex;
  if (!addObjLiteralData(writer, &cookedIndex)) {
    return false;
  }

  MOZ_ASSERT(sc->hasCallSiteObj());

  return emitInternedObjectOp(cookedIndex, op);
}

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(¶m->as<ListNode>(),
                                  DestructuringFlavor::Declaration)) {
          return false;
        }
        if (!emit1(JSOp::Pop)) {
          return false;
        }
        break;

      case ParseNodeKind::Name:
        if (!emitLexicalInitialization(¶m->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:
    //   goto <finally>
    //   [jump target for returning from 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;
}

[[nodiscard]] bool BytecodeEmitter::emitJumpToFinally(JumpList* jump,
                                                      uint32_t idx) {
  // Push the continuation index.
  if (!emitNumberOp(idx)) {
    return false;
  }

  // Push |exception_stack|.
  if (!emit1(JSOp::Null)) {
    return false;
  }

  // Push |throwing|.
  if (!emit1(JSOp::False)) {
    return false;
  }

  // Jump to the finally block.
  if (!emitJumpNoFallthrough(JSOp::Goto, jump)) {
    return false;
  }

  return true;
}

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

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

if_again:
  ParseNode* testNode = ifNode->kid1();
  auto conditionKind = IfEmitter::ConditionKind::Positive;
  if (testNode->isKind(ParseNodeKind::NotExpr)) {
    testNode = testNode->as<UnaryNode>().kid();
    conditionKind = IfEmitter::ConditionKind::Negative;
  }

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

  // Emit code for the condition before pushing stmtInfo.
  // NOTE: NotExpr of testNode may be unwrapped, and in that case the negation
  //       is handled by conditionKind.
  if (!emitTree(testNode)) {
    return false;
  }

  ParseNode* elseNode = ifNode->kid3();
  if (elseNode) {
    if (!ifThenElse.emitThenElse(conditionKind)) {
      return false;
    }
  } else {
    if (!ifThenElse.emitThen(conditionKind)) {
      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();
  }

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
  BlockKind blockKind = BlockKind::Other;
  if (body->isKind(ParseNodeKind::ForStmt) &&
      body->as<ForNode>().head()->isKind(ParseNodeKind::ForOf)) {
    MOZ_ASSERT(kind == ScopeKind::Lexical);
    blockKind = BlockKind::ForOf;
  }
#endif

  if (!lse.emitScope(kind, lexicalScope->scopeBindings()
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
                               ,
                     blockKind
#endif
                     )) {
    return false;
  }

  if (body->isKind(ParseNodeKind::ForStmt)) {
    // for loops need to emit JSOp::FreshenLexicalEnv/JSOp::RecreateLexicalEnv
    // 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,
                    TaggedParserAtomIndex::WellKnown::CopyDataProperties())) {
      //            [stack] TARGET SOURCE SET COPYDATAPROPERTIES
      return false;
    }
  } else {
    MOZ_ASSERT(depth > 1);
    //              [stack] TARGET SOURCE
    argc = 2;

    if (!emitAtomOp(
            JSOp::GetIntrinsic,
            TaggedParserAtomIndex::WellKnown::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;
    }
  }
  // Callee is always self-hosted instrinsic, and cannot be content function.
  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) {
  GCThingIndex index;
  if (!perScriptData().gcThingList().append(bigint, &index)) {
    return false;
  }
  return emitGCIndexOp(JSOp::BigInt, index);
}

bool BytecodeEmitter::emitIterable(ParseNode* value,
                                   SelfHostedIter selfHostedIter,
                                   IteratorKind iterKind) {
  MOZ_ASSERT(getSelfHostedIterFor(value) == selfHostedIter);

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

  switch (selfHostedIter) {
    case SelfHostedIter::Deny:
    case SelfHostedIter::AllowContent:
      //            [stack] ITERABLE
      return true;

    case SelfHostedIter::AllowContentWith: {
      // This is the following case:
      //
      //   for (const nextValue of allowContentIterWith(items, usingIterator)) {
      //
      // `items` is emitted by `emitTree(value)` above, and the result is on the
      // stack as ITERABLE.
      // `usingIterator` is the value of `items[Symbol.iterator]`, that's
      // already retrieved.
      ListNode* argsList = value->as<CallNode>().args();
      MOZ_ASSERT_IF(iterKind == IteratorKind::Sync, argsList->count() == 2);
      MOZ_ASSERT_IF(iterKind == IteratorKind::Async, argsList->count() == 3);

      if (!emitTree(argsList->head()->pn_next)) {
        //          [stack] ITERABLE ITERFN
        return false;
      }

      // Async iterator has two possible iterators: An async iterator and a sync
      // iterator.
      if (iterKind == IteratorKind::Async) {
        if (!emitTree(argsList->head()->pn_next->pn_next)) {
          //        [stack] ITERABLE ASYNC_ITERFN SYNC_ITERFN
          return false;
        }
      }

      //            [stack] ITERABLE ASYNC_ITERFN? SYNC_ITERFN
      return true;
    }

    case SelfHostedIter::AllowContentWithNext: {
      // This is the following case:
      //
      //   for (const nextValue of allowContentIterWithNext(iterator, next)) {
      //
      // `iterator` is emitted by `emitTree(value)` above, and the result is on
      // the stack as ITER.
      // `next` is the value of `iterator.next`, that's already retrieved.
      ListNode* argsList = value->as<CallNode>().args();
      MOZ_ASSERT(argsList->count() == 2);

      if (!emitTree(argsList->head()->pn_next)) {
        //          [stack] ITER NEXT
        return false;
      }

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

      //            [stack] NEXT ITER
      return true;
    }
  }

  MOZ_CRASH("invalid self-hosted iteration kind");
}

bool BytecodeEmitter::emitIterator(SelfHostedIter selfHostedIter) {
  MOZ_ASSERT(selfHostedIter != SelfHostedIter::Deny ||
                 emitterMode != BytecodeEmitter::SelfHosting,
             "[Symbol.iterator]() call is prohibited in self-hosted code "
             "because it can run user-modifiable iteration code");

  if (selfHostedIter == SelfHostedIter::AllowContentWithNext) {
    //              [stack] NEXT ITER

    // Nothing to do, stack already contains the iterator and its `next` method.
    return true;
  }

  if (selfHostedIter != SelfHostedIter::AllowContentWith) {
    //              [stack] OBJ

    // 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::GetElem)) {
      //            [stack] OBJ ITERFN
      return false;
    }
  }

  if (!emit1(JSOp::Swap)) {
    //              [stack] ITERFN OBJ
    return false;
  }
  if (!emitCall(getIterCallOp(JSOp::CallIter, selfHostedIter), 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, TaggedParserAtomIndex::WellKnown::next())) {
    //              [stack] ITER NEXT
    return false;
  }
  if (!emit1(JSOp::Swap)) {
    //              [stack] NEXT ITER
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitAsyncIterator(SelfHostedIter selfHostedIter) {
  MOZ_ASSERT(selfHostedIter != SelfHostedIter::AllowContentWithNext);
  MOZ_ASSERT(selfHostedIter != SelfHostedIter::Deny ||
                 emitterMode != BytecodeEmitter::SelfHosting,
             "[Symbol.asyncIterator]() call is prohibited in self-hosted code "
             "because it can run user-modifiable iteration code");

  if (selfHostedIter != SelfHostedIter::AllowContentWith) {
    //              [stack] OBJ

    // 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::GetElem)) {
      //            [stack] OBJ ASYNC_ITERFN
      return false;
    }
  } else {
    //              [stack] OBJ ASYNC_ITERFN SYNC_ITERFN

    if (!emitElemOpBase(JSOp::Swap)) {
      //            [stack] OBJ SYNC_ITERFN ASYNC_ITERFN
      return false;
    }
  }

  InternalIfEmitter ifAsyncIterIsUndefined(this);
  if (!emit1(JSOp::IsNullOrUndefined)) {
    //              [stack] OBJ SYNC_ITERFN? ASYNC_ITERFN NULL-OR-UNDEF
    return false;
  }
  if (!ifAsyncIterIsUndefined.emitThenElse()) {
    //              [stack] OBJ SYNC_ITERFN? ASYNC_ITERFN
    return false;
  }

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

  if (selfHostedIter != SelfHostedIter::AllowContentWith) {
    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::GetElem)) {
      //            [stack] OBJ SYNC_ITERFN
      return false;
    }
  } else {
    //              [stack] OBJ SYNC_ITERFN
  }

  if (!emit1(JSOp::Swap)) {
    //              [stack] SYNC_ITERFN OBJ
    return false;
  }
  if (!emitCall(getIterCallOp(JSOp::CallIter, selfHostedIter), 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, TaggedParserAtomIndex::WellKnown::next())) {
    //              [stack] ITER SYNCNEXT
    return false;
  }

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

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

  if (selfHostedIter == SelfHostedIter::AllowContentWith) {
    if (!emit1(JSOp::Swap)) {
      //            [stack] OBJ ASYNC_ITERFN SYNC_ITERFN
      return false;
    }
    if (!emit1(JSOp::Pop)) {
      //            [stack] OBJ ASYNC_ITERFN
      return false;
    }
  }

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

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

  if (!emit1(JSOp::Dup)) {
    //              [stack] ITER ITER
    return false;
  }
  if (!emitAtomOp(JSOp::GetProp, TaggedParserAtomIndex::WellKnown::next())) {
    //              [stack] ITER NEXT
    return false;
  }
  if (!emit1(JSOp::Swap)) {
    //              [stack] NEXT ITER
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitSpread(SelfHostedIter selfHostedIter) {
  // [stack] NEXT ITER ARR I
  return emitSpread(selfHostedIter, 2, JSOp::InitElemInc);
  // [stack] ARR FINAL_INDEX
}

bool BytecodeEmitter::emitSpread(SelfHostedIter selfHostedIter,
                                 int spreadeeStackItems, JSOp storeElementOp) {
  LoopControl loopInfo(this, StatementKind::Spread);
  // In the [stack] annotations, (spreadee) can be "ARR I" (when spreading
  // into an array or into call parameters, or "TUPLE" (when spreading into a
  // tuple)

  if (!loopInfo.emitLoopHead(this, Nothing())) {
    //              [stack] NEXT ITER (spreadee)
    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(spreadeeStackItems + 1, 2)) {
      //            [stack] NEXT ITER (spreadee) NEXT ITER
      return false;
    }
    if (!emitIteratorNext(Nothing(), IteratorKind::Sync, selfHostedIter)) {
      //            [stack] NEXT ITER (spreadee) RESULT
      return false;
    }
    if (!emit1(JSOp::Dup)) {
      //            [stack] NEXT ITER (spreadee) RESULT RESULT
      return false;
    }
    if (!emitAtomOp(JSOp::GetProp, TaggedParserAtomIndex::WellKnown::done())) {
      //            [stack] NEXT ITER (spreadee) RESULT DONE
      return false;
    }
    if (!emitJump(JSOp::JumpIfTrue, &loopInfo.breaks)) {
      //            [stack] NEXT ITER (spreadee) RESULT
      return false;
    }

    // Emit code to assign result.value to the iteration variable.
    if (!emitAtomOp(JSOp::GetProp, TaggedParserAtomIndex::WellKnown::value())) {
      //            [stack] NEXT ITER (spreadee) VALUE
      return false;
    }
    if (!emit1(storeElementOp)) {
      //            [stack] NEXT ITER (spreadee)
      return false;
    }

    if (!loopInfo.emitLoopEnd(this, JSOp::Goto, TryNoteKind::ForOf)) {
      //            [stack] NEXT ITER (spreadee)
      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, spreadeeStackItems + 2)) {
    //              [stack] ITER (spreadee) RESULT NEXT
    return false;
  }
  if (!emit2(JSOp::Pick, spreadeeStackItems + 2)) {
    //              [stack] (spreadee) RESULT NEXT ITER
    return false;
  }

  return emitPopN(3);
  //                [stack] (spreadee)
}

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 (!target->is<DeclarationListNode>()) {
    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.

  auto* declarationList = &target->as<DeclarationListNode>();
  if (!updateSourceCoordNotes(declarationList->pn_pos.begin)) {
    return false;
  }

  target = declarationList->singleBinding();

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

  if (nameNode) {
    auto nameAtom = 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);
    }

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    if (declarationList->isKind(ParseNodeKind::UsingDecl)) {
      if (!innermostEmitterScope()->prepareForDisposableAssignment(
              UsingHint::Sync)) {
        //            [stack] ENV? V
        return false;
      }
    } else if (declarationList->isKind(ParseNodeKind::AwaitUsingDecl)) {
      if (!innermostEmitterScope()->prepareForDisposableAssignment(
              UsingHint::Async)) {
        //            [stack] ENV? V
        return false;
      }
    }
#endif

    if (!noe.emitAssignment()) {
      return false;
    }

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

  MOZ_ASSERT(
      !target->isKind(ParseNodeKind::AssignExpr),
      "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->isSuspendableContext());
  MOZ_ASSERT_IF(iterKind == IteratorKind::Async,
                sc->asSuspendableContext()->isAsync());

  ParseNode* forHeadExpr = forOfHead->kid3();

  // Certain builtins (e.g. Array.from) are implemented in self-hosting
  // as for-of loops.
  auto selfHostedIter = getSelfHostedIterFor(forHeadExpr);
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
  ForOfEmitter::HeadUsingDeclarationKind headUsingDeclKind =
      ForOfEmitter::HeadUsingDeclarationKind::None;
  if (forOfHead->kid1()->isKind(ParseNodeKind::UsingDecl)) {
    headUsingDeclKind = ForOfEmitter::HeadUsingDeclarationKind::Sync;
  } else if (forOfHead->kid1()->isKind(ParseNodeKind::AwaitUsingDecl)) {
    headUsingDeclKind = ForOfEmitter::HeadUsingDeclarationKind::Async;
  }
#endif

  ForOfEmitter forOf(this, headLexicalEmitterScope, selfHostedIter, iterKind
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
                     ,
                     headUsingDeclKind
#endif
  );

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

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

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

  if (!forOf.emitInitialize(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(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 (forInTarget->is<DeclarationListNode>()) {
    auto* declarationList = &forInTarget->as<DeclarationListNode>();

    ParseNode* decl = declarationList->singleBinding();
    if (decl->isKind(ParseNodeKind::AssignExpr)) {
      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;
        }

        auto nameAtom = 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(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->is<DeclarationListNode>()) {
      MOZ_ASSERT(!init->as<DeclarationListNode>().empty());

      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(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 */) {
  FunctionBox* funbox = funNode->funbox();

  //                [stack]

  FunctionEmitter fe(this, funbox, funNode->syntaxKind(),
                     funNode->functionIsHoisted()
                         ? FunctionEmitter::IsHoisted::Yes
                         : FunctionEmitter::IsHoisted::No);

  // |wasEmittedByEnclosingScript| flag is set to true 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->wasEmittedByEnclosingScript()) {
    if (!fe.emitAgain()) {
      //            [stack]
      return false;
    }
    MOZ_ASSERT(funNode->functionIsHoisted());
  } else if (funbox->isInterpreted()) {
    if (!funbox->emitBytecode) {
      return fe.emitLazy();
      //            [stack] FUN?
    }

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

    BytecodeEmitter bce2(this, funbox);
    if (!bce2.init(funNode->pn_pos)) {
      return false;
    }

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

    if (!fe.emitNonLazyEnd()) {
      //            [stack] FUN?
      return false;
    }
  } else {
    if (!fe.emitAsmJSModule()) {
      //            [stack]
      return false;
    }
  }

  // Track the last emitted top-level self-hosted function, so that intrinsics
  // can adjust attributes at parse time.
  //
  // NOTE: We also disallow lambda functions in the top-level body. This is done
  // to simplify handling of the self-hosted stencil. Within normal function
  // declarations there are no such restrictions.
  if (emitterMode == EmitterMode::SelfHosting) {
    if (sc->isTopLevelContext()) {
      MOZ_ASSERT(!funbox->isLambda());
      MOZ_ASSERT(funbox->explicitName());
      prevSelfHostedTopLevelFunction = funbox;
    }
  }

  return true;
}

bool BytecodeEmitter::emitDo(BinaryNode* doNode) {
  ParseNode* bodyNode = doNode->left();

  DoWhileEmitter doWhile(this);
  if (!doWhile.emitBody(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(whileNode->pn_pos.begin, getOffsetForLoop(condNode),
                   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(TaggedParserAtomIndex 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, GotoKind::Break);
}

bool BytecodeEmitter::emitContinue(TaggedParserAtomIndex 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, GotoKind::Continue);
}

bool BytecodeEmitter::emitGetFunctionThis(NameNode* thisName) {
  MOZ_ASSERT(sc->hasFunctionThisBinding());
  MOZ_ASSERT(thisName->isName(TaggedParserAtomIndex::WellKnown::dot_this_()));

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

  if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_this_())) {
    //              [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);

  MOZ_ASSERT(outermostScope().hasNonSyntacticScopeOnChain() ==
             sc->hasNonSyntacticScope());
  if (sc->hasNonSyntacticScope()) {
    return emit1(JSOp::NonSyntacticGlobalThis);
    //                [stack] THIS
  }

  return emit1(JSOp::GlobalThis);
  //                [stack] THIS
}

bool BytecodeEmitter::emitCheckDerivedClassConstructorReturn() {
  MOZ_ASSERT(
      lookupName(TaggedParserAtomIndex::WellKnown::dot_this_()).hasKnownSlot());
  if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_this_())) {
    return false;
  }
  if (!emit1(JSOp::CheckReturn)) {
    return false;
  }
  if (!emit1(JSOp::SetRval)) {
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitNewTarget() {
  MOZ_ASSERT(sc->allowNewTarget());

  if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_newTarget_())) {
    //              [stack] NEW.TARGET
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitNewTarget(NewTargetNode* pn) {
  MOZ_ASSERT(pn->newTargetName()->isName(
      TaggedParserAtomIndex::WellKnown::dot_newTarget_()));

  return emitNewTarget();
}

bool BytecodeEmitter::emitNewTarget(CallNode* pn) {
  MOZ_ASSERT(pn->callOp() == JSOp::SuperCall ||
             pn->callOp() == JSOp::SpreadSuperCall);

  // The parser is responsible for marking the "new.target" binding as being
  // implicitly used in super() calls.
  return emitNewTarget();
}

bool BytecodeEmitter::emitReturn(UnaryNode* returnNode) {
  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->asSuspendableContext()->isAsync() &&
        sc->asSuspendableContext()->isGenerator()) {
      if (!emitAwaitInInnermostScope()) {
        return false;
      }
    }
  } else {
    /* No explicit return value provided */
    if (!emit1(JSOp::Undefined)) {
      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;
  }

  /*
   * The return value is currently on the stack. We would like to
   * generate JSOp::Return, but if we have work to do before returning,
   * we will instead generate JSOp::SetRval / JSOp::RetRval.
   *
   * We don't know whether we will need fixup code until after calling
   * prepareForNonLocalJumpToOutermost, so we start by generating
   * JSOp::SetRval, then mutate it to JSOp::Return in finishReturn if it
   * wasn't needed.
   */

  BytecodeOffset setRvalOffset = bytecodeSection().offset();
  if (!emit1(JSOp::SetRval)) {
    return false;
  }

  NonLocalExitControl nle(this, NonLocalExitKind::Return);
  return nle.emitReturn(setRvalOffset);
}

bool BytecodeEmitter::finishReturn(BytecodeOffset setRvalOffset) {
  // The return value is currently in rval. Depending on the current function,
  // we may have to do additional work before returning:
  // - Derived class constructors must check if the return value is an object.
  // - Generators and async functions must do a final yield.
  // - Non-async generators must return the value as an iterator result:
  //   { value: <rval>, done: true }
  // - Non-generator async functions must resolve the function's result promise
  //   with the value.
  //
  // If we have not generated any code since the SetRval that stored the return
  // value, we can also optimize the bytecode by rewriting that SetRval as a
  // JSOp::Return. See |emitReturn| above.

  bool isDerivedClassConstructor =
      sc->isFunctionBox() && sc->asFunctionBox()->isDerivedClassConstructor();
  bool needsFinalYield =
      sc->isFunctionBox() && sc->asFunctionBox()->needsFinalYield();
  bool isSimpleReturn =
      setRvalOffset.valid() &&
      setRvalOffset + BytecodeOffsetDiff(JSOpLength_SetRval) ==
          bytecodeSection().offset();

  if (isDerivedClassConstructor) {
    MOZ_ASSERT(!needsFinalYield);
    if (!emitJump(JSOp::Goto, &endOfDerivedClassConstructorBody)) {
      return false;
    }
    return true;
  }

  if (needsFinalYield) {
    if (!emitJump(JSOp::Goto, &finalYields)) {
      return false;
    }
    return true;
  }

  if (isSimpleReturn) {
    MOZ_ASSERT(JSOp(bytecodeSection().code()[setRvalOffset.value()]) ==
               JSOp::SetRval);
    bytecodeSection().code()[setRvalOffset.value()] = jsbytecode(JSOp::Return);
    return true;
  }

  // Nothing special needs to be done.
  return emitReturnRval();
}

bool BytecodeEmitter::emitGetDotGeneratorInScope(EmitterScope& currentScope) {
  if (!sc->isFunction() && sc->isModuleContext() &&
      sc->asModuleContext()->isAsync()) {
    NameLocation loc = *locationOfNameBoundInScopeType<ModuleScope>(
        TaggedParserAtomIndex::WellKnown::dot_generator_(), ¤tScope);
    return emitGetNameAtLocation(
        TaggedParserAtomIndex::WellKnown::dot_generator_(), loc);
  }
  NameLocation loc = *locationOfNameBoundInScopeType<FunctionScope>(
      TaggedParserAtomIndex::WellKnown::dot_generator_(), ¤tScope);
  return emitGetNameAtLocation(
      TaggedParserAtomIndex::WellKnown::dot_generator_(), 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;
    }
  }

  if (sc->asSuspendableContext()->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->isSuspendableContext());
  MOZ_ASSERT(awaitNode->isKind(ParseNodeKind::AwaitExpr));

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

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

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

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

  if (sc->asSuspendableContext()->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(getSelfHostedIterFor(iter) == SelfHostedIter::Deny,
             "yield* is prohibited in self-hosted code because it can run "
             "user-modifiable iteration code");

  MOZ_ASSERT(sc->isSuspendableContext());
  MOZ_ASSERT(sc->asSuspendableContext()->isGenerator());

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

  // Steps 2-5.
  if (!emitTree(iter)) {
    //              [stack] ITERABLE
    return false;
  }
  if (iterKind == IteratorKind::Async) {
    if (!emitAsyncIterator(SelfHostedIter::Deny)) {
      //            [stack] NEXT ITER
      return false;
    }
  } else {
    if (!emitIterator(SelfHostedIter::Deny)) {
      //            [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::GetProp,
                    TaggedParserAtomIndex::WellKnown::throw_())) {
      //            [stack] NEXT ITER RECEIVED ITER THROW
      return false;
    }

    // Step 7.b.ii.
    InternalIfEmitter ifThrowMethodIsNotDefined(this);
    if (!emit1(JSOp::IsNullOrUndefined)) {
      //            [stack] NEXT ITER RECEIVED ITER THROW NULL-OR-UNDEF
      return false;
    }

    if (!ifThrowMethodIsNotDefined.emitThenElse(
            IfEmitter::ConditionKind::Negative)) {
      //            [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, CompletionKind::Normal)) {
      //            [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)) {
      //            [stack] NEXT ITER RECEIVED
      return false;
    }

    // Step 7.c.i.
    //
    // Call iterator.return() for receiving a "forced return" completion from
    // the generator.

    // Step 7.c.ii.
    //
    // Get the "return" method.
    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::GetProp,
                    TaggedParserAtomIndex::WellKnown::return_())) {
      //            [stack] NEXT ITER RECEIVED ITER RET
      return false;
    }

    // Step 7.c.iii.
    //
    // Do nothing if "return" is undefined or null.
    InternalIfEmitter ifReturnMethodIsDefined(this);
    if (!emit1(JSOp::IsNullOrUndefined)) {
      //            [stack] NEXT ITER RECEIVED ITER RET NULL-OR-UNDEF
      return false;
    }

    // Step 7.c.iv.
    //
    // Call "return" with the argument passed to Generator.prototype.return.
    if (!ifReturnMethodIsDefined.emitThenElse(
            IfEmitter::ConditionKind::Negative)) {
      //            [stack] NEXT ITER RECEIVED ITER RET
      return false;
    }
    if (!emit1(JSOp::Swap)) {
      //            [stack] NEXT ITER RECEIVED RET ITER
      return false;
    }
    if (!emit2(JSOp::Pick, 2)) {
      //            [stack] NEXT ITER RET ITER RECEIVED
      return false;
    }
    if (needsIteratorResult) {
      if (!emitAtomOp(JSOp::GetProp,
                      TaggedParserAtomIndex::WellKnown::value())) {
        //          [stack] NEXT ITER RET ITER VAL
        return false;
      }
    }
    if (!emitCall(JSOp::Call, 1)) {
      //            [stack] NEXT ITER RESULT
      return false;
    }

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

    // Step 7.c.vi.
    if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) {
      //            [stack] NEXT ITER RESULT
      return false;
    }

    // Check if the returned object from iterator.return() is done. If not,
    // continue yielding.

    // Steps 7.c.vii-viii.
    InternalIfEmitter ifReturnDone(this);
    if (!emit1(JSOp::Dup)) {
      //            [stack] NEXT ITER RESULT RESULT
      return false;
    }
    if (!emitAtomOp(JSOp::GetProp, TaggedParserAtomIndex::WellKnown::done())) {
      //            [stack] NEXT ITER RESULT DONE
      return false;
    }
    if (!ifReturnDone.emitThenElse()) {
      //            [stack] NEXT ITER RESULT
      return false;
    }

    // Step 7.c.viii.1.
    if (!emitAtomOp(JSOp::GetProp, TaggedParserAtomIndex::WellKnown::value())) {
      //            [stack] NEXT ITER VALUE
      return false;
    }
    if (needsIteratorResult) {
      if (!emitPrepareIteratorResult()) {
        //          [stack] NEXT ITER VALUE RESULT
        return false;
      }
      if (!emit1(JSOp::Swap)) {
        //          [stack] NEXT ITER RESULT VALUE
        return false;
      }
      if (!emitFinishIteratorResult(true)) {
        //          [stack] NEXT ITER RESULT
        return false;
      }
    }

    if (!ifReturnDone.emitElse()) {
      //            [stack] NEXT ITER RESULT
      return false;
    }

    // Jump to continue label for steps 7.c.ix-x.
    if (!emitJump(JSOp::Goto, &loopInfo.continues)) {
      //            [stack] NEXT ITER RESULT
      return false;
    }

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

    // Step 7.c.iii.
    if (!ifReturnMethodIsDefined.emitElse()) {
      //            [stack] NEXT ITER RECEIVED ITER RET
      return false;
    }
    if (!emitPopN(2)) {
      //            [stack] NEXT ITER RECEIVED
      return false;
    }
    if (iterKind == IteratorKind::Async) {
      // Step 7.c.iii.1.
      if (!emitAwaitInInnermostScope()) {
        //          [stack] NEXT ITER RECEIVED
        return false;
      }
    }
    if (!ifReturnMethodIsDefined.emitEnd()) {
      //            [stack] NEXT ITER RECEIVED
      return false;
    }

    // Perform a "forced generator return".
    //
    // Step 7.c.iii.2.
    // Step 7.c.viii.2.
    if (!emitGetDotGeneratorInInnermostScope()) {
      //            [stack] NEXT ITER RESULT GENOBJ
      return false;
    }
    if (!emitPushResumeKind(GeneratorResumeKind::Return)) {
      //            [stack] NEXT ITER RESULT GENOBJ RESUMEKIND
      return false;
    }
    if (!emit1(JSOp::CheckResumeKind)) {
      //            [stack] NEXT ITER RESULT GENOBJ RESUMEKIND
      return false;
    }
  }

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

  // Shared tail for Normal/Throw completions.
  //
  // Steps 7.a.iv-v.
  // Steps 7.b.ii.5-6.
  //
  //                [stack] NEXT ITER RESULT

  // if (result.done) break;
  if (!emit1(JSOp::Dup)) {
    //              [stack] NEXT ITER RESULT RESULT
    return false;
  }
  if (!emitAtomOp(JSOp::GetProp, TaggedParserAtomIndex::WellKnown::done())) {
    //              [stack] NEXT ITER RESULT DONE
    return false;
  }
  if (!emitJump(JSOp::JumpIfTrue, &loopInfo.breaks)) {
    //              [stack] NEXT ITER RESULT
    return false;
  }

  // Steps 7.a.vi-vii.
  // Steps 7.b.ii.7-8.
  // Steps 7.c.ix-x.
  if (!loopInfo.emitContinueTarget(this)) {
    //              [stack] NEXT ITER RESULT
    return false;
  }
  if (iterKind == IteratorKind::Async) {
    if (!emitAtomOp(JSOp::GetProp, TaggedParserAtomIndex::WellKnown::value())) {
      //            [stack] NEXT ITER RESULT
      return false;
    }
  }
  if (!emitGetDotGeneratorInInnermostScope()) {
    //              [stack] NEXT ITER RESULT GENOBJ
    return false;
  }
  if (!emitYieldOp(JSOp::Yield)) {
    //              [stack] NEXT ITER RVAL GENOBJ RESUMEKIND
    return false;
  }
  if (!emit1(JSOp::Swap)) {
    //              [stack] NEXT ITER RVAL RESUMEKIND GENOBJ
    return false;
  }
  if (!emit1(JSOp::Pop)) {
    //              [stack] NEXT ITER RVAL RESUMEKIND
    return false;
  }
  if (!loopInfo.emitLoopEnd(this, JSOp::Goto, TryNoteKind::Loop)) {
    //              [stack] NEXT ITER RVAL RESUMEKIND
    return false;
  }

  // Jumps to this point have 3 (instead of 4) values on the stack.
  MOZ_ASSERT(bytecodeSection().stackDepth() == startDepth);
  bytecodeSection().setStackDepth(startDepth - 1);

  //                [stack] NEXT ITER RESULT

  // Step 7.a.v.1.
  // Step 7.b.ii.6.a.
  //
  // result.value
  if (!emit2(JSOp::Unpick, 2)) {
    //              [stack] RESULT NEXT ITER
    return false;
  }
  if (!emitPopN(2)) {
    //              [stack] RESULT
    return false;
  }
  if (!emitAtomOp(JSOp::GetProp, TaggedParserAtomIndex::WellKnown::value())) {
    //              [stack] VALUE
    return false;
  }

  MOZ_ASSERT(bytecodeSection().stackDepth() == startDepth - 3);

  return true;
}

bool BytecodeEmitter::emitStatementList(ListNode* stmtList) {
  for (ParseNode* stmt : stmtList->contents()) {
    if (!emitTree(stmt)) {
      return false;
    }
  }
  return true;
}

bool BytecodeEmitter::emitExpressionStatement(UnaryNode* exprStmt) {
  MOZ_ASSERT(exprStmt->isKind(ParseNodeKind::ExpressionStmt));

  /*
   * Top-level or called-from-a-native JS_Execute/EvaluateScript,
   * debugger, and eval frames may need the value of the ultimate
   * expression statement as the script's result, despite the fact
   * that it appears useless to the compiler.
   *
   * API users may also set the ReadOnlyCompileOptions::noScriptRval option when
   * calling JS_Compile* to suppress JSOp::SetRval.
   */

  bool wantval = false;
  bool useful = false;
  if (sc->isTopLevelContext()) {
    useful = wantval = !sc->noScriptRval();
  }

  /* Don't eliminate expressions with side effects. */
  ParseNode* expr = exprStmt->kid();
  if (!useful) {
    if (!checkSideEffects(expr, &useful)) {
      return false;
    }

    /*
     * Don't eliminate apparently useless expressions if they are labeled
     * expression statements. The startOffset() test catches the case
     * where we are nesting in emitTree for a labeled compound statement.
     */

    if (innermostNestableControl &&
        innermostNestableControl->is<LabelControl>() &&
        innermostNestableControl->as<LabelControl>().startOffset() >=
            bytecodeSection().offset()) {
      useful = true;
    }
  }

  if (useful) {
    ValueUsage valueUsage =
        wantval ? ValueUsage::WantValue : ValueUsage::IgnoreValue;
    ExpressionStatementEmitter ese(this, valueUsage);
    if (!ese.prepareForExpr(exprStmt->pn_pos.begin)) {
      return false;
    }
    if (!markStepBreakpoint()) {
      return false;
    }
    if (!emitTree(expr, valueUsage)) {
      return false;
    }
    if (!ese.emitEnd()) {
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitDeleteName(UnaryNode* deleteNode) {
  MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeleteNameExpr));

  NameNode* nameExpr = &deleteNode->kid()->as<NameNode>();
  MOZ_ASSERT(nameExpr->isKind(ParseNodeKind::Name));

  return emitAtomOp(JSOp::DelName, nameExpr->atom());
}

bool BytecodeEmitter::emitDeleteProperty(UnaryNode* deleteNode) {
  MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeletePropExpr));

  PropertyAccess* propExpr = &deleteNode->kid()->as<PropertyAccess>();
  PropOpEmitter poe(this, PropOpEmitter::Kind::Delete,
                    propExpr->as<PropertyAccess>().isSuper()
                        ? PropOpEmitter::ObjKind::Super
                        : PropOpEmitter::ObjKind::Other);

  if (!poe.prepareForObj()) {
    return false;
  }

  if (propExpr->isSuper()) {
    // The expression |delete super.foo;| has to evaluate |super.foo|, which
    // could throw if |this| hasn't yet been set by a |super(...)| call.
    auto* base = &propExpr->expression().as<UnaryNode>();
    if (!emitGetThisForSuperBase(base)) {
      //            [stack] THIS
      return false;
    }
  } else {
    if (!emitPropLHS(propExpr)) {
      //            [stack] OBJ
      return false;
    }
  }

  if (!poe.emitDelete(propExpr->key().atom())) {
    //              [stack] # if Super
    //              [stack] THIS
    //              [stack] # otherwise
    //              [stack] SUCCEEDED
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitDeleteElement(UnaryNode* deleteNode) {
  MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeleteElemExpr));

  auto* elemExpr = &deleteNode->kid()->as<PropertyByValue>();
  bool isSuper = elemExpr->isSuper();
  MOZ_ASSERT(!elemExpr->key().isKind(ParseNodeKind::PrivateName));
  ElemOpEmitter eoe(
      this, ElemOpEmitter::Kind::Delete,
      isSuper ? ElemOpEmitter::ObjKind::Super : ElemOpEmitter::ObjKind::Other);

  if (!emitElemObjAndKey(elemExpr, eoe)) {
    //              [stack] # if Super
    //              [stack] THIS KEY
    //              [stack] # otherwise
    //              [stack] OBJ KEY
    return false;
  }

  if (!eoe.emitDelete()) {
    //              [stack] # if Super
    //              [stack] THIS
    //              [stack] # otherwise
    //              [stack] SUCCEEDED
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitDeleteExpression(UnaryNode* deleteNode) {
  MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeleteExpr));

  ParseNode* expression = deleteNode->kid();

  // If useless, just emit JSOp::True; otherwise convert |delete <expr>| to
  // effectively |<expr>, true|.
  bool useful = false;
  if (!checkSideEffects(expression, &useful)) {
    return false;
  }

  if (useful) {
    if (!emitTree(expression)) {
      return false;
    }
    if (!emit1(JSOp::Pop)) {
      return false;
    }
  }

  return emit1(JSOp::True);
}

bool BytecodeEmitter::emitDeleteOptionalChain(UnaryNode* deleteNode) {
  MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeleteOptionalChainExpr));

  OptionalEmitter oe(this, bytecodeSection().stackDepth());

  ParseNode* kid = deleteNode->kid();
  switch (kid->getKind()) {
    case ParseNodeKind::ElemExpr:
    case ParseNodeKind::OptionalElemExpr: {
      auto* elemExpr = &kid->as<PropertyByValueBase>();
      if (!emitDeleteElementInOptChain(elemExpr, oe)) {
        //          [stack] # If shortcircuit
        //          [stack] UNDEFINED-OR-NULL
        //          [stack] # otherwise
        //          [stack] SUCCEEDED
        return false;
      }

      break;
    }
    case ParseNodeKind::ArgumentsLength:
    case ParseNodeKind::DotExpr:
    case ParseNodeKind::OptionalDotExpr: {
      auto* propExpr = &kid->as<PropertyAccessBase>();
      if (!emitDeletePropertyInOptChain(propExpr, oe)) {
        //          [stack] # If shortcircuit
        //          [stack] UNDEFINED-OR-NULL
        //          [stack] # otherwise
        //          [stack] SUCCEEDED
        return false;
      }
      break;
    }
    default:
      MOZ_ASSERT_UNREACHABLE("Unrecognized optional delete ParseNodeKind");
  }

  if (!oe.emitOptionalJumpTarget(JSOp::True)) {
    //              [stack] # If shortcircuit
    //              [stack] TRUE
    //              [stack] # otherwise
    //              [stack] SUCCEEDED
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitDeletePropertyInOptChain(PropertyAccessBase* propExpr,
                                                   OptionalEmitter& oe) {
  MOZ_ASSERT_IF(propExpr->is<PropertyAccess>(),
                !propExpr->as<PropertyAccess>().isSuper());
  PropOpEmitter poe(this, PropOpEmitter::Kind::Delete,
                    PropOpEmitter::ObjKind::Other);

  if (!poe.prepareForObj()) {
    //              [stack]
    return false;
  }
  if (!emitOptionalTree(&propExpr->expression(), oe)) {
    //              [stack] OBJ
    return false;
  }
  if (propExpr->isKind(ParseNodeKind::OptionalDotExpr)) {
    if (!oe.emitJumpShortCircuit()) {
      //            [stack] # if Jump
      //            [stack] UNDEFINED-OR-NULL
      //            [stack] # otherwise
      //            [stack] OBJ
      return false;
    }
  }

  if (!poe.emitDelete(propExpr->key().atom())) {
    //              [stack] SUCCEEDED
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitDeleteElementInOptChain(PropertyByValueBase* elemExpr,
                                                  OptionalEmitter& oe) {
  MOZ_ASSERT_IF(elemExpr->is<PropertyByValue>(),
                !elemExpr->as<PropertyByValue>().isSuper());
  ElemOpEmitter eoe(this, ElemOpEmitter::Kind::Delete,
                    ElemOpEmitter::ObjKind::Other);

  if (!eoe.prepareForObj()) {
    //              [stack]
    return false;
  }

  if (!emitOptionalTree(&elemExpr->expression(), oe)) {
    //              [stack] OBJ
    return false;
  }

  if (elemExpr->isKind(ParseNodeKind::OptionalElemExpr)) {
    if (!oe.emitJumpShortCircuit()) {
      //            [stack] # if Jump
      //            [stack] UNDEFINED-OR-NULL
      //            [stack] # otherwise
      //            [stack] OBJ
      return false;
    }
  }

  if (!eoe.prepareForKey()) {
    //              [stack] OBJ
    return false;
  }

  if (!emitTree(&elemExpr->key())) {
    //              [stack] OBJ KEY
    return false;
  }

  if (!eoe.emitDelete()) {
    //              [stack] SUCCEEDED
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitDebugCheckSelfHosted() {
  //                [stack] CALLEE

#ifdef DEBUG
  if (!emit1(JSOp::DebugCheckSelfHosted)) {
    //              [stack] CALLEE
    return false;
  }
#endif

  return true;
}

bool BytecodeEmitter::emitSelfHostedCallFunction(CallNode* callNode, JSOp op) {
  // Special-casing of callFunction to emit bytecode that directly
  // invokes the callee with the correct |this| object and arguments.
  // callFunction(fun, thisArg, arg0, arg1) thus becomes:
  // - emit lookup for fun
  // - emit lookup for thisArg
  // - emit lookups for arg0, arg1
  //
  // argc is set to the amount of actually emitted args and the
  // emitting of args below is disabled by setting emitArgs to false.
  NameNode* calleeNode = &callNode->callee()->as<NameNode>();
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() >= 2);

  MOZ_ASSERT(callNode->callOp() == JSOp::Call);

  bool constructing =
      calleeNode->name() ==
      TaggedParserAtomIndex::WellKnown::constructContentFunction();
  ParseNode* funNode = argsList->head();

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

#ifdef DEBUG
  MOZ_ASSERT(op == JSOp::Call || op == JSOp::CallContent ||
             op == JSOp::NewContent);
  if (op == JSOp::Call) {
    if (!emitDebugCheckSelfHosted()) {
      //            [stack] CALLEE
      return false;
    }
  }
#endif

  ParseNode* thisOrNewTarget = funNode->pn_next;
  if (constructing) {
    // Save off the new.target value, but here emit a proper |this| for a
    // constructing call.
    if (!emit1(JSOp::IsConstructing)) {
      //            [stack] CALLEE IS_CONSTRUCTING
      return false;
    }
  } else {
    // It's |this|, emit it.
    if (!emitTree(thisOrNewTarget)) {
      //            [stack] CALLEE THIS
      return false;
    }
  }

  for (ParseNode* argpn : argsList->contentsFrom(thisOrNewTarget->pn_next)) {
    if (!emitTree(argpn)) {
      //            [stack] CALLEE ... ARGS...
      return false;
    }
  }

  if (constructing) {
    if (!emitTree(thisOrNewTarget)) {
      //            [stack] CALLEE IS_CONSTRUCTING ARGS... NEW.TARGET
      return false;
    }
  }

  uint32_t argc = argsList->count() - 2;
  if (!emitCall(op, argc)) {
    //              [stack] RVAL
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitSelfHostedResumeGenerator(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  // Syntax: resumeGenerator(gen, value, 'next'|'throw'|'return')
  MOZ_ASSERT(argsList->count() == 3);

  ParseNode* genNode = argsList->head();
  if (!emitTree(genNode)) {
    //              [stack] GENERATOR
    return false;
  }

  ParseNode* valNode = genNode->pn_next;
  if (!emitTree(valNode)) {
    //              [stack] GENERATOR VALUE
    return false;
  }

  ParseNode* kindNode = valNode->pn_next;
  MOZ_ASSERT(kindNode->isKind(ParseNodeKind::StringExpr));
  GeneratorResumeKind kind =
      ParserAtomToResumeKind(kindNode->as<NameNode>().atom());
  MOZ_ASSERT(!kindNode->pn_next);

  if (!emitPushResumeKind(kind)) {
    //              [stack] GENERATOR VALUE RESUMEKIND
    return false;
  }

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

  return true;
}

bool BytecodeEmitter::emitSelfHostedForceInterpreter() {
  // JSScript::hasForceInterpreterOp() relies on JSOp::ForceInterpreter being
  // the first bytecode op in the script.
  MOZ_ASSERT(bytecodeSection().code().empty());

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

  return true;
}

bool BytecodeEmitter::emitSelfHostedAllowContentIter(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 1);

  // We're just here as a sentinel. Pass the value through directly.
  return emitTree(argsList->head());
}

bool BytecodeEmitter::emitSelfHostedAllowContentIterWith(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 2 || argsList->count() == 3);

  // We're just here as a sentinel. Pass the value through directly.
  return emitTree(argsList->head());
}

bool BytecodeEmitter::emitSelfHostedAllowContentIterWithNext(
    CallNode* callNode) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 2);

  // We're just here as a sentinel. Pass the value through directly.
  return emitTree(argsList->head());
}

bool BytecodeEmitter::emitSelfHostedDefineDataProperty(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  // Only optimize when 3 arguments are passed.
  MOZ_ASSERT(argsList->count() == 3);

  ParseNode* objNode = argsList->head();
  if (!emitTree(objNode)) {
    return false;
  }

  ParseNode* idNode = objNode->pn_next;
  if (!emitTree(idNode)) {
    return false;
  }

  ParseNode* valNode = idNode->pn_next;
  if (!emitTree(valNode)) {
    return false;
  }

  // This will leave the object on the stack instead of pushing |undefined|,
  // but that's fine because the self-hosted code doesn't use the return
  // value.
  return emit1(JSOp::InitElem);
}

bool BytecodeEmitter::emitSelfHostedHasOwn(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 2);

  ParseNode* idNode = argsList->head();
  if (!emitTree(idNode)) {
    return false;
  }

  ParseNode* objNode = idNode->pn_next;
  if (!emitTree(objNode)) {
    return false;
  }

  return emit1(JSOp::HasOwn);
}

bool BytecodeEmitter::emitSelfHostedGetPropertySuper(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 3);

  ParseNode* objNode = argsList->head();
  ParseNode* idNode = objNode->pn_next;
  ParseNode* receiverNode = idNode->pn_next;

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

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

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

  return emitElemOpBase(JSOp::GetElemSuper);
}

bool BytecodeEmitter::emitSelfHostedToNumeric(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 1);

  ParseNode* argNode = argsList->head();

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

  return emit1(JSOp::ToNumeric);
}

bool BytecodeEmitter::emitSelfHostedToString(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 1);

  ParseNode* argNode = argsList->head();

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

  return emit1(JSOp::ToString);
}

bool BytecodeEmitter::emitSelfHostedIsNullOrUndefined(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 1);

  ParseNode* argNode = argsList->head();

  if (!emitTree(argNode)) {
    //              [stack] ARG
    return false;
  }
  if (!emit1(JSOp::IsNullOrUndefined)) {
    //              [stack] ARG IS_NULL_OR_UNDEF
    return false;
  }
  if (!emit1(JSOp::Swap)) {
    //              [stack] IS_NULL_OR_UNDEF ARG
    return false;
  }
  if (!emit1(JSOp::Pop)) {
    //              [stack] IS_NULL_OR_UNDEF
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitSelfHostedIteratorClose(CallNode* callNode) {
  ListNode* argsList = callNode->args();
  MOZ_ASSERT(argsList->count() == 1);

  ParseNode* argNode = argsList->head();
  if (!emitTree(argNode)) {
    //              [stack] ARG
    return false;
  }

  if (!emit2(JSOp::CloseIter, uint8_t(CompletionKind::Normal))) {
    //              [stack]
    return false;
  }

  // This is still a call node, so we must generate a stack value.
  if (!emit1(JSOp::Undefined)) {
    //              [stack] RVAL
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitSelfHostedGetBuiltinConstructorOrPrototype(
    CallNode* callNode, bool isConstructor) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 1);

  ParseNode* argNode = argsList->head();

  if (!argNode->isKind(ParseNodeKind::StringExpr)) {
    reportError(callNode, JSMSG_UNEXPECTED_TYPE, "built-in name",
                "not a string constant");
    return false;
  }

  auto name = argNode->as<NameNode>().atom();

  BuiltinObjectKind kind;
  if (isConstructor) {
    kind = BuiltinConstructorForName(name);
  } else {
    kind = BuiltinPrototypeForName(name);
  }

  if (kind == BuiltinObjectKind::None) {
    reportError(callNode, JSMSG_UNEXPECTED_TYPE, "built-in name",
                "not a valid built-in");
    return false;
  }

  return emitBuiltinObject(kind);
}

bool BytecodeEmitter::emitSelfHostedGetBuiltinConstructor(CallNode* callNode) {
  return emitSelfHostedGetBuiltinConstructorOrPrototype(
      callNode, /* isConstructor = */ true);
}

bool BytecodeEmitter::emitSelfHostedGetBuiltinPrototype(CallNode* callNode) {
  return emitSelfHostedGetBuiltinConstructorOrPrototype(
      callNode, /* isConstructor = */ false);
}

JS::SymbolCode ParserAtomToSymbolCode(TaggedParserAtomIndex atom) {
  // NOTE: This is a linear search, but the set of entries is quite small and
  // this is only used for initial self-hosted parse.
#define MATCH_WELL_KNOWN_SYMBOL(NAME)                     \
  if (atom == TaggedParserAtomIndex::WellKnown::NAME()) { \
    return JS::SymbolCode::NAME;                          \
  }
  JS_FOR_EACH_WELL_KNOWN_SYMBOL(MATCH_WELL_KNOWN_SYMBOL)
#undef MATCH_WELL_KNOWN_SYMBOL

  return JS::SymbolCode::Limit;
}

bool BytecodeEmitter::emitSelfHostedGetBuiltinSymbol(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 1);

  ParseNode* argNode = argsList->head();

  if (!argNode->isKind(ParseNodeKind::StringExpr)) {
    reportError(callNode, JSMSG_UNEXPECTED_TYPE, "built-in name",
                "not a string constant");
    return false;
  }

  auto name = argNode->as<NameNode>().atom();

  JS::SymbolCode code = ParserAtomToSymbolCode(name);
  if (code == JS::SymbolCode::Limit) {
    reportError(callNode, JSMSG_UNEXPECTED_TYPE, "built-in name",
                "not a valid built-in");
    return false;
  }

  return emit2(JSOp::Symbol, uint8_t(code));
}

bool BytecodeEmitter::emitSelfHostedArgumentsLength(CallNode* callNode) {
  MOZ_ASSERT(!sc->asFunctionBox()->needsArgsObj());
  sc->asFunctionBox()->setUsesArgumentsIntrinsics();

  MOZ_ASSERT(callNode->args()->count() == 0);

  return emit1(JSOp::ArgumentsLength);
}

bool BytecodeEmitter::emitSelfHostedGetArgument(CallNode* callNode) {
  MOZ_ASSERT(!sc->asFunctionBox()->needsArgsObj());
  sc->asFunctionBox()->setUsesArgumentsIntrinsics();

  ListNode* argsList = callNode->args();
  MOZ_ASSERT(argsList->count() == 1);

  ParseNode* argNode = argsList->head();
  if (!emitTree(argNode)) {
    return false;
  }

  return emit1(JSOp::GetActualArg);
}

#ifdef DEBUG
void BytecodeEmitter::assertSelfHostedExpectedTopLevel(ParseNode* node) {
  // The function argument is expected to be a simple binding/function name.
  // Eg. `function foo() { }; SpecialIntrinsic(foo)`
  MOZ_ASSERT(node->isKind(ParseNodeKind::Name),
             "argument must be a function name");
  TaggedParserAtomIndex targetName = node->as<NameNode>().name();

  // The special intrinsics must follow the target functions definition. A
  // simple assert is fine here since any hoisted function will cause a non-null
  // value to be set here.
  MOZ_ASSERT(prevSelfHostedTopLevelFunction);

  // The target function must match the most recently defined top-level
  // self-hosted function.
  MOZ_ASSERT(prevSelfHostedTopLevelFunction->explicitName() == targetName,
             "selfhost decorator must immediately follow target function");
}
#endif

bool BytecodeEmitter::emitSelfHostedSetIsInlinableLargeFunction(
    CallNode* callNode) {
#ifdef DEBUG
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 1);

  assertSelfHostedExpectedTopLevel(argsList->head());
#endif

  MOZ_ASSERT(prevSelfHostedTopLevelFunction->isInitialCompilation);
  prevSelfHostedTopLevelFunction->setIsInlinableLargeFunction();

  // This is still a call node, so we must generate a stack value.
  return emit1(JSOp::Undefined);
}

bool BytecodeEmitter::emitSelfHostedSetCanonicalName(CallNode* callNode) {
  ListNode* argsList = callNode->args();

  MOZ_ASSERT(argsList->count() == 2);

#ifdef DEBUG
  assertSelfHostedExpectedTopLevel(argsList->head());
#endif

  ParseNode* nameNode = argsList->last();
  MOZ_ASSERT(nameNode->isKind(ParseNodeKind::StringExpr));
  TaggedParserAtomIndex specName = nameNode->as<NameNode>().atom();
  // Canonical name must be atomized.
  compilationState.parserAtoms.markUsedByStencil(specName,
                                                 ParserAtom::Atomize::Yes);

  // Store the canonical name for instantiation.
  prevSelfHostedTopLevelFunction->functionStencil().setSelfHostedCanonicalName(
      specName);

  return emit1(JSOp::Undefined);
}

#ifdef DEBUG
void BytecodeEmitter::assertSelfHostedUnsafeGetReservedSlot(
    ListNode* argsList) {
  MOZ_ASSERT(argsList->count() == 2);

  ParseNode* objNode = argsList->head();
  ParseNode* slotNode = objNode->pn_next;

  // Ensure that the slot argument is fixed, this is required by the JITs.
  MOZ_ASSERT(slotNode->isKind(ParseNodeKind::NumberExpr),
             "slot argument must be a constant");
}

void BytecodeEmitter::assertSelfHostedUnsafeSetReservedSlot(
    ListNode* argsList) {
  MOZ_ASSERT(argsList->count() == 3);

  ParseNode* objNode = argsList->head();
  ParseNode* slotNode = objNode->pn_next;

  // Ensure that the slot argument is fixed, this is required by the JITs.
  MOZ_ASSERT(slotNode->isKind(ParseNodeKind::NumberExpr),
             "slot argument must be a constant");
}
#endif

/* A version of emitCalleeAndThis for the optional cases:
 *   * a?.()
 *   * a?.b()
 *   * a?.["b"]()
 *   * (a?.b)()
 *   * a?.#b()
 *
 * See emitCallOrNew and emitOptionalCall for more context.
 */

bool BytecodeEmitter::emitOptionalCalleeAndThis(ParseNode* callee,
                                                CallNode* call,
                                                CallOrNewEmitter& cone,
                                                OptionalEmitter& oe) {
  AutoCheckRecursionLimit recursion(fc);
  if (!recursion.check(fc)) {
    return false;
  }

  switch (ParseNodeKind kind = callee->getKind()) {
    case ParseNodeKind::Name: {
      auto name = callee->as<NameNode>().name();
      if (!cone.emitNameCallee(name)) {
        //          [stack] CALLEE THIS
        return false;
      }
      break;
    }

    case ParseNodeKind::OptionalDotExpr: {
      MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
      OptionalPropertyAccess* prop = &callee->as<OptionalPropertyAccess>();
      bool isSuper = false;

      PropOpEmitter& poe = cone.prepareForPropCallee(isSuper);
      if (!emitOptionalDotExpression(prop, poe, isSuper, oe)) {
        //          [stack] CALLEE THIS
        return false;
      }
      break;
    }
    case ParseNodeKind::ArgumentsLength:
    case ParseNodeKind::DotExpr: {
      MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
      PropertyAccess* prop = &callee->as<PropertyAccess>();
      bool isSuper = prop->isSuper();

      PropOpEmitter& poe = cone.prepareForPropCallee(isSuper);
      if (!emitOptionalDotExpression(prop, poe, isSuper, oe)) {
        //          [stack] CALLEE THIS
        return false;
      }
      break;
    }

    case ParseNodeKind::OptionalElemExpr: {
      OptionalPropertyByValue* elem = &callee->as<OptionalPropertyByValue>();
      bool isSuper = false;
      MOZ_ASSERT(!elem->key().isKind(ParseNodeKind::PrivateName));
      ElemOpEmitter& eoe = cone.prepareForElemCallee(isSuper);
      if (!emitOptionalElemExpression(elem, eoe, isSuper, oe)) {
        //          [stack] CALLEE THIS
        return false;
      }
      break;
    }
    case ParseNodeKind::ElemExpr: {
      PropertyByValue* elem = &callee->as<PropertyByValue>();
      bool isSuper = elem->isSuper();
      MOZ_ASSERT(!elem->key().isKind(ParseNodeKind::PrivateName));
      ElemOpEmitter& eoe = cone.prepareForElemCallee(isSuper);
      if (!emitOptionalElemExpression(elem, eoe, isSuper, oe)) {
        //          [stack] CALLEE THIS
        return false;
      }
      break;
    }

    case ParseNodeKind::PrivateMemberExpr:
    case ParseNodeKind::OptionalPrivateMemberExpr: {
      PrivateMemberAccessBase* privateExpr =
          &callee->as<PrivateMemberAccessBase>();
      PrivateOpEmitter& xoe =
          cone.prepareForPrivateCallee(privateExpr->privateName().name());
      if (!emitOptionalPrivateExpression(privateExpr, xoe, oe)) {
        //          [stack] CALLEE THIS
        return false;
      }
      break;
    }

    case ParseNodeKind::Function:
      if (!cone.prepareForFunctionCallee()) {
        return false;
      }
      if (!emitOptionalTree(callee, oe)) {
        //          [stack] CALLEE
        return false;
      }
      break;

    case ParseNodeKind::OptionalChain: {
      return emitCalleeAndThisForOptionalChain(&callee->as<UnaryNode>(), call,
                                               cone);
    }

    default:
      MOZ_RELEASE_ASSERT(kind != ParseNodeKind::SuperBase);

      if (!cone.prepareForOtherCallee()) {
        return false;
      }
      if (!emitOptionalTree(callee, oe)) {
        //          [stack] CALLEE
        return false;
      }
      break;
  }

  if (!cone.emitThis()) {
    //              [stack] CALLEE THIS
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitCalleeAndThis(ParseNode* callee, CallNode* maybeCall,
                                        CallOrNewEmitter& cone) {
  MOZ_ASSERT_IF(maybeCall, maybeCall->callee() == callee);

  switch (callee->getKind()) {
    case ParseNodeKind::Name: {
      auto name = callee->as<NameNode>().name();
      if (!cone.emitNameCallee(name)) {
        //          [stack] CALLEE THIS?
        return false;
      }
      break;
    }
    case ParseNodeKind::ArgumentsLength:
    case ParseNodeKind::DotExpr: {
      MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
      PropertyAccess* prop = &callee->as<PropertyAccess>();
      bool isSuper = prop->isSuper();

      PropOpEmitter& poe = cone.prepareForPropCallee(isSuper);
      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.emitGet(prop->key().atom())) {
        //          [stack] CALLEE THIS?
        return false;
      }

      break;
    }
    case ParseNodeKind::ElemExpr: {
      MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
      PropertyByValue* elem = &callee->as<PropertyByValue>();
      bool isSuper = elem->isSuper();
      MOZ_ASSERT(!elem->key().isKind(ParseNodeKind::PrivateName));
      ElemOpEmitter& eoe = cone.prepareForElemCallee(isSuper);
      if (!emitElemObjAndKey(elem, eoe)) {
        //          [stack] # if Super
        //          [stack] THIS? THIS KEY
        //          [stack] # otherwise
        //          [stack] OBJ? OBJ KEY
        return false;
      }
      if (!eoe.emitGet()) {
        //          [stack] CALLEE THIS?
        return false;
      }

      break;
    }
    case ParseNodeKind::PrivateMemberExpr: {
      MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
      PrivateMemberAccessBase* privateExpr =
          &callee->as<PrivateMemberAccessBase>();
      PrivateOpEmitter& xoe =
          cone.prepareForPrivateCallee(privateExpr->privateName().name());
      if (!emitTree(&privateExpr->expression())) {
        //          [stack] OBJ
        return false;
      }
      if (!xoe.emitReference()) {
        //          [stack] OBJ NAME
        return false;
      }
      if (!xoe.emitGetForCallOrNew()) {
        //          [stack] CALLEE THIS
        return false;
      }

      break;
    }
    case ParseNodeKind::Function:
      if (!cone.prepareForFunctionCallee()) {
        return false;
      }
      if (!emitTree(callee)) {
        //          [stack] CALLEE
        return false;
      }
      break;
    case ParseNodeKind::SuperBase:
      MOZ_ASSERT(maybeCall);
      MOZ_ASSERT(maybeCall->isKind(ParseNodeKind::SuperCallExpr));
      MOZ_ASSERT(callee->isKind(ParseNodeKind::SuperBase));
      if (!cone.emitSuperCallee()) {
        //          [stack] CALLEE IsConstructing
        return false;
      }
      break;
    case ParseNodeKind::OptionalChain: {
      MOZ_ASSERT(maybeCall);
      return emitCalleeAndThisForOptionalChain(&callee->as<UnaryNode>(),
                                               maybeCall, cone);
    }
    default:
      if (!cone.prepareForOtherCallee()) {
        return false;
      }
      if (!emitTree(callee)) {
        return false;
      }
      break;
  }

  if (!cone.emitThis()) {
    //              [stack] CALLEE THIS
    return false;
  }

  return true;
}

ParseNode* BytecodeEmitter::getCoordNode(ParseNode* callNode,
                                         ParseNode* calleeNode, JSOp op,
                                         ListNode* argsList) const {
  ParseNode* coordNode = callNode;
  if (op == JSOp::Call || op == JSOp::SpreadCall) {
    // Default to using the location of the `(` itself.
    // obj[expr]() // expression
    //          ^  // column coord
    coordNode = argsList;

    switch (calleeNode->getKind()) {
      case ParseNodeKind::ArgumentsLength:
      case ParseNodeKind::DotExpr:
        // Use the position of a property access identifier.
        //
        // obj().aprop() // expression
        //       ^       // column coord
        //
        // Note: Because of the constant folding logic in FoldElement,
        // this case also applies for constant string properties.
        //
        // obj()['aprop']() // expression
        //       ^          // column coord
        coordNode = &calleeNode->as<PropertyAccess>().key();
        break;
      case ParseNodeKind::Name: {
        // Use the start of callee name unless it is at a separator
        // or has no args.
        //
        // 2 + obj()   // expression
        //     ^       // column coord
        //
        if (argsList->empty() ||
            !bytecodeSection().atSeparator(calleeNode->pn_pos.begin)) {
          // Use the start of callee names.
          coordNode = calleeNode;
        }
        break;
      }

      default:
        break;
    }
  }
  return coordNode;
}

bool BytecodeEmitter::emitArguments(ListNode* argsList, bool isCall,
                                    bool isSpread, CallOrNewEmitter& cone) {
  uint32_t argc = argsList->count();
  if (argc >= ARGC_LIMIT) {
    reportError(argsList,
                isCall ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
    return false;
  }
  if (!isSpread) {
    if (!cone.prepareForNonSpreadArguments()) {
      //            [stack] CALLEE THIS
      return false;
    }
    for (ParseNode* arg : argsList->contents()) {
      if (!updateSourceCoordNotesIfNonLiteral(arg)) {
        return false;
      }
      if (!emitTree(arg)) {
        //          [stack] CALLEE THIS ARG*
        return false;
      }
    }
  } else if (cone.wantSpreadOperand()) {
    auto* spreadNode = &argsList->head()->as<UnaryNode>();
    if (!updateSourceCoordNotesIfNonLiteral(spreadNode->kid())) {
      return false;
    }
    if (!emitTree(spreadNode->kid())) {
      //            [stack] CALLEE THIS ARG0
      return false;
    }

    if (!cone.emitSpreadArgumentsTest()) {
      //            [stack] CALLEE THIS ARG0
      return false;
    }

    if (cone.wantSpreadIteration()) {
      if (!emitSpreadIntoArray(spreadNode)) {
        //          [stack] CALLEE THIS ARR
        return false;
      }
    }

    if (!cone.emitSpreadArgumentsTestEnd()) {
      //            [stack] CALLEE THIS ARR
      return false;
    }
  } else {
    if (!cone.prepareForSpreadArguments()) {
      //            [stack] CALLEE THIS
      return false;
    }
    if (!emitArray(argsList)) {
      //            [stack] CALLEE THIS ARR
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitOptionalCall(CallNode* callNode, OptionalEmitter& oe,
                                       ValueUsage valueUsage) {
  /*
   * A modified version of emitCallOrNew that handles optional calls.
   *
   * These include the following:
   *    a?.()
   *    a.b?.()
   *    a.["b"]?.()
   *    (a?.b)?.()
   *
   * See CallOrNewEmitter for more context.
   */

  ParseNode* calleeNode = callNode->callee();
  ListNode* argsList = callNode->args();
  bool isSpread = IsSpreadOp(callNode->callOp());
  JSOp op = callNode->callOp();
  uint32_t argc = argsList->count();
  bool isOptimizableSpread = isSpread && argc == 1;

  CallOrNewEmitter cone(this, op,
                        isOptimizableSpread
                            ? CallOrNewEmitter::ArgumentsKind::SingleSpread
                            : CallOrNewEmitter::ArgumentsKind::Other,
                        valueUsage);

  ParseNode* coordNode = getCoordNode(callNode, calleeNode, op, argsList);

  if (!emitOptionalCalleeAndThis(calleeNode, callNode, cone, oe)) {
    //              [stack] CALLEE THIS
    return false;
  }

  if (callNode->isKind(ParseNodeKind::OptionalCallExpr)) {
    if (!oe.emitJumpShortCircuitForCall()) {
      //            [stack] CALLEE THIS
      return false;
    }
  }

  if (!emitArguments(argsList, /* isCall = */ true, isSpread, cone)) {
    //              [stack] CALLEE THIS ARGS...
    return false;
  }

  if (!cone.emitEnd(argc, coordNode->pn_pos.begin)) {
    //              [stack] RVAL
    return false;
  }

  return true;
}

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
bool BytecodeEmitter::emitSelfHostedDisposeResources(CallNode* callNode,
                                                     DisposalKind kind) {
  ListNode* argsList = callNode->args();
  MOZ_ASSERT(argsList->count() == 2);

  ParseNode* resourcesNode = argsList->head();
  ParseNode* countNode = resourcesNode->pn_next;

  DisposalEmitter de(thisbool(kind));

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

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

  if (!de.prepareForDisposeCapability()) {
    // [stack] RVAL? NEEDS-AWAIT? HAS-AWAITED? THROWING UNDEF
    return false;
  }

  if (!emitTree(resourcesNode)) {
    // [stack] RVAL? NEEDS-AWAIT? HAS-AWAITED? THROWING UNDEF RESOURCES
    return false;
  }

  if (!emitTree(countNode)) {
    // [stack] RVAL? NEEDS-AWAIT? HAS-AWAITED? THROWING UNDEF RESOURCES COUNT
    return false;
  }

  if (!de.emitEnd(*innermostEmitterScope())) {
    // [stack] EXC THROWING
    return false;
  }

  // [stack] EXC THROWING

  InternalIfEmitter ifThrow(this);

  if (!ifThrow.emitThenElse()) {
    // [stack] EXC
    return false;
  }

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

  if (!ifThrow.emitElse()) {
    // [stack] EXC
    return false;
  }

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

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

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

  return true;
}
#endif

bool BytecodeEmitter::emitCallOrNew(CallNode* callNode, ValueUsage valueUsage) {
  /*
   * Emit callable invocation or operator new (constructor call) code.
   * First, emit code for the left operand to evaluate the callable or
   * constructable object expression.
   *
   * Then (or in a call case that has no explicit reference-base
   * object) we emit JSOp::Undefined to produce the undefined |this|
   * value required for calls (which non-strict mode functions
   * will box into the global object).
   */

  bool isCall = callNode->isKind(ParseNodeKind::CallExpr) ||
                callNode->isKind(ParseNodeKind::TaggedTemplateExpr);
  ParseNode* calleeNode = callNode->callee();
  ListNode* argsList = callNode->args();
  JSOp op = callNode->callOp();

  if (calleeNode->isKind(ParseNodeKind::Name) &&
      emitterMode == BytecodeEmitter::SelfHosting && op == JSOp::Call) {
    // Calls to "forceInterpreter", "callFunction",
    // "callContentFunction", or "resumeGenerator" in self-hosted
    // code generate inline bytecode.
    //
    // NOTE: The list of special instruction names has to be kept in sync with
    // "js/src/builtin/.eslintrc.js".
    auto calleeName = calleeNode->as<NameNode>().name();
    if (calleeName == TaggedParserAtomIndex::WellKnown::callFunction()) {
      return emitSelfHostedCallFunction(callNode, JSOp::Call);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::callContentFunction()) {
      return emitSelfHostedCallFunction(callNode, JSOp::CallContent);
    }
    if (calleeName ==
        TaggedParserAtomIndex::WellKnown::constructContentFunction()) {
      return emitSelfHostedCallFunction(callNode, JSOp::NewContent);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::resumeGenerator()) {
      return emitSelfHostedResumeGenerator(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::forceInterpreter()) {
      return emitSelfHostedForceInterpreter();
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::allowContentIter()) {
      return emitSelfHostedAllowContentIter(callNode);
    }
    if (calleeName ==
        TaggedParserAtomIndex::WellKnown::allowContentIterWith()) {
      return emitSelfHostedAllowContentIterWith(callNode);
    }
    if (calleeName ==
        TaggedParserAtomIndex::WellKnown::allowContentIterWithNext()) {
      return emitSelfHostedAllowContentIterWithNext(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::DefineDataProperty() &&
        argsList->count() == 3) {
      return emitSelfHostedDefineDataProperty(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::hasOwn()) {
      return emitSelfHostedHasOwn(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::getPropertySuper()) {
      return emitSelfHostedGetPropertySuper(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::ToNumeric()) {
      return emitSelfHostedToNumeric(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::ToString()) {
      return emitSelfHostedToString(callNode);
    }
    if (calleeName ==
        TaggedParserAtomIndex::WellKnown::GetBuiltinConstructor()) {
      return emitSelfHostedGetBuiltinConstructor(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::GetBuiltinPrototype()) {
      return emitSelfHostedGetBuiltinPrototype(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::GetBuiltinSymbol()) {
      return emitSelfHostedGetBuiltinSymbol(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::ArgumentsLength()) {
      return emitSelfHostedArgumentsLength(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::GetArgument()) {
      return emitSelfHostedGetArgument(callNode);
    }
    if (calleeName ==
        TaggedParserAtomIndex::WellKnown::SetIsInlinableLargeFunction()) {
      return emitSelfHostedSetIsInlinableLargeFunction(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::SetCanonicalName()) {
      return emitSelfHostedSetCanonicalName(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::IsNullOrUndefined()) {
      return emitSelfHostedIsNullOrUndefined(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::IteratorClose()) {
      return emitSelfHostedIteratorClose(callNode);
    }
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    if (calleeName ==
        TaggedParserAtomIndex::WellKnown::DisposeResourcesAsync()) {
      return emitSelfHostedDisposeResources(callNode, DisposalKind::Async);
    }
    if (calleeName ==
        TaggedParserAtomIndex::WellKnown::DisposeResourcesSync()) {
      return emitSelfHostedDisposeResources(callNode, DisposalKind::Sync);
    }
#endif
#ifdef DEBUG
    if (calleeName ==
            TaggedParserAtomIndex::WellKnown::UnsafeGetReservedSlot() ||
        calleeName == TaggedParserAtomIndex::WellKnown::
                          UnsafeGetObjectFromReservedSlot() ||
        calleeName == TaggedParserAtomIndex::WellKnown::
                          UnsafeGetInt32FromReservedSlot() ||
        calleeName == TaggedParserAtomIndex::WellKnown::
                          UnsafeGetStringFromReservedSlot()) {
      // Make sure that this call is correct, but don't emit any special code.
      assertSelfHostedUnsafeGetReservedSlot(argsList);
    }
    if (calleeName ==
        TaggedParserAtomIndex::WellKnown::UnsafeSetReservedSlot()) {
      // Make sure that this call is correct, but don't emit any special code.
      assertSelfHostedUnsafeSetReservedSlot(argsList);
    }
#endif
    // Fall through
  }

  uint32_t argc = argsList->count();
  bool isSpread = IsSpreadOp(op);
  bool isOptimizableSpread = isSpread && argc == 1;
  bool isDefaultDerivedClassConstructor =
      sc->isFunctionBox() && sc->asFunctionBox()->isDerivedClassConstructor() &&
      sc->asFunctionBox()->isSyntheticFunction();
  MOZ_ASSERT_IF(isDefaultDerivedClassConstructor, isOptimizableSpread);
  CallOrNewEmitter cone(
      this, op,
      isOptimizableSpread
          ? isDefaultDerivedClassConstructor
                ? CallOrNewEmitter::ArgumentsKind::PassthroughRest
                : CallOrNewEmitter::ArgumentsKind::SingleSpread
          : CallOrNewEmitter::ArgumentsKind::Other,
      valueUsage);

  if (!emitCalleeAndThis(calleeNode, callNode, cone)) {
    //              [stack] CALLEE THIS
    return false;
  }
  if (!emitArguments(argsList, isCall, isSpread, cone)) {
    //              [stack] CALLEE THIS ARGS...
    return false;
  }

  // Push new.target for construct calls.
  if (IsConstructOp(op)) {
    if (op == JSOp::SuperCall || op == JSOp::SpreadSuperCall) {
      if (!emitNewTarget(callNode)) {
        //          [stack] CALLEE THIS ARGS.. NEW.TARGET
        return false;
      }
    } else {
      // Repush the callee as new.target
      uint32_t effectiveArgc = isSpread ? 1 : argc;
      if (!emitDupAt(effectiveArgc + 1)) {
        //          [stack] CALLEE THIS ARGS.. CALLEE
        return false;
      }
    }
  }

  ParseNode* coordNode = getCoordNode(callNode, calleeNode, op, argsList);

  if (!cone.emitEnd(argc, coordNode->pn_pos.begin)) {
    //              [stack] RVAL
    return false;
  }

  return true;
}

// This list must be kept in the same order in several places:
//   - The binary operators in ParseNode.h ,
//   - the binary operators in TokenKind.h
//   - the precedence list in Parser.cpp
static const JSOp ParseNodeKindToJSOp[] = {
    // Some binary ops require special code generation (PrivateIn);
    // these should not use BinaryOpParseNodeKindToJSOp. This table fills those
    // slots with Nops to make the rest of the table lookup work.
    JSOp::Coalesce, JSOp::Or,       JSOp::And, JSOp::BitOr,    JSOp::BitXor,
    JSOp::BitAnd,   JSOp::StrictEq, JSOp::Eq,  JSOp::StrictNe, JSOp::Ne,
    JSOp::Lt,       JSOp::Le,       JSOp::Gt,  JSOp::Ge,       JSOp::Instanceof,
    JSOp::In,       JSOp::Nop,      JSOp::Lsh, JSOp::Rsh,      JSOp::Ursh,
    JSOp::Add,      JSOp::Sub,      JSOp::Mul, JSOp::Div,      JSOp::Mod,
    JSOp::Pow};

static inline JSOp BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk) {
  MOZ_ASSERT(pnk >= ParseNodeKind::BinOpFirst);
  MOZ_ASSERT(pnk <= ParseNodeKind::BinOpLast);
  int parseNodeFirst = size_t(ParseNodeKind::BinOpFirst);
#ifdef DEBUG
  int jsopArraySize = std::size(ParseNodeKindToJSOp);
  int parseNodeKindListSize =
      size_t(ParseNodeKind::BinOpLast) - parseNodeFirst + 1;
  MOZ_ASSERT(jsopArraySize == parseNodeKindListSize);
  // Ensure we don't use this to find an op for a parse node
  // requiring special emission rules.
  MOZ_ASSERT(ParseNodeKindToJSOp[size_t(pnk) - parseNodeFirst] != JSOp::Nop);
#endif
  return ParseNodeKindToJSOp[size_t(pnk) - parseNodeFirst];
}

bool BytecodeEmitter::emitRightAssociative(ListNode* node) {
  // ** is the only right-associative operator.
  MOZ_ASSERT(node->isKind(ParseNodeKind::PowExpr));

  // Right-associative operator chain.
  for (ParseNode* subexpr : node->contents()) {
    if (!updateSourceCoordNotesIfNonLiteral(subexpr)) {
      return false;
    }
    if (!emitTree(subexpr)) {
      return false;
    }
  }
  for (uint32_t i = 0; i < node->count() - 1; i++) {
    if (!emit1(JSOp::Pow)) {
      return false;
    }
  }
  return true;
}

bool BytecodeEmitter::emitLeftAssociative(ListNode* node) {
  // Left-associative operator chain.
  if (!emitTree(node->head())) {
    return false;
  }
  JSOp op = BinaryOpParseNodeKindToJSOp(node->getKind());
  ParseNode* nextExpr = node->head()->pn_next;
  do {
    if (!updateSourceCoordNotesIfNonLiteral(nextExpr)) {
      return false;
    }
    if (!emitTree(nextExpr)) {
      return false;
    }
    if (!emit1(op)) {
      return false;
    }
  } while ((nextExpr = nextExpr->pn_next));
  return true;
}

bool BytecodeEmitter::emitPrivateInExpr(ListNode* node) {
  MOZ_ASSERT(node->head()->isKind(ParseNodeKind::PrivateName));

  NameNode& privateNameNode = node->head()->as<NameNode>();
  TaggedParserAtomIndex privateName = privateNameNode.name();

  PrivateOpEmitter xoe(this, PrivateOpEmitter::Kind::ErgonomicBrandCheck,
                       privateName);

  ParseNode* valueNode = node->head()->pn_next;
  MOZ_ASSERT(valueNode->pn_next == nullptr);

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

  if (!xoe.emitReference()) {
    //              [stack] OBJ BRAND  if private method
    //              [stack] OBJ NAME   if private field or accessor.
    return false;
  }

  if (!xoe.emitBrandCheck()) {
    //              [stack] OBJ BRAND BOOL if private method
    //              [stack] OBJ NAME  BOOL if private field or accessor.
    return false;
  }

  if (!emitUnpickN(2)) {
    //              [stack] BOOL OBJ BRAND if private method
    //              [stack] BOOL OBJ NAME   if private field or accessor.
    return false;
  }

  if (!emitPopN(2)) {
    //              [stack] BOOL
    return false;
  }

  return true;
}

/*
 * Special `emitTree` for Optional Chaining case.
 * Examples of this are `emitOptionalChain`, `emitDeleteOptionalChain` and
 * `emitCalleeAndThisForOptionalChain`.
 */

bool BytecodeEmitter::emitOptionalTree(
    ParseNode* pn, OptionalEmitter& oe,
    ValueUsage valueUsage /* = ValueUsage::WantValue */) {
  AutoCheckRecursionLimit recursion(fc);
  if (!recursion.check(fc)) {
    return false;
  }
  ParseNodeKind kind = pn->getKind();
  switch (kind) {
    case ParseNodeKind::OptionalDotExpr: {
      OptionalPropertyAccess* prop = &pn->as<OptionalPropertyAccess>();
      bool isSuper = false;
      PropOpEmitter poe(this, PropOpEmitter::Kind::Get,
                        PropOpEmitter::ObjKind::Other);
      if (!emitOptionalDotExpression(prop, poe, isSuper, oe)) {
        return false;
      }
      break;
    }
    case ParseNodeKind::ArgumentsLength:
    case ParseNodeKind::DotExpr: {
      PropertyAccess* prop = &pn->as<PropertyAccess>();
      bool isSuper = prop->isSuper();
      PropOpEmitter poe(this, PropOpEmitter::Kind::Get,
                        isSuper ? PropOpEmitter::ObjKind::Super
                                : PropOpEmitter::ObjKind::Other);
      if (!emitOptionalDotExpression(prop, poe, isSuper, oe)) {
        return false;
      }
      break;
    }

    case ParseNodeKind::OptionalElemExpr: {
      OptionalPropertyByValue* elem = &pn->as<OptionalPropertyByValue>();
      bool isSuper = false;
      MOZ_ASSERT(!elem->key().isKind(ParseNodeKind::PrivateName));
      ElemOpEmitter eoe(this, ElemOpEmitter::Kind::Get,
                        ElemOpEmitter::ObjKind::Other);

      if (!emitOptionalElemExpression(elem, eoe, isSuper, oe)) {
        return false;
      }
      break;
    }
    case ParseNodeKind::ElemExpr: {
      PropertyByValue* elem = &pn->as<PropertyByValue>();
      bool isSuper = elem->isSuper();
      MOZ_ASSERT(!elem->key().isKind(ParseNodeKind::PrivateName));
      ElemOpEmitter eoe(this, ElemOpEmitter::Kind::Get,
                        isSuper ? ElemOpEmitter::ObjKind::Super
                                : ElemOpEmitter::ObjKind::Other);

      if (!emitOptionalElemExpression(elem, eoe, isSuper, oe)) {
        return false;
      }
      break;
    }
    case ParseNodeKind::PrivateMemberExpr:
    case ParseNodeKind::OptionalPrivateMemberExpr: {
      PrivateMemberAccessBase* privateExpr = &pn->as<PrivateMemberAccessBase>();
      PrivateOpEmitter xoe(this, PrivateOpEmitter::Kind::Get,
                           privateExpr->privateName().name());
      if (!emitOptionalPrivateExpression(privateExpr, xoe, oe)) {
        return false;
      }
      break;
    }
    case ParseNodeKind::CallExpr:
    case ParseNodeKind::OptionalCallExpr:
      if (!emitOptionalCall(&pn->as<CallNode>(), oe, valueUsage)) {
        return false;
      }
      break;
    // List of accepted ParseNodeKinds that might appear only at the beginning
    // of an Optional Chain.
    // For example, a taggedTemplateExpr node might occur if we have
    // `test`?.b, with `test` as the taggedTemplateExpr ParseNode.
    default:
#ifdef DEBUG
      // https://tc39.es/ecma262/#sec-primary-expression
      bool isPrimaryExpression =
          kind == ParseNodeKind::ThisExpr || kind == ParseNodeKind::Name ||
          kind == ParseNodeKind::PrivateName ||
          kind == ParseNodeKind::NullExpr || kind == ParseNodeKind::TrueExpr ||
          kind == ParseNodeKind::FalseExpr ||
          kind == ParseNodeKind::NumberExpr ||
          kind == ParseNodeKind::BigIntExpr ||
          kind == ParseNodeKind::StringExpr ||
          kind == ParseNodeKind::ArrayExpr ||
          kind == ParseNodeKind::ObjectExpr ||
          kind == ParseNodeKind::Function || kind == ParseNodeKind::ClassDecl ||
          kind == ParseNodeKind::RegExpExpr ||
          kind == ParseNodeKind::TemplateStringExpr ||
          kind == ParseNodeKind::TemplateStringListExpr ||
          kind == ParseNodeKind::RawUndefinedExpr || pn->isInParens();

      // https://tc39.es/ecma262/#sec-left-hand-side-expressions
      bool isMemberExpression = isPrimaryExpression ||
                                kind == ParseNodeKind::TaggedTemplateExpr ||
                                kind == ParseNodeKind::NewExpr ||
                                kind == ParseNodeKind::NewTargetExpr ||
                                kind == ParseNodeKind::ImportMetaExpr;

      bool isCallExpression = kind == ParseNodeKind::SetThis ||
                              kind == ParseNodeKind::CallImportExpr;

      MOZ_ASSERT(isMemberExpression || isCallExpression,
                 "Unknown ParseNodeKind for OptionalChain");
#endif
      return emitTree(pn);
  }
  return true;
}

// Handle the case of a call made on a OptionalChainParseNode.
// For example `(a?.b)()` and `(a?.b)?.()`.
bool BytecodeEmitter::emitCalleeAndThisForOptionalChain(
    UnaryNode* optionalChain, CallNode* callNode, CallOrNewEmitter& cone) {
  ParseNode* calleeNode = optionalChain->kid();

  // Create a new OptionalEmitter, in order to emit the right bytecode
  // in isolation.
  OptionalEmitter oe(this, bytecodeSection().stackDepth());

  if (!emitOptionalCalleeAndThis(calleeNode, callNode, cone, oe)) {
    //              [stack] CALLEE THIS
    return false;
  }

  // complete the jump if necessary. This will set both the "this" value
  // and the "callee" value to undefined, if the callee is undefined. It
  // does not matter much what the this value is, the function call will
  // fail if it is not optional, and be set to undefined otherwise.
  if (!oe.emitOptionalJumpTarget(JSOp::Undefined,
                                 OptionalEmitter::Kind::Reference)) {
    //              [stack] # If shortcircuit
    //              [stack] UNDEFINED UNDEFINED
    //              [stack] # otherwise
    //              [stack] CALLEE THIS
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitOptionalChain(UnaryNode* optionalChain,
                                        ValueUsage valueUsage) {
  ParseNode* expr = optionalChain->kid();

  OptionalEmitter oe(this, bytecodeSection().stackDepth());

  if (!emitOptionalTree(expr, oe, valueUsage)) {
    //              [stack] VAL
    return false;
  }

  if (!oe.emitOptionalJumpTarget(JSOp::Undefined)) {
    //              [stack] # If shortcircuit
    //              [stack] UNDEFINED
    //              [stack] # otherwise
    //              [stack] VAL
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitOptionalDotExpression(PropertyAccessBase* prop,
                                                PropOpEmitter& poe,
                                                bool isSuper,
                                                OptionalEmitter& oe) {
  if (!poe.prepareForObj()) {
    //              [stack]
    return false;
  }

  if (isSuper) {
    UnaryNode* base = &prop->expression().as<UnaryNode>();
    if (!emitGetThisForSuperBase(base)) {
      //            [stack] OBJ
      return false;
    }
  } else {
    if (!emitOptionalTree(&prop->expression(), oe)) {
      //            [stack] OBJ
      return false;
    }
  }

  if (prop->isKind(ParseNodeKind::OptionalDotExpr)) {
    MOZ_ASSERT(!isSuper);
    if (!oe.emitJumpShortCircuit()) {
      //            [stack] # if Jump
      //            [stack] UNDEFINED-OR-NULL
      //            [stack] # otherwise
      //            [stack] OBJ
      return false;
    }
  }

  if (!poe.emitGet(prop->key().atom())) {
    //              [stack] PROP
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitOptionalElemExpression(PropertyByValueBase* elem,
                                                 ElemOpEmitter& eoe,
                                                 bool isSuper,
                                                 OptionalEmitter& oe) {
  if (!eoe.prepareForObj()) {
    //              [stack]
    return false;
  }

  if (isSuper) {
    UnaryNode* base = &elem->expression().as<UnaryNode>();
    if (!emitGetThisForSuperBase(base)) {
      //            [stack] OBJ
      return false;
    }
  } else {
    if (!emitOptionalTree(&elem->expression(), oe)) {
      //            [stack] OBJ
      return false;
    }
  }

  if (elem->isKind(ParseNodeKind::OptionalElemExpr)) {
    MOZ_ASSERT(!isSuper);
    if (!oe.emitJumpShortCircuit()) {
      //            [stack] # if Jump
      //            [stack] UNDEFINED-OR-NULL
      //            [stack] # otherwise
      //            [stack] OBJ
      return false;
    }
  }

  if (!eoe.prepareForKey()) {
    //              [stack] OBJ? OBJ
    return false;
  }

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

  if (!eoe.emitGet()) {
    //              [stack] ELEM
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitOptionalPrivateExpression(
    PrivateMemberAccessBase* privateExpr, PrivateOpEmitter& xoe,
    OptionalEmitter& oe) {
  if (!emitOptionalTree(&privateExpr->expression(), oe)) {
    //              [stack] OBJ
    return false;
  }

  if (privateExpr->isKind(ParseNodeKind::OptionalPrivateMemberExpr)) {
    if (!oe.emitJumpShortCircuit()) {
      //            [stack] # if Jump
      //            [stack] UNDEFINED-OR-NULL
      //            [stack] # otherwise
      //            [stack] OBJ
      return false;
    }
  }

  if (!xoe.emitReference()) {
    //              [stack] OBJ NAME
    return false;
  }
  if (!xoe.emitGet()) {
    //              [stack] CALLEE THIS  # if call
    //              [stack] VALUE        # otherwise
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitShortCircuit(ListNode* node, ValueUsage valueUsage) {
  MOZ_ASSERT(node->isKind(ParseNodeKind::OrExpr) ||
             node->isKind(ParseNodeKind::CoalesceExpr) ||
             node->isKind(ParseNodeKind::AndExpr));

  /*
   * JSOp::Or converts the operand on the stack to boolean, leaves the original
   * value on the stack and jumps if true; otherwise it falls into the next
   * bytecode, which pops the left operand and then evaluates the right operand.
   * The jump goes around the right operand evaluation.
   *
   * JSOp::And converts the operand on the stack to boolean and jumps if false;
   * otherwise it falls into the right operand's bytecode.
   */


  TDZCheckCache tdzCache(this);

  JSOp op;
  switch (node->getKind()) {
    case ParseNodeKind::OrExpr:
      op = JSOp::Or;
      break;
    case ParseNodeKind::CoalesceExpr:
      op = JSOp::Coalesce;
      break;
    case ParseNodeKind::AndExpr:
      op = JSOp::And;
      break;
    default:
      MOZ_CRASH("Unexpected ParseNodeKind");
  }

  JumpList jump;

  // Left-associative operator chain: avoid too much recursion.
  //
  // Emit all nodes but the last.
  for (ParseNode* expr : node->contentsTo(node->last())) {
    if (!emitTree(expr)) {
      return false;
    }
    if (!emitJump(op, &jump)) {
      return false;
    }
    if (!emit1(JSOp::Pop)) {
      return false;
    }
  }

  // Emit the last node
  if (!emitTree(node->last(), valueUsage)) {
    return false;
  }

  if (!emitJumpTargetAndPatch(jump)) {
    return false;
  }
  return true;
}

bool BytecodeEmitter::emitSequenceExpr(ListNode* node, ValueUsage valueUsage) {
  for (ParseNode* child : node->contentsTo(node->last())) {
    if (!updateSourceCoordNotes(child->pn_pos.begin)) {
      return false;
    }
    if (!emitTree(child, ValueUsage::IgnoreValue)) {
      return false;
    }
    if (!emit1(JSOp::Pop)) {
      return false;
    }
  }

  ParseNode* child = node->last();
  if (!updateSourceCoordNotes(child->pn_pos.begin)) {
    return false;
  }
  if (!emitTree(child, valueUsage)) {
    return false;
  }
  return true;
}

// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
// the comment on emitSwitch.
MOZ_NEVER_INLINE bool BytecodeEmitter::emitIncOrDec(UnaryNode* incDec,
                                                    ValueUsage valueUsage) {
  switch (incDec->kid()->getKind()) {
    case ParseNodeKind::ArgumentsLength:
    case ParseNodeKind::DotExpr:
      return emitPropIncDec(incDec, valueUsage);
    case ParseNodeKind::ElemExpr:
      return emitElemIncDec(incDec, valueUsage);
    case ParseNodeKind::PrivateMemberExpr:
      return emitPrivateIncDec(incDec, valueUsage);
    case ParseNodeKind::CallExpr:
      return emitCallIncDec(incDec);
    default:
      return emitNameIncDec(incDec, valueUsage);
  }
}

// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
// the comment on emitSwitch.
MOZ_NEVER_INLINE bool BytecodeEmitter::emitLabeledStatement(
    const LabeledStatement* labeledStmt) {
  auto name = labeledStmt->label();
  LabelEmitter label(this);

  label.emitLabel(name);

  if (!emitTree(labeledStmt->statement())) {
    return false;
  }
  if (!label.emitEnd()) {
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitConditionalExpression(
    ConditionalExpression& conditional, ValueUsage valueUsage) {
  CondEmitter cond(this);
  if (!cond.emitCond()) {
    return false;
  }

  ParseNode* conditionNode = &conditional.condition();
  auto conditionKind = IfEmitter::ConditionKind::Positive;
  if (conditionNode->isKind(ParseNodeKind::NotExpr)) {
    conditionNode = conditionNode->as<UnaryNode>().kid();
    conditionKind = IfEmitter::ConditionKind::Negative;
  }

  // NOTE: NotExpr of conditionNode may be unwrapped, and in that case the
  //       negation is handled by conditionKind.
  if (!emitTree(conditionNode)) {
    return false;
  }

  if (!cond.emitThenElse(conditionKind)) {
    return false;
  }

  if (!emitTree(&conditional.thenExpression(), valueUsage)) {
    return false;
  }

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

  if (!emitTree(&conditional.elseExpression(), valueUsage)) {
    return false;
  }

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

  return true;
}

// Check for an object-literal property list that can be handled by the
// ObjLiteral writer. We ensure that for each `prop: value` pair, the key is a
// constant name or numeric index, there is no accessor specified, and the value
// can be encoded by an ObjLiteral instruction (constant number, string,
// boolean, null/undefined).
void BytecodeEmitter::isPropertyListObjLiteralCompatible(
    ListNode* obj, bool* withValues, bool* withoutValues) const {
  bool keysOK = true;
  bool valuesOK = true;
  uint32_t propCount = 0;

  for (ParseNode* propdef : obj->contents()) {
    if (!propdef->is<BinaryNode>()) {
      keysOK = false;
      break;
    }
    propCount++;

    BinaryNode* prop = &propdef->as<BinaryNode>();
    ParseNode* key = prop->left();
    ParseNode* value = prop->right();

    // Computed keys not OK (ObjLiteral data stores constant keys).
    if (key->isKind(ParseNodeKind::ComputedName)) {
      keysOK = false;
      break;
    }

    // BigIntExprs should have been lowered to computed names at parse
    // time, and so should be excluded above.
    MOZ_ASSERT(!key->isKind(ParseNodeKind::BigIntExpr));

    // Numeric keys OK as long as they are integers and in range.
    if (key->isKind(ParseNodeKind::NumberExpr)) {
      double numValue = key->as<NumericLiteral>().value();
      int32_t i = 0;
      if (!NumberIsInt32(numValue, &i)) {
        keysOK = false;
        break;
      }
      if (!ObjLiteralWriter::arrayIndexInRange(i)) {
        keysOK = false;
        break;
      }
    }

    MOZ_ASSERT(key->isKind(ParseNodeKind::ObjectPropertyName) ||
               key->isKind(ParseNodeKind::StringExpr) ||
               key->isKind(ParseNodeKind::NumberExpr));

    AccessorType accessorType =
        prop->is<PropertyDefinition>()
            ? prop->as<PropertyDefinition>().accessorType()
            : AccessorType::None;
    if (accessorType != AccessorType::None) {
      keysOK = false;
      break;
    }

    if (!isRHSObjLiteralCompatible(value)) {
      valuesOK = false;
    }
  }

  if (propCount > SharedPropMap::MaxPropsForNonDictionary) {
    // JSOp::NewObject cannot accept dictionary-mode objects.
    keysOK = false;
  }

  *withValues = keysOK && valuesOK;
  *withoutValues = keysOK;
}

bool BytecodeEmitter::isArrayObjLiteralCompatible(ListNode* array) const {
  for (ParseNode* elem : array->contents()) {
    if (elem->isKind(ParseNodeKind::Spread)) {
      return false;
    }
    if (!isRHSObjLiteralCompatible(elem)) {
      return false;
    }
  }
  return true;
}

bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
                                       PropListType type) {
  //                [stack] CTOR? OBJ

  size_t curFieldKeyIndex = 0;
  size_t curStaticFieldKeyIndex = 0;
  for (ParseNode* propdef : obj->contents()) {
    if (propdef->is<ClassField>()) {
      MOZ_ASSERT(type == ClassBody);
      // Only handle computing field keys here: the .initializers lambda array
      // is created elsewhere.
      ClassField* field = &propdef->as<ClassField>();
      if (field->name().getKind() == ParseNodeKind::ComputedName) {
        auto fieldKeys =
            field->isStatic()
                ? TaggedParserAtomIndex::WellKnown::dot_staticFieldKeys_()
                : TaggedParserAtomIndex::WellKnown::dot_fieldKeys_();
        if (!emitGetName(fieldKeys)) {
          //        [stack] CTOR OBJ ARRAY
          return false;
        }

        ParseNode* nameExpr = field->name().as<UnaryNode>().kid();

        if (!emitTree(nameExpr, ValueUsage::WantValue)) {
          //        [stack] CTOR OBJ ARRAY KEY
          return false;
        }

        if (!emit1(JSOp::ToPropertyKey)) {
          //        [stack] CTOR OBJ ARRAY KEY
          return false;
        }

        size_t fieldKeysIndex;
        if (field->isStatic()) {
          fieldKeysIndex = curStaticFieldKeyIndex++;
        } else {
          fieldKeysIndex = curFieldKeyIndex++;
        }

        if (!emitUint32Operand(JSOp::InitElemArray, fieldKeysIndex)) {
          //        [stack] CTOR OBJ ARRAY
          return false;
        }

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

    if (propdef->isKind(ParseNodeKind::StaticClassBlock)) {
      // Static class blocks are emitted as part of
      // emitCreateMemberInitializers.
      continue;
    }

    if (propdef->is<LexicalScopeNode>()) {
      // Constructors are sometimes wrapped in LexicalScopeNodes. As we
      // already handled emitting the constructor, skip it.
      MOZ_ASSERT(
          propdef->as<LexicalScopeNode>().scopeBody()->is<ClassMethod>());
      continue;
    }

    // Handle __proto__: v specially because *only* this form, and no other
    // involving "__proto__", performs [[Prototype]] mutation.
    if (propdef->isKind(ParseNodeKind::MutateProto)) {
      //            [stack] OBJ
      MOZ_ASSERT(type == ObjectLiteral);
      if (!pe.prepareForProtoValue(propdef->pn_pos.begin)) {
        //          [stack] OBJ
        return false;
      }
      if (!emitTree(propdef->as<UnaryNode>().kid())) {
        //          [stack] OBJ PROTO
        return false;
      }
      if (!pe.emitMutateProto()) {
        //          [stack] OBJ
        return false;
      }
      continue;
    }

    if (propdef->isKind(ParseNodeKind::Spread)) {
      MOZ_ASSERT(type == ObjectLiteral);
      //            [stack] OBJ
      if (!pe.prepareForSpreadOperand(propdef->pn_pos.begin)) {
        //          [stack] OBJ OBJ
        return false;
      }
      if (!emitTree(propdef->as<UnaryNode>().kid())) {
        //          [stack] OBJ OBJ VAL
        return false;
      }
      if (!pe.emitSpread()) {
        //          [stack] OBJ
        return false;
      }
      continue;
    }

    BinaryNode* prop = &propdef->as<BinaryNode>();

    ParseNode* key = prop->left();
    AccessorType accessorType;
    if (prop->is<ClassMethod>()) {
      ClassMethod& method = prop->as<ClassMethod>();
      accessorType = method.accessorType();

      if (!method.isStatic() && key->isKind(ParseNodeKind::PrivateName) &&
          accessorType != AccessorType::None) {
        // Private non-static accessors are stamped onto instances from
        // initializers; see emitCreateMemberInitializers.
        continue;
      }
    } else if (prop->is<PropertyDefinition>()) {
      accessorType = prop->as<PropertyDefinition>().accessorType();
    } else {
      accessorType = AccessorType::None;
    }

    auto emitValue = [this, &key, &prop, accessorType, &pe]() {
      //            [stack] CTOR? OBJ CTOR? KEY?

      ParseNode* propVal = prop->right();
      if (propVal->isDirectRHSAnonFunction()) {
        // The following branches except for the last `else` clause emit the
        // cases handled in NameResolver::resolveFun (see NameFunctions.cpp)
        if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
            key->isKind(ParseNodeKind::PrivateName) ||
            key->isKind(ParseNodeKind::StringExpr)) {
          auto keyAtom = key->as<NameNode>().atom();
          if (!emitAnonymousFunctionWithName(propVal, keyAtom)) {
            //      [stack] CTOR? OBJ CTOR? VAL
            return false;
          }
        } else if (key->isKind(ParseNodeKind::NumberExpr)) {
          MOZ_ASSERT(accessorType == AccessorType::None);

          auto keyAtom = key->as<NumericLiteral>().toAtom(fc, parserAtoms());
          if (!keyAtom) {
            return false;
          }
          if (!emitAnonymousFunctionWithName(propVal, keyAtom)) {
            //      [stack] CTOR? OBJ CTOR? KEY VAL
            return false;
          }
        } else if (key->isKind(ParseNodeKind::ComputedName) &&
                   (key->as<UnaryNode>().kid()->isKind(
                        ParseNodeKind::NumberExpr) ||
                    key->as<UnaryNode>().kid()->isKind(
                        ParseNodeKind::StringExpr)) &&
                   accessorType == AccessorType::None) {
          ParseNode* keyKid = key->as<UnaryNode>().kid();
          if (keyKid->isKind(ParseNodeKind::NumberExpr)) {
            auto keyAtom =
                keyKid->as<NumericLiteral>().toAtom(fc, parserAtoms());
            if (!keyAtom) {
              return false;
            }
            if (!emitAnonymousFunctionWithName(propVal, keyAtom)) {
              //    [stack] CTOR? OBJ CTOR? KEY VAL
              return false;
            }
          } else {
            MOZ_ASSERT(keyKid->isKind(ParseNodeKind::StringExpr));
            auto keyAtom = keyKid->as<NameNode>().atom();
            if (!emitAnonymousFunctionWithName(propVal, keyAtom)) {
              //    [stack] CTOR? OBJ CTOR? KEY VAL
              return false;
            }
          }
        } else {
          // Either a proper computed property name or a synthetic computed
          // property name for BigInt keys.
          MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));

          FunctionPrefixKind prefix =
              accessorType == AccessorType::None     ? FunctionPrefixKind::None
              : accessorType == AccessorType::Getter ? FunctionPrefixKind::Get
                                                     : FunctionPrefixKind::Set;

          if (!emitAnonymousFunctionWithComputedName(propVal, prefix)) {
            //      [stack] CTOR? OBJ CTOR? KEY VAL
            return false;
          }
        }
      } else {
        if (!emitTree(propVal)) {
          //        [stack] CTOR? OBJ CTOR? KEY? VAL
          return false;
        }
      }

      if (propVal->is<FunctionNode>() &&
          propVal->as<FunctionNode>().funbox()->needsHomeObject()) {
        if (!pe.emitInitHomeObject()) {
          //        [stack] CTOR? OBJ CTOR? KEY? FUN
          return false;
        }
      }

#ifdef ENABLE_DECORATORS
      if (prop->is<ClassMethod>()) {
        ClassMethod& method = prop->as<ClassMethod>();
        if (method.decorators() && !method.decorators()->empty()) {
          DecoratorEmitter::Kind kind;
          switch (method.accessorType()) {
            case AccessorType::Getter:
              kind = DecoratorEmitter::Getter;
              break;
            case AccessorType::Setter:
              kind = DecoratorEmitter::Setter;
              break;
            case AccessorType::None:
              kind = DecoratorEmitter::Method;
              break;
          }

          if (!method.isStatic()) {
            bool hasKeyOnStack = key->isKind(ParseNodeKind::NumberExpr) ||
                                 key->isKind(ParseNodeKind::ComputedName);
            if (!emitDupAt(hasKeyOnStack ? 4 : 3)) {
              //        [stack] ADDINIT OBJ KEY? VAL ADDINIT
              return false;
            }
          } else {
            // TODO: See bug 1868220 for support for static methods.
            // Note: Key will be present if this has a private name.
            if (!emit1(JSOp::Undefined)) {
              //        [stack] CTOR OBJ CTOR KEY? VAL ADDINIT
              return false;
            }
          }

          if (!emit1(JSOp::Swap)) {
            //        [stack] ADDINIT CTOR? OBJ CTOR? KEY? ADDINIT VAL
            return false;
          }

          // The decorators are applied to the current value on the stack,
          // possibly replacing it.
          DecoratorEmitter de(this);
          if (!de.emitApplyDecoratorsToElementDefinition(
                  kind, key, method.decorators(), method.isStatic())) {
            //        [stack] ADDINIT CTOR? OBJ CTOR? KEY? ADDINIT VAL
            return false;
          }

          if (!emit1(JSOp::Swap)) {
            //        [stack] ADDINIT CTOR? OBJ CTOR? KEY? VAL ADDINIT
            return false;
          }

          if (!emitPopN(1)) {
            //        [stack] ADDINIT CTOR? OBJ CTOR? KEY? VAL
            return false;
          }
        }
      }
#endif

      return true;
    };

    PropertyEmitter::Kind kind =
        (type == ClassBody && propdef->as<ClassMethod>().isStatic())
            ? PropertyEmitter::Kind::Static
            : PropertyEmitter::Kind::Prototype;
    if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
        key->isKind(ParseNodeKind::StringExpr)) {
      //            [stack] CTOR? OBJ

      auto keyAtom = key->as<NameNode>().atom();

      // emitClass took care of constructor already.
      if (type == ClassBody &&
          keyAtom == TaggedParserAtomIndex::WellKnown::constructor() &&
          !propdef->as<ClassMethod>().isStatic()) {
        continue;
      }

      if (!pe.prepareForPropValue(propdef->pn_pos.begin, kind)) {
        //          [stack] CTOR? OBJ CTOR?
        return false;
      }

      if (!emitValue()) {
        //          [stack] CTOR? OBJ CTOR? VAL
        return false;
      }

      if (!pe.emitInit(accessorType, keyAtom)) {
        //          [stack] CTOR? OBJ
        return false;
      }

      continue;
    }

    if (key->isKind(ParseNodeKind::NumberExpr)) {
      //            [stack] CTOR? OBJ
      if (!pe.prepareForIndexPropKey(propdef->pn_pos.begin, kind)) {
        //          [stack] CTOR? OBJ CTOR?
        return false;
      }
      if (!emitNumberOp(key->as<NumericLiteral>().value())) {
        //        [stack] CTOR? OBJ CTOR? KEY
        return false;
      }
      if (!pe.prepareForIndexPropValue()) {
        //          [stack] CTOR? OBJ CTOR? KEY
        return false;
      }
      if (!emitValue()) {
        //          [stack] CTOR? OBJ CTOR? KEY VAL
        return false;
      }

      if (!pe.emitInitIndexOrComputed(accessorType)) {
        //          [stack] CTOR? OBJ
        return false;
      }

      continue;
    }

    if (key->isKind(ParseNodeKind::ComputedName)) {
      // Either a proper computed property name or a synthetic computed property
      // name for BigInt keys.

      //            [stack] CTOR? OBJ

      if (!pe.prepareForComputedPropKey(propdef->pn_pos.begin, kind)) {
        //          [stack] CTOR? OBJ CTOR?
        return false;
      }
      if (!emitTree(key->as<UnaryNode>().kid())) {
        //          [stack] CTOR? OBJ CTOR? KEY
        return false;
      }
      if (!pe.prepareForComputedPropValue()) {
        //          [stack] CTOR? OBJ CTOR? KEY
        return false;
      }
      if (!emitValue()) {
        //          [stack] CTOR? OBJ CTOR? KEY VAL
        return false;
      }

      if (!pe.emitInitIndexOrComputed(accessorType)) {
        //          [stack] CTOR? OBJ
        return false;
      }

      continue;
    }

    MOZ_ASSERT(key->isKind(ParseNodeKind::PrivateName));
    MOZ_ASSERT(type == ClassBody);

    auto* privateName = &key->as<NameNode>();

    if (kind == PropertyEmitter::Kind::Prototype) {
      MOZ_ASSERT(accessorType == AccessorType::None);
      if (!pe.prepareForPrivateMethod()) {
        //          [stack] CTOR OBJ
        return false;
      }
      NameOpEmitter noe(this, privateName->atom(),
                        NameOpEmitter::Kind::SimpleAssignment);

      // Ensure the NameOp emitter doesn't push an environment onto the stack,
      // because that would change the stack location of the home object.
      MOZ_ASSERT(noe.loc().kind() == NameLocation::Kind::FrameSlot ||
                 noe.loc().kind() == NameLocation::Kind::EnvironmentCoordinate);

      if (!noe.prepareForRhs()) {
        //          [stack] CTOR OBJ
        return false;
      }
      if (!emitValue()) {
        //          [stack] CTOR OBJ METHOD
        return false;
      }
      if (!noe.emitAssignment()) {
        //          [stack] CTOR OBJ METHOD
        return false;
      }
      if (!emit1(JSOp::Pop)) {
        //          [stack] CTOR OBJ
        return false;
      }
      if (!pe.skipInit()) {
        //          [stack] CTOR OBJ
        return false;
      }
      continue;
    }

    MOZ_ASSERT(kind == PropertyEmitter::Kind::Static);

    //              [stack] CTOR OBJ

    if (!pe.prepareForPrivateStaticMethod(propdef->pn_pos.begin)) {
      //            [stack] CTOR OBJ CTOR
      return false;
    }
    if (!emitGetPrivateName(privateName)) {
      //            [stack] CTOR OBJ CTOR KEY
      return false;
    }
    if (!emitValue()) {
      //            [stack] CTOR OBJ CTOR KEY VAL
      return false;
    }

    if (!pe.emitPrivateStaticMethod(accessorType)) {
      //            [stack] CTOR OBJ
      return false;
    }

    if (privateName->privateNameKind() == PrivateNameKind::Setter) {
      if (!emitDupAt(1)) {
        //          [stack] CTOR OBJ CTOR
        return false;
      }
      if (!emitGetPrivateName(privateName)) {
        //          [stack] CTOR OBJ CTOR NAME
        return false;
      }
      if (!emitAtomOp(JSOp::GetIntrinsic,
                      TaggedParserAtomIndex::WellKnown::NoPrivateGetter())) {
        //          [stack] CTOR OBJ CTOR NAME FUN
        return false;
      }
      if (!emit1(JSOp::InitHiddenElemGetter)) {
        //          [stack] CTOR OBJ CTOR
        return false;
      }
      if (!emit1(JSOp::Pop)) {
        //          [stack] CTOR OBJ
        return false;
      }
    }
  }

  return true;
}

bool BytecodeEmitter::emitPropertyListObjLiteral(ListNode* obj, JSOp op,
                                                 bool useObjLiteralValues) {
  ObjLiteralWriter writer;

#ifdef DEBUG
  // In self-hosted JS, we check duplication only on debug build.
  mozilla::Maybe<mozilla::HashSet<frontend::TaggedParserAtomIndex,
                                  frontend::TaggedParserAtomIndexHasher>>
      selfHostedPropNames;
  if (emitterMode == BytecodeEmitter::SelfHosting) {
    selfHostedPropNames.emplace();
  }
#endif

  if (op == JSOp::Object) {
    writer.beginObject(op);
  } else {
    MOZ_ASSERT(op == JSOp::NewObject);
    writer.beginShape(op);
  }

  for (ParseNode* propdef : obj->contents()) {
    BinaryNode* prop = &propdef->as<BinaryNode>();
    ParseNode* key = prop->left();

    if (key->is<NameNode>()) {
      if (emitterMode == BytecodeEmitter::SelfHosting) {
        auto propName = key->as<NameNode>().atom();
#ifdef DEBUG
        // Self-hosted JS shouldn't contain duplicate properties.
        auto p = selfHostedPropNames->lookupForAdd(propName);
        MOZ_ASSERT(!p);
        if (!selfHostedPropNames->add(p, propName)) {
          js::ReportOutOfMemory(fc);
          return false;
        }
#endif
        writer.setPropNameNoDuplicateCheck(parserAtoms(), propName);
      } else {
        if (!writer.setPropName(parserAtoms(), key->as<NameNode>().atom())) {
          return false;
        }
      }
    } else {
      double numValue = key->as<NumericLiteral>().value();
      int32_t i = 0;
      DebugOnly<bool> numIsInt =
          NumberIsInt32(numValue, &i);  // checked previously.
      MOZ_ASSERT(numIsInt);
      MOZ_ASSERT(
          ObjLiteralWriter::arrayIndexInRange(i));  // checked previously.

      // Ignore indexed properties if we're not storing property values, and
      // rely on InitElem ops to define those. These properties will be either
      // dense elements (not possible to represent in the literal's shape) or
      // sparse elements (enumerated separately, so this doesn't affect property
      // iteration order).
      if (!useObjLiteralValues) {
        continue;
      }

      writer.setPropIndex(i);
    }

    if (useObjLiteralValues) {
      MOZ_ASSERT(op == JSOp::Object);
      ParseNode* value = prop->right();
      if (!emitObjLiteralValue(writer, value)) {
        return false;
      }
    } else {
      if (!writer.propWithUndefinedValue(fc)) {
        return false;
      }
    }
  }

  GCThingIndex index;
  if (!addObjLiteralData(writer, &index)) {
    return false;
  }

  // JSOp::Object may only be used by (top-level) run-once scripts.
  MOZ_ASSERT_IF(op == JSOp::Object,
                sc->isTopLevelContext() && sc->treatAsRunOnce());

  if (!emitGCIndexOp(op, index)) {
    //              [stack] OBJ
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitDestructuringRestExclusionSetObjLiteral(
    ListNode* pattern) {
  // Note: if we want to squeeze out a little more performance, we could switch
  // to the `JSOp::Object` opcode, because the exclusion set object is never
  // exposed to the user, so it's safe to bake the object into the bytecode.
  constexpr JSOp op = JSOp::NewObject;

  ObjLiteralWriter writer;
  writer.beginShape(op);

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

    TaggedParserAtomIndex atom;
    if (member->isKind(ParseNodeKind::MutateProto)) {
      atom = TaggedParserAtomIndex::WellKnown::proto_();
    } else {
      ParseNode* key = member->as<BinaryNode>().left();
      atom = key->as<NameNode>().atom();
    }

    if (!writer.setPropName(parserAtoms(), atom)) {
      return false;
    }

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

  GCThingIndex index;
  if (!addObjLiteralData(writer, &index)) {
    return false;
  }

  if (!emitGCIndexOp(op, index)) {
    //              [stack] OBJ
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitObjLiteralArray(ListNode* array) {
  MOZ_ASSERT(checkSingletonContext());

  constexpr JSOp op = JSOp::Object;

  ObjLiteralWriter writer;
  writer.beginArray(op);

  writer.beginDenseArrayElements();
  for (ParseNode* elem : array->contents()) {
    if (!emitObjLiteralValue(writer, elem)) {
      return false;
    }
  }

  GCThingIndex index;
  if (!addObjLiteralData(writer, &index)) {
    return false;
  }

  if (!emitGCIndexOp(op, index)) {
    //              [stack] OBJ
    return false;
  }

  return true;
}

bool BytecodeEmitter::isRHSObjLiteralCompatible(ParseNode* value) const {
  return value->isKind(ParseNodeKind::NumberExpr) ||
         value->isKind(ParseNodeKind::TrueExpr) ||
         value->isKind(ParseNodeKind::FalseExpr) ||
         value->isKind(ParseNodeKind::NullExpr) ||
         value->isKind(ParseNodeKind::RawUndefinedExpr) ||
         value->isKind(ParseNodeKind::StringExpr) ||
         value->isKind(ParseNodeKind::TemplateStringExpr);
}

bool BytecodeEmitter::emitObjLiteralValue(ObjLiteralWriter& writer,
                                          ParseNode* value) {
  MOZ_ASSERT(isRHSObjLiteralCompatible(value));
  if (value->isKind(ParseNodeKind::NumberExpr)) {
    double numValue = value->as<NumericLiteral>().value();
    int32_t i = 0;
    js::Value v;
    if (NumberIsInt32(numValue, &i)) {
      v.setInt32(i);
    } else {
      v.setDouble(numValue);
    }
    if (!writer.propWithConstNumericValue(fc, v)) {
      return false;
    }
  } else if (value->isKind(ParseNodeKind::TrueExpr)) {
    if (!writer.propWithTrueValue(fc)) {
      return false;
    }
  } else if (value->isKind(ParseNodeKind::FalseExpr)) {
    if (!writer.propWithFalseValue(fc)) {
      return false;
    }
  } else if (value->isKind(ParseNodeKind::NullExpr)) {
    if (!writer.propWithNullValue(fc)) {
      return false;
    }
  } else if (value->isKind(ParseNodeKind::RawUndefinedExpr)) {
    if (!writer.propWithUndefinedValue(fc)) {
      return false;
    }
  } else if (value->isKind(ParseNodeKind::StringExpr) ||
             value->isKind(ParseNodeKind::TemplateStringExpr)) {
    if (!writer.propWithAtomValue(fc, parserAtoms(),
                                  value->as<NameNode>().atom())) {
      return false;
    }
  } else {
    MOZ_CRASH("Unexpected parse node");
  }
  return true;
}

static bool NeedsPrivateBrand(ParseNode* member) {
  return member->is<ClassMethod>() &&
         member->as<ClassMethod>().name().isKind(ParseNodeKind::PrivateName) &&
         !member->as<ClassMethod>().isStatic();
}

#ifdef ENABLE_DECORATORS
static bool HasDecorators(ParseNode* member) {
  return member->is<ClassMethod>() && member->as<ClassMethod>().decorators();
}
#endif

mozilla::Maybe<MemberInitializers> BytecodeEmitter::setupMemberInitializers(
    ListNode* classMembers, FieldPlacement placement) const {
  bool isStatic = placement == FieldPlacement::Static;

  size_t numFields = 0;
  size_t numPrivateInitializers = 0;
  bool hasPrivateBrand = false;
#ifdef ENABLE_DECORATORS
  bool hasDecorators = false;
#endif
  for (ParseNode* member : classMembers->contents()) {
    if (NeedsFieldInitializer(member, isStatic)) {
      numFields++;
    } else if (NeedsAccessorInitializer(member, isStatic)) {
      numPrivateInitializers++;
      hasPrivateBrand = true;
    } else if (NeedsPrivateBrand(member)) {
      hasPrivateBrand = true;
    }
#ifdef ENABLE_DECORATORS
    if (!hasDecorators && HasDecorators(member)) {
      hasDecorators = true;
    }
#endif
  }

  // If there are more initializers than can be represented, return invalid.
  if (numFields + numPrivateInitializers >
      MemberInitializers::MaxInitializers) {
    return Nothing();
  }
  return Some(MemberInitializers(hasPrivateBrand,
#ifdef ENABLE_DECORATORS
                                 hasDecorators,
#endif
                                 numFields + numPrivateInitializers));
}

// Purpose of .fieldKeys:
// Computed field names (`["x"] = 2;`) must be ran at class-evaluation time,
// not object construction time. The transformation to do so is roughly as
// follows:
//
// class C {
//   [keyExpr] = valueExpr;
// }
// -->
// let .fieldKeys = [keyExpr];
// let .initializers = [
//   () => {
//     this[.fieldKeys[0]] = valueExpr;
//   }
// ];
// class C {
//   constructor() {
//     .initializers[0]();
//   }
// }
//
// BytecodeEmitter::emitCreateFieldKeys does `let .fieldKeys = [...];`
// BytecodeEmitter::emitPropertyList fills in the elements of the array.
// See GeneralParser::fieldInitializer for the `this[.fieldKeys[0]]` part.
bool BytecodeEmitter::emitCreateFieldKeys(ListNode* obj,
                                          FieldPlacement placement) {
  bool isStatic = placement == FieldPlacement::Static;
  auto isFieldWithComputedName = [isStatic](ParseNode* propdef) {
    return propdef->is<ClassField>() &&
           propdef->as<ClassField>().isStatic() == isStatic &&
           propdef->as<ClassField>().name().getKind() ==
               ParseNodeKind::ComputedName;
  };

  size_t numFieldKeys = std::count_if(
      obj->contents().begin(), obj->contents().end(), isFieldWithComputedName);
  if (numFieldKeys == 0) {
    return true;
  }

  auto fieldKeys =
      isStatic ? TaggedParserAtomIndex::WellKnown::dot_staticFieldKeys_()
               : TaggedParserAtomIndex::WellKnown::dot_fieldKeys_();
  NameOpEmitter noe(this, fieldKeys, NameOpEmitter::Kind::Initialize);
  if (!noe.prepareForRhs()) {
    return false;
  }

  if (!emitUint32Operand(JSOp::NewArray, numFieldKeys)) {
    //              [stack] ARRAY
    return false;
  }

  if (!noe.emitAssignment()) {
    //              [stack] ARRAY
    return false;
  }

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

  return true;
}

static bool HasInitializer(ParseNode* node, bool isStaticContext) {
  return (node->is<ClassField>() &&
          node->as<ClassField>().isStatic() == isStaticContext) ||
         (isStaticContext && node->is<StaticClassBlock>());
}

static FunctionNode* GetInitializer(ParseNode* node, bool isStaticContext) {
  MOZ_ASSERT(HasInitializer(node, isStaticContext));
  MOZ_ASSERT_IF(!node->is<ClassField>(), isStaticContext);
  return node->is<ClassField>() ? node->as<ClassField>().initializer()
                                : node->as<StaticClassBlock>().function();
}

static bool IsPrivateInstanceAccessor(const ClassMethod* classMethod) {
  return !classMethod->isStatic() &&
         classMethod->name().isKind(ParseNodeKind::PrivateName) &&
         classMethod->accessorType() != AccessorType::None;
}

bool BytecodeEmitter::emitCreateMemberInitializers(ClassEmitter& ce,
                                                   ListNode* obj,
                                                   FieldPlacement placement
#ifdef ENABLE_DECORATORS
                                                   ,
                                                   bool hasHeritage
#endif
) {
  // FieldPlacement::Instance, hasHeritage == false
  //                [stack] HOME
  //
  // FieldPlacement::Instance, hasHeritage == true
  //                [stack] HOME HERIT
  //
  // FieldPlacement::Static
  //                [stack] CTOR HOME
#ifdef ENABLE_DECORATORS
  MOZ_ASSERT_IF(placement == FieldPlacement::Static, !hasHeritage);
#endif
  mozilla::Maybe<MemberInitializers> memberInitializers =
      setupMemberInitializers(obj, placement);
  if (!memberInitializers) {
    ReportAllocationOverflow(fc);
    return false;
  }

  size_t numInitializers = memberInitializers->numMemberInitializers;
  if (numInitializers == 0) {
    return true;
  }

  bool isStatic = placement == FieldPlacement::Static;
  if (!ce.prepareForMemberInitializers(numInitializers, isStatic)) {
    //              [stack] HOME HERIT? ARR
    // or:
    //              [stack] CTOR HOME ARR
    return false;
  }

  // Private accessors could be used in the field initializers, so make sure
  // accessor initializers appear earlier in the .initializers array so they
  // run first. Static private methods are not initialized using initializers
  // (emitPropertyList emits bytecode to stamp them onto the constructor), so
  // skip this step if isStatic.
  if (!isStatic) {
    if (!emitPrivateMethodInitializers(ce, obj)) {
      return false;
    }
  }

  for (ParseNode* propdef : obj->contents()) {
    if (!HasInitializer(propdef, isStatic)) {
      continue;
    }

    FunctionNode* initializer = GetInitializer(propdef, isStatic);

    if (!ce.prepareForMemberInitializer()) {
      return false;
    }
    if (!emitTree(initializer)) {
      //            [stack] HOME HERIT? ARR LAMBDA
      // or:
      //            [stack] CTOR HOME ARR LAMBDA
      return false;
    }
    if (initializer->funbox()->needsHomeObject()) {
      MOZ_ASSERT(initializer->funbox()->allowSuperProperty());
      if (!ce.emitMemberInitializerHomeObject(isStatic)) {
        //          [stack] HOME HERIT? ARR LAMBDA
        // or:
        //          [stack] CTOR HOME ARR LAMBDA
        return false;
      }
    }
    if (!ce.emitStoreMemberInitializer()) {
      //            [stack] HOME HERIT? ARR
      // or:
      //            [stack] CTOR HOME ARR
      return false;
    }
  }

#ifdef ENABLE_DECORATORS
  // Index to use to append new initializers returned by decorators to the array
  if (!emitNumberOp(numInitializers)) {
    //            [stack] HOME HERIT? ARR I
    // or:
    //            [stack] CTOR HOME ARR I
    return false;
  }

  for (ParseNode* propdef : obj->contents()) {
    if (!propdef->is<ClassField>()) {
      continue;
    }
    ClassField* field = &propdef->as<ClassField>();
    if (field->isStatic() != isStatic) {
      continue;
    }
    if (field->decorators() && !field->decorators()->empty()) {
      DecoratorEmitter de(this);
      if (!field->hasAccessor()) {
        if (!emitDupAt((hasHeritage || isStatic) ? 4 : 3)) {
          //            [stack] ADDINIT HOME HERIT? ARR I ADDINIT
          // or:
          //            [stack] ADDINIT CTOR HOME ARR I ADDINIT
          return false;
        }
        if (!de.emitApplyDecoratorsToFieldDefinition(
                &field->name(), field->decorators(), field->isStatic())) {
          //        [stack] HOME HERIT? ARR I ADDINIT INITS
          // or:
          //        [stack] CTOR HOME ARR I ADDINIT INITS
          return false;
        }
        if (!emit1(JSOp::Swap)) {
          //        [stack] HOME HERIT? ARR I INITS ADDINIT
          // or:
          //        [stack] CTOR HOME ARR I INITS ADDINIT
          return false;
        }
        if (!emitPopN(1)) {
          //            [stack] ADDINIT HOME HERIT? ARR I INITS
          // or:
          //            [stack] ADDINIT CTOR HOME ARR I INITS
          return false;
        }
      } else {
        ClassMethod* accessorGetterNode = field->accessorGetterNode();
        auto accessorGetterKeyAtom =
            accessorGetterNode->left()->as<NameNode>().atom();
        ClassMethod* accessorSetterNode = field->accessorSetterNode();
        auto accessorSetterKeyAtom =
            accessorSetterNode->left()->as<NameNode>().atom();
        if (!IsPrivateInstanceAccessor(accessorGetterNode)) {
          if (!emitTree(&accessorGetterNode->method())) {
            //      [stack] ADDINIT HOME HERIT? ARR I GET
            // or:
            //      [stack] ADDINIT CTOR HOME ARR I GET
            return false;
          }
          if (!emitTree(&accessorSetterNode->method())) {
            //      [stack] ADDINIT HOME HERIT? ARR I GET
            //      SET
            // or:
            //      [stack] ADDINIT CTOR HOME ARR I GET SET
            return false;
          }
        } else {
          MOZ_ASSERT(IsPrivateInstanceAccessor(accessorSetterNode));
          auto getAccessor = [this](
                                 ClassMethod* classMethod,
                                 TaggedParserAtomIndex& updatedAtom) -> bool {
            //      [stack]

            // Synthesize a name for the lexical variable that will store the
            // private method body.
            TaggedParserAtomIndex name =
                classMethod->name().as<NameNode>().atom();
            AccessorType accessorType = classMethod->accessorType();
            StringBuilder storedMethodName(fc);
            if (!storedMethodName.append(parserAtoms(), name)) {
              return false;
            }
            if (!storedMethodName.append(accessorType == AccessorType::Getter
                                             ? ".getter"
                                             : ".setter")) {
              return false;
            }
            updatedAtom = storedMethodName.finishParserAtom(parserAtoms(), fc);
            if (!updatedAtom) {
              return false;
            }

            return emitGetName(updatedAtom);
            //      [stack] ACCESSOR
          };

          if (!getAccessor(accessorGetterNode, accessorGetterKeyAtom)) {
            //      [stack] ADDINIT HOME HERIT? ARR I GET
            // or:
            //      [stack] ADDINIT CTOR HOME ARR I GET
            return false;
          };

          if (!getAccessor(accessorSetterNode, accessorSetterKeyAtom)) {
            //      [stack] ADDINIT HOME HERIT? ARR I GET SET
            // or:
            //      [stack] ADDINIT CTOR HOME ARR I GET SET
            return false;
          };
        }

        if (!emitDupAt((hasHeritage || isStatic) ? 6 : 5)) {
          //      [stack] ADDINIT HOME HERIT? ARR I GET SET ADDINIT
          // or:
          //      [stack] ADDINIT CTOR HOME ARR I GET SET ADDINIT
          return false;
        }

        if (!emitUnpickN(2)) {
          //      [stack] ADDINIT HOME HERIT? ARR I ADDINIT GET SET
          // or:
          //      [stack] ADDINIT CTOR HOME ARR I ADDINIT GET SET
          return false;
        }

        if (!de.emitApplyDecoratorsToAccessorDefinition(
                &field->name(), field->decorators(), field->isStatic())) {
          //        [stack] ADDINIT HOME HERIT? ARR I ADDINIT GET SET INITS
          // or:
          //        [stack] ADDINIT CTOR HOME ARR I ADDINIT GET SET INITS
          return false;
        }

        if (!emitPickN(3)) {
          //      [stack] HOME HERIT? ARR I GET SET INITS ADDINIT
          // or:
          //      [stack] CTOR HOME ARR I GET SET INITS ADDINIT
          return false;
        }

        if (!emitPopN(1)) {
          //      [stack] ADDINIT HOME HERIT? ARR I GET SET INITS
          // or:
          //      [stack] ADDINIT CTOR HOME ARR I GET SET INITS
          return false;
        }

        if (!emitUnpickN(2)) {
          //        [stack] ADDINIT HOME HERIT? ARR I INITS GET SET
          // or:
          //        [stack] ADDINIT CTOR HOME ARR I INITS GET SET
          return false;
        }

        if (!IsPrivateInstanceAccessor(accessorGetterNode)) {
          if (!isStatic) {
            if (!emitDupAt(hasHeritage ? 6 : 5)) {
              //    [stack] ADDINIT HOME HERIT? ARR I INITS GET SET HOME
              return false;
            }
          } else {
            if (!emitDupAt(6)) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET SET CTOR
              return false;
            }
            if (!emitDupAt(6)) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET SET CTOR HOME
              return false;
            }
          }

          PropertyEmitter::Kind kind = field->isStatic()
                                           ? PropertyEmitter::Kind::Static
                                           : PropertyEmitter::Kind::Prototype;
          if (!accessorGetterNode->name().isKind(ParseNodeKind::PrivateName)) {
            MOZ_ASSERT(
                !accessorSetterNode->name().isKind(ParseNodeKind::PrivateName));

            if (!ce.prepareForPropValue(propdef->pn_pos.begin, kind)) {
              //    [stack] ADDINIT HOME HERIT? ARR I INITS GET SET HOME
              // or:
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET SET CTOR HOME CTOR
              return false;
            }
            if (!emitPickN(isStatic ? 3 : 1)) {
              //    [stack] ADDINIT HOME HERIT? ARR I INITS GET HOME SET
              // or:
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET CTOR HOME CTOR SET
              return false;
            }
            if (!ce.emitInit(AccessorType::Setter, accessorSetterKeyAtom)) {
              //    [stack] ADDINIT HOME HERIT? ARR I INITS GET HOME
              // or:
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET CTOR HOME
              return false;
            }

            if (!ce.prepareForPropValue(propdef->pn_pos.begin, kind)) {
              //    [stack] ADDINIT HOME HERIT? ARR I INITS GET HOME
              // or:
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET CTOR HOME CTOR
              return false;
            }
            if (!emitPickN(isStatic ? 3 : 1)) {
              //    [stack] ADDINIT HOME HERIT? ARR I INITS HOME GET
              // or:
              //    [stack] ADDINIT CTOR HOME ARR I INITS CTOR HOME CTOR GET
              return false;
            }
            if (!ce.emitInit(AccessorType::Getter, accessorGetterKeyAtom)) {
              //    [stack] ADDINIT HOME HERIT? ARR I INITS HOME
              // or:
              //    [stack] ADDINIT CTOR HOME ARR I INITS CTOR HOME
              return false;
            }
          } else {
            MOZ_ASSERT(isStatic);
            // The getter and setter share the same name.
            if (!emitNewPrivateName(accessorSetterKeyAtom,
                                    accessorSetterKeyAtom)) {
              return false;
            }
            if (!ce.prepareForPrivateStaticMethod(propdef->pn_pos.begin)) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET SET CTOR HOME CTOR
              return false;
            }
            if (!emitGetPrivateName(
                    &accessorSetterNode->name().as<NameNode>())) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET SET CTOR HOME CTOR
              //    KEY
              return false;
            }
            if (!emitPickN(4)) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET CTOR HOME CTOR KEY
              //    SET
              return false;
            }
            if (!ce.emitPrivateStaticMethod(AccessorType::Setter)) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET CTOR HOME
              return false;
            }

            if (!ce.prepareForPrivateStaticMethod(propdef->pn_pos.begin)) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET CTOR HOME CTOR
              return false;
            }
            if (!emitGetPrivateName(
                    &accessorGetterNode->name().as<NameNode>())) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS GET CTOR HOME CTOR KEY
              return false;
            }
            if (!emitPickN(4)) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS CTOR HOME CTOR KEY GET
              return false;
            }
            if (!ce.emitPrivateStaticMethod(AccessorType::Getter)) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS CTOR HOME
              return false;
            }
          }

          if (!isStatic) {
            if (!emitPopN(1)) {
              //    [stack] ADDINIT HOME HERIT? ARR I INITS
              return false;
            }
          } else {
            if (!emitPopN(2)) {
              //    [stack] ADDINIT CTOR HOME ARR I INITS
              return false;
            }
          }
        } else {
          MOZ_ASSERT(IsPrivateInstanceAccessor(accessorSetterNode));

          if (!emitLexicalInitialization(accessorSetterKeyAtom)) {
            //      [stack] ADDINIT HOME HERIT? ARR I INITS GET SET
            // or:
            //      [stack] ADDINIT CTOR HOME ARR I INITS GET SET
            return false;
          }

          if (!emitPopN(1)) {
            //      [stack] ADDINIT HOME HERIT? ARR I INITS GET
            // or:
            //      [stack] ADDINIT CTOR HOME ARR I INITS GET
            return false;
          }

          if (!emitLexicalInitialization(accessorGetterKeyAtom)) {
            //      [stack] ADDINIT HOME HERIT? ARR I INITS GET
            // or:
            //      [stack] ADDINIT CTOR HOME ARR I INITS GET
            return false;
          }

          if (!emitPopN(1)) {
            //      [stack] ADDINIT HOME HERIT? ARR I INITS
            // or:
            //      [stack] ADDINIT CTOR HOME ARR I INITS
            return false;
          }
        }
      }
      if (!emit1(JSOp::InitElemInc)) {
        //          [stack] ADDINIT HOME HERIT? ARR I
        // or:
        //          [stack] ADDINIT CTOR HOME ARR I
        return false;
      }
    }
  }

  // Pop I
  if (!emitPopN(1)) {
    //              [stack] ADDINIT HOME HERIT? ARR
    // or:
    //              [stack] ADDINIT CTOR HOME ARR
    return false;
  }
#endif

  if (!ce.emitMemberInitializersEnd()) {
    //              [stack] ADDINIT HOME HERIT?
    // or:
    //              [stack] ADDINIT CTOR HOME
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitPrivateMethodInitializers(ClassEmitter& ce,
                                                    ListNode* obj) {
  for (ParseNode* propdef : obj->contents()) {
    if (!propdef->is<ClassMethod>()) {
      continue;
    }
    auto* classMethod = &propdef->as<ClassMethod>();

    // Skip over anything which isn't a private instance accessor.
    if (!IsPrivateInstanceAccessor(classMethod)) {
      continue;
    }

    if (!ce.prepareForMemberInitializer()) {
      //            [stack] HOMEOBJ HERITAGE? ARRAY
      // or:
      //            [stack] CTOR HOMEOBJ ARRAY
      return false;
    }

    // Synthesize a name for the lexical variable that will store the
    // private method body.
    TaggedParserAtomIndex name = classMethod->name().as<NameNode>().atom();
    AccessorType accessorType = classMethod->accessorType();
    StringBuilder storedMethodName(fc);
    if (!storedMethodName.append(parserAtoms(), name)) {
      return false;
    }
    if (!storedMethodName.append(
            accessorType == AccessorType::Getter ? ".getter" : ".setter")) {
      return false;
    }
    auto storedMethodAtom =
        storedMethodName.finishParserAtom(parserAtoms(), fc);

    // Emit the private method body and store it as a lexical var.
    if (!emitFunction(&classMethod->method())) {
      //            [stack] HOMEOBJ HERITAGE? ARRAY METHOD
      // or:
      //            [stack] CTOR HOMEOBJ ARRAY METHOD
      return false;
    }
    // The private method body needs to access the home object,
    // and the CE knows where that is on the stack.
    if (classMethod->method().funbox()->needsHomeObject()) {
      if (!ce.emitMemberInitializerHomeObject(false)) {
        //          [stack] HOMEOBJ HERITAGE? ARRAY METHOD
        // or:
        //          [stack] CTOR HOMEOBJ ARRAY METHOD
        return false;
      }
    }
    if (!emitLexicalInitialization(storedMethodAtom)) {
      //            [stack] HOMEOBJ HERITAGE? ARRAY METHOD
      // or:
      //            [stack] CTOR HOMEOBJ ARRAY METHOD
      return false;
    }
    if (!emit1(JSOp::Pop)) {
      //            [stack] HOMEOBJ HERITAGE? ARRAY
      // or:
      //            [stack] CTOR HOMEOBJ ARRAY
      return false;
    }

    if (!emitPrivateMethodInitializer(classMethod, storedMethodAtom)) {
      //            [stack] HOMEOBJ HERITAGE? ARRAY
      // or:
      //            [stack] CTOR HOMEOBJ ARRAY
      return false;
    }

    // Store the emitted initializer function into the .initializers array.
    if (!ce.emitStoreMemberInitializer()) {
      //            [stack] HOMEOBJ HERITAGE? ARRAY
      // or:
      //            [stack] CTOR HOMEOBJ ARRAY
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitPrivateMethodInitializer(
    ClassMethod* classMethod, TaggedParserAtomIndex storedMethodAtom) {
  MOZ_ASSERT(IsPrivateInstanceAccessor(classMethod));

  auto* name = &classMethod->name().as<NameNode>();

  // Emit the synthesized initializer function.
  FunctionNode* funNode = classMethod->initializerIfPrivate();
  MOZ_ASSERT(funNode);
  FunctionBox* funbox = funNode->funbox();
  FunctionEmitter fe(this, funbox, funNode->syntaxKind(),
                     FunctionEmitter::IsHoisted::No);
  if (!fe.prepareForNonLazy()) {
    //              [stack]
    return false;
  }

  BytecodeEmitter bce2(this, funbox);
  if (!bce2.init(funNode->pn_pos)) {
    return false;
  }
  ParamsBodyNode* paramsBody = funNode->body();
  FunctionScriptEmitter fse(&bce2, funbox, Nothing(), Nothing());
  if (!fse.prepareForParameters()) {
    //              [stack]
    return false;
  }
  if (!bce2.emitFunctionFormalParameters(paramsBody)) {
    //              [stack]
    return false;
  }
  if (!fse.prepareForBody()) {
    //              [stack]
    return false;
  }

  if (!bce2.emit1(JSOp::FunctionThis)) {
    //              [stack] THIS
    return false;
  }
  if (!bce2.emitGetPrivateName(name)) {
    //              [stack] THIS NAME
    return false;
  }
  if (!bce2.emitGetName(storedMethodAtom)) {
    //              [stack] THIS NAME METHOD
    return false;
  }

  switch (name->privateNameKind()) {
    case PrivateNameKind::Setter:
      if (!bce2.emit1(JSOp::InitHiddenElemSetter)) {
        //          [stack] THIS
        return false;
      }
      if (!bce2.emitGetPrivateName(name)) {
        //          [stack] THIS NAME
        return false;
      }
      if (!bce2.emitAtomOp(
              JSOp::GetIntrinsic,
              TaggedParserAtomIndex::WellKnown::NoPrivateGetter())) {
        //          [stack] THIS NAME FUN
        return false;
      }
      if (!bce2.emit1(JSOp::InitHiddenElemGetter)) {
        //          [stack] THIS
        return false;
      }
      break;
    case PrivateNameKind::Getter:
    case PrivateNameKind::GetterSetter:
      if (classMethod->accessorType() == AccessorType::Getter) {
        if (!bce2.emit1(JSOp::InitHiddenElemGetter)) {
          //        [stack] THIS
          return false;
        }
      } else {
        if (!bce2.emit1(JSOp::InitHiddenElemSetter)) {
          //        [stack] THIS
          return false;
        }
      }
      break;
    default:
      MOZ_CRASH("Invalid op");
  }

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

  if (!fse.emitEndBody()) {
    //              [stack]
    return false;
  }
  if (!fse.intoStencil()) {
    return false;
  }

  if (!fe.emitNonLazyEnd()) {
    //              [stack] HOMEOBJ HERITAGE? ARRAY FUN
    // or:
    //              [stack] CTOR HOMEOBJ ARRAY FUN
    return false;
  }

  return true;
}

const MemberInitializers& BytecodeEmitter::findMemberInitializersForCall()
    const {
  for (const auto* current = this; current; current = current->parent) {
    if (current->sc->isFunctionBox()) {
      FunctionBox* funbox = current->sc->asFunctionBox();

      if (funbox->isArrow()) {
        continue;
      }

      // If we found a non-arrow / non-constructor we were never allowed to
      // expect fields in the first place.
      MOZ_RELEASE_ASSERT(funbox->isClassConstructor());

      return funbox->useMemberInitializers() ? funbox->memberInitializers()
                                             : MemberInitializers::Empty();
    }
  }

  MOZ_RELEASE_ASSERT(compilationState.scopeContext.memberInitializers);
  return *compilationState.scopeContext.memberInitializers;
}

bool BytecodeEmitter::emitInitializeInstanceMembers(
    bool isDerivedClassConstructor) {
  const MemberInitializers& memberInitializers =
      findMemberInitializersForCall();
  MOZ_ASSERT(memberInitializers.valid);

  if (memberInitializers.hasPrivateBrand) {
    // This is guaranteed to run after super(), so we don't need TDZ checks.
    if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_this_())) {
      //            [stack] THIS
      return false;
    }
    if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_privateBrand_())) {
      //            [stack] THIS BRAND
      return false;
    }
    if (isDerivedClassConstructor) {
      if (!emitCheckPrivateField(ThrowCondition::ThrowHas,
                                 ThrowMsgKind::PrivateBrandDoubleInit)) {
        //          [stack] THIS BRAND BOOL
        return false;
      }
      if (!emit1(JSOp::Pop)) {
        //          [stack] THIS BRAND
        return false;
      }
    }
    if (!emit1(JSOp::Null)) {
      //            [stack] THIS BRAND NULL
      return false;
    }
    if (!emit1(JSOp::InitHiddenElem)) {
      //            [stack] THIS
      return false;
    }
    if (!emit1(JSOp::Pop)) {
      //            [stack]
      return false;
    }
  }

  size_t numInitializers = memberInitializers.numMemberInitializers;
  if (numInitializers > 0) {
    if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_initializers_())) {
      //              [stack] ARRAY
      return false;
    }

    for (size_t index = 0; index < numInitializers; index++) {
      if (index < numInitializers - 1) {
        // We Dup to keep the array around (it is consumed in the bytecode
        // below) for next iterations of this loop, except for the last
        // iteration, which avoids an extra Pop at the end of the loop.
        if (!emit1(JSOp::Dup)) {
          //          [stack] ARRAY ARRAY
          return false;
        }
      }

      if (!emitNumberOp(index)) {
        //            [stack] ARRAY? ARRAY INDEX
        return false;
      }

      if (!emit1(JSOp::GetElem)) {
        //            [stack] ARRAY? FUNC
        return false;
      }

      // This is guaranteed to run after super(), so we don't need TDZ checks.
      if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_this_())) {
        //            [stack] ARRAY? FUNC THIS
        return false;
      }

      // Callee is always internal function.
      if (!emitCall(JSOp::CallIgnoresRv, 0)) {
        //            [stack] ARRAY? RVAL
        return false;
      }

      if (!emit1(JSOp::Pop)) {
        //            [stack] ARRAY?
        return false;
      }
    }
#ifdef ENABLE_DECORATORS
    if (memberInitializers.hasDecorators) {
      // Decorators Proposal
      // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-initializeinstanceelements
      // 4. For each element e of elements, do
      //     4.a. If elementRecord.[[Kind]] is field or accessor, then
      //         4.a.i. Perform ? InitializeFieldOrAccessor(O, elementRecord).
      //

      // TODO: (See Bug 1817993) At the moment, we're applying the
      // initialization logic in two steps. The pre-decorator initialization
      // code runs, stores the initial value, and then we retrieve it here and
      // apply the initializers added by decorators. We should unify these two
      // steps.
      if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_initializers_())) {
        //              [stack] ARRAY
        return false;
      }

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

      if (!emitAtomOp(JSOp::GetProp,
                      TaggedParserAtomIndex::WellKnown::length())) {
        //          [stack] ARRAY LENGTH
        return false;
      }

      if (!emitNumberOp(static_cast<double>(numInitializers))) {
        //          [stack] ARRAY LENGTH INDEX
        return false;
      }

      InternalWhileEmitter wh(this);
      // At this point, we have no context to determine offsets in the
      // code for this while statement. Ideally, it would correspond to
      // the field we're initializing.
      if (!wh.emitCond()) {
        //          [stack] ARRAY LENGTH INDEX
        return false;
      }

      if (!emit1(JSOp::Dup)) {
        //          [stack] ARRAY LENGTH INDEX INDEX
        return false;
      }

      if (!emitDupAt(2)) {
        //          [stack] ARRAY LENGTH INDEX INDEX LENGTH
        return false;
      }

      if (!emit1(JSOp::Lt)) {
        //          [stack] ARRAY LENGTH INDEX BOOL
        return false;
      }

      if (!wh.emitBody()) {
        //          [stack] ARRAY LENGTH INDEX
        return false;
      }

      if (!emitDupAt(2)) {
        //          [stack] ARRAY LENGTH INDEX ARRAY
        return false;
      }

      if (!emitDupAt(1)) {
        //          [stack] ARRAY LENGTH INDEX ARRAY INDEX
        return false;
      }

      // Retrieve initializers for this field
      if (!emit1(JSOp::GetElem)) {
        //            [stack] ARRAY LENGTH INDEX INITIALIZERS
        return false;
      }

      // This is guaranteed to run after super(), so we don't need TDZ checks.
      if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_this_())) {
        //            [stack] ARRAY LENGTH INDEX INITIALIZERS THIS
        return false;
      }

      if (!emit1(JSOp::Swap)) {
        //            [stack] ARRAY LENGTH INDEX THIS INITIALIZERS
        return false;
      }

      DecoratorEmitter de(this);
      if (!de.emitInitializeFieldOrAccessor()) {
        //            [stack] ARRAY LENGTH INDEX
        return false;
      }

      if (!emit1(JSOp::Inc)) {
        //            [stack] ARRAY LENGTH INDEX
        return false;
      }

      if (!wh.emitEnd()) {
        //          [stack] ARRAY LENGTH INDEX
        return false;
      }

      if (!emitPopN(3)) {
        //            [stack]
        return false;
      }
      // 5. Return unused.

      if (!de.emitCallExtraInitializers(TaggedParserAtomIndex::WellKnown::
                                            dot_instanceExtraInitializers_())) {
        return false;
      }
    }
#endif
  }
  return true;
}

bool BytecodeEmitter::emitInitializeStaticFields(ListNode* classMembers) {
  auto isStaticField = [](ParseNode* propdef) {
    return HasInitializer(propdef, true);
  };
  size_t numFields =
      std::count_if(classMembers->contents().begin(),
                    classMembers->contents().end(), isStaticField);

  if (numFields == 0) {
    return true;
  }

  if (!emitGetName(
          TaggedParserAtomIndex::WellKnown::dot_staticInitializers_())) {
    //              [stack] CTOR ARRAY
    return false;
  }

  for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
    bool hasNext = fieldIndex < numFields - 1;
    if (hasNext) {
      // We Dup to keep the array around (it is consumed in the bytecode below)
      // for next iterations of this loop, except for the last iteration, which
      // avoids an extra Pop at the end of the loop.
      if (!emit1(JSOp::Dup)) {
        //          [stack] CTOR ARRAY ARRAY
        return false;
      }
    }

    if (!emitNumberOp(fieldIndex)) {
      //            [stack] CTOR ARRAY? ARRAY INDEX
      return false;
    }

    if (!emit1(JSOp::GetElem)) {
      //            [stack] CTOR ARRAY? FUNC
      return false;
    }

    if (!emitDupAt(1 + hasNext)) {
      //            [stack] CTOR ARRAY? FUNC CTOR
      return false;
    }

    // Callee is always internal function.
    if (!emitCall(JSOp::CallIgnoresRv, 0)) {
      //            [stack] CTOR ARRAY? RVAL
      return false;
    }

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

#ifdef ENABLE_DECORATORS
  // Decorators Proposal
  // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-initializeinstanceelements
  // 4. For each element e of elements, do
  //     4.a. If elementRecord.[[Kind]] is field or accessor, then
  //         4.a.i. Perform ? InitializeFieldOrAccessor(O, elementRecord).
  //

  // TODO: (See Bug 1817993) At the moment, we're applying the initialization
  // logic in two steps. The pre-decorator initialization code runs, stores
  // the initial value, and then we retrieve it here and apply the
  // initializers added by decorators. We should unify these two steps.
  if (!emitGetName(
          TaggedParserAtomIndex::WellKnown::dot_staticInitializers_())) {
    //          [stack] CTOR ARRAY
    return false;
  }

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

  if (!emitAtomOp(JSOp::GetProp, TaggedParserAtomIndex::WellKnown::length())) {
    //          [stack] CTOR ARRAY LENGTH
    return false;
  }

  if (!emitNumberOp(static_cast<double>(numFields))) {
    //          [stack] CTOR ARRAY LENGTH INDEX
    return false;
  }

  InternalWhileEmitter wh(this);
  // At this point, we have no context to determine offsets in the
  // code for this while statement. Ideally, it would correspond to
  // the field we're initializing.
  if (!wh.emitCond()) {
    //          [stack] CTOR ARRAY LENGTH INDEX
    return false;
  }

  if (!emit1(JSOp::Dup)) {
    //          [stack] CTOR ARRAY LENGTH INDEX INDEX
    return false;
  }

  if (!emitDupAt(2)) {
    //          [stack] CTOR ARRAY LENGTH INDEX INDEX LENGTH
    return false;
  }

  if (!emit1(JSOp::Lt)) {
    //          [stack] CTOR ARRAY LENGTH INDEX BOOL
    return false;
  }

  if (!wh.emitBody()) {
    //          [stack] CTOR ARRAY LENGTH INDEX
    return false;
  }

  if (!emitDupAt(2)) {
    //          [stack] CTOR ARRAY LENGTH INDEX ARRAY
    return false;
  }

  if (!emitDupAt(1)) {
    //          [stack] CTOR ARRAY LENGTH INDEX ARRAY INDEX
    return false;
  }

  // Retrieve initializers for this field
  if (!emit1(JSOp::GetElem)) {
    //            [stack] CTOR ARRAY LENGTH INDEX INITIALIZERS
    return false;
  }

  if (!emitDupAt(4)) {
    //            [stack] CTOR ARRAY LENGTH INDEX INITIALIZERS CTOR
    return false;
  }

  if (!emit1(JSOp::Swap)) {
    //            [stack] CTOR ARRAY LENGTH INDEX CTOR INITIALIZERS
    return false;
  }

  DecoratorEmitter de(this);
  if (!de.emitInitializeFieldOrAccessor()) {
    //            [stack] CTOR ARRAY LENGTH INDEX
    return false;
  }

  if (!emit1(JSOp::Inc)) {
    //            [stack] CTOR ARRAY LENGTH INDEX
    return false;
  }

  if (!wh.emitEnd()) {
    //          [stack] CTOR ARRAY LENGTH INDEX
    return false;
  }

  if (!emitPopN(3)) {
    //            [stack] CTOR
    return false;
  }
  // 5. Return unused.
#endif

  // Overwrite |.staticInitializers| and |.staticFieldKeys| with undefined to
  // avoid keeping the arrays alive indefinitely.
  auto clearStaticFieldSlot = [&](TaggedParserAtomIndex name) {
    NameOpEmitter noe(this, name, NameOpEmitter::Kind::SimpleAssignment);
    if (!noe.prepareForRhs()) {
      //            [stack] ENV? VAL?
      return false;
    }

    if (!emit1(JSOp::Undefined)) {
      //            [stack] ENV? VAL? UNDEFINED
      return false;
    }

    if (!noe.emitAssignment()) {
      //            [stack] VAL
      return false;
    }

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

    return true;
  };

  if (!clearStaticFieldSlot(
          TaggedParserAtomIndex::WellKnown::dot_staticInitializers_())) {
    return false;
  }

  auto isStaticFieldWithComputedName = [](ParseNode* propdef) {
    return propdef->is<ClassField>() && propdef->as<ClassField>().isStatic() &&
           propdef->as<ClassField>().name().getKind() ==
               ParseNodeKind::ComputedName;
  };

  if (std::any_of(classMembers->contents().begin(),
                  classMembers->contents().end(),
                  isStaticFieldWithComputedName)) {
    if (!clearStaticFieldSlot(
            TaggedParserAtomIndex::WellKnown::dot_staticFieldKeys_())) {
      return false;
    }
  }

  return true;
}

// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
// the comment on emitSwitch.
MOZ_NEVER_INLINE bool BytecodeEmitter::emitObject(ListNode* objNode) {
  // Note: this method uses the ObjLiteralWriter and emits ObjLiteralStencil
  // objects into the GCThingList, which will evaluate them into real GC objects
  // or shapes during JSScript::fullyInitFromEmitter. Eventually we want
  // JSOp::Object to be a real opcode, but for now, performance constraints
  // limit us to evaluating object literals at the end of parse, when we're
  // allowed to allocate GC things.
  //
  // There are four cases here, in descending order of preference:
  //
  // 1. The list of property names is "normal" and constant (no computed
  //    values, no integer indices), the values are all simple constants
  //    (numbers, booleans, strings), *and* this occurs in a run-once
  //    (singleton) context. In this case, we can emit ObjLiteral
  //    instructions to build an object with values, and the object will be
  //    attached to a JSOp::Object opcode, whose semantics are for the backend
  //    to simply steal the object from the script.
  //
  // 2. The list of property names is "normal" and constant as above, *and* this
  //    occurs in a run-once (singleton) context, but some values are complex
  //    (computed expressions, sub-objects, functions, etc.). In this case, we
  //    can still use JSOp::Object (because singleton context), but the object
  //    has |undefined| property values and InitProp ops are emitted to set the
  //    values.
  //
  // 3. The list of property names is "normal" and constant as above, but this
  //    occurs in a non-run-once (non-singleton) context. In this case, we can
  //    use the ObjLiteral functionality to describe an *empty* object (all
  //    values left undefined) with the right fields, which will become a
  //    JSOp::NewObject opcode using the object's shape to speed up the creation
  //    of the object each time it executes. The emitted bytecode still needs
  //    InitProp ops to set the values in this case.
  //
  // 4. Any other case. As a fallback, we use NewInit to create a new, empty
  //    object (i.e., `{}`) and then emit bytecode to initialize its properties
  //    one-by-one.

  bool useObjLiteral = false;
  bool useObjLiteralValues = false;
  isPropertyListObjLiteralCompatible(objNode, &useObjLiteralValues,
                                     &useObjLiteral);

  //                [stack]
  //
  ObjectEmitter oe(this);
  if (useObjLiteral) {
    bool singleton = checkSingletonContext() &&
                     !objNode->hasNonConstInitializer() && objNode->head();
    JSOp op;
    if (singleton) {
      // Case 1 or 2.
      op = JSOp::Object;
    } else {
      // Case 3.
      useObjLiteralValues = false;
      op = JSOp::NewObject;
    }

    // Use an ObjLiteral op. This will record ObjLiteral insns in the
    // objLiteralWriter's buffer and add a fixup to the list of ObjLiteral
    // fixups so that at GC-publish time at the end of parse, the full object
    // (case 1 or 2) or shape (case 3) can be allocated and the bytecode can be
    // patched to refer to it.
    if (!emitPropertyListObjLiteral(objNode, op, useObjLiteralValues)) {
      //            [stack] OBJ
      return false;
    }
    // Put the ObjectEmitter in the right state. This tells it that there will
    // already be an object on the stack as a result of the (eventual)
    // NewObject or Object op, and prepares it to emit values if needed.
    if (!oe.emitObjectWithTemplateOnStack()) {
      //            [stack] OBJ
      return false;
    }
    if (!useObjLiteralValues) {
      // Case 2 or 3 above: we still need to emit bytecode to fill in the
      // object's property values.
      if (!emitPropertyList(objNode, oe, ObjectLiteral)) {
        //          [stack] OBJ
        return false;
      }
    }
  } else {
    // Case 4 above: no ObjLiteral use, just bytecode to build the object from
    // scratch.
    if (!oe.emitObject(objNode->count())) {
      //            [stack] OBJ
      return false;
    }
    if (!emitPropertyList(objNode, oe, ObjectLiteral)) {
      //            [stack] OBJ
      return false;
    }
  }

  if (!oe.emitEnd()) {
    //              [stack] OBJ
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitArrayLiteral(ListNode* array) {
  // Emit JSOp::Object if the array consists entirely of primitive values and we
  // are in a singleton context.
  if (checkSingletonContext() && !array->hasNonConstInitializer() &&
      !array->empty() && isArrayObjLiteralCompatible(array)) {
    return emitObjLiteralArray(array);
  }

  return emitArray(array);
}

bool BytecodeEmitter::emitArray(ListNode* array) {
  /*
   * Emit code for [a, b, c] that is equivalent to constructing a new
   * array and in source order evaluating each element value and adding
   * it to the array, without invoking latent setters.  We use the
   * JSOp::NewInit and JSOp::InitElemArray bytecodes to ignore setters and
   * to avoid dup'ing and popping the array as each element is added, as
   * JSOp::SetElem/JSOp::SetProp would do.
   */


  uint32_t nspread = 0;
  for (ParseNode* elem : array->contents()) {
    if (elem->isKind(ParseNodeKind::Spread)) {
      nspread++;
    }
  }

  // Array literal's length is limited to NELEMENTS_LIMIT in parser.
  static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX,
                "array literals' maximum length must not exceed limits "
                "required by BaselineCompiler::emit_NewArray, "
                "BaselineCompiler::emit_InitElemArray, "
                "and DoSetElemFallback's handling of JSOp::InitElemArray");

  uint32_t count = array->count();
  MOZ_ASSERT(count >= nspread);
  MOZ_ASSERT(count <= NativeObject::MAX_DENSE_ELEMENTS_COUNT,
             "the parser must throw an error if the array exceeds maximum "
             "length");

  // For arrays with spread, this is a very pessimistic allocation, the
  // minimum possible final size.
  if (!emitUint32Operand(JSOp::NewArray, count - nspread)) {
    //              [stack] ARRAY
    return false;
  }

  uint32_t index = 0;
  bool afterSpread = false;
  for (ParseNode* elem : array->contents()) {
    if (elem->isKind(ParseNodeKind::Spread)) {
      if (!afterSpread) {
        afterSpread = true;
        if (!emitNumberOp(index)) {
          //        [stack] ARRAY INDEX
          return false;
        }
      }

      ParseNode* expr = elem->as<UnaryNode>().kid();
      SelfHostedIter selfHostedIter = getSelfHostedIterFor(expr);

      if (!updateSourceCoordNotes(elem->pn_pos.begin)) {
        return false;
      }
      if (!emitIterable(expr, selfHostedIter)) {
        //          [stack] ARRAY INDEX ITERABLE
        return false;
      }
      if (!emitIterator(selfHostedIter)) {
        //          [stack] ARRAY INDEX NEXT ITER
        return false;
      }
      if (!emit2(JSOp::Pick, 3)) {
        //          [stack] INDEX NEXT ITER ARRAY
        return false;
      }
      if (!emit2(JSOp::Pick, 3)) {
        //          [stack] NEXT ITER ARRAY INDEX
        return false;
      }
      if (!emitSpread(selfHostedIter)) {
        //          [stack] ARRAY INDEX
        return false;
      }
    } else {
      if (!updateSourceCoordNotesIfNonLiteral(elem)) {
        return false;
      }
      if (elem->isKind(ParseNodeKind::Elision)) {
        if (!emit1(JSOp::Hole)) {
          return false;
        }
      } else {
        if (!emitTree(elem, ValueUsage::WantValue)) {
          //        [stack] ARRAY INDEX? VALUE
          return false;
        }
      }

      if (afterSpread) {
        if (!emit1(JSOp::InitElemInc)) {
          //        [stack] ARRAY (INDEX+1)
          return false;
        }
      } else {
        if (!emitUint32Operand(JSOp::InitElemArray, index)) {
          //        [stack] ARRAY
          return false;
        }
      }
    }

    index++;
  }
  MOZ_ASSERT(index == count);
  if (afterSpread) {
    if (!emit1(JSOp::Pop)) {
      //            [stack] ARRAY
      return false;
    }
  }
  return true;
}

bool BytecodeEmitter::emitSpreadIntoArray(UnaryNode* elem) {
  MOZ_ASSERT(elem->isKind(ParseNodeKind::Spread));

  if (!updateSourceCoordNotes(elem->pn_pos.begin)) {
    //              [stack] VALUE
    return false;
  }

  SelfHostedIter selfHostedIter = getSelfHostedIterFor(elem->kid());
  MOZ_ASSERT(selfHostedIter == SelfHostedIter::Deny ||
             selfHostedIter == SelfHostedIter::AllowContent);

  if (!emitIterator(selfHostedIter)) {
    //              [stack] NEXT ITER
    return false;
  }

  if (!emitUint32Operand(JSOp::NewArray, 0)) {
    //              [stack] NEXT ITER ARRAY
    return false;
  }

  if (!emitNumberOp(0)) {
    //              [stack] NEXT ITER ARRAY INDEX
    return false;
  }

  if (!emitSpread(selfHostedIter)) {
    //              [stack] ARRAY INDEX
    return false;
  }

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

#ifdef ENABLE_RECORD_TUPLE
bool BytecodeEmitter::emitRecordLiteral(ListNode* record) {
  if (!emitUint32Operand(JSOp::InitRecord, record->count())) {
    //              [stack] RECORD
    return false;
  }

  for (ParseNode* propdef : record->contents()) {
    if (propdef->isKind(ParseNodeKind::Spread)) {
      if (!emitTree(propdef->as<UnaryNode>().kid())) {
        //          [stack] RECORD SPREADEE
        return false;
      }
      if (!emit1(JSOp::AddRecordSpread)) {
        //          [stack] RECORD
        return false;
      }
    } else {
      BinaryNode* prop = &propdef->as<BinaryNode>();

      ParseNode* key = prop->left();
      ParseNode* value = prop->right();

      switch (key->getKind()) {
        case ParseNodeKind::ObjectPropertyName:
          if (!emitStringOp(JSOp::String, key->as<NameNode>().atom())) {
            return false;
          }
          break;
        case ParseNodeKind::ComputedName:
          if (!emitTree(key->as<UnaryNode>().kid())) {
            return false;
          }
          break;
        default:
          MOZ_ASSERT(key->isKind(ParseNodeKind::StringExpr) ||
                     key->isKind(ParseNodeKind::NumberExpr) ||
                     key->isKind(ParseNodeKind::BigIntExpr));
          if (!emitTree(key)) {
            return false;
          }
          break;
      }
      //            [stack] RECORD KEY

      if (!emitTree(value)) {
        //          [stack] RECORD KEY VALUE
        return false;
      }

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

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

  return true;
}

bool BytecodeEmitter::emitTupleLiteral(ListNode* tuple) {
  if (!emitUint32Operand(JSOp::InitTuple, tuple->count())) {
    //              [stack] TUPLE
    return false;
  }

  for (ParseNode* elt : tuple->contents()) {
    if (elt->isKind(ParseNodeKind::Spread)) {
      ParseNode* expr = elt->as<UnaryNode>().kid();
      auto selfHostedIter = getSelfHostedIterFor(expr);

      if (!emitIterable(expr, selfHostedIter)) {
        //          [stack] TUPLE ITERABLE
        return false;
      }
      if (!emitIterator(selfHostedIter)) {
        //          [stack] TUPLE NEXT ITER
        return false;
      }
      if (!emit2(JSOp::Pick, 2)) {
        //          [stack] NEXT ITER TUPLE
        return false;
      }
      if (!emitSpread(selfHostedIter, /* spreadeeStackItems = */ 1,
                      JSOp::AddTupleElement)) {
        //          [stack] TUPLE
        return false;
      }
    } else {
      // Update location to throw errors about non-primitive elements
      // in the correct position.
      if (!updateSourceCoordNotesIfNonLiteral(elt)) {
        return false;
      }

      if (!emitTree(elt)) {
        //          [stack] TUPLE VALUE
        return false;
      }

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

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

  return true;
}
#endif

static inline JSOp UnaryOpParseNodeKindToJSOp(ParseNodeKind pnk) {
  switch (pnk) {
    case ParseNodeKind::ThrowStmt:
      return JSOp::Throw;
    case ParseNodeKind::VoidExpr:
      return JSOp::Void;
    case ParseNodeKind::NotExpr:
      return JSOp::Not;
    case ParseNodeKind::BitNotExpr:
      return JSOp::BitNot;
    case ParseNodeKind::PosExpr:
      return JSOp::Pos;
    case ParseNodeKind::NegExpr:
      return JSOp::Neg;
    default:
      MOZ_CRASH("unexpected unary op");
  }
}

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

  JSOp op = UnaryOpParseNodeKindToJSOp(unaryNode->getKind());
  ValueUsage valueUsage =
      op == JSOp::Void ? ValueUsage::IgnoreValue : ValueUsage::WantValue;
  if (!emitTree(unaryNode->kid(), valueUsage)) {
    return false;
  }
  return emit1(op);
}

bool BytecodeEmitter::emitTypeof(UnaryNode* typeofNode, JSOp op) {
  MOZ_ASSERT(op == JSOp::Typeof || op == JSOp::TypeofExpr);

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

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

  return emit1(op);
}

bool BytecodeEmitter::tryEmitTypeofEq(ListNode* node, bool* emitted) {
  // Emit specialized opcode for `typeof val == "type` or `typeof val != "type`
  // if possible.
  //
  // NOTE: Given the comparison is done for string, `==` and `===` have
  //       no difference. Same for `!=` and `!==`.
  MOZ_ASSERT(node->isKind(ParseNodeKind::StrictEqExpr) ||
             node->isKind(ParseNodeKind::EqExpr) ||
             node->isKind(ParseNodeKind::StrictNeExpr) ||
             node->isKind(ParseNodeKind::NeExpr));

  if (node->count() != 2) {
    *emitted = false;
    return true;
  }

  ParseNode* left = node->head();
  ParseNode* right = left->pn_next;
  MOZ_ASSERT(right);

  UnaryNode* typeofNode;
  NameNode* typenameNode;
  JSOp op;

  if (node->isKind(ParseNodeKind::StrictEqExpr) ||
      node->isKind(ParseNodeKind::EqExpr)) {
    op = JSOp::Eq;
  } else {
    op = JSOp::Ne;
  }

  // NOTE: ParseNodeKind::TypeOfExpr cannot use JSOp::TypeofEq.
  //       See JSOp::GetName document.
  if (left->isKind(ParseNodeKind::TypeOfNameExpr) &&
      right->isKind(ParseNodeKind::StringExpr)) {
    typeofNode = &left->as<UnaryNode>();
    typenameNode = &right->as<NameNode>();
  } else if (right->isKind(ParseNodeKind::TypeOfNameExpr) &&
             left->isKind(ParseNodeKind::StringExpr)) {
    typeofNode = &right->as<UnaryNode>();
    typenameNode = &left->as<NameNode>();
  } else {
    *emitted = false;
    return true;
  }

  JSType type;
  TaggedParserAtomIndex typeName = typenameNode->atom();
  if (typeName == TaggedParserAtomIndex::WellKnown::undefined()) {
    type = JSTYPE_UNDEFINED;
  } else if (typeName == TaggedParserAtomIndex::WellKnown::object()) {
    type = JSTYPE_OBJECT;
  } else if (typeName == TaggedParserAtomIndex::WellKnown::function()) {
    type = JSTYPE_FUNCTION;
  } else if (typeName == TaggedParserAtomIndex::WellKnown::string()) {
    type = JSTYPE_STRING;
  } else if (typeName == TaggedParserAtomIndex::WellKnown::number()) {
    type = JSTYPE_NUMBER;
  } else if (typeName == TaggedParserAtomIndex::WellKnown::boolean()) {
    type = JSTYPE_BOOLEAN;
  } else if (typeName == TaggedParserAtomIndex::WellKnown::symbol()) {
    type = JSTYPE_SYMBOL;
  } else if (typeName == TaggedParserAtomIndex::WellKnown::bigint()) {
    type = JSTYPE_BIGINT;
  }
#ifdef ENABLE_RECORD_TUPLE
  else if (typeName == TaggedParserAtomIndex::WellKnown::record()) {
    type = JSTYPE_RECORD;
  } else if (typeName == TaggedParserAtomIndex::WellKnown::tuple()) {
    type = JSTYPE_TUPLE;
  }
#endif
  else {
    *emitted = false;
    return true;
  }

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

  if (!emitTree(typeofNode->kid())) {
    //          [stack] VAL
    return false;
  }

  if (!emit2(JSOp::TypeofEq, TypeofEqOperand(type, op).rawValue())) {
    //          [stack] CMP
    return false;
  }

  *emitted = true;
  return true;
}

bool BytecodeEmitter::emitFunctionFormalParameters(ParamsBodyNode* paramsBody) {
  FunctionBox* funbox = sc->asFunctionBox();

  bool hasRest = funbox->hasRest();

  FunctionParamsEmitter fpe(this, funbox);
  for (ParseNode* arg : paramsBody->parameters()) {
    ParseNode* bindingElement = arg;
    ParseNode* initializer = nullptr;
    if (arg->isKind(ParseNodeKind::AssignExpr)) {
      bindingElement = arg->as<BinaryNode>().left();
      initializer = arg->as<BinaryNode>().right();
    }
    bool hasInitializer = !!initializer;
    bool isRest =
        hasRest && arg->pn_next == *std::end(paramsBody->parameters());
    bool isDestructuring = !bindingElement->isKind(ParseNodeKind::Name);

    // Left-hand sides are either simple names or destructuring patterns.
    MOZ_ASSERT(bindingElement->isKind(ParseNodeKind::Name) ||
               bindingElement->isKind(ParseNodeKind::ArrayExpr) ||
               bindingElement->isKind(ParseNodeKind::ObjectExpr));

    auto emitDefaultInitializer = [this, &initializer, &bindingElement]() {
      //            [stack]

      if (!this->emitInitializer(initializer, bindingElement)) {
        //          [stack] DEFAULT
        return false;
      }
      return true;
    };

    auto emitDestructuring = [this, &bindingElement]() {
      //            [stack] ARG

      if (!this->emitDestructuringOps(&bindingElement->as<ListNode>(),
                                      DestructuringFlavor::Declaration)) {
        //          [stack] ARG
        return false;
      }

      return true;
    };

    if (isRest) {
      if (isDestructuring) {
        if (!fpe.prepareForDestructuringRest()) {
          //        [stack]
          return false;
        }
        if (!emitDestructuring()) {
          //        [stack]
          return false;
        }
        if (!fpe.emitDestructuringRestEnd()) {
          //        [stack]
          return false;
        }
      } else {
        auto paramName = bindingElement->as<NameNode>().name();
        if (!fpe.emitRest(paramName)) {
          //        [stack]
          return false;
        }
      }

      continue;
    }

    if (isDestructuring) {
      if (hasInitializer) {
        if (!fpe.prepareForDestructuringDefaultInitializer()) {
          //        [stack]
          return false;
        }
        if (!emitDefaultInitializer()) {
          //        [stack]
          return false;
        }
        if (!fpe.prepareForDestructuringDefault()) {
          //        [stack]
          return false;
        }
        if (!emitDestructuring()) {
          //        [stack]
          return false;
        }
        if (!fpe.emitDestructuringDefaultEnd()) {
          //        [stack]
          return false;
        }
      } else {
        if (!fpe.prepareForDestructuring()) {
          //        [stack]
          return false;
        }
        if (!emitDestructuring()) {
          //        [stack]
          return false;
        }
        if (!fpe.emitDestructuringEnd()) {
          //        [stack]
          return false;
        }
      }

      continue;
    }

    if (hasInitializer) {
      if (!fpe.prepareForDefault()) {
        //          [stack]
        return false;
      }
      if (!emitDefaultInitializer()) {
        //          [stack]
        return false;
      }
      auto paramName = bindingElement->as<NameNode>().name();
      if (!fpe.emitDefaultEnd(paramName)) {
        //          [stack]
        return false;
      }

      continue;
    }

    auto paramName = bindingElement->as<NameNode>().name();
    if (!fpe.emitSimple(paramName)) {
      //            [stack]
      return false;
    }
  }

  return true;
}

bool BytecodeEmitter::emitInitializeFunctionSpecialNames() {
  FunctionBox* funbox = sc->asFunctionBox();

  //                [stack]

  auto emitInitializeFunctionSpecialName =
      [](BytecodeEmitter* bce, TaggedParserAtomIndex name, JSOp op) {
        // A special name must be slotful, either on the frame or on the
        // call environment.
        MOZ_ASSERT(bce->lookupName(name).hasKnownSlot());

        NameOpEmitter noe(bce, name, NameOpEmitter::Kind::Initialize);
        if (!noe.prepareForRhs()) {
          //        [stack]
          return false;
        }
        if (!bce->emit1(op)) {
          //        [stack] THIS/ARGUMENTS/NEW.TARGET
          return false;
        }
        if (!noe.emitAssignment()) {
          //        [stack] THIS/ARGUMENTS/NEW.TARGET
          return false;
        }
        if (!bce->emit1(JSOp::Pop)) {
          //        [stack]
          return false;
        }

        return true;
      };

  // Do nothing if the function doesn't have an arguments binding.
  if (funbox->needsArgsObj()) {
    // Self-hosted code should use the more efficient ArgumentsLength and
    // GetArgument intrinsics instead of `arguments`.
    MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
    if (!emitInitializeFunctionSpecialName(
            this, TaggedParserAtomIndex::WellKnown::arguments(),
            JSOp::Arguments)) {
      //            [stack]
      return false;
    }
  }

  // Do nothing if the function doesn't have a this-binding (this
  // happens for instance if it doesn't use this/eval or if it's an
  // arrow function).
  if (funbox->functionHasThisBinding()) {
    if (!emitInitializeFunctionSpecialName(
            this, TaggedParserAtomIndex::WellKnown::dot_this_(),
            JSOp::FunctionThis)) {
      return false;
    }
  }

  // Do nothing if the function doesn't have a new.target-binding (this happens
  // for instance if it doesn't use new.target/eval or if it's an arrow
  // function).
  if (funbox->functionHasNewTargetBinding()) {
    if (!emitInitializeFunctionSpecialName(
            this, TaggedParserAtomIndex::WellKnown::dot_newTarget_(),
            JSOp::NewTarget)) {
      return false;
    }
  }

  // Do nothing if the function doesn't implicitly return a promise result.
  if (funbox->needsPromiseResult()) {
    if (!emitInitializeFunctionSpecialName(
            this, TaggedParserAtomIndex::WellKnown::dot_generator_(),
            JSOp::Generator)) {
      //            [stack]
      return false;
    }
  }
  return true;
}

bool BytecodeEmitter::emitLexicalInitialization(NameNode* name) {
  return emitLexicalInitialization(name->name());
}

bool BytecodeEmitter::emitLexicalInitialization(TaggedParserAtomIndex name) {
  NameOpEmitter noe(this, name, NameOpEmitter::Kind::Initialize);
  if (!noe.prepareForRhs()) {
    return false;
  }

  // The caller has pushed the RHS to the top of the stack. Assert that the
  // binding can be initialized without a binding object on the stack, and that
  // no JSOp::BindUnqualifiedName or JSOp::BindUnqualifiedGName ops were
  // emitted.
  MOZ_ASSERT(noe.loc().isLexical() || noe.loc().isSynthetic() ||
             noe.loc().isPrivateMethod());
  MOZ_ASSERT(!noe.emittedBindOp());

  if (!noe.emitAssignment()) {
    return false;
  }

  return true;
}

static MOZ_ALWAYS_INLINE ParseNode* FindConstructor(ListNode* classMethods) {
  for (ParseNode* classElement : classMethods->contents()) {
    ParseNode* unwrappedElement = classElement;
    if (unwrappedElement->is<LexicalScopeNode>()) {
      unwrappedElement = unwrappedElement->as<LexicalScopeNode>().scopeBody();
    }
    if (unwrappedElement->is<ClassMethod>()) {
      ClassMethod& method = unwrappedElement->as<ClassMethod>();
      ParseNode& methodName = method.name();
      if (!method.isStatic() &&
          (methodName.isKind(ParseNodeKind::ObjectPropertyName) ||
           methodName.isKind(ParseNodeKind::StringExpr)) &&
          methodName.as<NameNode>().atom() ==
              TaggedParserAtomIndex::WellKnown::constructor()) {
        return classElement;
      }
    }
  }
  return nullptr;
}

bool BytecodeEmitter::emitNewPrivateName(TaggedParserAtomIndex bindingName,
                                         TaggedParserAtomIndex symbolName) {
  if (!emitAtomOp(JSOp::NewPrivateName, symbolName)) {
    //              [stack] HERITAGE PRIVATENAME
    return false;
  }

  // Add a binding for #name => privatename
  if (!emitLexicalInitialization(bindingName)) {
    //              [stack] HERITAGE PRIVATENAME
    return false;
  }

  // Pop Private name off the stack.
  if (!emit1(JSOp::Pop)) {
    //              [stack] HERITAGE
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitNewPrivateNames(
    TaggedParserAtomIndex privateBrandName, ListNode* classMembers) {
  bool hasPrivateBrand = false;

  for (ParseNode* classElement : classMembers->contents()) {
    ParseNode* elementName;
    if (classElement->is<ClassMethod>()) {
      elementName = &classElement->as<ClassMethod>().name();
    } else if (classElement->is<ClassField>()) {
      elementName = &classElement->as<ClassField>().name();
    } else {
      continue;
    }

    if (!elementName->isKind(ParseNodeKind::PrivateName)) {
      continue;
    }

    // Non-static private methods' private names are optimized away.
    bool isOptimized = false;
    if (classElement->is<ClassMethod>() &&
        !classElement->as<ClassMethod>().isStatic()) {
      hasPrivateBrand = true;
      if (classElement->as<ClassMethod>().accessorType() ==
          AccessorType::None) {
        isOptimized = true;
      }
    }

    if (!isOptimized) {
      auto privateName = elementName->as<NameNode>().name();
      if (!emitNewPrivateName(privateName, privateName)) {
        return false;
      }
    }
  }

  if (hasPrivateBrand) {
    // We don't make a private name for every optimized method, but we need one
    // private name per class, the `.privateBrand`.
    if (!emitNewPrivateName(
            TaggedParserAtomIndex::WellKnown::dot_privateBrand_(),
            privateBrandName)) {
      return false;
    }
  }
  return true;
}

// This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
// (BindingClassDeclarationEvaluation).
bool BytecodeEmitter::emitClass(
    ClassNode* classNode,
    ClassNameKind nameKind /* = ClassNameKind::BindingName */,
    TaggedParserAtomIndex
        nameForAnonymousClass /* = TaggedParserAtomIndex::null() */) {
  MOZ_ASSERT((nameKind == ClassNameKind::InferredName) ==
             bool(nameForAnonymousClass));

  ParseNode* heritageExpression = classNode->heritage();
  ListNode* classMembers = classNode->memberList();
  ParseNode* constructor = FindConstructor(classMembers);

  // If |nameKind != ClassNameKind::ComputedName|
  //                [stack]
  // Else
  //                [stack] NAME

  ClassEmitter ce(this);
  TaggedParserAtomIndex innerName;
  ClassEmitter::Kind kind = ClassEmitter::Kind::Expression;
  if (ClassNames* names = classNode->names()) {
    MOZ_ASSERT(nameKind == ClassNameKind::BindingName);
    innerName = names->innerBinding()->name();
    MOZ_ASSERT(innerName);

    if (names->outerBinding()) {
      MOZ_ASSERT(names->outerBinding()->name());
      MOZ_ASSERT(names->outerBinding()->name() == innerName);
      kind = ClassEmitter::Kind::Declaration;
    }
  }

  if (LexicalScopeNode* scopeBindings = classNode->scopeBindings()) {
    if (!ce.emitScope(scopeBindings->scopeBindings())) {
      //            [stack]
      return false;
    }
  }

  bool isDerived = !!heritageExpression;
  if (isDerived) {
    if (!updateSourceCoordNotes(classNode->pn_pos.begin)) {
      return false;
    }
    if (!markStepBreakpoint()) {
      return false;
    }
    if (!emitTree(heritageExpression)) {
      //            [stack] HERITAGE
      return false;
    }
  }

  // The class body scope holds any private names. Those mustn't be visible in
  // the heritage expression and hence the scope must be emitted after the
  // heritage expression.
  if (ClassBodyScopeNode* bodyScopeBindings = classNode->bodyScopeBindings()) {
    if (!ce.emitBodyScope(bodyScopeBindings->scopeBindings())) {
      //            [stack] HERITAGE
      return false;
    }

    // The spec does not say anything about private brands being symbols.  It's
    // an implementation detail. So we can give the special private brand
    // symbol any description we want and users won't normally see it. For
    // debugging, use the class name.
    auto privateBrandName = innerName;
    if (!innerName) {
      privateBrandName = nameForAnonymousClass
                             ? nameForAnonymousClass
                             : TaggedParserAtomIndex::WellKnown::anonymous();
    }
    if (!emitNewPrivateNames(privateBrandName, classMembers)) {
      return false;
    }
  }

  bool hasNameOnStack = nameKind == ClassNameKind::ComputedName;
  if (isDerived) {
    if (!ce.emitDerivedClass(innerName, nameForAnonymousClass,
                             hasNameOnStack)) {
      //            [stack] HOMEOBJ HERITAGE
      return false;
    }
  } else {
    if (!ce.emitClass(innerName, nameForAnonymousClass, hasNameOnStack)) {
      //            [stack] HOMEOBJ
      return false;
    }
  }

  // Stack currently has HOMEOBJ followed by optional HERITAGE. When HERITAGE
  // is not used, an implicit value of %FunctionPrototype% is implied.

  // See |Parser::classMember(...)| for the reason why |.initializers| is
  // created within its own scope.
  Maybe<LexicalScopeEmitter> lse;
  FunctionNode* ctor;
#ifdef ENABLE_DECORATORS
  bool extraInitializersPresent = false;
#endif
  if (constructor->is<LexicalScopeNode>()) {
    LexicalScopeNode* constructorScope = &constructor->as<LexicalScopeNode>();

    MOZ_ASSERT(!constructorScope->isEmptyScope());
#ifdef ENABLE_DECORATORS
    // With decorators enabled we expect to see |.initializers|,
    // and |.instanceExtraInitializers| in this scope.
    MOZ_ASSERT(constructorScope->scopeBindings()->length == 2);
    MOZ_ASSERT(GetScopeDataTrailingNames(constructorScope->scopeBindings())[0]
                   .name() ==
               TaggedParserAtomIndex::WellKnown::dot_initializers_());
    MOZ_ASSERT(
        GetScopeDataTrailingNames(constructorScope->scopeBindings())[1]
            .name() ==
        TaggedParserAtomIndex::WellKnown::dot_instanceExtraInitializers_());

    // We should only call this code if we know decorators are present, see bug
    // 1871147.
    lse.emplace(this);
    if (!lse->emitScope(ScopeKind::Lexical,
                        constructorScope->scopeBindings())) {
      return false;
    }

    // TODO: See bug 1868220 for support for static extra initializers.
    if (!ce.prepareForExtraInitializers(TaggedParserAtomIndex::WellKnown::
                                            dot_instanceExtraInitializers_())) {
      return false;
    }

    if (classNode->addInitializerFunction()) {
      DecoratorEmitter de(this);
      if (!de.emitCreateAddInitializerFunction(
              classNode->addInitializerFunction(),
              TaggedParserAtomIndex::WellKnown::
                  dot_instanceExtraInitializers_())) {
        //            [stack] HOMEOBJ HERITAGE? ADDINIT
        return false;
      }

      if (!emitUnpickN(isDerived ? 2 : 1)) {
        //            [stack] ADDINIT HOMEOBJ HERITAGE?
        return false;
      }

      extraInitializersPresent = true;
    }
#else
    // The constructor scope should only contain the |.initializers| binding.
    MOZ_ASSERT(constructorScope->scopeBindings()->length == 1);
    MOZ_ASSERT(GetScopeDataTrailingNames(constructorScope->scopeBindings())[0]
                   .name() ==
               TaggedParserAtomIndex::WellKnown::dot_initializers_());
#endif

    auto needsInitializer = [](ParseNode* propdef) {
      return NeedsFieldInitializer(propdef, false) ||
             NeedsAccessorInitializer(propdef, false);
    };

    // As an optimization omit the |.initializers| binding when no instance
    // fields or private methods are present.
    bool needsInitializers =
        std::any_of(classMembers->contents().begin(),
                    classMembers->contents().end(), needsInitializer);
    if (needsInitializers) {
#ifndef ENABLE_DECORATORS
      lse.emplace(this);
      if (!lse->emitScope(ScopeKind::Lexical,
                          constructorScope->scopeBindings())) {
        return false;
      }
#endif
      // Any class with field initializers will have a constructor
      if (!emitCreateMemberInitializers(ce, classMembers,
                                        FieldPlacement::Instance
#ifdef ENABLE_DECORATORS
                                        ,
                                        isDerived
#endif
                                        )) {
        return false;
      }
    }

    ctor = &constructorScope->scopeBody()->as<ClassMethod>().method();
  } else {
    // The |.initializers| binding is never emitted when in self-hosting mode.
    MOZ_ASSERT(emitterMode == BytecodeEmitter::SelfHosting);
    ctor = &constructor->as<ClassMethod>().method();
  }

  bool needsHomeObject = ctor->funbox()->needsHomeObject();
  // HERITAGE is consumed inside emitFunction.
  if (nameKind == ClassNameKind::InferredName) {
    setFunName(ctor->funbox(), nameForAnonymousClass);
  }
  if (!emitFunction(ctor, isDerived)) {
    //              [stack] HOMEOBJ CTOR
    return false;
  }
  if (lse.isSome()) {
    if (!lse->emitEnd()) {
      return false;
    }
    lse.reset();
  }
  if (!ce.emitInitConstructor(needsHomeObject)) {
    //              [stack] CTOR HOMEOBJ
    return false;
  }

  if (!emitCreateFieldKeys(classMembers, FieldPlacement::Instance)) {
    return false;
  }

#ifdef ENABLE_DECORATORS
  // TODO: See Bug 1868220 for support for static extra initializers.
  if (!emit1(JSOp::Undefined)) {
    //              [stack] ADDINIT? CTOR HOMEOBJ UNDEFINED
    return false;
  }
  if (!emitUnpickN(2)) {
    //              [stack] ADDINIT? UNDEFINED CTOR HOMEOBJ
    return false;
  }
#endif

  if (!emitCreateMemberInitializers(ce, classMembers, FieldPlacement::Static
#ifdef ENABLE_DECORATORS
                                    ,
                                    false
#endif
                                    )) {
    return false;
  }

#ifdef ENABLE_DECORATORS
  if (!emitPickN(2)) {
    //              [stack] ADDINIT? CTOR HOMEOBJ UNDEFINED
    return false;
  }
  if (!emitPopN(1)) {
    //              [stack] ADDINIT? CTOR HOMEOBJ
    return false;
  }
#endif

  if (!emitCreateFieldKeys(classMembers, FieldPlacement::Static)) {
    return false;
  }

  if (!emitPropertyList(classMembers, ce, ClassBody)) {
    //              [stack] CTOR HOMEOBJ
    return false;
  }

#ifdef ENABLE_DECORATORS
  if (extraInitializersPresent) {
    if (!emitPickN(2)) {
      //              [stack] CTOR HOMEOBJ ADDINIT
      return false;
    }
    if (!emitPopN(1)) {
      //              [stack] CTOR HOMEOBJ
      return false;
    }
  }
#endif

  if (!ce.emitBinding()) {
    //              [stack] CTOR
    return false;
  }

  if (!emitInitializeStaticFields(classMembers)) {
    //              [stack] CTOR
    return false;
  }

#if ENABLE_DECORATORS
  if (!ce.prepareForDecorators()) {
    //            [stack] CTOR
    return false;
  }
  if (classNode->decorators() != nullptr) {
    DecoratorEmitter de(this);
    NameNode* className =
        classNode->names() ? classNode->names()->innerBinding() : nullptr;
    if (!de.emitApplyDecoratorsToClassDefinition(className,
                                                 classNode->decorators())) {
      //            [stack] CTOR
      return false;
    }
  }
#endif

  if (!ce.emitEnd(kind)) {
    //              [stack] # class declaration
    //              [stack]
    //              [stack] # class expression
    //              [stack] CTOR
    return false;
  }

  return true;
}

bool BytecodeEmitter::emitExportDefault(BinaryNode* exportNode) {
  MOZ_ASSERT(exportNode->isKind(ParseNodeKind::ExportDefaultStmt));

  ParseNode* valueNode = exportNode->left();
  if (valueNode->isDirectRHSAnonFunction()) {
    MOZ_ASSERT(exportNode->right());

    if (!emitAnonymousFunctionWithName(
            valueNode, TaggedParserAtomIndex::WellKnown::default_())) {
      return false;
    }
  } else {
    if (!emitTree(valueNode)) {
      return false;
    }
  }

  if (ParseNode* binding = exportNode->right()) {
    if (!emitLexicalInitialization(&binding->as<NameNode>())) {
      return false;
    }

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

  return true;
}

bool BytecodeEmitter::emitTree(
    ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */,
    EmitLineNumberNote emitLineNote /* = EMIT_LINENOTE */) {
  AutoCheckRecursionLimit recursion(fc);
  if (!recursion.check(fc)) {
    return false;
  }

  /* Emit notes to tell the current bytecode's source line number.
     However, a couple trees require special treatment; see the
     relevant emitter functions for details. */

  if (emitLineNote == EMIT_LINENOTE &&
      !ParseNodeRequiresSpecialLineNumberNotes(pn)) {
    if (!updateLineNumberNotes(pn->pn_pos.begin)) {
      return false;
    }
  }

  switch (pn->getKind()) {
    case ParseNodeKind::Function:
      if (!emitFunction(&pn->as<FunctionNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::ParamsBody:
      MOZ_ASSERT_UNREACHABLE(
          "ParamsBody should be handled in emitFunctionScript.");
      break;

    case ParseNodeKind::IfStmt:
      if (!emitIf(&pn->as<TernaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::SwitchStmt:
      if (!emitSwitch(&pn->as<SwitchStatement>())) {
        return false;
      }
      break;

    case ParseNodeKind::WhileStmt:
      if (!emitWhile(&pn->as<BinaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::DoWhileStmt:
      if (!emitDo(&pn->as<BinaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::ForStmt:
      if (!emitFor(&pn->as<ForNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::BreakStmt:
      // Ensure that the column of the 'break' is set properly.
      if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
        return false;
      }
      if (!markStepBreakpoint()) {
        return false;
      }

      if (!emitBreak(pn->as<BreakStatement>().label())) {
        return false;
      }
      break;

    case ParseNodeKind::ContinueStmt:
      // Ensure that the column of the 'continue' is set properly.
      if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
        return false;
      }
      if (!markStepBreakpoint()) {
        return false;
      }

      if (!emitContinue(pn->as<ContinueStatement>().label())) {
        return false;
      }
      break;

    case ParseNodeKind::WithStmt:
      if (!emitWith(&pn->as<BinaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::TryStmt:
      if (!emitTry(&pn->as<TryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::Catch:
      if (!emitCatch(&pn->as<BinaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::VarStmt:
      if (!emitDeclarationList(&pn->as<ListNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::ReturnStmt:
      if (!emitReturn(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::YieldStarExpr:
      if (!emitYieldStar(pn->as<UnaryNode>().kid())) {
        return false;
      }
      break;

    case ParseNodeKind::Generator:
      if (!emit1(JSOp::Generator)) {
        return false;
      }
      break;

    case ParseNodeKind::InitialYield:
      if (!emitInitialYield(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::YieldExpr:
      if (!emitYield(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::AwaitExpr:
      if (!emitAwaitInInnermostScope(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::StatementList:
      if (!emitStatementList(&pn->as<ListNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::EmptyStmt:
      break;

    case ParseNodeKind::ExpressionStmt:
      if (!emitExpressionStatement(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::LabelStmt:
      if (!emitLabeledStatement(&pn->as<LabeledStatement>())) {
        return false;
      }
      break;

    case ParseNodeKind::CommaExpr:
      if (!emitSequenceExpr(&pn->as<ListNode>(), valueUsage)) {
        return false;
      }
      break;

    case ParseNodeKind::InitExpr:
    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: {
      BinaryNode* assignNode = &pn->as<BinaryNode>();
      if (!emitAssignmentOrInit(assignNode->getKind(), assignNode->left(),
                                assignNode->right())) {
        return false;
      }
      break;
    }

    case ParseNodeKind::CoalesceAssignExpr:
    case ParseNodeKind::OrAssignExpr:
    case ParseNodeKind::AndAssignExpr:
      if (!emitShortCircuitAssignment(&pn->as<AssignmentNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::ConditionalExpr:
      if (!emitConditionalExpression(pn->as<ConditionalExpression>(),
                                     valueUsage)) {
        return false;
      }
      break;

    case ParseNodeKind::OrExpr:
    case ParseNodeKind::CoalesceExpr:
    case ParseNodeKind::AndExpr:
      if (!emitShortCircuit(&pn->as<ListNode>(), valueUsage)) {
        return false;
      }
      break;

    case ParseNodeKind::StrictEqExpr:
    case ParseNodeKind::EqExpr:
    case ParseNodeKind::StrictNeExpr:
    case ParseNodeKind::NeExpr: {
      bool emitted;
      if (!tryEmitTypeofEq(&pn->as<ListNode>(), &emitted)) {
        return false;
      }
      if (emitted) {
        return true;
      }
    }
      [[fallthrough]];

    case ParseNodeKind::AddExpr:
    case ParseNodeKind::SubExpr:
    case ParseNodeKind::BitOrExpr:
    case ParseNodeKind::BitXorExpr:
    case ParseNodeKind::BitAndExpr:
    case ParseNodeKind::LtExpr:
    case ParseNodeKind::LeExpr:
    case ParseNodeKind::GtExpr:
    case ParseNodeKind::GeExpr:
    case ParseNodeKind::InExpr:
    case ParseNodeKind::InstanceOfExpr:
    case ParseNodeKind::LshExpr:
    case ParseNodeKind::RshExpr:
    case ParseNodeKind::UrshExpr:
    case ParseNodeKind::MulExpr:
    case ParseNodeKind::DivExpr:
    case ParseNodeKind::ModExpr:
      if (!emitLeftAssociative(&pn->as<ListNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::PrivateInExpr:
      if (!emitPrivateInExpr(&pn->as<ListNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::PowExpr:
      if (!emitRightAssociative(&pn->as<ListNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::TypeOfNameExpr:
      if (!emitTypeof(&pn->as<UnaryNode>(), JSOp::Typeof)) {
        return false;
      }
      break;

    case ParseNodeKind::TypeOfExpr:
      if (!emitTypeof(&pn->as<UnaryNode>(), JSOp::TypeofExpr)) {
        return false;
      }
      break;

    case ParseNodeKind::ThrowStmt:
      if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
        return false;
      }
      if (!markStepBreakpoint()) {
        return false;
      }
      [[fallthrough]];
    case ParseNodeKind::VoidExpr:
    case ParseNodeKind::NotExpr:
    case ParseNodeKind::BitNotExpr:
    case ParseNodeKind::PosExpr:
    case ParseNodeKind::NegExpr:
      if (!emitUnary(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::PreIncrementExpr:
    case ParseNodeKind::PreDecrementExpr:
    case ParseNodeKind::PostIncrementExpr:
    case ParseNodeKind::PostDecrementExpr:
      if (!emitIncOrDec(&pn->as<UnaryNode>(), valueUsage)) {
        return false;
      }
      break;

    case ParseNodeKind::DeleteNameExpr:
      if (!emitDeleteName(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::DeletePropExpr:
      if (!emitDeleteProperty(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::DeleteElemExpr:
      if (!emitDeleteElement(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::DeleteExpr:
      if (!emitDeleteExpression(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::DeleteOptionalChainExpr:
      if (!emitDeleteOptionalChain(&pn->as<UnaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::OptionalChain:
      if (!emitOptionalChain(&pn->as<UnaryNode>(), valueUsage)) {
        return false;
      }
      break;

    case ParseNodeKind::DotExpr: {
      PropertyAccess* prop = &pn->as<PropertyAccess>();
      bool isSuper = prop->isSuper();
      PropOpEmitter poe(this, PropOpEmitter::Kind::Get,
                        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.emitGet(prop->key().atom())) {
        //          [stack] PROP
        return false;
      }
      break;
    }

    case ParseNodeKind::ArgumentsLength: {
      if (sc->isFunctionBox() &&
          sc->asFunctionBox()->isEligibleForArgumentsLength() &&
          !sc->asFunctionBox()->needsArgsObj()) {
        if (!emit1(JSOp::ArgumentsLength)) {
          return false;
        }
      } else {
        PropOpEmitter poe(this, PropOpEmitter::Kind::Get,
                          PropOpEmitter::ObjKind::Other);
        if (!poe.prepareForObj()) {
          return false;
        }

        NameOpEmitter noe(this, TaggedParserAtomIndex::WellKnown::arguments(),
                          NameOpEmitter::Kind::Get);
        if (!noe.emitGet()) {
          return false;
        }
        if (!poe.emitGet(TaggedParserAtomIndex::WellKnown::length())) {
          return false;
        }
      }
      break;
    }

    case ParseNodeKind::ElemExpr: {
      PropertyByValue* elem = &pn->as<PropertyByValue>();
      bool isSuper = elem->isSuper();
      MOZ_ASSERT(!elem->key().isKind(ParseNodeKind::PrivateName));
      ElemOpEmitter eoe(this, ElemOpEmitter::Kind::Get,
                        isSuper ? ElemOpEmitter::ObjKind::Super
                                : ElemOpEmitter::ObjKind::Other);
      if (!emitElemObjAndKey(elem, eoe)) {
        //          [stack] # if Super
        //          [stack] THIS KEY
        //          [stack] # otherwise
        //          [stack] OBJ KEY
        return false;
      }
      if (!eoe.emitGet()) {
        //          [stack] ELEM
        return false;
      }

      break;
    }

    case ParseNodeKind::PrivateMemberExpr: {
      PrivateMemberAccess* privateExpr = &pn->as<PrivateMemberAccess>();
      PrivateOpEmitter xoe(this, PrivateOpEmitter::Kind::Get,
                           privateExpr->privateName().name());
      if (!emitTree(&privateExpr->expression())) {
        //          [stack] OBJ
        return false;
      }
      if (!xoe.emitReference()) {
        //          [stack] OBJ NAME
        return false;
      }
      if (!xoe.emitGet()) {
        //          [stack] VALUE
        return false;
      }

      break;
    }

    case ParseNodeKind::NewExpr:
    case ParseNodeKind::TaggedTemplateExpr:
    case ParseNodeKind::CallExpr:
    case ParseNodeKind::SuperCallExpr:
      if (!emitCallOrNew(&pn->as<CallNode>(), valueUsage)) {
        return false;
      }
      break;

    case ParseNodeKind::LexicalScope:
      if (!emitLexicalScope(&pn->as<LexicalScopeNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::ConstDecl:
    case ParseNodeKind::LetDecl:
      if (!emitDeclarationList(&pn->as<ListNode>())) {
        return false;
      }
      break;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    case ParseNodeKind::AwaitUsingDecl:
    case ParseNodeKind::UsingDecl:
      if (!emitDeclarationList(&pn->as<ListNode>())) {
        return false;
      }
      break;
#endif

    case ParseNodeKind::ImportDecl:
      MOZ_ASSERT(sc->isModuleContext());
      break;

    case ParseNodeKind::ExportStmt: {
      MOZ_ASSERT(sc->isModuleContext());
      UnaryNode* node = &pn->as<UnaryNode>();
      ParseNode* decl = node->kid();
      if (decl->getKind() != ParseNodeKind::ExportSpecList) {
        if (!emitTree(decl)) {
          return false;
        }
      }
      break;
    }

    case ParseNodeKind::ExportDefaultStmt:
      MOZ_ASSERT(sc->isModuleContext());
      if (!emitExportDefault(&pn->as<BinaryNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::ExportFromStmt:
      MOZ_ASSERT(sc->isModuleContext());
      break;

    case ParseNodeKind::CallSiteObj:
      if (!emitCallSiteObject(&pn->as<CallSiteNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::ArrayExpr:
      if (!emitArrayLiteral(&pn->as<ListNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::ObjectExpr:
      if (!emitObject(&pn->as<ListNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::Name:
      if (!emitGetName(&pn->as<NameNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::PrivateName:
      if (!emitGetPrivateName(&pn->as<NameNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::TemplateStringListExpr:
      if (!emitTemplateString(&pn->as<ListNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::TemplateStringExpr:
    case ParseNodeKind::StringExpr:
      if (!emitStringOp(JSOp::String, pn->as<NameNode>().atom())) {
        return false;
      }
      break;

    case ParseNodeKind::NumberExpr:
      if (!emitNumberOp(pn->as<NumericLiteral>().value())) {
        return false;
      }
      break;

    case ParseNodeKind::BigIntExpr:
      if (!emitBigIntOp(&pn->as<BigIntLiteral>())) {
        return false;
      }
      break;

    case ParseNodeKind::RegExpExpr: {
      GCThingIndex index;
      if (!perScriptData().gcThingList().append(&pn->as<RegExpLiteral>(),
                                                &index)) {
        return false;
      }
      if (!emitRegExp(index)) {
        return false;
      }
      break;
    }

    case ParseNodeKind::TrueExpr:
      if (!emit1(JSOp::True)) {
        return false;
      }
      break;
    case ParseNodeKind::FalseExpr:
      if (!emit1(JSOp::False)) {
        return false;
      }
      break;
    case ParseNodeKind::NullExpr:
      if (!emit1(JSOp::Null)) {
        return false;
      }
      break;
    case ParseNodeKind::RawUndefinedExpr:
      if (!emit1(JSOp::Undefined)) {
        return false;
      }
      break;

    case ParseNodeKind::ThisExpr:
      if (!emitThisLiteral(&pn->as<ThisLiteral>())) {
        return false;
      }
      break;

    case ParseNodeKind::DebuggerStmt:
      if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
        return false;
      }
      if (!markStepBreakpoint()) {
        return false;
      }
      if (!emit1(JSOp::Debugger)) {
        return false;
      }
      break;

    case ParseNodeKind::ClassDecl:
      if (!emitClass(&pn->as<ClassNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::NewTargetExpr:
      if (!emitNewTarget(&pn->as<NewTargetNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::ImportMetaExpr:
      if (!emit1(JSOp::ImportMeta)) {
        return false;
      }
      break;

    case ParseNodeKind::CallImportExpr: {
      BinaryNode* spec = &pn->as<BinaryNode>().right()->as<BinaryNode>();

      if (!emitTree(spec->left())) {
        //          [stack] specifier
        return false;
      }

      if (!spec->right()->isKind(ParseNodeKind::PosHolder)) {
        //          [stack] specifier options
        if (!emitTree(spec->right())) {
          return false;
        }
      } else {
        //          [stack] specifier undefined
        if (!emit1(JSOp::Undefined)) {
          return false;
        }
      }

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

      break;
    }

    case ParseNodeKind::SetThis:
      if (!emitSetThis(&pn->as<BinaryNode>())) {
        return false;
      }
      break;

#ifdef ENABLE_RECORD_TUPLE
    case ParseNodeKind::RecordExpr:
      if (!emitRecordLiteral(&pn->as<ListNode>())) {
        return false;
      }
      break;

    case ParseNodeKind::TupleExpr:
      if (!emitTupleLiteral(&pn->as<ListNode>())) {
        return false;
      }
      break;
#endif

    case ParseNodeKind::PropertyNameExpr:
    case ParseNodeKind::PosHolder:
      MOZ_FALLTHROUGH_ASSERT(
          "Should never try to emit ParseNodeKind::PosHolder or ::Property");

    default:
      MOZ_ASSERT(0);
  }

  return true;
}

static bool AllocSrcNote(FrontendContext* fc, SrcNotesVector& notes,
                         unsigned size, unsigned* index) {
  size_t oldLength = notes.length();

  if (MOZ_UNLIKELY(oldLength + size > MaxSrcNotesLength)) {
    ReportAllocationOverflow(fc);
    return false;
  }

  if (!notes.growByUninitialized(size)) {
    return false;
  }

  *index = oldLength;
  return true;
}

bool BytecodeEmitter::addTryNote(TryNoteKind kind, uint32_t stackDepth,
                                 BytecodeOffset start, BytecodeOffset end) {
  MOZ_ASSERT(!inPrologue());
  return bytecodeSection().tryNoteList().append(kind, stackDepth, start, end);
}

bool BytecodeEmitter::newSrcNote(SrcNoteType type, unsigned* indexp) {
  SrcNotesVector& notes = bytecodeSection().notes();
  unsigned index;

  /*
   * Compute delta from the last annotated bytecode's offset.  If it's too
   * big to fit in sn, allocate one or more xdelta notes and reset sn.
   */

  BytecodeOffset offset = bytecodeSection().offset();
  ptrdiff_t delta = (offset - bytecodeSection().lastNoteOffset()).value();
  bytecodeSection().setLastNoteOffset(offset);

  auto allocator = [&](unsigned size) -> SrcNote* {
    if (!AllocSrcNote(fc, notes, size, &index)) {
      return nullptr;
    }
    return ¬es[index];
  };

  if (!SrcNoteWriter::writeNote(type, delta, allocator)) {
    return false;
  }

  if (indexp) {
    *indexp = index;
  }

  if (type == SrcNoteType::NewLine || type == SrcNoteType::SetLine) {
    lastLineOnlySrcNoteIndex = index;
  } else {
    lastLineOnlySrcNoteIndex = LastSrcNoteIsNotLineOnly;
  }

  return true;
}

bool BytecodeEmitter::newSrcNote2(SrcNoteType type, ptrdiff_t offset,
                                  unsigned* indexp) {
  unsigned index;
  if (!newSrcNote(type, &index)) {
    return false;
  }
  if (!newSrcNoteOperand(offset)) {
    return false;
  }
  if (indexp) {
    *indexp = index;
  }
  return true;
}

bool BytecodeEmitter::convertLastNewLineToNewLineColumn(
    JS::LimitedColumnNumberOneOrigin column) {
  SrcNotesVector& notes = bytecodeSection().notes();
  MOZ_ASSERT(lastLineOnlySrcNoteIndex == notes.length() - 1);
  SrcNote* sn = ¬es[lastLineOnlySrcNoteIndex];
  MOZ_ASSERT(sn->type() == SrcNoteType::NewLine);

  SrcNoteWriter::convertNote(sn, SrcNoteType::NewLineColumn);
  if (!newSrcNoteOperand(SrcNote::NewLineColumn::toOperand(column))) {
    return false;
  }

  lastLineOnlySrcNoteIndex = LastSrcNoteIsNotLineOnly;
  return true;
}

bool BytecodeEmitter::convertLastSetLineToSetLineColumn(
    JS::LimitedColumnNumberOneOrigin column) {
  SrcNotesVector& notes = bytecodeSection().notes();
  // The Line operand is either 1 byte or 4 bytes.
  MOZ_ASSERT(lastLineOnlySrcNoteIndex == notes.length() - 1 - 1 ||
             lastLineOnlySrcNoteIndex == notes.length() - 1 - 4);
  SrcNote* sn = ¬es[lastLineOnlySrcNoteIndex];
  MOZ_ASSERT(sn->type() == SrcNoteType::SetLine);

  SrcNoteWriter::convertNote(sn, SrcNoteType::SetLineColumn);
  if (!newSrcNoteOperand(SrcNote::SetLineColumn::columnToOperand(column))) {
    return false;
  }

  lastLineOnlySrcNoteIndex = LastSrcNoteIsNotLineOnly;
  return true;
}

bool BytecodeEmitter::newSrcNoteOperand(ptrdiff_t operand) {
  if (!SrcNote::isRepresentableOperand(operand)) {
    reportError(nullptr, JSMSG_NEED_DIET, "script");
    return false;
  }

  SrcNotesVector& notes = bytecodeSection().notes();

  auto allocator = [&](unsigned size) -> SrcNote* {
    unsigned index;
    if (!AllocSrcNote(fc, notes, size, &index)) {
      return nullptr;
    }
    return ¬es[index];
  };

  return SrcNoteWriter::writeOperand(operand, allocator);
}

bool BytecodeEmitter::intoScriptStencil(ScriptIndex scriptIndex) {
  js::UniquePtr<ImmutableScriptData> immutableScriptData =
      createImmutableScriptData();
  if (!immutableScriptData) {
    return false;
  }

  MOZ_ASSERT(outermostScope().hasNonSyntacticScopeOnChain() ==
             sc->hasNonSyntacticScope());

  auto& things = perScriptData().gcThingList().objects();
  if (!compilationState.appendGCThings(fc, scriptIndex, things)) {
    return false;
  }

  // Hand over the ImmutableScriptData instance generated by BCE.
  auto* sharedData =
      SharedImmutableScriptData::createWith(fc, std::move(immutableScriptData));
  if (!sharedData) {
    return false;
  }

  // De-duplicate the bytecode within the runtime.
  if (!compilationState.sharedData.addAndShare(fc, scriptIndex, sharedData)) {
    return false;
  }

  ScriptStencil& script = compilationState.scriptData[scriptIndex];
  script.setHasSharedData();

  // Update flags specific to functions.
  if (sc->isFunctionBox()) {
    FunctionBox* funbox = sc->asFunctionBox();
    MOZ_ASSERT(&script == &funbox->functionStencil());
    funbox->copyUpdatedImmutableFlags();
    MOZ_ASSERT(script.isFunction());
  } else {
    ScriptStencilExtra& scriptExtra = compilationState.scriptExtra[scriptIndex];
    sc->copyScriptExtraFields(scriptExtra);
  }

  return true;
}

SelfHostedIter BytecodeEmitter::getSelfHostedIterFor(
    ParseNode* parseNode) const {
  if (emitterMode == BytecodeEmitter::SelfHosting &&
      parseNode->isKind(ParseNodeKind::CallExpr)) {
    auto* callee = parseNode->as<CallNode>().callee();
    if (callee->isName(TaggedParserAtomIndex::WellKnown::allowContentIter())) {
      return SelfHostedIter::AllowContent;
    }
    if (callee->isName(
            TaggedParserAtomIndex::WellKnown::allowContentIterWith())) {
      return SelfHostedIter::AllowContentWith;
    }
    if (callee->isName(
            TaggedParserAtomIndex::WellKnown::allowContentIterWithNext())) {
      return SelfHostedIter::AllowContentWithNext;
    }
  }

  return SelfHostedIter::Deny;
}

#if defined(DEBUG) || defined(JS_JITSPEW)
void BytecodeEmitter::dumpAtom(TaggedParserAtomIndex index) const {
  parserAtoms().dump(index);
}
#endif

Messung V0.5 in Prozent
C=84 H=99 G=91

¤ Dauer der Verarbeitung: 0.716 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.