js/src/frontend/BytecodeEmitter.cpp
author Shu-yu Guo <shu@rfrn.org>
Wed, 24 May 2017 07:22:56 -0700
changeset 360375 8401a27059039469d23d3f692e4f3e5be036fd6d
parent 359393 8084d6ac8a07ce27df04642a46b0a9c86ff84aba
child 360378 1d58c6daabeda497d1505f7af1fc61baa0e3e6d5
permissions -rw-r--r--
Bug 1355046 - Don't reset column and line info when emitting useless statements in BCE. (r=jorendorff)

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

/*
 * JS bytecode generation.
 */

#include "frontend/BytecodeEmitter.h"

#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Maybe.h"
#include "mozilla/PodOperations.h"

#include <string.h>

#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsfun.h"
#include "jsnum.h"
#include "jsopcode.h"
#include "jsscript.h"
#include "jstypes.h"
#include "jsutil.h"

#include "ds/Nestable.h"
#include "frontend/Parser.h"
#include "frontend/TokenStream.h"
#include "vm/Debugger.h"
#include "vm/GeneratorObject.h"
#include "vm/Stack.h"
#include "wasm/AsmJS.h"

#include "jsatominlines.h"
#include "jsobjinlines.h"
#include "jsscriptinlines.h"

#include "frontend/ParseNode-inl.h"
#include "vm/EnvironmentObject-inl.h"
#include "vm/NativeObject-inl.h"

using namespace js;
using namespace js::gc;
using namespace js::frontend;

using mozilla::AssertedCast;
using mozilla::DebugOnly;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::NumberIsInt32;
using mozilla::PodCopy;
using mozilla::Some;

class BreakableControl;
class LabelControl;
class LoopControl;
class ForOfLoopControl;
class TryFinallyControl;

static bool
ParseNodeRequiresSpecialLineNumberNotes(ParseNode* pn)
{
    return pn->getKind() == PNK_WHILE || pn->getKind() == PNK_FOR;
}

// A cache that tracks superfluous TDZ checks.
//
// Each basic block should have a TDZCheckCache in scope. Some NestableControl
// subclasses contain a TDZCheckCache.
class BytecodeEmitter::TDZCheckCache : public Nestable<BytecodeEmitter::TDZCheckCache>
{
    PooledMapPtr<CheckTDZMap> cache_;

    MOZ_MUST_USE bool ensureCache(BytecodeEmitter* bce) {
        return cache_ || cache_.acquire(bce->cx);
    }

  public:
    explicit TDZCheckCache(BytecodeEmitter* bce)
      : Nestable<TDZCheckCache>(&bce->innermostTDZCheckCache),
        cache_(bce->cx->frontendCollectionPool())
    { }

    Maybe<MaybeCheckTDZ> needsTDZCheck(BytecodeEmitter* bce, JSAtom* name);
    MOZ_MUST_USE bool noteTDZCheck(BytecodeEmitter* bce, JSAtom* name, MaybeCheckTDZ check);
};

class BytecodeEmitter::NestableControl : public Nestable<BytecodeEmitter::NestableControl>
{
    StatementKind kind_;

    // The innermost scope when this was pushed.
    EmitterScope* emitterScope_;

  protected:
    NestableControl(BytecodeEmitter* bce, StatementKind kind)
      : Nestable<NestableControl>(&bce->innermostNestableControl),
        kind_(kind),
        emitterScope_(bce->innermostEmitterScope)
    { }

  public:
    using Nestable<NestableControl>::enclosing;
    using Nestable<NestableControl>::findNearest;

    StatementKind kind() const {
        return kind_;
    }

    EmitterScope* emitterScope() const {
        return emitterScope_;
    }

    template <typename T>
    bool is() const;

    template <typename T>
    T& as() {
        MOZ_ASSERT(this->is<T>());
        return static_cast<T&>(*this);
    }
};

// Template specializations are disallowed in different namespaces; specialize
// all the NestableControl subtypes up front.
namespace js {
namespace frontend {

template <>
bool
BytecodeEmitter::NestableControl::is<BreakableControl>() const
{
    return StatementKindIsUnlabeledBreakTarget(kind_) || kind_ == StatementKind::Label;
}

template <>
bool
BytecodeEmitter::NestableControl::is<LabelControl>() const
{
    return kind_ == StatementKind::Label;
}

template <>
bool
BytecodeEmitter::NestableControl::is<LoopControl>() const
{
    return StatementKindIsLoop(kind_);
}

template <>
bool
BytecodeEmitter::NestableControl::is<ForOfLoopControl>() const
{
    return kind_ == StatementKind::ForOfLoop;
}

template <>
bool
BytecodeEmitter::NestableControl::is<TryFinallyControl>() const
{
    return kind_ == StatementKind::Try || kind_ == StatementKind::Finally;
}

} // namespace frontend
} // namespace js

class BreakableControl : public BytecodeEmitter::NestableControl
{
  public:
    // Offset of the last break.
    JumpList breaks;

    BreakableControl(BytecodeEmitter* bce, StatementKind kind)
      : NestableControl(bce, kind)
    {
        MOZ_ASSERT(is<BreakableControl>());
    }

    MOZ_MUST_USE bool patchBreaks(BytecodeEmitter* bce) {
        return bce->emitJumpTargetAndPatch(breaks);
    }
};

class LabelControl : public BreakableControl
{
    RootedAtom label_;

    // The code offset when this was pushed. Used for effectfulness checking.
    ptrdiff_t startOffset_;

  public:
    LabelControl(BytecodeEmitter* bce, JSAtom* label, ptrdiff_t startOffset)
      : BreakableControl(bce, StatementKind::Label),
        label_(bce->cx, label),
        startOffset_(startOffset)
    { }

    HandleAtom label() const {
        return label_;
    }

    ptrdiff_t startOffset() const {
        return startOffset_;
    }
};

class LoopControl : public BreakableControl
{
    // Loops' children are emitted in dominance order, so they can always
    // have a TDZCheckCache.
    BytecodeEmitter::TDZCheckCache tdzCache_;

    // Stack depth when this loop was pushed on the control stack.
    int32_t stackDepth_;

    // The loop nesting depth. Used as a hint to Ion.
    uint32_t loopDepth_;

    // Can we OSR into Ion from here? True unless there is non-loop state on the stack.
    bool canIonOsr_;

  public:
    // The target of continue statement jumps, e.g., the update portion of a
    // for(;;) loop.
    JumpTarget continueTarget;

    // Offset of the last continue in the loop.
    JumpList continues;

    LoopControl(BytecodeEmitter* bce, StatementKind loopKind)
      : BreakableControl(bce, loopKind),
        tdzCache_(bce),
        continueTarget({ -1 })
    {
        MOZ_ASSERT(is<LoopControl>());

        LoopControl* enclosingLoop = findNearest<LoopControl>(enclosing());

        stackDepth_ = bce->stackDepth;
        loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1;

        int loopSlots;
        if (loopKind == StatementKind::Spread)
            loopSlots = 3;
        else if (loopKind == StatementKind::ForInLoop || loopKind == StatementKind::ForOfLoop)
            loopSlots = 2;
        else
            loopSlots = 0;

        MOZ_ASSERT(loopSlots <= stackDepth_);

        if (enclosingLoop) {
            canIonOsr_ = (enclosingLoop->canIonOsr_ &&
                          stackDepth_ == enclosingLoop->stackDepth_ + loopSlots);
        } else {
            canIonOsr_ = stackDepth_ == loopSlots;
        }
    }

    uint32_t loopDepth() const {
        return loopDepth_;
    }

    bool canIonOsr() const {
        return canIonOsr_;
    }

    MOZ_MUST_USE bool emitSpecialBreakForDone(BytecodeEmitter* bce) {
        // This doesn't pop stack values, nor handle any other controls.
        // Should be called on the toplevel of the loop.
        MOZ_ASSERT(bce->stackDepth == stackDepth_);
        MOZ_ASSERT(bce->innermostNestableControl == this);

        if (!bce->newSrcNote(SRC_BREAK))
            return false;
        if (!bce->emitJump(JSOP_GOTO, &breaks))
            return false;

        return true;
    }

    MOZ_MUST_USE bool patchBreaksAndContinues(BytecodeEmitter* bce) {
        MOZ_ASSERT(continueTarget.offset != -1);
        if (!patchBreaks(bce))
            return false;
        bce->patchJumpsToTarget(continues, continueTarget);
        return true;
    }
};

class TryFinallyControl : public BytecodeEmitter::NestableControl
{
    bool emittingSubroutine_;

  public:
    // The subroutine when emitting a finally block.
    JumpList gosubs;

    // Offset of the last catch guard, if any.
    JumpList guardJump;

    TryFinallyControl(BytecodeEmitter* bce, StatementKind kind)
      : NestableControl(bce, kind),
        emittingSubroutine_(false)
    {
        MOZ_ASSERT(is<TryFinallyControl>());
    }

    void setEmittingSubroutine() {
        emittingSubroutine_ = true;
    }

    bool emittingSubroutine() const {
        return emittingSubroutine_;
    }
};

static inline void
MarkAllBindingsClosedOver(LexicalScope::Data& data)
{
    BindingName* names = data.names;
    for (uint32_t i = 0; i < data.length; i++)
        names[i] = BindingName(names[i].name(), true);
}

// A scope that introduces bindings.
class BytecodeEmitter::EmitterScope : public Nestable<BytecodeEmitter::EmitterScope>
{
    // The cache of bound names that may be looked up in the
    // scope. Initially populated as the set of names this scope binds. As
    // names are looked up in enclosing scopes, they are cached on the
    // current scope.
    PooledMapPtr<NameLocationMap> nameCache_;

    // If this scope's cache does not include free names, such as the
    // global scope, the NameLocation to return.
    Maybe<NameLocation> fallbackFreeNameLocation_;

    // True if there is a corresponding EnvironmentObject on the environment
    // chain, false if all bindings are stored in frame slots on the stack.
    bool hasEnvironment_;

    // The number of enclosing environments. Used for error checking.
    uint8_t environmentChainLength_;

    // The next usable slot on the frame for not-closed over bindings.
    //
    // The initial frame slot when assigning slots to bindings is the
    // enclosing scope's nextFrameSlot. For the first scope in a frame,
    // the initial frame slot is 0.
    uint32_t nextFrameSlot_;

    // The index in the BytecodeEmitter's interned scope vector, otherwise
    // ScopeNote::NoScopeIndex.
    uint32_t scopeIndex_;

    // If kind is Lexical, Catch, or With, the index in the BytecodeEmitter's
    // block scope note list. Otherwise ScopeNote::NoScopeNote.
    uint32_t noteIndex_;

    MOZ_MUST_USE bool ensureCache(BytecodeEmitter* bce) {
        return nameCache_.acquire(bce->cx);
    }

    template <typename BindingIter>
    MOZ_MUST_USE bool checkSlotLimits(BytecodeEmitter* bce, const BindingIter& bi) {
        if (bi.nextFrameSlot() >= LOCALNO_LIMIT ||
            bi.nextEnvironmentSlot() >= ENVCOORD_SLOT_LIMIT)
        {
            bce->reportError(nullptr, JSMSG_TOO_MANY_LOCALS);
            return false;
        }
        return true;
    }

    MOZ_MUST_USE bool checkEnvironmentChainLength(BytecodeEmitter* bce) {
        uint32_t hops;
        if (EmitterScope* emitterScope = enclosing(&bce))
            hops = emitterScope->environmentChainLength_;
        else
            hops = bce->sc->compilationEnclosingScope()->environmentChainLength();

        if (hops >= ENVCOORD_HOPS_LIMIT - 1) {
            bce->reportError(nullptr, JSMSG_TOO_DEEP, js_function_str);
            return false;
        }

        environmentChainLength_ = mozilla::AssertedCast<uint8_t>(hops + 1);
        return true;
    }

    void updateFrameFixedSlots(BytecodeEmitter* bce, const BindingIter& bi) {
        nextFrameSlot_ = bi.nextFrameSlot();
        if (nextFrameSlot_ > bce->maxFixedSlots)
            bce->maxFixedSlots = nextFrameSlot_;
        MOZ_ASSERT_IF(bce->sc->isFunctionBox() &&
                      (bce->sc->asFunctionBox()->isStarGenerator() ||
                       bce->sc->asFunctionBox()->isLegacyGenerator() ||
                       bce->sc->asFunctionBox()->isAsync()),
                      bce->maxFixedSlots == 0);
    }

    MOZ_MUST_USE bool putNameInCache(BytecodeEmitter* bce, JSAtom* name, NameLocation loc) {
        NameLocationMap& cache = *nameCache_;
        NameLocationMap::AddPtr p = cache.lookupForAdd(name);
        MOZ_ASSERT(!p);
        if (!cache.add(p, name, loc)) {
            ReportOutOfMemory(bce->cx);
            return false;
        }
        return true;
    }

    Maybe<NameLocation> lookupInCache(BytecodeEmitter* bce, JSAtom* name) {
        if (NameLocationMap::Ptr p = nameCache_->lookup(name))
            return Some(p->value().wrapped);
        if (fallbackFreeNameLocation_ && nameCanBeFree(bce, name))
            return fallbackFreeNameLocation_;
        return Nothing();
    }

    friend bool BytecodeEmitter::needsImplicitThis();

    EmitterScope* enclosing(BytecodeEmitter** bce) const {
        // There is an enclosing scope with access to the same frame.
        if (EmitterScope* inFrame = enclosingInFrame())
            return inFrame;

        // We are currently compiling the enclosing script, look in the
        // enclosing BCE.
        if ((*bce)->parent) {
            *bce = (*bce)->parent;
            return (*bce)->innermostEmitterScope;
        }

        return nullptr;
    }

    Scope* enclosingScope(BytecodeEmitter* bce) const {
        if (EmitterScope* es = enclosing(&bce))
            return es->scope(bce);

        // The enclosing script is already compiled or the current script is the
        // global script.
        return bce->sc->compilationEnclosingScope();
    }

    static bool nameCanBeFree(BytecodeEmitter* bce, JSAtom* name) {
        // '.generator' cannot be accessed by name.
        return name != bce->cx->names().dotGenerator;
    }

    static NameLocation searchInEnclosingScope(JSAtom* name, Scope* scope, uint8_t hops);
    NameLocation searchAndCache(BytecodeEmitter* bce, JSAtom* name);

    template <typename ScopeCreator>
    MOZ_MUST_USE bool internScope(BytecodeEmitter* bce, ScopeCreator createScope);
    template <typename ScopeCreator>
    MOZ_MUST_USE bool internBodyScope(BytecodeEmitter* bce, ScopeCreator createScope);
    MOZ_MUST_USE bool appendScopeNote(BytecodeEmitter* bce);

    MOZ_MUST_USE bool deadZoneFrameSlotRange(BytecodeEmitter* bce, uint32_t slotStart,
                                             uint32_t slotEnd);

  public:
    explicit EmitterScope(BytecodeEmitter* bce)
      : Nestable<EmitterScope>(&bce->innermostEmitterScope),
        nameCache_(bce->cx->frontendCollectionPool()),
        hasEnvironment_(false),
        environmentChainLength_(0),
        nextFrameSlot_(0),
        scopeIndex_(ScopeNote::NoScopeIndex),
        noteIndex_(ScopeNote::NoScopeNoteIndex)
    { }

    void dump(BytecodeEmitter* bce);

    MOZ_MUST_USE bool enterLexical(BytecodeEmitter* bce, ScopeKind kind,
                                   Handle<LexicalScope::Data*> bindings);
    MOZ_MUST_USE bool enterNamedLambda(BytecodeEmitter* bce, FunctionBox* funbox);
    MOZ_MUST_USE bool enterComprehensionFor(BytecodeEmitter* bce,
                                            Handle<LexicalScope::Data*> bindings);
    MOZ_MUST_USE bool enterFunction(BytecodeEmitter* bce, FunctionBox* funbox);
    MOZ_MUST_USE bool enterFunctionExtraBodyVar(BytecodeEmitter* bce, FunctionBox* funbox);
    MOZ_MUST_USE bool enterParameterExpressionVar(BytecodeEmitter* bce);
    MOZ_MUST_USE bool enterGlobal(BytecodeEmitter* bce, GlobalSharedContext* globalsc);
    MOZ_MUST_USE bool enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc);
    MOZ_MUST_USE bool enterModule(BytecodeEmitter* module, ModuleSharedContext* modulesc);
    MOZ_MUST_USE bool enterWith(BytecodeEmitter* bce);
    MOZ_MUST_USE bool deadZoneFrameSlots(BytecodeEmitter* bce);

    MOZ_MUST_USE bool leave(BytecodeEmitter* bce, bool nonLocal = false);

    uint32_t index() const {
        MOZ_ASSERT(scopeIndex_ != ScopeNote::NoScopeIndex, "Did you forget to intern a Scope?");
        return scopeIndex_;
    }

    uint32_t noteIndex() const {
        return noteIndex_;
    }

    Scope* scope(const BytecodeEmitter* bce) const {
        return bce->scopeList.vector[index()];
    }

    bool hasEnvironment() const {
        return hasEnvironment_;
    }

    // The first frame slot used.
    uint32_t frameSlotStart() const {
        if (EmitterScope* inFrame = enclosingInFrame())
            return inFrame->nextFrameSlot_;
        return 0;
    }

    // The last frame slot used + 1.
    uint32_t frameSlotEnd() const {
        return nextFrameSlot_;
    }

    uint32_t numFrameSlots() const {
        return frameSlotEnd() - frameSlotStart();
    }

    EmitterScope* enclosingInFrame() const {
        return Nestable<EmitterScope>::enclosing();
    }

    NameLocation lookup(BytecodeEmitter* bce, JSAtom* name) {
        if (Maybe<NameLocation> loc = lookupInCache(bce, name))
            return *loc;
        return searchAndCache(bce, name);
    }

    Maybe<NameLocation> locationBoundInScope(BytecodeEmitter* bce, JSAtom* name,
                                             EmitterScope* target);
};

void
BytecodeEmitter::EmitterScope::dump(BytecodeEmitter* bce)
{
    fprintf(stdout, "EmitterScope [%s] %p\n", ScopeKindString(scope(bce)->kind()), this);

    for (NameLocationMap::Range r = nameCache_->all(); !r.empty(); r.popFront()) {
        const NameLocation& l = r.front().value();

        JSAutoByteString bytes;
        if (!AtomToPrintableString(bce->cx, r.front().key(), &bytes))
            return;
        if (l.kind() != NameLocation::Kind::Dynamic)
            fprintf(stdout, "  %s %s ", BindingKindString(l.bindingKind()), bytes.ptr());
        else
            fprintf(stdout, "  %s ", bytes.ptr());

        switch (l.kind()) {
          case NameLocation::Kind::Dynamic:
            fprintf(stdout, "dynamic\n");
            break;
          case NameLocation::Kind::Global:
            fprintf(stdout, "global\n");
            break;
          case NameLocation::Kind::Intrinsic:
            fprintf(stdout, "intrinsic\n");
            break;
          case NameLocation::Kind::NamedLambdaCallee:
            fprintf(stdout, "named lambda callee\n");
            break;
          case NameLocation::Kind::Import:
            fprintf(stdout, "import\n");
            break;
          case NameLocation::Kind::ArgumentSlot:
            fprintf(stdout, "arg slot=%u\n", l.argumentSlot());
            break;
          case NameLocation::Kind::FrameSlot:
            fprintf(stdout, "frame slot=%u\n", l.frameSlot());
            break;
          case NameLocation::Kind::EnvironmentCoordinate:
            fprintf(stdout, "environment hops=%u slot=%u\n",
                    l.environmentCoordinate().hops(), l.environmentCoordinate().slot());
            break;
          case NameLocation::Kind::DynamicAnnexBVar:
            fprintf(stdout, "dynamic annex b var\n");
            break;
        }
    }

    fprintf(stdout, "\n");
}

template <typename ScopeCreator>
bool
BytecodeEmitter::EmitterScope::internScope(BytecodeEmitter* bce, ScopeCreator createScope)
{
    RootedScope enclosing(bce->cx, enclosingScope(bce));
    Scope* scope = createScope(bce->cx, enclosing);
    if (!scope)
        return false;
    hasEnvironment_ = scope->hasEnvironment();
    scopeIndex_ = bce->scopeList.length();
    return bce->scopeList.append(scope);
}

template <typename ScopeCreator>
bool
BytecodeEmitter::EmitterScope::internBodyScope(BytecodeEmitter* bce, ScopeCreator createScope)
{
    MOZ_ASSERT(bce->bodyScopeIndex == UINT32_MAX, "There can be only one body scope");
    bce->bodyScopeIndex = bce->scopeList.length();
    return internScope(bce, createScope);
}

bool
BytecodeEmitter::EmitterScope::appendScopeNote(BytecodeEmitter* bce)
{
    MOZ_ASSERT(ScopeKindIsInBody(scope(bce)->kind()) && enclosingInFrame(),
               "Scope notes are not needed for body-level scopes.");
    noteIndex_ = bce->scopeNoteList.length();
    return bce->scopeNoteList.append(index(), bce->offset(), bce->inPrologue(),
                                     enclosingInFrame() ? enclosingInFrame()->noteIndex()
                                                        : ScopeNote::NoScopeNoteIndex);
}

#ifdef DEBUG
static bool
NameIsOnEnvironment(Scope* scope, JSAtom* name)
{
    for (BindingIter bi(scope); bi; bi++) {
        // If found, the name must already be on the environment or an import,
        // or else there is a bug in the closed-over name analysis in the
        // Parser.
        if (bi.name() == name) {
            BindingLocation::Kind kind = bi.location().kind();

            if (bi.hasArgumentSlot()) {
                JSScript* script = scope->as<FunctionScope>().script();
                if (!script->strict() && !script->functionHasParameterExprs()) {
                    // Check for duplicate positional formal parameters.
                    for (BindingIter bi2(bi); bi2 && bi2.hasArgumentSlot(); bi2++) {
                        if (bi2.name() == name)
                            kind = bi2.location().kind();
                    }
                }
            }

            return kind == BindingLocation::Kind::Global ||
                   kind == BindingLocation::Kind::Environment ||
                   kind == BindingLocation::Kind::Import;
        }
    }

    // If not found, assume it's on the global or dynamically accessed.
    return true;
}
#endif

/* static */ NameLocation
BytecodeEmitter::EmitterScope::searchInEnclosingScope(JSAtom* name, Scope* scope, uint8_t hops)
{
    for (ScopeIter si(scope); si; si++) {
        MOZ_ASSERT(NameIsOnEnvironment(si.scope(), name));

        bool hasEnv = si.hasSyntacticEnvironment();

        switch (si.kind()) {
          case ScopeKind::Function:
            if (hasEnv) {
                JSScript* script = si.scope()->as<FunctionScope>().script();
                if (script->funHasExtensibleScope())
                    return NameLocation::Dynamic();

                for (BindingIter bi(si.scope()); bi; bi++) {
                    if (bi.name() != name)
                        continue;

                    BindingLocation bindLoc = bi.location();
                    if (bi.hasArgumentSlot() &&
                        !script->strict() &&
                        !script->functionHasParameterExprs())
                    {
                        // Check for duplicate positional formal parameters.
                        for (BindingIter bi2(bi); bi2 && bi2.hasArgumentSlot(); bi2++) {
                            if (bi2.name() == name)
                                bindLoc = bi2.location();
                        }
                    }

                    MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
                    return NameLocation::EnvironmentCoordinate(bi.kind(), hops, bindLoc.slot());
                }
            }
            break;

          case ScopeKind::FunctionBodyVar:
          case ScopeKind::ParameterExpressionVar:
          case ScopeKind::Lexical:
          case ScopeKind::NamedLambda:
          case ScopeKind::StrictNamedLambda:
          case ScopeKind::SimpleCatch:
          case ScopeKind::Catch:
            if (hasEnv) {
                for (BindingIter bi(si.scope()); bi; bi++) {
                    if (bi.name() != name)
                        continue;

                    // The name must already have been marked as closed
                    // over. If this assertion is hit, there is a bug in the
                    // name analysis.
                    BindingLocation bindLoc = bi.location();
                    MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
                    return NameLocation::EnvironmentCoordinate(bi.kind(), hops, bindLoc.slot());
                }
            }
            break;

          case ScopeKind::Module:
            if (hasEnv) {
                for (BindingIter bi(si.scope()); bi; bi++) {
                    if (bi.name() != name)
                        continue;

                    BindingLocation bindLoc = bi.location();

                    // Imports are on the environment but are indirect
                    // bindings and must be accessed dynamically instead of
                    // using an EnvironmentCoordinate.
                    if (bindLoc.kind() == BindingLocation::Kind::Import) {
                        MOZ_ASSERT(si.kind() == ScopeKind::Module);
                        return NameLocation::Import();
                    }

                    MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
                    return NameLocation::EnvironmentCoordinate(bi.kind(), hops, bindLoc.slot());
                }
            }
            break;

          case ScopeKind::Eval:
          case ScopeKind::StrictEval:
            // As an optimization, if the eval doesn't have its own var
            // environment and its immediate enclosing scope is a global
            // scope, all accesses are global.
            if (!hasEnv && si.scope()->enclosing()->is<GlobalScope>())
                return NameLocation::Global(BindingKind::Var);
            return NameLocation::Dynamic();

          case ScopeKind::Global:
            return NameLocation::Global(BindingKind::Var);

          case ScopeKind::With:
          case ScopeKind::NonSyntactic:
            return NameLocation::Dynamic();

          case ScopeKind::WasmFunction:
            MOZ_CRASH("No direct eval inside wasm functions");
        }

        if (hasEnv) {
            MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1);
            hops++;
        }
    }

    MOZ_CRASH("Malformed scope chain");
}

NameLocation
BytecodeEmitter::EmitterScope::searchAndCache(BytecodeEmitter* bce, JSAtom* name)
{
    Maybe<NameLocation> loc;
    uint8_t hops = hasEnvironment() ? 1 : 0;
    DebugOnly<bool> inCurrentScript = enclosingInFrame();

    // Start searching in the current compilation.
    for (EmitterScope* es = enclosing(&bce); es; es = es->enclosing(&bce)) {
        loc = es->lookupInCache(bce, name);
        if (loc) {
            if (loc->kind() == NameLocation::Kind::EnvironmentCoordinate)
                *loc = loc->addHops(hops);
            break;
        }

        if (es->hasEnvironment())
            hops++;

#ifdef DEBUG
        if (!es->enclosingInFrame())
            inCurrentScript = false;
#endif
    }

    // If the name is not found in the current compilation, walk the Scope
    // chain encompassing the compilation.
    if (!loc) {
        inCurrentScript = false;
        loc = Some(searchInEnclosingScope(name, bce->sc->compilationEnclosingScope(), hops));
    }

    // Each script has its own frame. A free name that is accessed
    // from an inner script must not be a frame slot access. If this
    // assertion is hit, it is a bug in the free name analysis in the
    // parser.
    MOZ_ASSERT_IF(!inCurrentScript, loc->kind() != NameLocation::Kind::FrameSlot);

    // It is always correct to not cache the location. Ignore OOMs to make
    // lookups infallible.
    if (!putNameInCache(bce, name, *loc))
        bce->cx->recoverFromOutOfMemory();

    return *loc;
}

Maybe<NameLocation>
BytecodeEmitter::EmitterScope::locationBoundInScope(BytecodeEmitter* bce, JSAtom* name,
                                                    EmitterScope* target)
{
    // The target scope must be an intra-frame enclosing scope of this
    // one. Count the number of extra hops to reach it.
    uint8_t extraHops = 0;
    for (EmitterScope* es = this; es != target; es = es->enclosingInFrame()) {
        if (es->hasEnvironment())
            extraHops++;
    }

    // Caches are prepopulated with bound names. So if the name is bound in a
    // particular scope, it must already be in the cache. Furthermore, don't
    // consult the fallback location as we only care about binding names.
    Maybe<NameLocation> loc;
    if (NameLocationMap::Ptr p = target->nameCache_->lookup(name)) {
        NameLocation l = p->value().wrapped;
        if (l.kind() == NameLocation::Kind::EnvironmentCoordinate)
            loc = Some(l.addHops(extraHops));
        else
            loc = Some(l);
    }
    return loc;
}

bool
BytecodeEmitter::EmitterScope::deadZoneFrameSlotRange(BytecodeEmitter* bce, uint32_t slotStart,
                                                      uint32_t slotEnd)
{
    // Lexical bindings throw ReferenceErrors if they are used before
    // initialization. See ES6 8.1.1.1.6.
    //
    // For completeness, lexical bindings are initialized in ES6 by calling
    // InitializeBinding, after which touching the binding will no longer
    // throw reference errors. See 13.1.11, 9.2.13, 13.6.3.4, 13.6.4.6,
    // 13.6.4.8, 13.14.5, 15.1.8, and 15.2.0.15.
    if (slotStart != slotEnd) {
        if (!bce->emit1(JSOP_UNINITIALIZED))
            return false;
        for (uint32_t slot = slotStart; slot < slotEnd; slot++) {
            if (!bce->emitLocalOp(JSOP_INITLEXICAL, slot))
                return false;
        }
        if (!bce->emit1(JSOP_POP))
            return false;
    }

    return true;
}

bool
BytecodeEmitter::EmitterScope::deadZoneFrameSlots(BytecodeEmitter* bce)
{
    return deadZoneFrameSlotRange(bce, frameSlotStart(), frameSlotEnd());
}

bool
BytecodeEmitter::EmitterScope::enterLexical(BytecodeEmitter* bce, ScopeKind kind,
                                            Handle<LexicalScope::Data*> bindings)
{
    MOZ_ASSERT(kind != ScopeKind::NamedLambda && kind != ScopeKind::StrictNamedLambda);
    MOZ_ASSERT(this == bce->innermostEmitterScope);

    if (!ensureCache(bce))
        return false;

    // Marks all names as closed over if the the context requires it. This
    // cannot be done in the Parser as we may not know if the context requires
    // all bindings to be closed over until after parsing is finished. For
    // example, legacy generators require all bindings to be closed over but
    // it is unknown if a function is a legacy generator until the first
    // 'yield' expression is parsed.
    //
    // This is not a problem with other scopes, as all other scopes with
    // bindings are body-level. At the time of their creation, whether or not
    // the context requires all bindings to be closed over is already known.
    if (bce->sc->allBindingsClosedOver())
        MarkAllBindingsClosedOver(*bindings);

    // Resolve bindings.
    TDZCheckCache* tdzCache = bce->innermostTDZCheckCache;
    uint32_t firstFrameSlot = frameSlotStart();
    BindingIter bi(*bindings, firstFrameSlot, /* isNamedLambda = */ false);
    for (; bi; bi++) {
        if (!checkSlotLimits(bce, bi))
            return false;

        NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
        if (!putNameInCache(bce, bi.name(), loc))
            return false;

        if (!tdzCache->noteTDZCheck(bce, bi.name(), CheckTDZ))
            return false;
    }

    updateFrameFixedSlots(bce, bi);

    // Create and intern the VM scope.
    auto createScope = [kind, bindings, firstFrameSlot](JSContext* cx,
                                                        HandleScope enclosing)
    {
        return LexicalScope::create(cx, kind, bindings, firstFrameSlot, enclosing);
    };
    if (!internScope(bce, createScope))
        return false;

    if (ScopeKindIsInBody(kind) && hasEnvironment()) {
        // After interning the VM scope we can get the scope index.
        if (!bce->emitInternedScopeOp(index(), JSOP_PUSHLEXICALENV))
            return false;
    }

    // Lexical scopes need notes to be mapped from a pc.
    if (!appendScopeNote(bce))
        return false;

    // Put frame slots in TDZ. Environment slots are poisoned during
    // environment creation.
    //
    // This must be done after appendScopeNote to be considered in the extent
    // of the scope.
    if (!deadZoneFrameSlotRange(bce, firstFrameSlot, frameSlotEnd()))
        return false;

    return checkEnvironmentChainLength(bce);
}

bool
BytecodeEmitter::EmitterScope::enterNamedLambda(BytecodeEmitter* bce, FunctionBox* funbox)
{
    MOZ_ASSERT(this == bce->innermostEmitterScope);
    MOZ_ASSERT(funbox->namedLambdaBindings());

    if (!ensureCache(bce))
        return false;

    // See comment in enterLexical about allBindingsClosedOver.
    if (funbox->allBindingsClosedOver())
        MarkAllBindingsClosedOver(*funbox->namedLambdaBindings());

    BindingIter bi(*funbox->namedLambdaBindings(), LOCALNO_LIMIT, /* isNamedLambda = */ true);
    MOZ_ASSERT(bi.kind() == BindingKind::NamedLambdaCallee);

    // The lambda name, if not closed over, is accessed via JSOP_CALLEE and
    // not a frame slot. Do not update frame slot information.
    NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
    if (!putNameInCache(bce, bi.name(), loc))
        return false;

    bi++;
    MOZ_ASSERT(!bi, "There should be exactly one binding in a NamedLambda scope");

    auto createScope = [funbox](JSContext* cx, HandleScope enclosing) {
        ScopeKind scopeKind =
            funbox->strict() ? ScopeKind::StrictNamedLambda : ScopeKind::NamedLambda;
        return LexicalScope::create(cx, scopeKind, funbox->namedLambdaBindings(),
                                    LOCALNO_LIMIT, enclosing);
    };
    if (!internScope(bce, createScope))
        return false;

    return checkEnvironmentChainLength(bce);
}

bool
BytecodeEmitter::EmitterScope::enterComprehensionFor(BytecodeEmitter* bce,
                                                     Handle<LexicalScope::Data*> bindings)
{
    if (!enterLexical(bce, ScopeKind::Lexical, bindings))
        return false;

    // For comprehensions, initialize all lexical names up front to undefined
    // because they're now a dead feature and don't interact properly with
    // TDZ.
    auto nop = [](BytecodeEmitter*, const NameLocation&, bool) {
        return true;
    };

    if (!bce->emit1(JSOP_UNDEFINED))
        return false;

    RootedAtom name(bce->cx);
    for (BindingIter bi(*bindings, frameSlotStart(), /* isNamedLambda = */ false); bi; bi++) {
        name = bi.name();
        if (!bce->emitInitializeName(name, nop))
            return false;
    }

    if (!bce->emit1(JSOP_POP))
        return false;

    return true;
}

bool
BytecodeEmitter::EmitterScope::enterParameterExpressionVar(BytecodeEmitter* bce)
{
    MOZ_ASSERT(this == bce->innermostEmitterScope);

    if (!ensureCache(bce))
        return false;

    // Parameter expressions var scopes have no pre-set bindings and are
    // always extensible, as they are needed for eval.
    fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());

    // Create and intern the VM scope.
    uint32_t firstFrameSlot = frameSlotStart();
    auto createScope = [firstFrameSlot](JSContext* cx, HandleScope enclosing) {
        return VarScope::create(cx, ScopeKind::ParameterExpressionVar,
                                /* data = */ nullptr, firstFrameSlot,
                                /* needsEnvironment = */ true, enclosing);
    };
    if (!internScope(bce, createScope))
        return false;

    MOZ_ASSERT(hasEnvironment());
    if (!bce->emitInternedScopeOp(index(), JSOP_PUSHVARENV))
        return false;

    // The extra var scope needs a note to be mapped from a pc.
    if (!appendScopeNote(bce))
        return false;

    return checkEnvironmentChainLength(bce);
}

bool
BytecodeEmitter::EmitterScope::enterFunction(BytecodeEmitter* bce, FunctionBox* funbox)
{
    MOZ_ASSERT(this == bce->innermostEmitterScope);

    // If there are parameter expressions, there is an extra var scope.
    if (!funbox->hasExtraBodyVarScope())
        bce->setVarEmitterScope(this);

    if (!ensureCache(bce))
        return false;

    // Resolve body-level bindings, if there are any.
    auto bindings = funbox->functionScopeBindings();
    Maybe<uint32_t> lastLexicalSlot;
    if (bindings) {
        NameLocationMap& cache = *nameCache_;

        BindingIter bi(*bindings, funbox->hasParameterExprs);
        for (; bi; bi++) {
            if (!checkSlotLimits(bce, bi))
                return false;

            NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
            NameLocationMap::AddPtr p = cache.lookupForAdd(bi.name());

            // The only duplicate bindings that occur are simple formal
            // parameters, in which case the last position counts, so update the
            // location.
            if (p) {
                MOZ_ASSERT(bi.kind() == BindingKind::FormalParameter);
                MOZ_ASSERT(!funbox->hasDestructuringArgs);
                MOZ_ASSERT(!funbox->hasRest());
                p->value() = loc;
                continue;
            }

            if (!cache.add(p, bi.name(), loc)) {
                ReportOutOfMemory(bce->cx);
                return false;
            }
        }

        updateFrameFixedSlots(bce, bi);
    } else {
        nextFrameSlot_ = 0;
    }

    // If the function's scope may be extended at runtime due to sloppy direct
    // eval and there is no extra var scope, any names beyond the function
    // scope must be accessed dynamically as we don't know if the name will
    // become a 'var' binding due to direct eval.
    if (!funbox->hasParameterExprs && funbox->hasExtensibleScope())
        fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());

    // In case of parameter expressions, the parameters are lexical
    // bindings and have TDZ.
    if (funbox->hasParameterExprs && nextFrameSlot_) {
        uint32_t paramFrameSlotEnd = 0;
        for (BindingIter bi(*bindings, true); bi; bi++) {
            if (!BindingKindIsLexical(bi.kind()))
                break;

            NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
            if (loc.kind() == NameLocation::Kind::FrameSlot) {
                MOZ_ASSERT(paramFrameSlotEnd <= loc.frameSlot());
                paramFrameSlotEnd = loc.frameSlot() + 1;
            }
        }

        if (!deadZoneFrameSlotRange(bce, 0, paramFrameSlotEnd))
            return false;
    }

    // Create and intern the VM scope.
    auto createScope = [funbox](JSContext* cx, HandleScope enclosing) {
        RootedFunction fun(cx, funbox->function());
        return FunctionScope::create(cx, funbox->functionScopeBindings(),
                                     funbox->hasParameterExprs,
                                     funbox->needsCallObjectRegardlessOfBindings(),
                                     fun, enclosing);
    };
    if (!internBodyScope(bce, createScope))
        return false;

    return checkEnvironmentChainLength(bce);
}

bool
BytecodeEmitter::EmitterScope::enterFunctionExtraBodyVar(BytecodeEmitter* bce, FunctionBox* funbox)
{
    MOZ_ASSERT(funbox->hasParameterExprs);
    MOZ_ASSERT(funbox->extraVarScopeBindings() ||
               funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings());
    MOZ_ASSERT(this == bce->innermostEmitterScope);

    // The extra var scope is never popped once it's entered. It replaces the
    // function scope as the var emitter scope.
    bce->setVarEmitterScope(this);

    if (!ensureCache(bce))
        return false;

    // Resolve body-level bindings, if there are any.
    uint32_t firstFrameSlot = frameSlotStart();
    if (auto bindings = funbox->extraVarScopeBindings()) {
        BindingIter bi(*bindings, firstFrameSlot);
        for (; bi; bi++) {
            if (!checkSlotLimits(bce, bi))
                return false;

            NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
            if (!putNameInCache(bce, bi.name(), loc))
                return false;
        }

        updateFrameFixedSlots(bce, bi);
    } else {
        nextFrameSlot_ = firstFrameSlot;
    }

    // If the extra var scope may be extended at runtime due to sloppy
    // direct eval, any names beyond the var scope must be accessed
    // dynamically as we don't know if the name will become a 'var' binding
    // due to direct eval.
    if (funbox->hasExtensibleScope())
        fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());

    // Create and intern the VM scope.
    auto createScope = [funbox, firstFrameSlot](JSContext* cx, HandleScope enclosing) {
        return VarScope::create(cx, ScopeKind::FunctionBodyVar,
                                funbox->extraVarScopeBindings(), firstFrameSlot,
                                funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings(),
                                enclosing);
    };
    if (!internScope(bce, createScope))
        return false;

    if (hasEnvironment()) {
        if (!bce->emitInternedScopeOp(index(), JSOP_PUSHVARENV))
            return false;
    }

    // The extra var scope needs a note to be mapped from a pc.
    if (!appendScopeNote(bce))
        return false;

    return checkEnvironmentChainLength(bce);
}

class DynamicBindingIter : public BindingIter
{
  public:
    explicit DynamicBindingIter(GlobalSharedContext* sc)
      : BindingIter(*sc->bindings)
    { }

    explicit DynamicBindingIter(EvalSharedContext* sc)
      : BindingIter(*sc->bindings, /* strict = */ false)
    {
        MOZ_ASSERT(!sc->strict());
    }

    JSOp bindingOp() const {
        switch (kind()) {
          case BindingKind::Var:
            return JSOP_DEFVAR;
          case BindingKind::Let:
            return JSOP_DEFLET;
          case BindingKind::Const:
            return JSOP_DEFCONST;
          default:
            MOZ_CRASH("Bad BindingKind");
        }
    }
};

bool
BytecodeEmitter::EmitterScope::enterGlobal(BytecodeEmitter* bce, GlobalSharedContext* globalsc)
{
    MOZ_ASSERT(this == bce->innermostEmitterScope);

    bce->setVarEmitterScope(this);

    if (!ensureCache(bce))
        return false;

    if (bce->emitterMode == BytecodeEmitter::SelfHosting) {
        // In self-hosting, it is incorrect to consult the global scope because
        // self-hosted scripts are cloned into their target compartments before
        // they are run. Instead of Global, Intrinsic is used for all names.
        //
        // Intrinsic lookups are redirected to the special intrinsics holder
        // in the global object, into which any missing values are cloned
        // lazily upon first access.
        fallbackFreeNameLocation_ = Some(NameLocation::Intrinsic());

        auto createScope = [](JSContext* cx, HandleScope enclosing) {
            MOZ_ASSERT(!enclosing);
            return &cx->global()->emptyGlobalScope();
        };
        return internBodyScope(bce, createScope);
    }

    // Resolve binding names and emit DEF{VAR,LET,CONST} prologue ops.
    if (globalsc->bindings) {
        for (DynamicBindingIter bi(globalsc); bi; bi++) {
            NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
            JSAtom* name = bi.name();
            if (!putNameInCache(bce, name, loc))
                return false;

            // Define the name in the prologue. Do not emit DEFVAR for
            // functions that we'll emit DEFFUN for.
            if (bi.isTopLevelFunction())
                continue;

            if (!bce->emitAtomOp(name, bi.bindingOp()))
                return false;
        }
    }

    // Note that to save space, we don't add free names to the cache for
    // global scopes. They are assumed to be global vars in the syntactic
    // global scope, dynamic accesses under non-syntactic global scope.
    if (globalsc->scopeKind() == ScopeKind::Global)
        fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));
    else
        fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());

    auto createScope = [globalsc](JSContext* cx, HandleScope enclosing) {
        MOZ_ASSERT(!enclosing);
        return GlobalScope::create(cx, globalsc->scopeKind(), globalsc->bindings);
    };
    return internBodyScope(bce, createScope);
}

bool
BytecodeEmitter::EmitterScope::enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc)
{
    MOZ_ASSERT(this == bce->innermostEmitterScope);

    bce->setVarEmitterScope(this);

    if (!ensureCache(bce))
        return false;

    // For simplicity, treat all free name lookups in eval scripts as dynamic.
    fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());

    // Create the `var` scope. Note that there is also a lexical scope, created
    // separately in emitScript().
    auto createScope = [evalsc](JSContext* cx, HandleScope enclosing) {
        ScopeKind scopeKind = evalsc->strict() ? ScopeKind::StrictEval : ScopeKind::Eval;
        return EvalScope::create(cx, scopeKind, evalsc->bindings, enclosing);
    };
    if (!internBodyScope(bce, createScope))
        return false;

    if (hasEnvironment()) {
        if (!bce->emitInternedScopeOp(index(), JSOP_PUSHVARENV))
            return false;
    } else {
        // Resolve binding names and emit DEFVAR prologue ops if we don't have
        // an environment (i.e., a sloppy eval not in a parameter expression).
        // Eval scripts always have their own lexical scope, but non-strict
        // scopes may introduce 'var' bindings to the nearest var scope.
        //
        // TODO: We may optimize strict eval bindings in the future to be on
        // the frame. For now, handle everything dynamically.
        if (!hasEnvironment() && evalsc->bindings) {
            for (DynamicBindingIter bi(evalsc); bi; bi++) {
                MOZ_ASSERT(bi.bindingOp() == JSOP_DEFVAR);

                if (bi.isTopLevelFunction())
                    continue;

                if (!bce->emitAtomOp(bi.name(), JSOP_DEFVAR))
                    return false;
            }
        }

        // As an optimization, if the eval does not have its own var
        // environment and is directly enclosed in a global scope, then all
        // free name lookups are global.
        if (scope(bce)->enclosing()->is<GlobalScope>())
            fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));
    }

    return true;
}

bool
BytecodeEmitter::EmitterScope::enterModule(BytecodeEmitter* bce, ModuleSharedContext* modulesc)
{
    MOZ_ASSERT(this == bce->innermostEmitterScope);

    bce->setVarEmitterScope(this);

    if (!ensureCache(bce))
        return false;

    // Resolve body-level bindings, if there are any.
    TDZCheckCache* tdzCache = bce->innermostTDZCheckCache;
    Maybe<uint32_t> firstLexicalFrameSlot;
    if (ModuleScope::Data* bindings = modulesc->bindings) {
        BindingIter bi(*bindings);
        for (; bi; bi++) {
            if (!checkSlotLimits(bce, bi))
                return false;

            NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
            if (!putNameInCache(bce, bi.name(), loc))
                return false;

            if (BindingKindIsLexical(bi.kind())) {
                if (loc.kind() == NameLocation::Kind::FrameSlot && !firstLexicalFrameSlot)
                    firstLexicalFrameSlot = Some(loc.frameSlot());

                if (!tdzCache->noteTDZCheck(bce, bi.name(), CheckTDZ))
                    return false;
            }
        }

        updateFrameFixedSlots(bce, bi);
    } else {
        nextFrameSlot_ = 0;
    }

    // Modules are toplevel, so any free names are global.
    fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));

    // Put lexical frame slots in TDZ. Environment slots are poisoned during
    // environment creation.
    if (firstLexicalFrameSlot) {
        if (!deadZoneFrameSlotRange(bce, *firstLexicalFrameSlot, frameSlotEnd()))
            return false;
    }

    // Create and intern the VM scope.
    auto createScope = [modulesc](JSContext* cx, HandleScope enclosing) {
        return ModuleScope::create(cx, modulesc->bindings, modulesc->module(), enclosing);
    };
    if (!internBodyScope(bce, createScope))
        return false;

    return checkEnvironmentChainLength(bce);
}

bool
BytecodeEmitter::EmitterScope::enterWith(BytecodeEmitter* bce)
{
    MOZ_ASSERT(this == bce->innermostEmitterScope);

    if (!ensureCache(bce))
        return false;

    // 'with' make all accesses dynamic and unanalyzable.
    fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());

    auto createScope = [](JSContext* cx, HandleScope enclosing) {
        return WithScope::create(cx, enclosing);
    };
    if (!internScope(bce, createScope))
        return false;

    if (!bce->emitInternedScopeOp(index(), JSOP_ENTERWITH))
        return false;

    if (!appendScopeNote(bce))
        return false;

    return checkEnvironmentChainLength(bce);
}

bool
BytecodeEmitter::EmitterScope::leave(BytecodeEmitter* bce, bool nonLocal)
{
    // If we aren't leaving the scope due to a non-local jump (e.g., break),
    // we must be the innermost scope.
    MOZ_ASSERT_IF(!nonLocal, this == bce->innermostEmitterScope);

    ScopeKind kind = scope(bce)->kind();
    switch (kind) {
      case ScopeKind::Lexical:
      case ScopeKind::SimpleCatch:
      case ScopeKind::Catch:
        if (!bce->emit1(hasEnvironment() ? JSOP_POPLEXICALENV : JSOP_DEBUGLEAVELEXICALENV))
            return false;
        break;

      case ScopeKind::With:
        if (!bce->emit1(JSOP_LEAVEWITH))
            return false;
        break;

      case ScopeKind::ParameterExpressionVar:
        MOZ_ASSERT(hasEnvironment());
        if (!bce->emit1(JSOP_POPVARENV))
            return false;
        break;

      case ScopeKind::Function:
      case ScopeKind::FunctionBodyVar:
      case ScopeKind::NamedLambda:
      case ScopeKind::StrictNamedLambda:
      case ScopeKind::Eval:
      case ScopeKind::StrictEval:
      case ScopeKind::Global:
      case ScopeKind::NonSyntactic:
      case ScopeKind::Module:
        break;

      case ScopeKind::WasmFunction:
        MOZ_CRASH("No wasm function scopes in JS");
    }

    // Finish up the scope if we are leaving it in LIFO fashion.
    if (!nonLocal) {
        // Popping scopes due to non-local jumps generate additional scope
        // notes. See NonLocalExitControl::prepareForNonLocalJump.
        if (ScopeKindIsInBody(kind)) {
            // The extra function var scope is never popped once it's pushed,
            // so its scope note extends until the end of any possible code.
            uint32_t offset = kind == ScopeKind::FunctionBodyVar ? UINT32_MAX : bce->offset();
            bce->scopeNoteList.recordEnd(noteIndex_, offset, bce->inPrologue());
        }
    }

    return true;
}

Maybe<MaybeCheckTDZ>
BytecodeEmitter::TDZCheckCache::needsTDZCheck(BytecodeEmitter* bce, JSAtom* name)
{
    if (!ensureCache(bce))
        return Nothing();

    CheckTDZMap::AddPtr p = cache_->lookupForAdd(name);
    if (p)
        return Some(p->value().wrapped);

    MaybeCheckTDZ rv = CheckTDZ;
    for (TDZCheckCache* it = enclosing(); it; it = it->enclosing()) {
        if (it->cache_) {
            if (CheckTDZMap::Ptr p2 = it->cache_->lookup(name)) {
                rv = p2->value();
                break;
            }
        }
    }

    if (!cache_->add(p, name, rv)) {
        ReportOutOfMemory(bce->cx);
        return Nothing();
    }

    return Some(rv);
}

bool
BytecodeEmitter::TDZCheckCache::noteTDZCheck(BytecodeEmitter* bce, JSAtom* name,
                                             MaybeCheckTDZ check)
{
    if (!ensureCache(bce))
        return false;

    CheckTDZMap::AddPtr p = cache_->lookupForAdd(name);
    if (p) {
        MOZ_ASSERT(!check, "TDZ only needs to be checked once per binding per basic block.");
        p->value() = check;
    } else {
        if (!cache_->add(p, name, check))
            return false;
    }

    return true;
}

class MOZ_STACK_CLASS TryEmitter
{
  public:
    enum Kind {
        TryCatch,
        TryCatchFinally,
        TryFinally
    };
    enum ShouldUseRetVal {
        UseRetVal,
        DontUseRetVal
    };
    enum ShouldUseControl {
        UseControl,
        DontUseControl,
    };

  private:
    BytecodeEmitter* bce_;
    Kind kind_;
    ShouldUseRetVal retValKind_;

    // Track jumps-over-catches and gosubs-to-finally for later fixup.
    //
    // When a finally block is active, non-local jumps (including
    // jumps-over-catches) result in a GOSUB being written into the bytecode
    // stream and fixed-up later.
    //
    // If ShouldUseControl is DontUseControl, all that handling is skipped.
    // DontUseControl is used by yield* and the internal try-catch around
    // IteratorClose. These internal uses must:
    //   * have only one catch block
    //   * have no catch guard
    //   * have JSOP_GOTO at the end of catch-block
    //   * have no non-local-jump
    //   * don't use finally block for normal completion of try-block and
    //     catch-block
    //
    // Additionally, a finally block may be emitted when ShouldUseControl is
    // DontUseControl, even if the kind is not TryCatchFinally or TryFinally,
    // because GOSUBs are not emitted. This internal use shares the
    // requirements as above.
    Maybe<TryFinallyControl> controlInfo_;

    int depth_;
    unsigned noteIndex_;
    ptrdiff_t tryStart_;
    JumpList catchAndFinallyJump_;
    JumpTarget tryEnd_;
    JumpTarget finallyStart_;

    enum State {
        Start,
        Try,
        TryEnd,
        Catch,
        CatchEnd,
        Finally,
        FinallyEnd,
        End
    };
    State state_;

    bool hasCatch() const {
        return kind_ == TryCatch || kind_ == TryCatchFinally;
    }
    bool hasFinally() const {
        return kind_ == TryCatchFinally || kind_ == TryFinally;
    }

  public:
    TryEmitter(BytecodeEmitter* bce, Kind kind, ShouldUseRetVal retValKind = UseRetVal,
               ShouldUseControl controlKind = UseControl)
      : bce_(bce),
        kind_(kind),
        retValKind_(retValKind),
        depth_(0),
        noteIndex_(0),
        tryStart_(0),
        state_(Start)
    {
        if (controlKind == UseControl)
            controlInfo_.emplace(bce_, hasFinally() ? StatementKind::Finally : StatementKind::Try);
        finallyStart_.offset = 0;
    }

    bool emitJumpOverCatchAndFinally() {
        if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
            return false;
        return true;
    }

    bool emitTry() {
        MOZ_ASSERT(state_ == Start);

        // Since an exception can be thrown at any place inside the try block,
        // we need to restore the stack and the scope chain before we transfer
        // the control to the exception handler.
        //
        // For that we store in a try note associated with the catch or
        // finally block the stack depth upon the try entry. The interpreter
        // uses this depth to properly unwind the stack and the scope chain.
        depth_ = bce_->stackDepth;

        // Record the try location, then emit the try block.
        if (!bce_->newSrcNote(SRC_TRY, &noteIndex_))
            return false;
        if (!bce_->emit1(JSOP_TRY))
            return false;
        tryStart_ = bce_->offset();

        state_ = Try;
        return true;
    }

  private:
    bool emitTryEnd() {
        MOZ_ASSERT(state_ == Try);
        MOZ_ASSERT(depth_ == bce_->stackDepth);

        // GOSUB to finally, if present.
        if (hasFinally() && controlInfo_) {
            if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs))
                return false;
        }

        // Source note points to the jump at the end of the try block.
        if (!bce_->setSrcNoteOffset(noteIndex_, 0, bce_->offset() - tryStart_ + JSOP_TRY_LENGTH))
            return false;

        // Emit jump over catch and/or finally.
        if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
            return false;

        if (!bce_->emitJumpTarget(&tryEnd_))
            return false;

        return true;
    }

  public:
    bool emitCatch() {
        if (state_ == Try) {
            if (!emitTryEnd())
                return false;
        } else {
            MOZ_ASSERT(state_ == Catch);
            if (!emitCatchEnd(true))
                return false;
        }

        MOZ_ASSERT(bce_->stackDepth == depth_);

        if (retValKind_ == UseRetVal) {
            // Clear the frame's return value that might have been set by the
            // try block:
            //
            //   eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1
            if (!bce_->emit1(JSOP_UNDEFINED))
                return false;
            if (!bce_->emit1(JSOP_SETRVAL))
                return false;
        }

        state_ = Catch;
        return true;
    }

  private:
    bool emitCatchEnd(bool hasNext) {
        MOZ_ASSERT(state_ == Catch);

        if (!controlInfo_)
            return true;

        // gosub <finally>, if required.
        if (hasFinally()) {
            if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs))
                return false;
            MOZ_ASSERT(bce_->stackDepth == depth_);
        }

        // Jump over the remaining catch blocks.  This will get fixed
        // up to jump to after catch/finally.
        if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
            return false;

        // If this catch block had a guard clause, patch the guard jump to
        // come here.
        if (controlInfo_->guardJump.offset != -1) {
            if (!bce_->emitJumpTargetAndPatch(controlInfo_->guardJump))
                return false;
            controlInfo_->guardJump.offset = -1;

            // If this catch block is the last one, rethrow, delegating
            // execution of any finally block to the exception handler.
            if (!hasNext) {
                if (!bce_->emit1(JSOP_EXCEPTION))
                    return false;
                if (!bce_->emit1(JSOP_THROW))
                    return false;
            }
        }

        return true;
    }

  public:
    bool emitFinally(const Maybe<uint32_t>& finallyPos = Nothing()) {
        // If we are using controlInfo_ (i.e., emitting a syntactic try
        // blocks), we must have specified up front if there will be a finally
        // close. For internal try blocks, like those emitted for yield* and
        // IteratorClose inside for-of loops, we can emitFinally even without
        // specifying up front, since the internal try blocks emit no GOSUBs.
        if (!controlInfo_) {
            if (kind_ == TryCatch)
                kind_ = TryCatchFinally;
        } else {
            MOZ_ASSERT(hasFinally());
        }

        if (state_ == Try) {
            if (!emitTryEnd())
                return false;
        } else {
            MOZ_ASSERT(state_ == Catch);
            if (!emitCatchEnd(false))
                return false;
        }

        MOZ_ASSERT(bce_->stackDepth == depth_);

        if (!bce_->emitJumpTarget(&finallyStart_))
            return false;

        if (controlInfo_) {
            // Fix up the gosubs that might have been emitted before non-local
            // jumps to the finally code.
            bce_->patchJumpsToTarget(controlInfo_->gosubs, finallyStart_);

            // Indicate that we're emitting a subroutine body.
            controlInfo_->setEmittingSubroutine();
        }
        if (finallyPos) {
            if (!bce_->updateSourceCoordNotes(finallyPos.value()))
                return false;
        }
        if (!bce_->emit1(JSOP_FINALLY))
            return false;

        if (retValKind_ == UseRetVal) {
            if (!bce_->emit1(JSOP_GETRVAL))
                return false;

            // Clear the frame's return value to make break/continue return
            // correct value even if there's no other statement before them:
            //
            //   eval("x: try { 1 } finally { break x; }"); // undefined, not 1
            if (!bce_->emit1(JSOP_UNDEFINED))
                return false;
            if (!bce_->emit1(JSOP_SETRVAL))
                return false;
        }

        state_ = Finally;
        return true;
    }

  private:
    bool emitFinallyEnd() {
        MOZ_ASSERT(state_ == Finally);

        if (retValKind_ == UseRetVal) {
            if (!bce_->emit1(JSOP_SETRVAL))
                return false;
        }

        if (!bce_->emit1(JSOP_RETSUB))
            return false;

        bce_->hasTryFinally = true;
        return true;
    }

  public:
    bool emitEnd() {
        if (state_ == Catch) {
            MOZ_ASSERT(!hasFinally());
            if (!emitCatchEnd(false))
                return false;
        } else {
            MOZ_ASSERT(state_ == Finally);
            MOZ_ASSERT(hasFinally());
            if (!emitFinallyEnd())
                return false;
        }

        MOZ_ASSERT(bce_->stackDepth == depth_);

        // ReconstructPCStack needs a NOP here to mark the end of the last
        // catch block.
        if (!bce_->emit1(JSOP_NOP))
            return false;

        // Fix up the end-of-try/catch jumps to come here.
        if (!bce_->emitJumpTargetAndPatch(catchAndFinallyJump_))
            return false;

        // Add the try note last, to let post-order give us the right ordering
        // (first to last for a given nesting level, inner to outer by level).
        if (hasCatch()) {
            if (!bce_->tryNoteList.append(JSTRY_CATCH, depth_, tryStart_, tryEnd_.offset))
                return false;
        }

        // If we've got a finally, mark try+catch region with additional
        // trynote to catch exceptions (re)thrown from a catch block or
        // for the try{}finally{} case.
        if (hasFinally()) {
            if (!bce_->tryNoteList.append(JSTRY_FINALLY, depth_, tryStart_, finallyStart_.offset))
                return false;
        }

        state_ = End;
        return true;
    }
};

class MOZ_STACK_CLASS IfThenElseEmitter
{
    BytecodeEmitter* bce_;
    JumpList jumpAroundThen_;
    JumpList jumpsAroundElse_;
    unsigned noteIndex_;
    int32_t thenDepth_;
#ifdef DEBUG
    int32_t pushed_;
    bool calculatedPushed_;
#endif
    enum State {
        Start,
        If,
        Cond,
        IfElse,
        Else,
        End
    };
    State state_;

  public:
    explicit IfThenElseEmitter(BytecodeEmitter* bce)
      : bce_(bce),
        noteIndex_(-1),
        thenDepth_(0),
#ifdef DEBUG
        pushed_(0),
        calculatedPushed_(false),
#endif
        state_(Start)
    {}

    ~IfThenElseEmitter()
    {}

  private:
    bool emitIf(State nextState) {
        MOZ_ASSERT(state_ == Start || state_ == Else);
        MOZ_ASSERT(nextState == If || nextState == IfElse || nextState == Cond);

        // Clear jumpAroundThen_ offset that points previous JSOP_IFEQ.
        if (state_ == Else)
            jumpAroundThen_ = JumpList();

        // Emit an annotated branch-if-false around the then part.
        SrcNoteType type = nextState == If ? SRC_IF : nextState == IfElse ? SRC_IF_ELSE : SRC_COND;
        if (!bce_->newSrcNote(type, &noteIndex_))
            return false;
        if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_))
            return false;

        // To restore stack depth in else part, save depth of the then part.
#ifdef DEBUG
        // If DEBUG, this is also necessary to calculate |pushed_|.
        thenDepth_ = bce_->stackDepth;
#else
        if (nextState == IfElse || nextState == Cond)
            thenDepth_ = bce_->stackDepth;
#endif
        state_ = nextState;
        return true;
    }

  public:
    bool emitIf() {
        return emitIf(If);
    }

    bool emitCond() {
        return emitIf(Cond);
    }

    bool emitIfElse() {
        return emitIf(IfElse);
    }

    bool emitElse() {
        MOZ_ASSERT(state_ == IfElse || state_ == Cond);

        calculateOrCheckPushed();

        // Emit a jump from the end of our then part around the else part. The
        // patchJumpsToTarget call at the bottom of this function will fix up
        // the offset with jumpsAroundElse value.
        if (!bce_->emitJump(JSOP_GOTO, &jumpsAroundElse_))
            return false;

        // Ensure the branch-if-false comes here, then emit the else.
        if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
            return false;

        // Annotate SRC_IF_ELSE or SRC_COND with the offset from branch to
        // jump, for IonMonkey's benefit.  We can't just "back up" from the pc
        // of the else clause, because we don't know whether an extended
        // jump was required to leap from the end of the then clause over
        // the else clause.
        if (!bce_->setSrcNoteOffset(noteIndex_, 0,
                                    jumpsAroundElse_.offset - jumpAroundThen_.offset))
        {
            return false;
        }

        // Restore stack depth of the then part.
        bce_->stackDepth = thenDepth_;
        state_ = Else;
        return true;
    }

    bool emitEnd() {
        MOZ_ASSERT(state_ == If || state_ == Else);

        calculateOrCheckPushed();

        if (state_ == If) {
            // No else part, fixup the branch-if-false to come here.
            if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
                return false;
        }

        // Patch all the jumps around else parts.
        if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
            return false;

        state_ = End;
        return true;
    }

    void calculateOrCheckPushed() {
#ifdef DEBUG
        if (!calculatedPushed_) {
            pushed_ = bce_->stackDepth - thenDepth_;
            calculatedPushed_ = true;
        } else {
            MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_);
        }
#endif
    }

#ifdef DEBUG
    int32_t pushed() const {
        return pushed_;
    }

    int32_t popped() const {
        return -pushed_;
    }
#endif
};

class ForOfLoopControl : public LoopControl
{
    // The stack depth of the iterator.
    int32_t iterDepth_;

    // for-of loops, when throwing from non-iterator code (i.e. from the body
    // or from evaluating the LHS of the loop condition), need to call
    // IteratorClose.  This is done by enclosing non-iterator code with
    // try-catch and call IteratorClose in `catch` block.
    // If IteratorClose itself throws, we must not re-call IteratorClose. Since
    // non-local jumps like break and return call IteratorClose, whenever a
    // non-local jump is emitted, we must tell catch block not to perform
    // IteratorClose.
    //
    //   for (x of y) {
    //     // Operations for iterator (IteratorNext etc) are outside of
    //     // try-block.
    //     try {
    //       ...
    //       if (...) {
    //         // Before non-local jump, clear iterator on the stack to tell
    //         // catch block not to perform IteratorClose.
    //         tmpIterator = iterator;
    //         iterator = undefined;
    //         IteratorClose(tmpIterator, { break });
    //         break;
    //       }
    //       ...
    //     } catch (e) {
    //       // Just throw again when iterator is cleared by non-local jump.
    //       if (iterator === undefined)
    //         throw e;
    //       IteratorClose(iterator, { throw, e });
    //     }
    //   }
    Maybe<TryEmitter> tryCatch_;

    // Used to track if any yields were emitted between calls to to
    // emitBeginCodeNeedingIteratorClose and emitEndCodeNeedingIteratorClose.
    uint32_t numYieldsAtBeginCodeNeedingIterClose_;

    bool allowSelfHosted_;

    IteratorKind iterKind_;

  public:
    ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted,
                     IteratorKind iterKind)
      : LoopControl(bce, StatementKind::ForOfLoop),
        iterDepth_(iterDepth),
        numYieldsAtBeginCodeNeedingIterClose_(UINT32_MAX),
        allowSelfHosted_(allowSelfHosted),
        iterKind_(iterKind)
    {
    }

    bool emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce) {
        tryCatch_.emplace(bce, TryEmitter::TryCatch, TryEmitter::DontUseRetVal,
                          TryEmitter::DontUseControl);

        if (!tryCatch_->emitTry())
            return false;

        MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX);
        numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldAndAwaitOffsetList.numYields;

        return true;
    }

    bool emitEndCodeNeedingIteratorClose(BytecodeEmitter* bce) {
        if (!tryCatch_->emitCatch())              // ITER ...
            return false;

        if (!bce->emit1(JSOP_EXCEPTION))          // ITER ... EXCEPTION
            return false;
        unsigned slotFromTop = bce->stackDepth - iterDepth_;
        if (!bce->emitDupAt(slotFromTop))         // ITER ... EXCEPTION ITER
            return false;

        // If ITER is undefined, it means the exception is thrown by
        // IteratorClose for non-local jump, and we should't perform
        // IteratorClose again here.
        if (!bce->emit1(JSOP_UNDEFINED))          // ITER ... EXCEPTION ITER UNDEF
            return false;
        if (!bce->emit1(JSOP_STRICTNE))           // ITER ... EXCEPTION NE
            return false;

        IfThenElseEmitter ifIteratorIsNotClosed(bce);
        if (!ifIteratorIsNotClosed.emitIf())      // ITER ... EXCEPTION
            return false;

        MOZ_ASSERT(slotFromTop == unsigned(bce->stackDepth - iterDepth_));
        if (!bce->emitDupAt(slotFromTop))         // ITER ... EXCEPTION ITER
            return false;
        if (!emitIteratorClose(bce, CompletionKind::Throw)) // ITER ... EXCEPTION
            return false;

        if (!ifIteratorIsNotClosed.emitEnd())     // ITER ... EXCEPTION
            return false;

        if (!bce->emit1(JSOP_THROW))              // ITER ...
            return false;

        // If any yields were emitted, then this for-of loop is inside a star
        // generator and must handle the case of Generator.return. Like in
        // yield*, it is handled with a finally block.
        uint32_t numYieldsEmitted = bce->yieldAndAwaitOffsetList.numYields;
        if (numYieldsEmitted > numYieldsAtBeginCodeNeedingIterClose_) {
            if (!tryCatch_->emitFinally())
                return false;

            IfThenElseEmitter ifGeneratorClosing(bce);
            if (!bce->emit1(JSOP_ISGENCLOSING))   // ITER ... FTYPE FVALUE CLOSING
                return false;
            if (!ifGeneratorClosing.emitIf())     // ITER ... FTYPE FVALUE
                return false;
            if (!bce->emitDupAt(slotFromTop + 1)) // ITER ... FTYPE FVALUE ITER
                return false;
            if (!emitIteratorClose(bce, CompletionKind::Normal)) // ITER ... FTYPE FVALUE
                return false;
            if (!ifGeneratorClosing.emitEnd())    // ITER ... FTYPE FVALUE
                return false;
        }

        if (!tryCatch_->emitEnd())
            return false;

        tryCatch_.reset();
        numYieldsAtBeginCodeNeedingIterClose_ = UINT32_MAX;

        return true;
    }

    bool emitIteratorClose(BytecodeEmitter* bce,
                           CompletionKind completionKind = CompletionKind::Normal) {
        ptrdiff_t start = bce->offset();
        if (!bce->emitIteratorClose(iterKind_, completionKind, allowSelfHosted_))
            return false;
        ptrdiff_t end = bce->offset();
        return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end);
    }

    bool emitPrepareForNonLocalJump(BytecodeEmitter* bce, bool isTarget) {
        // Pop unnecessary value from the stack.  Effectively this means
        // leaving try-catch block.  However, the performing IteratorClose can
        // reach the depth for try-catch, and effectively re-enter the
        // try-catch block.
        if (!bce->emit1(JSOP_POP))                        // ITER
            return false;

        // Clear ITER slot on the stack to tell catch block to avoid performing
        // IteratorClose again.
        if (!bce->emit1(JSOP_UNDEFINED))                  // ITER UNDEF
            return false;
        if (!bce->emit1(JSOP_SWAP))                       // UNDEF ITER
            return false;

        if (!emitIteratorClose(bce))                      // UNDEF
            return false;

        if (isTarget) {
            // At the level of the target block, there's bytecode after the
            // loop that will pop the iterator and the value, so push
            // an undefined to balance the stack.
            if (!bce->emit1(JSOP_UNDEFINED))              // UNDEF UNDEF
                return false;
        } else {
            if (!bce->emit1(JSOP_POP))                    //
                return false;
        }

        return true;
    }
};

BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
                                 const EitherParser<FullParseHandler>& parser, SharedContext* sc,
                                 HandleScript script, Handle<LazyScript*> lazyScript,
                                 uint32_t lineNum, EmitterMode emitterMode)
  : sc(sc),
    cx(sc->context),
    parent(parent),
    script(cx, script),
    lazyScript(cx, lazyScript),
    prologue(cx, lineNum),
    main(cx, lineNum),
    current(&main),
    parser(parser),
    atomIndices(cx->frontendCollectionPool()),
    firstLine(lineNum),
    maxFixedSlots(0),
    maxStackDepth(0),
    stackDepth(0),
    arrayCompDepth(0),
    emitLevel(0),
    bodyScopeIndex(UINT32_MAX),
    varEmitterScope(nullptr),
    innermostNestableControl(nullptr),
    innermostEmitterScope(nullptr),
    innermostTDZCheckCache(nullptr),
    constList(cx),
    scopeList(cx),
    tryNoteList(cx),
    scopeNoteList(cx),
    yieldAndAwaitOffsetList(cx),
    typesetCount(0),
    hasSingletons(false),
    hasTryFinally(false),
    emittingRunOnceLambda(false),
    emitterMode(emitterMode),
    functionBodyEndPosSet(false)
{
    MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript);
}

BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
                                 const EitherParser<FullParseHandler>& parser, SharedContext* sc,
                                 HandleScript script, Handle<LazyScript*> lazyScript,
                                 TokenPos bodyPosition, EmitterMode emitterMode)
    : BytecodeEmitter(parent, parser, sc, script, lazyScript,
                      parser.tokenStream().srcCoords.lineNum(bodyPosition.begin),
                      emitterMode)
{
    setFunctionBodyEndPos(bodyPosition);
}

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

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

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

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

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

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

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

bool
BytecodeEmitter::emitCheck(ptrdiff_t delta, ptrdiff_t* offset)
{
    *offset = code().length();

    // Start it off moderately large to avoid repeated resizings early on.
    // ~98% of cases fit within 1024 bytes.
    if (code().capacity() == 0 && !code().reserve(1024))
        return false;

    if (!code().growBy(delta)) {
        ReportOutOfMemory(cx);
        return false;
    }
    return true;
}

void
BytecodeEmitter::updateDepth(ptrdiff_t target)
{
    jsbytecode* pc = code(target);

    int nuses = StackUses(nullptr, pc);
    int ndefs = StackDefs(nullptr, pc);

    stackDepth -= nuses;
    MOZ_ASSERT(stackDepth >= 0);
    stackDepth += ndefs;

    if ((uint32_t)stackDepth > maxStackDepth)
        maxStackDepth = stackDepth;
}

#ifdef DEBUG
bool
BytecodeEmitter::checkStrictOrSloppy(JSOp op)
{
    if (IsCheckStrictOp(op) && !sc->strict())
        return false;
    if (IsCheckSloppyOp(op) && sc->strict())
        return false;
    return true;
}
#endif

bool
BytecodeEmitter::emit1(JSOp op)
{
    MOZ_ASSERT(checkStrictOrSloppy(op));

    ptrdiff_t offset;
    if (!emitCheck(1, &offset))
        return false;

    jsbytecode* code = this->code(offset);
    code[0] = jsbytecode(op);
    updateDepth(offset);
    return true;
}

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

    ptrdiff_t offset;
    if (!emitCheck(2, &offset))
        return false;

    jsbytecode* code = this->code(offset);
    code[0] = jsbytecode(op);
    code[1] = jsbytecode(op1);
    updateDepth(offset);
    return true;
}

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

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

    ptrdiff_t offset;
    if (!emitCheck(3, &offset))
        return false;

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

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

    ptrdiff_t off;
    if (!emitCheck(length, &off))
        return false;

    jsbytecode* code = this->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)
        updateDepth(off);

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

bool
BytecodeEmitter::emitJumpTarget(JumpTarget* target)
{
    ptrdiff_t off = offset();

    // Alias consecutive jump targets.
    if (off == current->lastTarget.offset + ptrdiff_t(JSOP_JUMPTARGET_LENGTH)) {
        target->offset = current->lastTarget.offset;
        return true;
    }

    target->offset = off;
    current->lastTarget.offset = off;
    if (!emit1(JSOP_JUMPTARGET))
        return false;
    return true;
}

void
JumpList::push(jsbytecode* code, ptrdiff_t jumpOffset)
{
    SET_JUMP_OFFSET(&code[jumpOffset], offset - jumpOffset);
    offset = jumpOffset;
}

void
JumpList::patchAll(jsbytecode* code, JumpTarget target)
{
    ptrdiff_t delta;
    for (ptrdiff_t jumpOffset = offset; jumpOffset != -1; jumpOffset += delta) {
        jsbytecode* pc = &code[jumpOffset];
        MOZ_ASSERT(IsJumpOpcode(JSOp(*pc)) || JSOp(*pc) == JSOP_LABEL);
        delta = GET_JUMP_OFFSET(pc);
        MOZ_ASSERT(delta < 0);
        ptrdiff_t span = target.offset - jumpOffset;
        SET_JUMP_OFFSET(pc, span);
    }
}

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

    jsbytecode* code = this->code(offset);
    code[0] = jsbytecode(op);
    MOZ_ASSERT(-1 <= jump->offset && jump->offset < offset);
    jump->push(this->code(0), offset);
    updateDepth(offset);
    return true;
}

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

bool
BytecodeEmitter::emitBackwardJump(JSOp op, JumpTarget target, JumpList* jump, JumpTarget* fallthrough)
{
    if (!emitJumpNoFallthrough(op, jump))
        return false;
    patchJumpsToTarget(*jump, target);

    // Unconditionally create a fallthrough for closing iterators, and as a
    // target for break statements.
    if (!emitJumpTarget(fallthrough))
        return false;
    return true;
}

void
BytecodeEmitter::patchJumpsToTarget(JumpList jump, JumpTarget target)
{
    MOZ_ASSERT(-1 <= jump.offset && jump.offset <= offset());
    MOZ_ASSERT(0 <= target.offset && target.offset <= offset());
    MOZ_ASSERT_IF(jump.offset != -1 && target.offset + 4 <= offset(),
                  BytecodeIsJumpTarget(JSOp(*code(target.offset))));
    jump.patchAll(code(0), target);
}

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

bool
BytecodeEmitter::emitCall(JSOp op, uint16_t argc, ParseNode* pn)
{
    if (pn && !updateSourceCoordNotes(pn->pn_pos.begin))
        return false;
    return emit3(op, ARGC_HI(argc), ARGC_LO(argc));
}

bool
BytecodeEmitter::emitDupAt(unsigned slotFromTop)
{
    MOZ_ASSERT(slotFromTop < unsigned(stackDepth));

    if (slotFromTop == 0)
        return emit1(JSOP_DUP);

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

    ptrdiff_t off;
    if (!emitN(JSOP_DUPAT, 3, &off))
        return false;

    jsbytecode* pc = 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_POPs (2 bytes) are shorter than JSOP_POPN (3 bytes).
    if (n == 2)
        return emit1(JSOP_POP) && emit1(JSOP_POP);

    return emitUint16Operand(JSOP_POPN, n);
}

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

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

static inline unsigned
LengthOfSetLine(unsigned line)
{
    return 1 /* SN_SETLINE */ + (line > SN_4BYTE_OFFSET_MASK ? 4 : 1);
}

/* Updates line number notes, not column notes. */
bool
BytecodeEmitter::updateLineNumberNotes(uint32_t offset)
{
    TokenStreamAnyChars* ts = &parser.tokenStream();
    bool onThisLine;
    if (!ts->srcCoords.isOnThisLine(offset, currentLine(), &onThisLine)) {
        ts->reportErrorNoOffset(JSMSG_OUT_OF_MEMORY);
        return false;
    }

    if (!onThisLine) {
        unsigned line = ts->srcCoords.lineNum(offset);
        unsigned delta = line - currentLine();

        /*
         * Encode any change in the current source line number by using
         * either several SRC_NEWLINE notes or just one SRC_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
         * SRC_SETLINE.
         */
        current->currentLine = line;
        current->lastColumn  = 0;
        if (delta >= LengthOfSetLine(line)) {
            if (!newSrcNote2(SRC_SETLINE, ptrdiff_t(line)))
                return false;
        } else {
            do {
                if (!newSrcNote(SRC_NEWLINE))
                    return false;
            } while (--delta != 0);
        }
    }
    return true;
}

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

    uint32_t columnIndex = parser.tokenStream().srcCoords.columnIndex(offset);
    ptrdiff_t colspan = ptrdiff_t(columnIndex) - ptrdiff_t(current->lastColumn);
    if (colspan != 0) {
        // If the column span is so large that we can't store it, then just
        // discard this information. This can happen with minimized or otherwise
        // machine-generated code. Even gigantic column numbers are still
        // valuable if you have a source map to relate them to something real;
        // but it's better to fail soft here.
        if (!SN_REPRESENTABLE_COLSPAN(colspan))
            return true;
        if (!newSrcNote2(SRC_COLSPAN, SN_COLSPAN_TO_OFFSET(colspan)))
            return false;
        current->lastColumn = columnIndex;
    }
    return true;
}

bool
BytecodeEmitter::emitLoopHead(ParseNode* nextpn, JumpTarget* top)
{
    if (nextpn) {
        /*
         * 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->isKind(PNK_LEXICALSCOPE))
            nextpn = nextpn->scopeBody();
        MOZ_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
        if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
            nextpn = nextpn->pn_head;
        if (!updateSourceCoordNotes(nextpn->pn_pos.begin))
            return false;
    }

    *top = { offset() };
    return emit1(JSOP_LOOPHEAD);
}

bool
BytecodeEmitter::emitLoopEntry(ParseNode* nextpn, JumpList entryJump)
{
    if (nextpn) {
        /* Update the line number, as for LOOPHEAD. */
        if (nextpn->isKind(PNK_LEXICALSCOPE))
            nextpn = nextpn->scopeBody();
        MOZ_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
        if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
            nextpn = nextpn->pn_head;
        if (!updateSourceCoordNotes(nextpn->pn_pos.begin))
            return false;
    }

    JumpTarget entry{ offset() };
    patchJumpsToTarget(entryJump, entry);

    LoopControl& loopInfo = innermostNestableControl->as<LoopControl>();
    MOZ_ASSERT(loopInfo.loopDepth() > 0);

    uint8_t loopDepthAndFlags = PackLoopEntryDepthHintAndFlags(loopInfo.loopDepth(),
                                                               loopInfo.canIonOsr());
    return emit2(JSOP_LOOPENTRY, loopDepthAndFlags);
}

void
BytecodeEmitter::checkTypeSet(JSOp op)
{
    if (CodeSpec[op].format & JOF_TYPESET) {
        if (typesetCount < UINT16_MAX)
            typesetCount++;
    }
}

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

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

namespace {

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

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

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

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

    NonLocalExitControl(const NonLocalExitControl&) = delete;

    MOZ_MUST_USE bool leaveScope(BytecodeEmitter::EmitterScope* scope);

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

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

    MOZ_MUST_USE bool prepareForNonLocalJump(BytecodeEmitter::NestableControl* target);

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

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

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

    return true;
}

/*
 * Emit additional bytecode(s) for non-local jumps.
 */
bool
NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* target)
{
    using NestableControl = BytecodeEmitter::NestableControl;
    using EmitterScope = BytecodeEmitter::EmitterScope;

    EmitterScope* es = bce_->innermostEmitterScope;
    int npops = 0;

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

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

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

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

          case StatementKind::ForOfLoop:
            if (emitIteratorClose) {
                if (!flushPops(bce_))
                    return false;

                ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>();
                if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ false)) // ...
                    return false;
            } else {
                npops += 2;
            }
            break;

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

            // The iterator and the current value are on the stack.
            if (!bce_->emit1(JSOP_POP))                   // ... ITER
                return false;
            if (!bce_->emit1(JSOP_ENDITER))               // ...
                return false;
            break;

          default:
            break;
        }
    }

    if (!flushPops(bce_))
        return false;

    if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) {
        ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>();
        if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF
            return false;
    }

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

    return true;
}

}  // anonymous namespace

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

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

    if (noteType != SRC_NULL) {
        if (!newSrcNote(noteType))
            return false;
    }

    return emitJump(JSOP_GOTO, jumplist);
}

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

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

    const size_t len = 1 + UINT32_INDEX_LEN;
    MOZ_ASSERT(len == size_t(CodeSpec[op].length));

    ptrdiff_t offset;
    if (!emitCheck(len, &offset))
        return false;

    jsbytecode* code = this->code(offset);
    code[0] = jsbytecode(op);
    SET_UINT32_INDEX(code, index);
    checkTypeSet(op);
    updateDepth(offset);
    return true;
}

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

    const size_t len = CodeSpec[op].length;
    MOZ_ASSERT(len >= 1 + UINT32_INDEX_LEN);

    ptrdiff_t offset;
    if (!emitCheck(len, &offset))
        return false;

    jsbytecode* code = this->code(offset);
    code[0] = jsbytecode(op);
    SET_UINT32_INDEX(code, index);
    checkTypeSet(op);
    updateDepth(offset);
    return true;
}

bool
BytecodeEmitter::emitAtomOp(JSAtom* atom, JSOp op)
{
    MOZ_ASSERT(atom);
    MOZ_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);

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

    if (op == JSOP_GETPROP && atom == cx->names().length) {
        /* Specialize length accesses for the interpreter. */
        op = JSOP_LENGTH;
    }

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

    return emitIndexOp(op, index);
}

bool
BytecodeEmitter::emitAtomOp(ParseNode* pn, JSOp op)
{
    MOZ_ASSERT(pn->pn_atom != nullptr);
    return emitAtomOp(pn->pn_atom, op);
}

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

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

bool
BytecodeEmitter::emitObjectOp(ObjectBox* objbox, JSOp op)
{
    return emitInternedObjectOp(objectList.add(objbox), op);
}

bool
BytecodeEmitter::emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op)
{
    uint32_t index = objectList.add(objbox1);
    objectList.add(objbox2);
    return emitInternedObjectOp(index, op);
}

bool
BytecodeEmitter::emitRegExp(uint32_t index)
{
    return emitIndex32(JSOP_REGEXP, index);
}

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

    ptrdiff_t off;
    if (!emitN(op, LOCALNO_LEN, &off))
        return false;

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

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

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

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

    unsigned n = ENVCOORD_HOPS_LEN + ENVCOORD_SLOT_LEN;
    MOZ_ASSERT(int(n) + 1 /* op */ == CodeSpec[op].length);

    ptrdiff_t off;
    if (!emitN(op, n, &off))
        return false;

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

static JSOp
GetIncDecInfo(ParseNodeKind kind, bool* post)
{
    MOZ_ASSERT(kind == PNK_POSTINCREMENT || kind == PNK_PREINCREMENT ||
               kind == PNK_POSTDECREMENT || kind == PNK_PREDECREMENT);
    *post = kind == PNK_POSTINCREMENT || kind == PNK_POSTDECREMENT;
    return (kind == PNK_POSTINCREMENT || kind == PNK_PREINCREMENT) ? JSOP_ADD : JSOP_SUB;
}

JSOp
BytecodeEmitter::strictifySetNameOp(JSOp op)
{
    switch (op) {
      case JSOP_SETNAME:
        if (sc->strict())
            op = JSOP_STRICTSETNAME;
        break;
      case JSOP_SETGNAME:
        if (sc->strict())
            op = JSOP_STRICTSETGNAME;
        break;
        default:;
    }
    return op;
}

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

 restart:

    switch (pn->getKind()) {
      // Trivial cases with no side effects.
      case PNK_NOP:
      case PNK_STRING:
      case PNK_TEMPLATE_STRING:
      case PNK_REGEXP:
      case PNK_TRUE:
      case PNK_FALSE:
      case PNK_NULL:
      case PNK_RAW_UNDEFINED:
      case PNK_ELISION:
      case PNK_GENERATOR:
      case PNK_NUMBER:
      case PNK_OBJECT_PROPERTY_NAME:
        MOZ_ASSERT(pn->isArity(PN_NULLARY));
        *answer = false;
        return true;

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

      // Trivial binary nodes with more token pos holders.
      case PNK_NEWTARGET:
        MOZ_ASSERT(pn->isArity(PN_BINARY));
        MOZ_ASSERT(pn->pn_left->isKind(PNK_POSHOLDER));
        MOZ_ASSERT(pn->pn_right->isKind(PNK_POSHOLDER));
        *answer = false;
        return true;

      case PNK_BREAK:
      case PNK_CONTINUE:
      case PNK_DEBUGGER:
        MOZ_ASSERT(pn->isArity(PN_NULLARY));
        *answer = true;
        return true;

      // Watch out for getters!
      case PNK_DOT:
        MOZ_ASSERT(pn->isArity(PN_NAME));
        *answer = true;
        return true;

      // Unary cases with side effects only if the child has them.
      case PNK_TYPEOFEXPR:
      case PNK_VOID:
      case PNK_NOT:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        return checkSideEffects(pn->pn_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 PNK_COMPUTED_NAME:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        *answer = true;
        return true;

      // Looking up or evaluating the associated name could throw.
      case PNK_TYPEOFNAME:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        *answer = true;
        return true;

      // These unary cases have side effects on the enclosing object/array,
      // 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, for an object literal having a mutated prototype
      // and an array comprehension containing no other effectful operations
      // only produce a value, without affecting anything else.
      case PNK_MUTATEPROTO:
      case PNK_ARRAYPUSH:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        return checkSideEffects(pn->pn_kid, answer);

      // Unary cases with obvious side effects.
      case PNK_PREINCREMENT:
      case PNK_POSTINCREMENT:
      case PNK_PREDECREMENT:
      case PNK_POSTDECREMENT:
      case PNK_THROW:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        *answer = true;
        return true;

      // These might invoke valueOf/toString, even with a subexpression without
      // side effects!  Consider |+{ valueOf: null, toString: null }|.
      case PNK_BITNOT:
      case PNK_POS:
      case PNK_NEG:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        *answer = true;
        return true;

      // This invokes the (user-controllable) iterator protocol.
      case PNK_SPREAD:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        *answer = true;
        return true;

      case PNK_INITIALYIELD:
      case PNK_YIELD_STAR:
      case PNK_YIELD:
      case PNK_AWAIT:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        *answer = true;
        return true;

      // Deletion generally has side effects, even if isolated cases have none.
      case PNK_DELETENAME:
      case PNK_DELETEPROP:
      case PNK_DELETEELEM:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        *answer = true;
        return true;

      // Deletion of a non-Reference expression has side effects only through
      // evaluating the expression.
      case PNK_DELETEEXPR: {
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        ParseNode* expr = pn->pn_kid;
        return checkSideEffects(expr, answer);
      }

      case PNK_SEMI:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        if (ParseNode* expr = pn->pn_kid)
            return checkSideEffects(expr, answer);
        *answer = false;
        return true;

      // Binary cases with obvious side effects.
      case PNK_ASSIGN:
      case PNK_ADDASSIGN:
      case PNK_SUBASSIGN:
      case PNK_BITORASSIGN:
      case PNK_BITXORASSIGN:
      case PNK_BITANDASSIGN:
      case PNK_LSHASSIGN:
      case PNK_RSHASSIGN:
      case PNK_URSHASSIGN:
      case PNK_MULASSIGN:
      case PNK_DIVASSIGN:
      case PNK_MODASSIGN:
      case PNK_POWASSIGN:
      case PNK_SETTHIS:
        MOZ_ASSERT(pn->isArity(PN_BINARY));
        *answer = true;
        return true;

      case PNK_STATEMENTLIST:
      case PNK_CATCHLIST:
      // Strict equality operations and logical operators are well-behaved and
      // perform no conversions.
      case PNK_OR:
      case PNK_AND:
      case PNK_STRICTEQ:
      case PNK_STRICTNE:
      // Any subexpression of a comma expression could be effectful.
      case PNK_COMMA:
        MOZ_ASSERT(pn->pn_count > 0);
        MOZ_FALLTHROUGH;
      // Subcomponents of a literal may be effectful.
      case PNK_ARRAY:
      case PNK_OBJECT:
        MOZ_ASSERT(pn->isArity(PN_LIST));
        for (ParseNode* item = pn->pn_head; item; item = item->pn_next) {
            if (!checkSideEffects(item, answer))
                return false;
            if (*answer)
                return true;
        }
        return true;

      // Most other binary operations (parsed as lists in SpiderMonkey) may
      // perform conversions triggering side effects.  Math operations perform
      // ToNumber and may fail invoking invalid user-defined toString/valueOf:
      // |5 < { toString: null }|.  |instanceof| throws if provided a
      // non-object constructor: |null instanceof null|.  |in| throws if given
      // a non-object RHS: |5 in null|.
      case PNK_BITOR:
      case PNK_BITXOR:
      case PNK_BITAND:
      case PNK_EQ:
      case PNK_NE:
      case PNK_LT:
      case PNK_LE:
      case PNK_GT:
      case PNK_GE:
      case PNK_INSTANCEOF:
      case PNK_IN:
      case PNK_LSH:
      case PNK_RSH:
      case PNK_URSH:
      case PNK_ADD:
      case PNK_SUB:
      case PNK_STAR:
      case PNK_DIV:
      case PNK_MOD:
      case PNK_POW:
        MOZ_ASSERT(pn->isArity(PN_LIST));
        MOZ_ASSERT(pn->pn_count >= 2);
        *answer = true;
        return true;

      case PNK_COLON:
      case PNK_CASE:
        MOZ_ASSERT(pn->isArity(PN_BINARY));
        if (!checkSideEffects(pn->pn_left, answer))
            return false;
        if (*answer)
            return true;
        return checkSideEffects(pn->pn_right, answer);

      // More getters.
      case PNK_ELEM:
        MOZ_ASSERT(pn->isArity(PN_BINARY));
        *answer = true;
        return true;

      // These affect visible names in this code, or in other code.
      case PNK_IMPORT:
      case PNK_EXPORT_FROM:
      case PNK_EXPORT_DEFAULT:
        MOZ_ASSERT(pn->isArity(PN_BINARY));
        *answer = true;
        return true;

      // Likewise.
      case PNK_EXPORT:
        MOZ_ASSERT(pn->isArity(PN_UNARY));
        *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 PNK_DOWHILE:
      case PNK_WHILE:
      case PNK_FOR:
      case PNK_COMPREHENSIONFOR:
        MOZ_ASSERT(pn->isArity(PN_BINARY));
        *answer = true;
        return true;

      // Declarations affect the name set of the relevant scope.
      case PNK_VAR:
      case PNK_CONST:
      case PNK_LET:
        MOZ_ASSERT(pn->isArity(PN_LIST));
        *answer = true;
        return true;

      case PNK_IF:
      case PNK_CONDITIONAL:
        MOZ_ASSERT(pn->isArity(PN_TERNARY));
        if (!checkSideEffects(pn->pn_kid1, answer))
            return false;
        if (*answer)
            return true;
        if (!checkSideEffects(pn->pn_kid2, answer))
            return false;
        if (*answer)
            return true;
        if ((pn = pn->pn_kid3))
            goto restart;
        return true;

      // Function calls can invoke non-local code.
      case PNK_NEW:
      case PNK_CALL:
      case PNK_TAGGED_TEMPLATE:
      case PNK_SUPERCALL:
        MOZ_ASSERT(pn->isArity(PN_LIST));
        *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 PNK_CLASS:
        MOZ_ASSERT(pn->isArity(PN_TERNARY));
        *answer = true;
        return true;

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

      case PNK_RETURN:
        MOZ_ASSERT(pn->isArity(PN_BINARY));
        *answer = true;
        return true;

      case PNK_NAME:
        MOZ_ASSERT(pn->isArity(PN_NAME));
        *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 PNK_SHORTHAND:
        MOZ_ASSERT(pn->isArity(PN_BINARY));
        *answer = true;
        return true;

      case PNK_FUNCTION:
        MOZ_ASSERT(pn->isArity(PN_CODE));
        /*
         * 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 PNK_MODULE:
        *answer = false;
        return true;

      // Generator expressions have no side effects on their own.
      case PNK_GENEXP:
        MOZ_ASSERT(pn->isArity(PN_LIST));
        *answer = false;
        return true;

      case PNK_TRY:
        MOZ_ASSERT(pn->isArity(PN_TERNARY));
        if (!checkSideEffects(pn->pn_kid1, answer))
            return false;
        if (*answer)
            return true;
        if (ParseNode* catchList = pn->pn_kid2) {
            MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
            if (!checkSideEffects(catchList, answer))
                return false;
            if (*answer)
                return true;
        }
        if (ParseNode* finallyBlock = pn->pn_kid3) {
            if (!checkSideEffects(finallyBlock, answer))
                return false;
        }
        return true;

      case PNK_CATCH:
        MOZ_ASSERT(pn->isArity(PN_TERNARY));
        if (!checkSideEffects(pn->pn_kid1, answer))
            return false;
        if (*answer)
            return true;
        if (ParseNode* cond = pn->pn_kid2) {
            if (!checkSideEffects(cond, answer))
                return false;
            if (*answer)
                return true;
        }
        return checkSideEffects(pn->pn_kid3, answer);

      case PNK_SWITCH:
        MOZ_ASSERT(pn->isArity(PN_BINARY));
        if (!checkSideEffects(pn->pn_left, answer))
            return false;
        return *answer || checkSideEffects(pn->pn_right, answer);

      case PNK_LABEL:
        MOZ_ASSERT(pn->isArity(PN_NAME));
        return checkSideEffects(pn->expr(), answer);

      case PNK_LEXICALSCOPE:
        MOZ_ASSERT(pn->isArity(PN_SCOPE));
        return checkSideEffects(pn->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 PNK_TEMPLATE_STRING_LIST:
        MOZ_ASSERT(pn->isArity(PN_LIST));
        MOZ_ASSERT(pn->pn_count > 0);
        MOZ_ASSERT((pn->pn_count % 2) == 1,
                   "template strings must alternate template and substitution "
                   "parts");
        *answer = pn->pn_count > 1;
        return true;

      case PNK_ARRAYCOMP:
        MOZ_ASSERT(pn->isArity(PN_LIST));
        MOZ_ASSERT(pn->pn_count == 1);
        return checkSideEffects(pn->pn_head, answer);

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

      case PNK_FORIN:           // by PNK_FOR/PNK_COMPREHENSIONFOR
      case PNK_FOROF:           // by PNK_FOR/PNK_COMPREHENSIONFOR
      case PNK_FORHEAD:         // by PNK_FOR/PNK_COMPREHENSIONFOR
      case PNK_CLASSMETHOD:     // by PNK_CLASS
      case PNK_CLASSNAMES:      // by PNK_CLASS
      case PNK_CLASSMETHODLIST: // by PNK_CLASS
      case PNK_IMPORT_SPEC_LIST: // by PNK_IMPORT
      case PNK_IMPORT_SPEC:      // by PNK_IMPORT
      case PNK_EXPORT_BATCH_SPEC:// by PNK_EXPORT
      case PNK_EXPORT_SPEC_LIST: // by PNK_EXPORT
      case PNK_EXPORT_SPEC:      // by PNK_EXPORT
      case PNK_CALLSITEOBJ:      // by PNK_TAGGED_TEMPLATE
      case PNK_POSHOLDER:        // by PNK_NEWTARGET
      case PNK_SUPERBASE:        // by PNK_ELEM and others
        MOZ_CRASH("handled by parent nodes");

      case PNK_LIMIT: // invalid sentinel value
        MOZ_CRASH("invalid node kind");
    }

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

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

bool
BytecodeEmitter::checkSingletonContext()
{
    if (!script->treatAsRunOnce() || sc->isFunctionBox() || isInLoop())
        return false;
    hasSingletons = true;
    return true;
}

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

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

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

    return false;
}

bool
BytecodeEmitter::maybeSetDisplayURL()
{
    if (tokenStream().hasDisplayURL()) {
        if (!parser.ss()->setDisplayURL(cx, tokenStream().displayURL()))
            return false;
    }
    return true;
}

bool
BytecodeEmitter::maybeSetSourceMap()
{
    if (tokenStream().hasSourceMapURL()) {
        MOZ_ASSERT(!parser.ss()->hasSourceMapURL());
        if (!parser.ss()->setSourceMapURL(cx, tokenStream().sourceMapURL()))
            return false;
    }

    /*
     * Source map URLs passed as a compile option (usually via a HTTP source map
     * header) override any source map urls passed as comment pragmas.
     */
    if (parser.options().sourceMapURL()) {
        // Warn about the replacement, but use the new one.
        if (parser.ss()->hasSourceMapURL()) {
            if (!parser.reportNoOffset(ParseWarning, false, JSMSG_ALREADY_HAS_PRAGMA,
                                       parser.ss()->filename(), "//# sourceMappingURL"))
            {
                return false;
            }
        }

        if (!parser.ss()->setSourceMapURL(cx, parser.options().sourceMapURL()))
            return false;
    }

    return true;
}

void
BytecodeEmitter::tellDebuggerAboutCompiledScript(JSContext* cx)
{
    // Note: when parsing off thread the resulting scripts need to be handed to
    // the debugger after rejoining to the active thread.
    if (cx->helperThread())
        return;

    // Lazy scripts are never top level (despite always being invoked with a
    // nullptr parent), and so the hook should never be fired.
    if (emitterMode != LazyFunction && !parent)
        Debugger::onNewScript(cx, script);
}

inline TokenStreamAnyChars&
BytecodeEmitter::tokenStream()
{
    return parser.tokenStream();
}

void
BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...)
{
    TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;

    va_list args;
    va_start(args, errorNumber);

    ErrorMetadata metadata;
    if (parser.computeErrorMetadata(&metadata, pos.begin))
        ReportCompileError(cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);

    va_end(args);
}

bool
BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...)
{
    TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;

    va_list args;
    va_start(args, errorNumber);

    bool result = parser.reportExtraWarningErrorNumberVA(nullptr, pos.begin, errorNumber, args);

    va_end(args);
    return result;
}

bool
BytecodeEmitter::reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...)
{
    TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;

    va_list args;
    va_start(args, errorNumber);
    bool result = parser.reportStrictModeErrorNumberVA(nullptr, pos.begin, sc->strict(),
                                                       errorNumber, args);
    va_end(args);
    return result;
}

bool
BytecodeEmitter::emitNewInit(JSProtoKey key)
{
    const size_t len = 1 + UINT32_INDEX_LEN;
    ptrdiff_t offset;
    if (!emitCheck(len, &offset))
        return false;

    jsbytecode* code = this->code(offset);
    code[0] = JSOP_NEWINIT;
    code[1] = jsbytecode(key);
    code[2] = 0;
    code[3] = 0;
    code[4] = 0;
    checkTypeSet(JSOP_NEWINIT);
    updateDepth(offset);
    return true;
}

bool
BytecodeEmitter::iteratorResultShape(unsigned* shape)
{
    // No need to do any guessing for the object kind, since we know exactly how
    // many properties we plan to have.
    gc::AllocKind kind = gc::GetGCObjectKind(2);
    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
    if (!obj)
        return false;

    Rooted<jsid> value_id(cx, AtomToId(cx->names().value));
    Rooted<jsid> done_id(cx, AtomToId(cx->names().done));
    if (!NativeDefineProperty(cx, obj, value_id, UndefinedHandleValue, nullptr, nullptr,
                              JSPROP_ENUMERATE))
    {
        return false;
    }
    if (!NativeDefineProperty(cx, obj, done_id, UndefinedHandleValue, nullptr, nullptr,
                              JSPROP_ENUMERATE))
    {
        return false;
    }

    ObjectBox* objbox = parser.newObjectBox(obj);
    if (!objbox)
        return false;

    *shape = objectList.add(objbox);

    return true;
}

bool
BytecodeEmitter::emitPrepareIteratorResult()
{
    unsigned shape;
    if (!iteratorResultShape(&shape))
        return false;
    return emitIndex32(JSOP_NEWOBJECT, shape);
}

bool
BytecodeEmitter::emitFinishIteratorResult(bool done)
{
    uint32_t value_id;
    if (!makeAtomIndex(cx->names().value, &value_id))
        return false;
    uint32_t done_id;
    if (!makeAtomIndex(cx->names().done, &done_id))
        return false;

    if (!emitIndex32(JSOP_INITPROP, value_id))
        return false;
    if (!emit1(done ? JSOP_TRUE : JSOP_FALSE))
        return false;
    if (!emitIndex32(JSOP_INITPROP, done_id))
        return false;
    return true;
}

bool
BytecodeEmitter::emitToIteratorResult(bool done)
{
    if (!emitPrepareIteratorResult())    // VALUE OBJ
        return false;
    if (!emit1(JSOP_SWAP))               // OBJ VALUE
        return false;
    if (!emitFinishIteratorResult(done)) // RESULT
        return false;
    return true;
}

bool
BytecodeEmitter::emitGetNameAtLocation(JSAtom* name, const NameLocation& loc, bool callContext)
{
    switch (loc.kind()) {
      case NameLocation::Kind::Dynamic:
        if (!emitAtomOp(name, JSOP_GETNAME))
            return false;
        break;

      case NameLocation::Kind::Global:
        if (!emitAtomOp(name, JSOP_GETGNAME))
            return false;
        break;

      case NameLocation::Kind::Intrinsic:
        if (!emitAtomOp(name, JSOP_GETINTRINSIC))
            return false;
        break;

      case NameLocation::Kind::NamedLambdaCallee:
        if (!emit1(JSOP_CALLEE))
            return false;
        break;

      case NameLocation::Kind::Import:
        if (!emitAtomOp(name, JSOP_GETIMPORT))
            return false;
        break;

      case NameLocation::Kind::ArgumentSlot:
        if (!emitArgOp(JSOP_GETARG, loc.argumentSlot()))
            return false;
        break;

      case NameLocation::Kind::FrameSlot:
        if (loc.isLexical()) {
            if (!emitTDZCheckIfNeeded(name, loc))
                return false;
        }
        if (!emitLocalOp(JSOP_GETLOCAL, loc.frameSlot()))
            return false;
        break;

      case NameLocation::Kind::EnvironmentCoordinate:
        if (loc.isLexical()) {
            if (!emitTDZCheckIfNeeded(name, loc))
                return false;
        }
        if (!emitEnvCoordOp(JSOP_GETALIASEDVAR, loc.environmentCoordinate()))
            return false;
        break;

      case NameLocation::Kind::DynamicAnnexBVar:
        MOZ_CRASH("Synthesized vars for Annex B.3.3 should only be used in initialization");
    }

    // Need to provide |this| value for call.
    if (callContext) {
        switch (loc.kind()) {
          case NameLocation::Kind::Dynamic: {
            JSOp thisOp = needsImplicitThis() ? JSOP_IMPLICITTHIS : JSOP_GIMPLICITTHIS;
            if (!emitAtomOp(name, thisOp))
                return false;
            break;
          }

          case NameLocation::Kind::Global:
            if (!emitAtomOp(name, JSOP_GIMPLICITTHIS))
                return false;
            break;

          case NameLocation::Kind::Intrinsic:
          case NameLocation::Kind::NamedLambdaCallee:
          case NameLocation::Kind::Import:
          case NameLocation::Kind::ArgumentSlot:
          case NameLocation::Kind::FrameSlot:
          case NameLocation::Kind::EnvironmentCoordinate:
            if (!emit1(JSOP_UNDEFINED))
                return false;
            break;

          case NameLocation::Kind::DynamicAnnexBVar:
            MOZ_CRASH("Synthesized vars for Annex B.3.3 should only be used in initialization");
        }
    }

    return true;
}

bool
BytecodeEmitter::emitGetName(ParseNode* pn, bool callContext)
{
    return emitGetName(pn->name(), callContext);
}

template <typename RHSEmitter>
bool
BytecodeEmitter::emitSetOrInitializeNameAtLocation(HandleAtom name, const NameLocation& loc,
                                                   RHSEmitter emitRhs, bool initialize)
{
    bool emittedBindOp = false;

    switch (loc.kind()) {
      case NameLocation::Kind::Dynamic:
      case NameLocation::Kind::Import:
      case NameLocation::Kind::DynamicAnnexBVar: {
        uint32_t atomIndex;
        if (!makeAtomIndex(name, &atomIndex))
            return false;
        if (loc.kind() == NameLocation::Kind::DynamicAnnexBVar) {
            // Annex B vars always go on the nearest variable environment,
            // even if lexical environments in between contain same-named
            // bindings.
            if (!emit1(JSOP_BINDVAR))
                return false;
        } else {
            if (!emitIndexOp(JSOP_BINDNAME, atomIndex))
                return false;
        }
        emittedBindOp = true;
        if (!emitRhs(this, loc, emittedBindOp))
            return false;
        if (!emitIndexOp(strictifySetNameOp(JSOP_SETNAME), atomIndex))
            return false;
        break;
      }

      case NameLocation::Kind::Global: {
        JSOp op;
        uint32_t atomIndex;
        if (!makeAtomIndex(name, &atomIndex))
            return false;
        if (loc.isLexical() && initialize) {
            // INITGLEXICAL always gets the global lexical scope. It doesn't
            // need a BINDGNAME.
            MOZ_ASSERT(innermostScope()->is<GlobalScope>());
            op = JSOP_INITGLEXICAL;
        } else {
            if (!emitIndexOp(JSOP_BINDGNAME, atomIndex))
                return false;
            emittedBindOp = true;
            op = strictifySetNameOp(JSOP_SETGNAME);
        }
        if (!emitRhs(this, loc, emittedBindOp))
            return false;
        if (!emitIndexOp(op, atomIndex))
            return false;
        break;
      }

      case NameLocation::Kind::Intrinsic:
        if (!emitRhs(this, loc, emittedBindOp))
            return false;
        if (!emitAtomOp(name, JSOP_SETINTRINSIC))
            return false;
        break;

      case NameLocation::Kind::NamedLambdaCallee:
        if (!emitRhs(this, loc, emittedBindOp))
            return false;
        // Assigning to the named lambda is a no-op in sloppy mode but
        // throws in strict mode.
        if (sc->strict() && !emit1(JSOP_THROWSETCALLEE))
            return false;
        break;

      case NameLocation::Kind::ArgumentSlot: {
        // If we assign to a positional formal parameter and the arguments
        // object is unmapped (strict mode or function with
        // default/rest/destructing args), parameters do not alias
        // arguments[i], and to make the arguments object reflect initial
        // parameter values prior to any mutation we create it eagerly
        // whenever parameters are (or might, in the case of calls to eval)
        // assigned.
        FunctionBox* funbox = sc->asFunctionBox();
        if (funbox->argumentsHasLocalBinding() && !funbox->hasMappedArgsObj())
            funbox->setDefinitelyNeedsArgsObj();

        if (!emitRhs(this, loc, emittedBindOp))
            return false;
        if (!emitArgOp(JSOP_SETARG, loc.argumentSlot()))
            return false;
        break;
      }

      case NameLocation::Kind::FrameSlot: {
        JSOp op = JSOP_SETLOCAL;
        if (!emitRhs(this, loc, emittedBindOp))
            return false;
        if (loc.isLexical()) {
            if (initialize) {
                op = JSOP_INITLEXICAL;
            } else {
                if (loc.isConst())
                    op = JSOP_THROWSETCONST;

                if (!emitTDZCheckIfNeeded(name, loc))
                    return false;
            }
        }
        if (!emitLocalOp(op, loc.frameSlot()))
            return false;
        if (op == JSOP_INITLEXICAL) {
            if (!innermostTDZCheckCache->noteTDZCheck(this, name, DontCheckTDZ))
                return false;
        }
        break;
      }

      case NameLocation::Kind::EnvironmentCoordinate: {
        JSOp op = JSOP_SETALIASEDVAR;
        if (!emitRhs(this, loc, emittedBindOp))
            return false;
        if (loc.isLexical()) {
            if (initialize) {
                op = JSOP_INITALIASEDLEXICAL;
            } else {
                if (loc.isConst())
                    op = JSOP_THROWSETALIASEDCONST;

                if (!emitTDZCheckIfNeeded(name, loc))
                    return false;
            }
        }
        if (loc.bindingKind() == BindingKind::NamedLambdaCallee) {
            // Assigning to the named lambda is a no-op in sloppy mode and throws
            // in strict mode.
            op = JSOP_THROWSETALIASEDCONST;
            if (sc->strict() && !emitEnvCoordOp(op, loc.environmentCoordinate()))
                return false;
        } else {
            if (!emitEnvCoordOp(op, loc.environmentCoordinate()))
                return false;
        }
        if (op == JSOP_INITALIASEDLEXICAL) {
            if (!innermostTDZCheckCache->noteTDZCheck(this, name, DontCheckTDZ))
                return false;
        }
        break;
      }
    }

    return true;
}

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

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

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

    if (loc.kind() == NameLocation::Kind::FrameSlot) {
        if (!emitLocalOp(JSOP_CHECKLEXICAL, loc.frameSlot()))
            return false;
    } else {
        if (!emitEnvCoordOp(JSOP_CHECKALIASEDLEXICAL, loc.environmentCoordinate()))
            return false;
    }

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

bool
BytecodeEmitter::emitPropLHS(ParseNode* pn)
{
    MOZ_ASSERT(pn->isKind(PNK_DOT));
    MOZ_ASSERT(!pn->as<PropertyAccess>().isSuper());

    ParseNode* pn2 = pn->pn_expr;

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

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

        do {
            /* Walk back up the list, emitting annotated name ops. */
            if (!emitAtomOp(pndot, JSOP_GETPROP))
                return false;

            /* Reverse the pn_expr link again. */
            pnup = pndot->pn_expr;
            pndot->pn_expr = pndown;
            pndown = pndot;
        } while ((pndot = pnup) != nullptr);
        return true;
    }

    // The non-optimized case.
    return emitTree(pn2);
}

bool
BytecodeEmitter::emitSuperPropLHS(ParseNode* superBase, bool isCall)
{
    if (!emitGetThisForSuperBase(superBase))
        return false;
    if (isCall && !emit1(JSOP_DUP))
        return false;
    if (!emit1(JSOP_SUPERBASE))
        return false;
    return true;
}

bool
BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
{
    MOZ_ASSERT(pn->isArity(PN_NAME));

    if (!emitPropLHS(pn))
        return false;

    if (op == JSOP_CALLPROP && !emit1(JSOP_DUP))
        return false;

    if (!emitAtomOp(pn, op))
        return false;

    if (op == JSOP_CALLPROP && !emit1(JSOP_SWAP))
        return false;

    return true;
}

bool
BytecodeEmitter::emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall)
{
    ParseNode* base = &pn->as<PropertyAccess>().expression();
    if (!emitSuperPropLHS(base, isCall))
        return false;

    if (!emitAtomOp(pn, op))
        return false;

    if (isCall && !emit1(JSOP_SWAP))
        return false;

    return true;
}

bool
BytecodeEmitter::emitPropIncDec(ParseNode* pn)
{
    MOZ_ASSERT(pn->pn_kid->isKind(PNK_DOT));

    bool post;
    bool isSuper = pn->pn_kid->as<PropertyAccess>().isSuper();
    JSOp binop = GetIncDecInfo(pn->getKind(), &post);

    if (isSuper) {
        ParseNode* base = &pn->pn_kid->as<PropertyAccess>().expression();
        if (!emitSuperPropLHS(base))                // THIS OBJ
            return false;
        if (!emit1(JSOP_DUP2))                      // THIS OBJ THIS OBJ
            return false;
    } else {
        if (!emitPropLHS(pn->pn_kid))               // OBJ
            return false;
        if (!emit1(JSOP_DUP))                       // OBJ OBJ
            return false;
    }
    if (!emitAtomOp(pn->pn_kid, isSuper? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V
        return false;
    if (!emit1(JSOP_POS))                           // OBJ N
        return false;
    if (post && !emit1(JSOP_DUP))                   // OBJ N? N
        return false;
    if (!emit1(JSOP_ONE))                           // OBJ N? N 1
        return false;
    if (!emit1(binop))                              // OBJ N? N+1
        return false;

    if (post) {
        if (!emit2(JSOP_PICK, 2 + isSuper))        // N? N+1 OBJ
            return false;
        if (!emit1(JSOP_SWAP))                     // N? OBJ N+1
            return false;
        if (isSuper) {
            if (!emit2(JSOP_PICK, 3))              // N THIS N+1 OBJ
                return false;
            if (!emit1(JSOP_SWAP))                 // N THIS OBJ N+1
                return false;
        }
    }

    JSOp setOp = isSuper ? sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
                         : sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
    if (!emitAtomOp(pn->pn_kid, setOp))             // N? N+1
        return false;
    if (post && !emit1(JSOP_POP))                   // RESULT
        return false;

    return true;
}

bool
BytecodeEmitter::emitGetNameAtLocationForCompoundAssignment(JSAtom* name, const NameLocation& loc)
{
    if (loc.kind() == NameLocation::Kind::Dynamic) {
        // For dynamic accesses we need to emit GETBOUNDNAME instead of
        // GETNAME for correctness: looking up @@unscopables on the
        // environment chain (due to 'with' environments) must only happen
        // once.
        //
        // GETBOUNDNAME uses the environment already pushed on the stack from
        // the earlier BINDNAME.
        if (!emit1(JSOP_DUP))                              // ENV ENV
            return false;
        if (!emitAtomOp(name, JSOP_GETBOUNDNAME))          // ENV V
            return false;
    } else {
        if (!emitGetNameAtLocation(name, loc))             // ENV? V
            return false;
    }

    return true;
}

bool
BytecodeEmitter::emitNameIncDec(ParseNode* pn)
{
    MOZ_ASSERT(pn->pn_kid->isKind(PNK_NAME));

    bool post;
    JSOp binop = GetIncDecInfo(pn->getKind(), &post);

    auto emitRhs = [pn, post, binop](BytecodeEmitter* bce, const NameLocation& loc,
                                     bool emittedBindOp)
    {
        JSAtom* name = pn->pn_kid->name();
        if (!bce->emitGetNameAtLocationForCompoundAssignment(name, loc)) // ENV? V
            return false;
        if (!bce->emit1(JSOP_POS))                         // ENV? N
            return false;
        if (post && !bce->emit1(JSOP_DUP))                 // ENV? N? N
            return false;
        if (!bce->emit1(JSOP_ONE))                         // ENV? N? N 1
            return false;
        if (!bce->emit1(binop))                            // ENV? N? N+1
            return false;

        if (post && emittedBindOp) {
            if (!bce->emit2(JSOP_PICK, 2))                 // N? N+1 ENV?
                return false;
            if (!bce->emit1(JSOP_SWAP))                    // N? ENV? N+1
                return false;
        }

        return true;
    };

    if (!emitSetName(pn->pn_kid, emitRhs))
        return false;

    if (post && !emit1(JSOP_POP))
        return false;

    return true;
}

bool
BytecodeEmitter::emitElemOperands(ParseNode* pn, EmitElemOption opts)
{
    MOZ_ASSERT(pn->isArity(PN_BINARY));

    if (!emitTree(pn->pn_left))
        return false;

    if (opts == EmitElemOption::IncDec) {
        if (!emit1(JSOP_CHECKOBJCOERCIBLE))
            return false;
    } else if (opts == EmitElemOption::Call) {
        if (!emit1(JSOP_DUP))
            return false;
    }

    if (!emitTree(pn->pn_right))
        return false;

    if (opts == EmitElemOption::Set) {
        if (!emit2(JSOP_PICK, 2))
            return false;
    } else if (opts == EmitElemOption::IncDec || opts == EmitElemOption::CompoundAssign) {
        if (!emit1(JSOP_TOID))
            return false;
    }
    return true;
}

bool
BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, EmitElemOption opts)
{
    MOZ_ASSERT(pn->isKind(PNK_ELEM) && pn->as<PropertyByValue>().isSuper());

    // The ordering here is somewhat screwy. We need to evaluate the propval
    // first, by spec. Do a little dance to not emit more than one JSOP_THIS.
    // Since JSOP_THIS might throw in derived class constructors, we cannot
    // just push it earlier as the receiver. We have to swap it down instead.

    if (!emitTree(pn->pn_right))
        return false;

    // We need to convert the key to an object id first, so that we do not do
    // it inside both the GETELEM and the SETELEM.
    if (opts == EmitElemOption::IncDec || opts == EmitElemOption::CompoundAssign) {
        if (!emit1(JSOP_TOID))
            return false;
    }

    if (!emitGetThisForSuperBase(pn->pn_left))
        return false;

    if (opts == EmitElemOption::Call) {
        if (!emit1(JSOP_SWAP))
            return false;

        // We need another |this| on top, also
        if (!emitDupAt(1))
            return false;
    }

    if (!emit1(JSOP_SUPERBASE))
        return false;

    if (opts == EmitElemOption::Set && !emit2(JSOP_PICK, 3))
        return false;

    return true;
}

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

    checkTypeSet(op);
    return true;
}

bool
BytecodeEmitter::emitElemOp(ParseNode* pn, JSOp op)
{
    EmitElemOption opts = EmitElemOption::Get;
    if (op == JSOP_CALLELEM)
        opts = EmitElemOption::Call;
    else if (op == JSOP_SETELEM || op == JSOP_STRICTSETELEM)
        opts = EmitElemOption::Set;

    return emitElemOperands(pn, opts) && emitElemOpBase(op);
}

bool
BytecodeEmitter::emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall)
{
    EmitElemOption opts = EmitElemOption::Get;
    if (isCall)
        opts = EmitElemOption::Call;
    else if (op == JSOP_SETELEM_SUPER || op == JSOP_STRICTSETELEM_SUPER)
        opts = EmitElemOption::Set;

    if (!emitSuperElemOperands(pn, opts))
        return false;
    if (!emitElemOpBase(op))
        return false;

    if (isCall && !emit1(JSOP_SWAP))
        return false;

    return true;
}

bool
BytecodeEmitter::emitElemIncDec(ParseNode* pn)
{
    MOZ_ASSERT(pn->pn_kid->isKind(PNK_ELEM));

    bool isSuper = pn->pn_kid->as<PropertyByValue>().isSuper();

    // We need to convert the key to an object id first, so that we do not do
    // it inside both the GETELEM and the SETELEM. This is done by
    // emit(Super)ElemOperands.
    if (isSuper) {
        if (!emitSuperElemOperands(pn->pn_kid, EmitElemOption::IncDec))
            return false;
    } else {
        if (!emitElemOperands(pn->pn_kid, EmitElemOption::IncDec))
            return false;
    }

    bool post;
    JSOp binop = GetIncDecInfo(pn->getKind(), &post);

    JSOp getOp;
    if (isSuper) {
        // There's no such thing as JSOP_DUP3, so we have to be creative.
        // Note that pushing things again is no fewer JSOps.
        if (!emitDupAt(2))                              // KEY THIS OBJ KEY
            return false;
        if (!emitDupAt(2))                              // KEY THIS OBJ KEY THIS
            return false;
        if (!emitDupAt(2))                              // KEY THIS OBJ KEY THIS OBJ
            return false;
        getOp = JSOP_GETELEM_SUPER;
    } else {
                                                        // OBJ KEY
        if (!emit1(JSOP_DUP2))                          // OBJ KEY OBJ KEY
            return false;
        getOp = JSOP_GETELEM;
    }
    if (!emitElemOpBase(getOp))                         // OBJ KEY V
        return false;
    if (!emit1(JSOP_POS))                               // OBJ KEY N
        return false;
    if (post && !emit1(JSOP_DUP))                       // OBJ KEY N? N
        return false;
    if (!emit1(JSOP_ONE))                               // OBJ KEY N? N 1
        return false;
    if (!emit1(binop))                                  // OBJ KEY N? N+1
        return false;

    if (post) {
        if (isSuper) {
            // We have one more value to rotate around, because of |this|
            // on the stack
            if (!emit2(JSOP_PICK, 4))
                return false;
        }
        if (!emit2(JSOP_PICK, 3 + isSuper))             // KEY N N+1 OBJ
            return false;
        if (!emit2(JSOP_PICK, 3 + isSuper))             // N N+1 OBJ KEY
            return false;
        if (!emit2(JSOP_PICK, 2 + isSuper))             // N OBJ KEY N+1
            return false;
    }

    JSOp setOp = isSuper ? (sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER)
                         : (sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM);
    if (!emitElemOpBase(setOp))                         // N? N+1
        return false;
    if (post && !emit1(JSOP_POP))                       // RESULT
        return false;

    return true;
}

bool
BytecodeEmitter::emitCallIncDec(ParseNode* incDec)
{
    MOZ_ASSERT(incDec->isKind(PNK_PREINCREMENT) ||
               incDec->isKind(PNK_POSTINCREMENT) ||
               incDec->isKind(PNK_PREDECREMENT) ||
               incDec->isKind(PNK_POSTDECREMENT));

    MOZ_ASSERT(incDec->pn_kid->isKind(PNK_CALL));

    ParseNode* call = incDec->pn_kid;
    if (!emitTree(call))                                // CALLRESULT
        return false;
    if (!emit1(JSOP_POS))                               // N
        return false;

    // The increment/decrement has no side effects, so proceed to throw for
    // invalid assignment target.
    return emitUint16Operand(JSOP_THROWMSG, JSMSG_BAD_LEFTSIDE_OF_ASS);
}

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 < JS_BIT(16)) {
            if (!emitUint16Operand(JSOP_UINT16, u))
                return false;
        } else if (u < JS_BIT(24)) {
            ptrdiff_t off;
            if (!emitN(JSOP_UINT24, 3, &off))
                return false;
            SET_UINT24(code(off), u);
        } else {
            ptrdiff_t off;
            if (!emitN(JSOP_INT32, 4, &off))
                return false;
            SET_INT32(code(off), ival);
        }
        return true;
    }

    if (!constList.append(DoubleValue(dval)))
        return false;

    return emitIndex32(JSOP_DOUBLE, constList.length() - 1);
}

/*
 * 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(ParseNode* pn)
{
    ParseNode* cases = pn->pn_right;
    MOZ_ASSERT(cases->isKind(PNK_LEXICALSCOPE) || cases->isKind(PNK_STATEMENTLIST));

    // Emit code for the discriminant.
    if (!emitTree(pn->pn_left))
        return false;

    // Enter the scope before pushing the switch BreakableControl since all
    // breaks are under this scope.
    Maybe<TDZCheckCache> tdzCache;
    Maybe<EmitterScope> emitterScope;
    if (cases->isKind(PNK_LEXICALSCOPE)) {
        if (!cases->isEmptyScope()) {
            tdzCache.emplace(this);
            emitterScope.emplace(this);
            if (!emitterScope->enterLexical(this, ScopeKind::Lexical, cases->scopeBindings()))
                return false;
        }

        // Advance |cases| to refer to the switch case list.
        cases = cases->scopeBody();

        // A switch statement may contain hoisted functions inside its
        // cases. The PNX_FUNCDEFS flag is propagated from the STATEMENTLIST
        // bodies of the cases to the case list.
        if (cases->pn_xflags & PNX_FUNCDEFS) {
            MOZ_ASSERT(emitterScope);
            for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
                if (caseNode->pn_right->pn_xflags & PNX_FUNCDEFS) {
                    if (!emitHoistedFunctionsInList(caseNode->pn_right))
                        return false;
                }
            }
        }
    }

    // After entering the scope, push the switch control.
    BreakableControl controlInfo(this, StatementKind::Switch);

    ptrdiff_t top = offset();

    // Switch bytecodes run from here till end of final case.
    uint32_t caseCount = cases->pn_count;
    if (caseCount > JS_BIT(16)) {
        parser.reportError(JSMSG_TOO_MANY_CASES);
        return false;
    }

    // Try for most optimal, fall back if not dense ints.
    JSOp switchOp = JSOP_TABLESWITCH;
    uint32_t tableLength = 0;
    int32_t low, high;
    bool hasDefault = false;
    CaseClause* firstCase = cases->pn_head ? &cases->pn_head->as<CaseClause>() : nullptr;
    if (caseCount == 0 ||
        (caseCount == 1 && (hasDefault = firstCase->isDefault())))
    {
        caseCount = 0;
        low = 0;
        high = -1;
    } else {
        Vector<jsbitmap, 128, SystemAllocPolicy> intmap;
        int32_t intmapBitLength = 0;

        low  = JSVAL_INT_MAX;
        high = JSVAL_INT_MIN;

        for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
            if (caseNode->isDefault()) {
                hasDefault = true;
                caseCount--;  // one of the "cases" was the default
                continue;
            }

            if (switchOp == JSOP_CONDSWITCH)
                continue;

            MOZ_ASSERT(switchOp == JSOP_TABLESWITCH);

            ParseNode* caseValue = caseNode->caseExpression();

            if (caseValue->getKind() != PNK_NUMBER) {
                switchOp = JSOP_CONDSWITCH;
                continue;
            }

            int32_t i;
            if (!NumberIsInt32(caseValue->pn_dval, &i)) {
                switchOp = JSOP_CONDSWITCH;
                continue;
            }

            if (unsigned(i + int(JS_BIT(15))) >= unsigned(JS_BIT(16))) {
                switchOp = JSOP_CONDSWITCH;
                continue;
            }
            if (i < low)
                low = i;
            if (i > high)
                high = i;

            // Check for duplicates, which require a JSOP_CONDSWITCH.
            // We bias i by 65536 if it's negative, and hope that's a rare
            // case (because it requires a malloc'd bitmap).
            if (i < 0)
                i += JS_BIT(16);
            if (i >= intmapBitLength) {
                size_t newLength = (i / JS_BITMAP_NBITS) + 1;
                if (!intmap.resize(newLength))
                    return false;
                intmapBitLength = newLength * JS_BITMAP_NBITS;
            }
            if (JS_TEST_BIT(intmap, i)) {
                switchOp = JSOP_CONDSWITCH;
                continue;
            }
            JS_SET_BIT(intmap, i);
        }

        // Compute table length and select condswitch instead if overlarge or
        // more than half-sparse.
        if (switchOp == JSOP_TABLESWITCH) {
            tableLength = uint32_t(high - low + 1);
            if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount)
                switchOp = JSOP_CONDSWITCH;
        }
    }

    // The note has one or two offsets: first tells total switch code length;
    // second (if condswitch) tells offset to first JSOP_CASE.
    unsigned noteIndex;
    size_t switchSize;
    if (switchOp == JSOP_CONDSWITCH) {
        // 0 bytes of immediate for unoptimized switch.
        switchSize = 0;
        if (!newSrcNote3(SRC_CONDSWITCH, 0, 0, &noteIndex))
            return false;
    } else {
        MOZ_ASSERT(switchOp == JSOP_TABLESWITCH);

        // 3 offsets (len, low, high) before the table, 1 per entry.
        switchSize = size_t(JUMP_OFFSET_LEN * (3 + tableLength));
        if (!newSrcNote2(SRC_TABLESWITCH, 0, &noteIndex))
            return false;
    }

    // Emit switchOp followed by switchSize bytes of jump or lookup table.
    MOZ_ASSERT(top == offset());
    if (!emitN(switchOp, switchSize))
        return false;

    Vector<CaseClause*, 32, SystemAllocPolicy> table;

    JumpList condSwitchDefaultOff;
    if (switchOp == JSOP_CONDSWITCH) {
        unsigned caseNoteIndex;
        bool beforeCases = true;
        ptrdiff_t lastCaseOffset = -1;

        // The case conditions need their own TDZ cache since they might not
        // all execute.
        TDZCheckCache tdzCache(this);

        // Emit code for evaluating cases and jumping to case statements.
        for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
            ParseNode* caseValue = caseNode->caseExpression();

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

            if (!beforeCases) {
                // prevCase is the previous JSOP_CASE's bytecode offset.
                if (!setSrcNoteOffset(caseNoteIndex, 0, offset() - lastCaseOffset))
                    return false;
            }
            if (!caseValue) {
                // This is the default clause.
                continue;
            }

            if (!newSrcNote2(SRC_NEXTCASE, 0, &caseNoteIndex))
                return false;

            // The case clauses are produced before any of the case body. The
            // JumpList is saved on the parsed tree, then later restored and
            // patched when generating the cases body.
            JumpList caseJump;
            if (!emitJump(JSOP_CASE, &caseJump))
                return false;
            caseNode->setOffset(caseJump.offset);
            lastCaseOffset = caseJump.offset;

            if (beforeCases) {
                // Switch note's second offset is to first JSOP_CASE.
                unsigned noteCount = notes().length();
                if (!setSrcNoteOffset(noteIndex, 1, lastCaseOffset - top))
                    return false;
                unsigned noteCountDelta = notes().length() - noteCount;
                if (noteCountDelta != 0)
                    caseNoteIndex += noteCountDelta;
                beforeCases = false;
            }
        }

        // If we didn't have an explicit default (which could fall in between
        // cases, preventing us from fusing this setSrcNoteOffset with the call
        // in the loop above), link the last case to the implicit default for
        // the benefit of IonBuilder.
        if (!hasDefault &&
            !beforeCases &&
            !setSrcNoteOffset(caseNoteIndex, 0, offset() - lastCaseOffset))
        {
            return false;
        }

        // Emit default even if no explicit default statement.
        if (!emitJump(JSOP_DEFAULT, &condSwitchDefaultOff))
            return false;
    } else {
        MOZ_ASSERT(switchOp == JSOP_TABLESWITCH);

        // skip default offset.
        jsbytecode* pc = code(top + JUMP_OFFSET_LEN);

        // Fill in switch bounds, which we know fit in 16-bit offsets.
        SET_JUMP_OFFSET(pc, low);
        pc += JUMP_OFFSET_LEN;
        SET_JUMP_OFFSET(pc, high);
        pc += JUMP_OFFSET_LEN;

        if (tableLength != 0) {
            if (!table.growBy(tableLength))
                return false;

            for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
                if (ParseNode* caseValue = caseNode->caseExpression()) {
                    MOZ_ASSERT(caseValue->isKind(PNK_NUMBER));

                    int32_t i = int32_t(caseValue->pn_dval);
                    MOZ_ASSERT(double(i) == caseValue->pn_dval);

                    i -= low;
                    MOZ_ASSERT(uint32_t(i) < tableLength);
                    MOZ_ASSERT(!table[i]);
                    table[i] = caseNode;
                }
            }
        }
    }

    JumpTarget defaultOffset{ -1 };

    // Emit code for each case's statements.
    for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
        if (switchOp == JSOP_CONDSWITCH && !caseNode->isDefault()) {
            // The case offset got saved in the caseNode structure after
            // emitting the JSOP_CASE jump instruction above.
            JumpList caseCond;
            caseCond.offset = caseNode->offset();
            if (!emitJumpTargetAndPatch(caseCond))
                return false;
        }

        JumpTarget here;
        if (!emitJumpTarget(&here))
            return false;
        if (caseNode->isDefault())
            defaultOffset = here;

        // If this is emitted as a TABLESWITCH, we'll need to know this case's
        // offset later when emitting the table. Store it in the node's
        // pn_offset (giving the field a different meaning vs. how we used it
        // on the immediately preceding line of code).
        caseNode->setOffset(here.offset);

        TDZCheckCache tdzCache(this);

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

    if (!hasDefault) {
        // If no default case, offset for default is to end of switch.
        if (!emitJumpTarget(&defaultOffset))
            return false;
    }
    MOZ_ASSERT(defaultOffset.offset != -1);

    // Set the default offset (to end of switch if no default).
    jsbytecode* pc;
    if (switchOp == JSOP_CONDSWITCH) {
        pc = nullptr;
        patchJumpsToTarget(condSwitchDefaultOff, defaultOffset);
    } else {
        MOZ_ASSERT(switchOp == JSOP_TABLESWITCH);
        pc = code(top);
        SET_JUMP_OFFSET(pc, defaultOffset.offset - top);
        pc += JUMP_OFFSET_LEN;
    }

    // Set the SRC_SWITCH note's offset operand to tell end of switch.
    if (!setSrcNoteOffset(noteIndex, 0, lastNonJumpTargetOffset() - top))
        return false;

    if (switchOp == JSOP_TABLESWITCH) {
        // Skip over the already-initialized switch bounds.
        pc += 2 * JUMP_OFFSET_LEN;

        // Fill in the jump table, if there is one.
        for (uint32_t i = 0; i < tableLength; i++) {
            CaseClause* caseNode = table[i];
            ptrdiff_t off = caseNode ? caseNode->offset() - top : 0;
            SET_JUMP_OFFSET(pc, off);
            pc += JUMP_OFFSET_LEN;
        }
    }

    // Patch breaks before leaving the scope, as all breaks are under the
    // lexical scope if it exists.
    if (!controlInfo.patchBreaks(this))
        return false;

    if (emitterScope && !emitterScope->leave(this))
        return false;

    return true;
}

bool
BytecodeEmitter::isRunOnceLambda()
{
    // The run once lambda flags set by the parser are approximate, and we look
    // at properties of the function itself before deciding to emit a function
    // as a run once lambda.

    if (!(parent && parent->emittingRunOnceLambda) &&
        (emitterMode != LazyFunction || !lazyScript->treatAsRunOnce()))
    {
        return false;
    }

    FunctionBox* funbox = sc->asFunctionBox();
    return !funbox->argumentsHasLocalBinding() &&
           !funbox->isStarGenerator() &&
           !funbox->isLegacyGenerator() &&
           !funbox->isAsync() &&
           !funbox->function()->explicitName();
}

bool
BytecodeEmitter::emitYieldOp(JSOp op)
{
    if (op == JSOP_FINALYIELDRVAL)
        return emit1(JSOP_FINALYIELDRVAL);

    MOZ_ASSERT(op == JSOP_INITIALYIELD || op == JSOP_YIELD || op == JSOP_AWAIT);

    ptrdiff_t off;
    if (!emitN(op, 3, &off))
        return false;

    uint32_t yieldAndAwaitIndex = yieldAndAwaitOffsetList.length();
    if (yieldAndAwaitIndex >= JS_BIT(24)) {
        reportError(nullptr, JSMSG_TOO_MANY_YIELDS);
        return false;
    }

    if (op == JSOP_AWAIT)
        yieldAndAwaitOffsetList.numAwaits++;
    else
        yieldAndAwaitOffsetList.numYields++;

    SET_UINT24(code(off), yieldAndAwaitIndex);

    if (!yieldAndAwaitOffsetList.append(offset()))
        return false;

    return emit1(JSOP_DEBUGAFTERYIELD);
}

bool
BytecodeEmitter::emitSetThis(ParseNode* pn)
{
    // PNK_SETTHIS is used to update |this| after a super() call in a derived
    // class constructor.

    MOZ_ASSERT(pn->isKind(PNK_SETTHIS));
    MOZ_ASSERT(pn->pn_left->isKind(PNK_NAME));

    RootedAtom name(cx, pn->pn_left->name());
    auto emitRhs = [&name, pn](BytecodeEmitter* bce, const NameLocation&, bool) {
        // Emit the new |this| value.
        if (!bce->emitTree(pn->pn_right))
            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 (!bce->emitGetName(name))
            return false;
        if (!bce->emit1(JSOP_CHECKTHISREINIT))
            return false;
        if (!bce->emit1(JSOP_POP))
            return false;
        return true;
    };

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

    return emitSetOrInitializeNameAtLocation(name, lexicalLoc, emitRhs, true);
}

bool
BytecodeEmitter::emitScript(ParseNode* body)
{
    AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, tokenStream(), body);

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

    setFunctionBodyEndPos(body->pn_pos);

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

        switchToPrologue();
        if (!lexicalEmitterScope.enterLexical(this, ScopeKind::Lexical, body->scopeBindings()))
            return false;
        switchToMain();

        if (!emitLexicalScopeBody(body->scopeBody()))
            return false;

        if (!lexicalEmitterScope.leave(this))
            return false;
    } else {
        if (!emitTree(body))
            return false;
    }

    if (!emit1(JSOP_RETRVAL))
        return false;

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

    if (!JSScript::fullyInitFromEmitter(cx, script, this))
        return false;

    // URL and source map information must be set before firing
    // Debugger::onNewScript.
    if (!maybeSetDisplayURL() || !maybeSetSourceMap())
        return false;

    tellDebuggerAboutCompiledScript(cx);

    return true;
}

bool
BytecodeEmitter::emitFunctionScript(ParseNode* body)
{
    FunctionBox* funbox = sc->asFunctionBox();
    AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, tokenStream(), funbox);

    // The ordering of these EmitterScopes is important. The named lambda
    // scope needs to enclose the function scope needs to enclose the extra
    // var scope.

    Maybe<EmitterScope> namedLambdaEmitterScope;
    if (funbox->namedLambdaBindings()) {
        namedLambdaEmitterScope.emplace(this);
        if (!namedLambdaEmitterScope->enterNamedLambda(this, funbox))
            return false;
    }

    /*
     * Emit a prologue for run-once scripts which will deoptimize JIT code
     * if the script ends up running multiple times via foo.caller related
     * shenanigans.
     *
     * Also mark the script so that initializers created within it may be
     * given more precise types.
     */
    if (isRunOnceLambda()) {
        script->setTreatAsRunOnce();
        MOZ_ASSERT(!script->hasRunOnce());

        switchToPrologue();
        if (!emit1(JSOP_RUNONCE))
            return false;
        switchToMain();
    }

    setFunctionBodyEndPos(body->pn_pos);
    if (!emitTree(body))
        return false;

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

    // Always end the script with a JSOP_RETRVAL. Some other parts of the
    // codebase depend on this opcode,
    // e.g. InterpreterRegs::setToEndOfScript.
    if (!emit1(JSOP_RETRVAL))
        return false;

    if (namedLambdaEmitterScope) {
        if (!namedLambdaEmitterScope->leave(this))
            return false;
        namedLambdaEmitterScope.reset();
    }

    if (!JSScript::fullyInitFromEmitter(cx, script, this))
        return false;

    // URL and source map information must be set before firing
    // Debugger::onNewScript. Only top-level functions need this, as compiling
    // the outer scripts of nested functions already processed the source.
    if (emitterMode != LazyFunction && !parent) {
        if (!maybeSetDisplayURL() || !maybeSetSourceMap())
            return false;

        tellDebuggerAboutCompiledScript(cx);
    }

    return true;
}

template <typename NameEmitter>
bool
BytecodeEmitter::emitDestructuringDeclsWithEmitter(ParseNode* pattern, NameEmitter emitName)
{
    if (pattern->isKind(PNK_ARRAY)) {
        for (ParseNode* element = pattern->pn_head; element; element = element->pn_next) {
            if (element->isKind(PNK_ELISION))
                continue;
            ParseNode* target = element;
            if (element->isKind(PNK_SPREAD)) {
                target = element->pn_kid;
            }
            if (target->isKind(PNK_ASSIGN))
                target = target->pn_left;
            if (target->isKind(PNK_NAME)) {
                if (!emitName(this, target))
                    return false;
            } else {
                if (!emitDestructuringDeclsWithEmitter(target, emitName))
                    return false;
            }
        }
        return true;
    }

    MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
    for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
        MOZ_ASSERT(member->isKind(PNK_MUTATEPROTO) ||
                   member->isKind(PNK_COLON) ||
                   member->isKind(PNK_SHORTHAND));

        ParseNode* target = member->isKind(PNK_MUTATEPROTO) ? member->pn_kid : member->pn_right;

        if (target->isKind(PNK_ASSIGN))
            target = target->pn_left;
        if (target->isKind(PNK_NAME)) {
            if (!emitName(this, target))
                return false;
        } else {
            if (!emitDestructuringDeclsWithEmitter(target, emitName))
                return false;
        }
    }
    return true;
}

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

    if (target->isKind(PNK_SPREAD))
        target = target->pn_kid;
    else if (target->isKind(PNK_ASSIGN))
        target = target->pn_left;

    // No need to recur into PNK_ARRAY and PNK_OBJECT subpatterns here, since
    // emitSetOrInitializeDestructuring does the recursion when setting or
    // initializing value.  Getting reference doesn't recur.
    if (target->isKind(PNK_NAME) || target->isKind(PNK_ARRAY) || target->isKind(PNK_OBJECT))
        return true;

#ifdef DEBUG
    int depth = stackDepth;
#endif

    switch (target->getKind()) {
      case PNK_DOT: {
        if (target->as<PropertyAccess>().isSuper()) {
            if (!emitSuperPropLHS(&target->as<PropertyAccess>().expression()))
                return false;
            *emitted = 2;
        } else {
            if (!emitTree(target->pn_expr))
                return false;
            *emitted = 1;
        }
        break;
      }

      case PNK_ELEM: {
        if (target->as<PropertyByValue>().isSuper()) {
            if (!emitSuperElemOperands(target, EmitElemOption::Ref))
                return false;
            *emitted = 3;
        } else {
            if (!emitElemOperands(target, EmitElemOption::Ref))
                return false;
            *emitted = 2;
        }
        break;
      }

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

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

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

    return true;
}

bool
BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav)
{
    // Now emit the lvalue opcode sequence. If the lvalue is a nested
    // destructuring initialiser-form, call ourselves to handle it, then pop
    // the matched value. Otherwise emit an lvalue bytecode sequence followed
    // by an assignment op.
    if (target->isKind(PNK_SPREAD))
        target = target->pn_kid;
    else if (target->isKind(PNK_ASSIGN))
        target = target->pn_left;
    if (target->isKind(PNK_ARRAY) || target->isKind(PNK_OBJECT)) {
        if (!emitDestructuringOps(target, flav))
            return false;
        // Per its post-condition, emitDestructuringOps has left the
        // to-be-destructured value on top of the stack.
        if (!emit1(JSOP_POP))
            return false;
    } else {
        switch (target->getKind()) {
          case PNK_NAME: {
            auto emitSwapScopeAndRhs = [](BytecodeEmitter* bce, const NameLocation&,
                                          bool emittedBindOp)
            {
                if (emittedBindOp) {
                    // This is like ordinary assignment, but with one
                    // difference.
                    //
                    // In `a = b`, we first determine a binding for `a` (using
                    // JSOP_BINDNAME or JSOP_BINDGNAME), then we evaluate `b`,
                    // then a JSOP_SETNAME instruction.
                    //
                    // In `[a] = [b]`, per spec, `b` is evaluated first, then
                    // we determine a binding for `a`. Then we need to do
                    // assignment-- but the operands are on the stack in the
                    // wrong order for JSOP_SETPROP, so we have to add a
                    // JSOP_SWAP.
                    //
                    // In the cases where we are emitting a name op, emit a
                    // swap because of this.
                    return bce->emit1(JSOP_SWAP);
                }

                // In cases of emitting a frame slot or environment slot,
                // nothing needs be done.
                return true;
            };

            RootedAtom name(cx, target->name());
            switch (flav) {
              case DestructuringDeclaration:
                if (!emitInitializeName(name, emitSwapScopeAndRhs))
                    return false;
                break;

              case DestructuringFormalParameterInVarScope: {
                // If there's an parameter expression var scope, the
                // destructuring declaration needs to initialize the name in
                // the function scope. The innermost scope is the var scope,
                // and its enclosing scope is the function scope.
                EmitterScope* funScope = innermostEmitterScope->enclosingInFrame();
                NameLocation paramLoc = *locationOfNameBoundInScope(name, funScope);
                if (!emitSetOrInitializeNameAtLocation(name, paramLoc, emitSwapScopeAndRhs, true))
                    return false;
                break;
              }

              case DestructuringAssignment:
                if (!emitSetName(name, emitSwapScopeAndRhs))
                    return false;
                break;
            }

            break;
          }

          case PNK_DOT: {
            // The reference is already pushed by emitDestructuringLHSRef.
            JSOp setOp;
            if (target->as<PropertyAccess>().isSuper())
                setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
            else
                setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
            if (!emitAtomOp(target, setOp))
                return false;
            break;
          }

          case PNK_ELEM: {
            // The reference is already pushed by emitDestructuringLHSRef.
            if (target->as<PropertyByValue>().isSuper()) {
                JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER;
                // emitDestructuringLHSRef already did emitSuperElemOperands
                // part of emitSuperElemOp.  Perform remaining part here.
                if (!emitElemOpBase(setOp))
                    return false;
            } else {
                JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
                if (!emitElemOpBase(setOp))
                    return false;
            }
            break;
          }

          case PNK_CALL:
            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))
            return false;
    }

    return true;
}

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

    if (!emit1(JSOP_DUP))                                 // ... ITER ITER
        return false;
    if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))     // ... ITER NEXT
        return false;
    if (!emit1(JSOP_SWAP))                                // ... NEXT ITER
        return false;
    if (!emitCall(JSOP_CALL, 0, pn))                      // ... RESULT
        return false;

    if (iterKind == IteratorKind::Async) {
        if (!emitAwait())                                 // ... RESULT
            return false;
    }

    if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ... RESULT
        return false;
    checkTypeSet(JSOP_CALL);
    return true;
}

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

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

    if (!emit1(JSOP_DUP))                                 // ... ITER ITER
        return false;

    // Step 3.
    //
    // Get the "return" method.
    if (!emitAtomOp(cx->names().return_, JSOP_CALLPROP))  // ... ITER RET
        return false;

    // Step 4.
    //
    // Do nothing if "return" is null or undefined.
    IfThenElseEmitter ifReturnMethodIsDefined(this);
    if (!emit1(JSOP_DUP))                                 // ... ITER RET RET
        return false;
    if (!emit1(JSOP_UNDEFINED))                           // ... ITER RET RET UNDEFINED
        return false;
    if (!emit1(JSOP_NE))                                  // ... ITER RET ?NEQL
        return false;
    if (!ifReturnMethodIsDefined.emitIfElse())
        return false;

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

    // Steps 5, 8.
    //
    // Call "return" if it is not undefined or null, and check that it returns
    // an Object.
    if (!emit1(JSOP_SWAP))                                // ... RET ITER
        return false;

    Maybe<TryEmitter> tryCatch;

    if (completionKind == CompletionKind::Throw) {
        tryCatch.emplace(this, TryEmitter::TryCatch, TryEmitter::DontUseRetVal,
                         TryEmitter::DontUseControl);

        // Mutate stack to balance stack for try-catch.
        if (!emit1(JSOP_UNDEFINED))                       // ... RET ITER UNDEF
            return false;
        if (!tryCatch->emitTry())                         // ... RET ITER UNDEF
            return false;
        if (!emitDupAt(2))                                // ... RET ITER UNDEF RET
            return false;
        if (!emitDupAt(2))                                // ... RET ITER UNDEF RET ITER
            return false;
    }

    if (!emitCall(JSOP_CALL, 0))                          // ... ... RESULT
        return false;
    checkTypeSet(JSOP_CALL);

    if (iterKind == IteratorKind::Async) {
        if (completionKind != CompletionKind::Throw) {
            // Await clobbers rval, so save the current rval.
            if (!emit1(JSOP_GETRVAL))                     // ... ... RESULT RVAL
                return false;
            if (!emit1(JSOP_SWAP))                        // ... ... RVAL RESULT
                return false;
        }
        if (!emitAwait())                                 // ... ... RVAL? RESULT
            return false;
    }

    if (completionKind == CompletionKind::Throw) {
        if (!emit1(JSOP_SWAP))                            // ... RET ITER RESULT UNDEF
            return false;
        if (!emit1(JSOP_POP))                             // ... RET ITER RESULT
            return false;

        if (!tryCatch->emitCatch())                       // ... RET ITER RESULT
            return false;

        // Just ignore the exception thrown by call and await.
        if (!emit1(JSOP_EXCEPTION))                       // ... RET ITER RESULT EXC
            return false;
        if (!emit1(JSOP_POP))                             // ... RET ITER RESULT
            return false;

        if (!tryCatch->emitEnd())                         // ... RET ITER RESULT
            return false;

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

        if (iterKind == IteratorKind::Async) {
            if (!emit1(JSOP_SWAP))                        // ... RESULT RVAL
                return false;
            if (!emit1(JSOP_SETRVAL))                     // ... RESULT
                return false;
        }
    }

    if (!ifReturnMethodIsDefined.emitElse())
        return false;
    if (!emit1(JSOP_POP))                                 // ... ITER
        return false;
    if (!ifReturnMethodIsDefined.emitEnd())
        return false;

    return emit1(JSOP_POP);                               // ...
}

template <typename InnerEmitter>
bool
BytecodeEmitter::wrapWithDestructuringIteratorCloseTryNote(int32_t iterDepth, InnerEmitter emitter)
{
    MOZ_ASSERT(this->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_TRY_DESTRUCTURING_ITERCLOSE))
        return false;

    ptrdiff_t start = offset();
    if (!emitter(this))
        return false;
    ptrdiff_t end = offset();
    if (start != end)
        return tryNoteList.append(JSTRY_DESTRUCTURING_ITERCLOSE, iterDepth, start, end);
    return true;
}

bool
BytecodeEmitter::emitDefault(ParseNode* defaultExpr, ParseNode* pattern)
{
    if (!emit1(JSOP_DUP))                                 // VALUE VALUE
        return false;
    if (!emit1(JSOP_UNDEFINED))                           // VALUE VALUE UNDEFINED
        return false;
    if (!emit1(JSOP_STRICTEQ))                            // VALUE EQL?
        return false;
    // Emit source note to enable ion compilation.
    if (!newSrcNote(SRC_IF))
        return false;
    JumpList jump;
    if (!emitJump(JSOP_IFEQ, &jump))                      // VALUE
        return false;
    if (!emit1(JSOP_POP))                                 // .
        return false;
    if (!emitInitializerInBranch(defaultExpr, pattern))   // DEFAULTVALUE
        return false;
    if (!emitJumpTargetAndPatch(jump))
        return false;
    return true;
}

bool
BytecodeEmitter::setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name,
                                     FunctionPrefixKind prefixKind)
{
    if (maybeFun->isKind(PNK_FUNCTION)) {
        // Function doesn't have 'name' property at this point.
        // Set function's name at compile time.
        JSFunction* fun = maybeFun->pn_funbox->function();

        // Single node can be emitted multiple times if it appears in
        // array destructuring default.  If function already has a name,
        // just return.
        if (fun->hasCompileTimeName()) {
#ifdef DEBUG
            RootedFunction rootedFun(cx, fun);
            JSAtom* funName = NameToFunctionName(cx, name, prefixKind);
            if (!funName)
                return false;
            MOZ_ASSERT(funName == rootedFun->compileTimeName());
#endif
            return true;
        }

        fun->setCompileTimeName(name);
        return true;
    }

    uint32_t nameIndex;
    if (!makeAtomIndex(name, &nameIndex))
        return false;
    if (!emitIndexOp(JSOP_STRING, nameIndex))   // FUN NAME
        return false;
    uint8_t kind = uint8_t(prefixKind);
    if (!emit2(JSOP_SETFUNNAME, kind))          // FUN
        return false;
    return true;
}

bool
BytecodeEmitter::emitInitializer(ParseNode* initializer, ParseNode* pattern)
{
    if (!emitTree(initializer))
        return false;

    if (!pattern->isInParens() && pattern->isKind(PNK_NAME) &&
        initializer->isDirectRHSAnonFunction())
    {
        RootedAtom name(cx, pattern->name());
        if (!setOrEmitSetFunName(initializer, name, FunctionPrefixKind::None))
            return false;
    }

    return true;
}

bool
BytecodeEmitter::emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern)
{
    TDZCheckCache tdzCache(this);
    return emitInitializer(initializer, pattern);
}

bool
BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlavor flav)
{
    MOZ_ASSERT(pattern->isKind(PNK_ARRAY));
    MOZ_ASSERT(pattern->isArity(PN_LIST));
    MOZ_ASSERT(this->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, lref, result, done, value; // stack values
    //
    //   iter = x[Symbol.iterator]();
    //
    //   // ==== emitted by loop for a ====
    //   lref = GetReference(a);              // covered by trynote
    //
    //   result = iter.next();
    //   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 = iter.next();
    //     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 = iter.next();
    //     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 = iter.next();
    //     done = result.done;
    //     if (done)
    //       value = undefined;
    //     else
    //       value = result.value;
    //   }
    //
    //   if (value === undefined)
    //     value = y;                         // covered by trynote
    //
    //   SetOrInitialize(lref, value);        // covered by trynote
    //
    //   // ==== emitted by loop for d ====
    //   lref = GetReference(d);              // covered by trynote
    //
    //   if (done)
    //     value = [];
    //   else
    //     value = [...iter];
    //
    //   SetOrInitialize(lref, value);        // covered by trynote
    //
    //   // === emitted after loop ===
    //   if (!done)
    //      IteratorClose(iter);

    // Use an iterator to destructure the RHS, instead of index lookup. We
    // must leave the *original* value on the stack.
    if (!emit1(JSOP_DUP))                                         // ... OBJ OBJ
        return false;
    if (!emitIterator())                                          // ... OBJ ITER
        return false;

    // For an empty pattern [], call IteratorClose unconditionally. Nothing
    // else needs to be done.
    if (!pattern->pn_head)
        return emitIteratorClose();                               // ... OBJ

    // Push an initial FALSE value for DONE.
    if (!emit1(JSOP_FALSE))                                       // ... OBJ ITER FALSE
        return false;

    // JSTRY_DESTRUCTURING_ITERCLOSE 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 = stackDepth;

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

        size_t emitted = 0;

        // Spec requires LHS reference to be evaluated first.
        ParseNode* lhsPattern = member;
        if (lhsPattern->isKind(PNK_ASSIGN))
            lhsPattern = lhsPattern->pn_left;

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

        // Pick the DONE value to the top of the stack.
        if (emitted) {
            if (!emit2(JSOP_PICK, emitted))                       // ... OBJ 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))                                 // ... OBJ ITER *LREF
                return false;
        }

        if (member->isKind(PNK_SPREAD)) {
            IfThenElseEmitter ifThenElse(this);
            if (!isFirst) {
                // If spread is not the first element of the pattern,
                // iterator can already be completed.
                                                                  // ... OBJ ITER *LREF DONE
                if (!ifThenElse.emitIfElse())                     // ... OBJ ITER *LREF
                    return false;

                if (!emitUint32Operand(JSOP_NEWARRAY, 0))         // ... OBJ ITER *LREF ARRAY
                    return false;
                if (!ifThenElse.emitElse())                       // ... OBJ ITER *LREF
                    return false;
            }

            // If iterator is not completed, create a new array with the rest
            // of the iterator.
            if (!emitDupAt(emitted))                              // ... OBJ ITER *LREF ITER
                return false;
            if (!emitUint32Operand(JSOP_NEWARRAY, 0))             // ... OBJ ITER *LREF ITER ARRAY
                return false;
            if (!emitNumberOp(0))                                 // ... OBJ ITER *LREF ITER ARRAY INDEX
                return false;
            if (!emitSpread())                                    // ... OBJ ITER *LREF ARRAY INDEX
                return false;
            if (!emit1(JSOP_POP))                                 // ... OBJ 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))                                // ... OBJ ITER *LREF ARRAY TRUE
                return false;
            if (!emit2(JSOP_UNPICK, emitted + 1))                 // ... OBJ ITER TRUE *LREF ARRAY
                return false;

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

            MOZ_ASSERT(!hasNext);
            break;
        }

        ParseNode* pndefault = nullptr;
        if (member->isKind(PNK_ASSIGN))
            pndefault = member->pn_right;

        MOZ_ASSERT(!member->isKind(PNK_SPREAD));

        IfThenElseEmitter ifAlreadyDone(this);
        if (!isFirst) {
                                                                  // ... OBJ ITER *LREF DONE
            if (!ifAlreadyDone.emitIfElse())                      // ... OBJ ITER *LREF
                return false;

            if (!emit1(JSOP_UNDEFINED))                           // ... OBJ ITER *LREF UNDEF
                return false;
            if (!emit1(JSOP_NOP_DESTRUCTURING))                   // ... OBJ ITER *LREF UNDEF
                return false;

            // The iterator is done. Unpick a TRUE value for DONE above ITER.
            if (!emit1(JSOP_TRUE))                                // ... OBJ ITER *LREF UNDEF TRUE
                return false;
            if (!emit2(JSOP_UNPICK, emitted + 1))                 // ... OBJ ITER TRUE *LREF UNDEF
                return false;

            if (!ifAlreadyDone.emitElse())                        // ... OBJ ITER *LREF
                return false;
        }

        if (!emitDupAt(emitted))                                  // ... OBJ ITER *LREF ITER
            return false;
        if (!emitIteratorNext(pattern))                           // ... OBJ ITER *LREF RESULT
            return false;
        if (!emit1(JSOP_DUP))                                     // ... OBJ ITER *LREF RESULT RESULT
            return false;
        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))          // ... OBJ ITER *LREF RESULT DONE
            return false;

        if (!emit1(JSOP_DUP))                                     // ... OBJ ITER *LREF RESULT DONE DONE
            return false;
        if (!emit2(JSOP_UNPICK, emitted + 2))                     // ... OBJ ITER DONE *LREF RESULT DONE
            return false;

        IfThenElseEmitter ifDone(this);
        if (!ifDone.emitIfElse())                                 // ... OBJ ITER DONE *LREF RESULT
            return false;

        if (!emit1(JSOP_POP))                                     // ... OBJ ITER DONE *LREF
            return false;
        if (!emit1(JSOP_UNDEFINED))                               // ... OBJ ITER DONE *LREF UNDEF
            return false;
        if (!emit1(JSOP_NOP_DESTRUCTURING))                       // ... OBJ ITER DONE *LREF UNDEF
            return false;

        if (!ifDone.emitElse())                                   // ... OBJ ITER DONE *LREF RESULT
            return false;

        if (!emitAtomOp(cx->names().value, JSOP_GETPROP))         // ... OBJ 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);    // ... OBJ ITER DONE *LREF VALUE
            };

            if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitDefault))
                return false;
        }

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

            if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitAssignment))
                return false;
        } else {
            if (!emit1(JSOP_POP))                                 // ... OBJ ITER DONE
                return false;
        }
    }

    // The last DONE value is on top of the stack. If not DONE, call
    // IteratorClose.
                                                                  // ... OBJ ITER DONE
    IfThenElseEmitter ifDone(this);
    if (!ifDone.emitIfElse())                                     // ... OBJ ITER
        return false;
    if (!emit1(JSOP_POP))                                         // ... OBJ
        return false;
    if (!ifDone.emitElse())                                       // ... OBJ ITER
        return false;
    if (!emitIteratorClose())                                     // ... OBJ
        return false;
    if (!ifDone.emitEnd())
        return false;

    return true;
}

bool
BytecodeEmitter::emitComputedPropertyName(ParseNode* computedPropName)
{
    MOZ_ASSERT(computedPropName->isKind(PNK_COMPUTED_NAME));
    return emitTree(computedPropName->pn_kid) && emit1(JSOP_TOID);
}

bool
BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFlavor flav)
{
    MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
    MOZ_ASSERT(pattern->isArity(PN_LIST));

    MOZ_ASSERT(this->stackDepth > 0);                             // ... RHS

    if (!emit1(JSOP_CHECKOBJCOERCIBLE))                           // ... RHS
        return false;

    bool needsRestPropertyExcludedSet = pattern->pn_count > 1 &&
                                        pattern->last()->isKind(PNK_SPREAD);
    if (needsRestPropertyExcludedSet) {
        if (!emitDestructuringObjRestExclusionSet(pattern))       // ... RHS SET
            return false;

        if (!emit1(JSOP_SWAP))                                    // ... SET RHS
            return false;
    }

    for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
        ParseNode* subpattern;
        if (member->isKind(PNK_MUTATEPROTO) || member->isKind(PNK_SPREAD))
            subpattern = member->pn_kid;
        else
            subpattern = member->pn_right;

        ParseNode* lhs = subpattern;
        MOZ_ASSERT_IF(member->isKind(PNK_SPREAD), !lhs->isKind(PNK_ASSIGN));
        if (lhs->isKind(PNK_ASSIGN))
            lhs = lhs->pn_left;

        size_t emitted;
        if (!emitDestructuringLHSRef(lhs, &emitted))              // ... *SET RHS *LREF
            return false;

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

        if (member->isKind(PNK_SPREAD)) {
            if (!updateSourceCoordNotes(member->pn_pos.begin))
                return false;

            if (!emitNewInit(JSProto_Object))                     // ... *SET RHS *LREF RHS TARGET
                return false;
            if (!emit1(JSOP_DUP))                                 // ... *SET RHS *LREF RHS TARGET TARGET
                return false;
            if (!emit2(JSOP_PICK, 2))                             // ... *SET RHS *LREF TARGET TARGET RHS
                return false;

            if (needsRestPropertyExcludedSet) {
                if (!emit2(JSOP_PICK, emitted + 4))               // ... RHS *LREF TARGET TARGET RHS SET
                    return false;
            }

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

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

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

        // Now push the property name currently being matched, which is the
        // current property name "label" on the left of a colon in the object
        // initialiser.
        bool needsGetElem = true;

        if (member->isKind(PNK_MUTATEPROTO)) {
            if (!emitAtomOp(cx->names().proto, JSOP_GETPROP))     // ... *SET RHS *LREF PROP
                return false;
            needsGetElem = false;
        } else {
            MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));

            ParseNode* key = member->pn_left;
            if (key->isKind(PNK_NUMBER)) {
                if (!emitNumberOp(key->pn_dval))                  // ... *SET RHS *LREF RHS KEY
                    return false;
            } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
                if (!emitAtomOp(key->pn_atom, JSOP_GETPROP))      // ... *SET RHS *LREF PROP
                    return false;
                needsGetElem = false;
            } else {
                if (!emitComputedPropertyName(key))               // ... *SET RHS *LREF RHS KEY
                    return false;

                // Add the computed property key to the exclusion set.
                if (needsRestPropertyExcludedSet) {
                    if (!emitDupAt(emitted + 3))                  // ... SET RHS *LREF RHS KEY SET
                        return false;
                    if (!emitDupAt(1))                            // ... SET RHS *LREF RHS KEY SET KEY
                        return false;
                    if (!emit1(JSOP_UNDEFINED))                   // ... SET RHS *LREF RHS KEY SET KEY UNDEFINED
                        return false;
                    if (!emit1(JSOP_INITELEM))                    // ... SET RHS *LREF RHS KEY SET
                        return false;
                    if (!emit1(JSOP_POP))                         // ... SET RHS *LREF RHS KEY
                        return false;
                }
            }
        }

        // Get the property value if not done already.
        if (needsGetElem && !emitElemOpBase(JSOP_GETELEM))        // ... *SET RHS *LREF PROP
            return false;

        if (subpattern->isKind(PNK_ASSIGN)) {
            if (!emitDefault(subpattern->pn_right, lhs))          // ... *SET RHS *LREF VALUE
                return false;
        }

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

    return true;
}

bool
BytecodeEmitter::emitDestructuringObjRestExclusionSet(ParseNode* pattern)
{
    MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
    MOZ_ASSERT(pattern->isArity(PN_LIST));
    MOZ_ASSERT(pattern->last()->isKind(PNK_SPREAD));

    ptrdiff_t offset = this->offset();
    if (!emitNewInit(JSProto_Object))
        return false;

    // Try to construct the shape of the object as we go, so we can emit a
    // JSOP_NEWOBJECT with the final shape instead.
    // In the case of computed property names and indices, we cannot fix the
    // shape at bytecode compile time. When the shape cannot be determined,
    // |obj| is nulled out.

    // No need to do any guessing for the object kind, since we know the upper
    // bound of how many properties we plan to have.
    gc::AllocKind kind = gc::GetGCObjectKind(pattern->pn_count - 1);
    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
    if (!obj)
        return false;

    RootedAtom pnatom(cx);
    for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
        if (member->isKind(PNK_SPREAD))
            break;

        bool isIndex = false;
        if (member->isKind(PNK_MUTATEPROTO)) {
            pnatom.set(cx->names().proto);
        } else {
            ParseNode* key = member->pn_left;
            if (key->isKind(PNK_NUMBER)) {
                if (!emitNumberOp(key->pn_dval))
                    return false;
                isIndex = true;
            } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
                pnatom.set(key->pn_atom);
            } else {
                // Otherwise this is a computed property name which needs to
                // be added dynamically.
                obj.set(nullptr);