/* -*- 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/. */

#include "vm/Stack-inl.h"

#include "mozilla/PodOperations.h"

#include "jscntxt.h"

#include "gc/Marking.h"
#ifdef JS_ION
#include "jit/AsmJSModule.h"
#include "jit/BaselineFrame.h"
#include "jit/JitCompartment.h"
#endif
#include "js/GCAPI.h"
#include "vm/Opcodes.h"

#include "jit/JitFrameIterator-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/Probes-inl.h"
#include "vm/ScopeObject-inl.h"

using namespace js;

using mozilla::PodCopy;

/*****************************************************************************/

void
InterpreterFrame::initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr evalInFramePrev,
                                   const Value &thisv, JSObject &scopeChain, ExecuteType type)
{
    /*
     * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
     * script in the context of another frame and the frame type is determined
     * by the context.
     */
    flags_ = type | HAS_SCOPECHAIN;

    JSObject *callee = nullptr;
    if (!(flags_ & (GLOBAL))) {
        if (evalInFramePrev) {
            JS_ASSERT(evalInFramePrev.isFunctionFrame() || evalInFramePrev.isGlobalFrame());
            if (evalInFramePrev.isFunctionFrame()) {
                callee = evalInFramePrev.callee();
                flags_ |= FUNCTION;
            } else {
                flags_ |= GLOBAL;
            }
        } else {
            FrameIter iter(cx);
            JS_ASSERT(iter.isFunctionFrame() || iter.isGlobalFrame());
            JS_ASSERT(!iter.isAsmJS());
            if (iter.isFunctionFrame()) {
                callee = iter.callee();
                flags_ |= FUNCTION;
            } else {
                flags_ |= GLOBAL;
            }
        }
    }

    Value *dstvp = (Value *)this - 2;
    dstvp[1] = thisv;

    if (isFunctionFrame()) {
        dstvp[0] = ObjectValue(*callee);
        exec.fun = &callee->as<JSFunction>();
        u.evalScript = script;
    } else {
        JS_ASSERT(isGlobalFrame());
        dstvp[0] = NullValue();
        exec.script = script;
#ifdef DEBUG
        u.evalScript = (JSScript *)0xbad;
#endif
    }

    scopeChain_ = &scopeChain;
    prev_ = nullptr;
    prevpc_ = nullptr;
    prevsp_ = nullptr;

    JS_ASSERT_IF(evalInFramePrev, isDebuggerFrame());
    evalInFramePrev_ = evalInFramePrev;

#ifdef DEBUG
    Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
    hookData_ = (void *)0xbad;
#endif
}

template <InterpreterFrame::TriggerPostBarriers doPostBarrier>
void
InterpreterFrame::copyFrameAndValues(JSContext *cx, Value *vp, InterpreterFrame *otherfp,
                                     const Value *othervp, Value *othersp)
{
    JS_ASSERT(othervp == otherfp->generatorArgsSnapshotBegin());
    JS_ASSERT(othersp >= otherfp->slots());
    JS_ASSERT(othersp <= otherfp->generatorSlotsSnapshotBegin() + otherfp->script()->nslots());

    /* Copy args, InterpreterFrame, and slots. */
    const Value *srcend = otherfp->generatorArgsSnapshotEnd();
    Value *dst = vp;
    for (const Value *src = othervp; src < srcend; src++, dst++) {
        *dst = *src;
        if (doPostBarrier)
            HeapValue::writeBarrierPost(*dst, dst);
    }

    *this = *otherfp;
    argv_ = vp + 2;
    unsetPushedSPSFrame();
    if (doPostBarrier)
        writeBarrierPost();

    srcend = othersp;
    dst = slots();
    for (const Value *src = otherfp->slots(); src < srcend; src++, dst++) {
        *dst = *src;
        if (doPostBarrier)
            HeapValue::writeBarrierPost(*dst, dst);
    }
}

/* Note: explicit instantiation for js_NewGenerator located in jsiter.cpp. */
template
void InterpreterFrame::copyFrameAndValues<InterpreterFrame::NoPostBarrier>(
                                    JSContext *, Value *, InterpreterFrame *, const Value *, Value *);
template
void InterpreterFrame::copyFrameAndValues<InterpreterFrame::DoPostBarrier>(
                                    JSContext *, Value *, InterpreterFrame *, const Value *, Value *);

void
InterpreterFrame::writeBarrierPost()
{
    /* This needs to follow the same rules as in InterpreterFrame::mark. */
    if (scopeChain_)
        JSObject::writeBarrierPost(scopeChain_, &scopeChain_);
    if (flags_ & HAS_ARGS_OBJ)
        JSObject::writeBarrierPost(argsObj_, &argsObj_);
    if (isFunctionFrame()) {
        JSFunction::writeBarrierPost(exec.fun, &exec.fun);
        if (isEvalFrame())
            JSScript::writeBarrierPost(u.evalScript, &u.evalScript);
    } else {
        JSScript::writeBarrierPost(exec.script, &exec.script);
    }
    if (hasReturnValue())
        HeapValue::writeBarrierPost(rval_, &rval_);
}

bool
InterpreterFrame::copyRawFrameSlots(AutoValueVector *vec)
{
    if (!vec->resize(numFormalArgs() + script()->nfixed()))
        return false;
    PodCopy(vec->begin(), argv(), numFormalArgs());
    PodCopy(vec->begin() + numFormalArgs(), slots(), script()->nfixed());
    return true;
}

JSObject *
InterpreterFrame::createRestParameter(JSContext *cx)
{
    JS_ASSERT(fun()->hasRest());
    unsigned nformal = fun()->nargs() - 1, nactual = numActualArgs();
    unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
    Value *restvp = argv() + nformal;
    JSObject *obj = NewDenseCopiedArray(cx, nrest, restvp, nullptr);
    if (!obj)
        return nullptr;
    types::FixRestArgumentsType(cx, obj);
    return obj;
}

static inline void
AssertDynamicScopeMatchesStaticScope(JSContext *cx, JSScript *script, JSObject *scope)
{
#ifdef DEBUG
    RootedObject enclosingScope(cx, script->enclosingStaticScope());
    for (StaticScopeIter<NoGC> i(enclosingScope); !i.done(); i++) {
        if (i.hasDynamicScopeObject()) {
            switch (i.type()) {
              case StaticScopeIter<NoGC>::BLOCK:
                JS_ASSERT(&i.block() == scope->as<ClonedBlockObject>().staticScope());
                scope = &scope->as<ClonedBlockObject>().enclosingScope();
                break;
              case StaticScopeIter<NoGC>::WITH:
                JS_ASSERT(&i.staticWith() == scope->as<DynamicWithObject>().staticScope());
                scope = &scope->as<DynamicWithObject>().enclosingScope();
                break;
              case StaticScopeIter<NoGC>::FUNCTION:
                JS_ASSERT(scope->as<CallObject>().callee().nonLazyScript() == i.funScript());
                scope = &scope->as<CallObject>().enclosingScope();
                break;
              case StaticScopeIter<NoGC>::NAMED_LAMBDA:
                scope = &scope->as<DeclEnvObject>().enclosingScope();
                break;
            }
        }
    }

    /*
     * Ideally, we'd JS_ASSERT(!scope->is<ScopeObject>()) but the enclosing
     * lexical scope chain stops at eval() boundaries. See StaticScopeIter
     * comment.
     */
#endif
}

bool
InterpreterFrame::initFunctionScopeObjects(JSContext *cx)
{
    CallObject *callobj = CallObject::createForFunction(cx, this);
    if (!callobj)
        return false;
    pushOnScopeChain(*callobj);
    flags_ |= HAS_CALL_OBJ;
    return true;
}

bool
InterpreterFrame::prologue(JSContext *cx)
{
    RootedScript script(cx, this->script());

    JS_ASSERT(!isGeneratorFrame());
    JS_ASSERT(cx->interpreterRegs().pc == script->code());

    if (isEvalFrame()) {
        if (script->strict()) {
            CallObject *callobj = CallObject::createForStrictEval(cx, this);
            if (!callobj)
                return false;
            pushOnScopeChain(*callobj);
            flags_ |= HAS_CALL_OBJ;
        }
        return probes::EnterScript(cx, script, nullptr, this);
    }

    if (isGlobalFrame())
        return probes::EnterScript(cx, script, nullptr, this);

    JS_ASSERT(isNonEvalFunctionFrame());
    AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain());

    if (fun()->isHeavyweight() && !initFunctionScopeObjects(cx))
        return false;

    if (isConstructing()) {
        RootedObject callee(cx, &this->callee());
        JSObject *obj = CreateThisForFunction(cx, callee,
                                              useNewType() ? SingletonObject : GenericObject);
        if (!obj)
            return false;
        functionThis() = ObjectValue(*obj);
    }

    return probes::EnterScript(cx, script, script->functionNonDelazifying(), this);
}

void
InterpreterFrame::epilogue(JSContext *cx)
{
    JS_ASSERT(!isYielding());

    RootedScript script(cx, this->script());
    probes::ExitScript(cx, script, script->functionNonDelazifying(), hasPushedSPSFrame());

    if (isEvalFrame()) {
        if (isStrictEvalFrame()) {
            JS_ASSERT_IF(hasCallObj(), scopeChain()->as<CallObject>().isForEval());
            if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
                DebugScopes::onPopStrictEvalScope(this);
        } else if (isDirectEvalFrame()) {
            if (isDebuggerFrame())
                JS_ASSERT(!scopeChain()->is<ScopeObject>());
        } else {
            /*
             * Debugger.Object.prototype.evalInGlobal creates indirect eval
             * frames scoped to the given global;
             * Debugger.Object.prototype.evalInGlobalWithBindings creates
             * indirect eval frames scoped to an object carrying the introduced
             * bindings.
             */
            if (isDebuggerFrame()) {
                JS_ASSERT(scopeChain()->is<GlobalObject>() ||
                          scopeChain()->enclosingScope()->is<GlobalObject>());
            } else {
                JS_ASSERT(scopeChain()->is<GlobalObject>());
            }
        }
        return;
    }

    if (isGlobalFrame()) {
        JS_ASSERT(!scopeChain()->is<ScopeObject>());
        return;
    }

    JS_ASSERT(isNonEvalFunctionFrame());

    if (fun()->isHeavyweight())
        JS_ASSERT_IF(hasCallObj(),
                     scopeChain()->as<CallObject>().callee().nonLazyScript() == script);
    else
        AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain());

    if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
        DebugScopes::onPopCall(this, cx);

    if (isConstructing() && thisValue().isObject() && returnValue().isPrimitive())
        setReturnValue(ObjectValue(constructorThis()));
}

bool
InterpreterFrame::pushBlock(JSContext *cx, StaticBlockObject &block)
{
    JS_ASSERT (block.needsClone());

    Rooted<StaticBlockObject *> blockHandle(cx, &block);
    ClonedBlockObject *clone = ClonedBlockObject::create(cx, blockHandle, this);
    if (!clone)
        return false;

    pushOnScopeChain(*clone);

    return true;
}

void
InterpreterFrame::popBlock(JSContext *cx)
{
    JS_ASSERT(scopeChain_->is<ClonedBlockObject>());
    popOffScopeChain();
}

void
InterpreterFrame::popWith(JSContext *cx)
{
    if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
        DebugScopes::onPopWith(this);

    JS_ASSERT(scopeChain()->is<DynamicWithObject>());
    popOffScopeChain();
}

void
InterpreterFrame::mark(JSTracer *trc)
{
    /*
     * Normally we would use MarkRoot here, except that generators also take
     * this path. However, generators use a special write barrier when the stack
     * frame is copied to the floating frame. Therefore, no barrier is needed.
     */
    if (flags_ & HAS_SCOPECHAIN)
        gc::MarkObjectUnbarriered(trc, &scopeChain_, "scope chain");
    if (flags_ & HAS_ARGS_OBJ)
        gc::MarkObjectUnbarriered(trc, &argsObj_, "arguments");
    if (isFunctionFrame()) {
        gc::MarkObjectUnbarriered(trc, &exec.fun, "fun");
        if (isEvalFrame())
            gc::MarkScriptUnbarriered(trc, &u.evalScript, "eval script");
    } else {
        gc::MarkScriptUnbarriered(trc, &exec.script, "script");
    }
    if (IS_GC_MARKING_TRACER(trc))
        script()->compartment()->zone()->active = true;
    gc::MarkValueUnbarriered(trc, returnValue().address(), "rval");
}

void
InterpreterFrame::markValues(JSTracer *trc, unsigned start, unsigned end)
{
    if (start < end)
        gc::MarkValueRootRange(trc, end - start, slots() + start, "vm_stack");
}

void
InterpreterFrame::markValues(JSTracer *trc, Value *sp, jsbytecode *pc)
{
    JS_ASSERT(sp >= slots());

    NestedScopeObject *staticScope;

    staticScope = script()->getStaticScope(pc);
    while (staticScope && !staticScope->is<StaticBlockObject>())
        staticScope = staticScope->enclosingNestedScope();

    size_t nfixed = script()->nfixed();
    size_t nlivefixed;

    if (staticScope) {
        StaticBlockObject &blockObj = staticScope->as<StaticBlockObject>();
        nlivefixed = blockObj.localOffset() + blockObj.numVariables();
    } else {
        nlivefixed = script()->nfixedvars();
    }

    if (nfixed == nlivefixed) {
        // All locals are live.
        markValues(trc, 0, sp - slots());
    } else {
        // Mark operand stack.
        markValues(trc, nfixed, sp - slots());

        // Clear dead locals.
        while (nfixed > nlivefixed)
            unaliasedLocal(--nfixed, DONT_CHECK_ALIASING).setUndefined();

        // Mark live locals.
        markValues(trc, 0, nlivefixed);
    }

    if (hasArgs()) {
        // Mark callee, |this| and arguments.
        unsigned argc = Max(numActualArgs(), numFormalArgs());
        gc::MarkValueRootRange(trc, argc + 2, argv_ - 2, "fp argv");
    } else {
        // Mark callee and |this|
        gc::MarkValueRootRange(trc, 2, ((Value *)this) - 2, "stack callee and this");
    }
}

static void
MarkInterpreterActivation(JSTracer *trc, InterpreterActivation *act)
{
    for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
        InterpreterFrame *fp = frames.frame();
        fp->markValues(trc, frames.sp(), frames.pc());
        fp->mark(trc);
    }
}

void
js::MarkInterpreterActivations(JSRuntime *rt, JSTracer *trc)
{
    for (ActivationIterator iter(rt); !iter.done(); ++iter) {
        Activation *act = iter.activation();
        if (act->isInterpreter())
            MarkInterpreterActivation(trc, act->asInterpreter());
    }

}

/*****************************************************************************/

// Unlike the other methods of this calss, this method is defined here so that
// we don't have to #include jsautooplen.h in vm/Stack.h.
void
InterpreterRegs::setToEndOfScript()
{
    JSScript *script = fp()->script();
    sp = fp()->base();
    pc = script->codeEnd() - JSOP_RETRVAL_LENGTH;
    JS_ASSERT(*pc == JSOP_RETRVAL);
}

/*****************************************************************************/

InterpreterFrame *
InterpreterStack::pushInvokeFrame(JSContext *cx, const CallArgs &args, InitialFrameFlags initial)
{
    LifoAlloc::Mark mark = allocator_.mark();

    RootedFunction fun(cx, &args.callee().as<JSFunction>());
    RootedScript script(cx, fun->nonLazyScript());

    InterpreterFrame::Flags flags = ToFrameFlags(initial);
    Value *argv;
    InterpreterFrame *fp = getCallFrame(cx, args, script, &flags, &argv);
    if (!fp)
        return nullptr;

    fp->mark_ = mark;
    fp->initCallFrame(cx, nullptr, nullptr, nullptr, *fun, script, argv, args.length(), flags);
    return fp;
}

InterpreterFrame *
InterpreterStack::pushExecuteFrame(JSContext *cx, HandleScript script, const Value &thisv,
                                   HandleObject scopeChain, ExecuteType type,
                                   AbstractFramePtr evalInFrame)
{
    LifoAlloc::Mark mark = allocator_.mark();

    unsigned nvars = 2 /* callee, this */ + script->nslots();
    uint8_t *buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value));
    if (!buffer)
        return nullptr;

    InterpreterFrame *fp = reinterpret_cast<InterpreterFrame *>(buffer + 2 * sizeof(Value));
    fp->mark_ = mark;
    fp->initExecuteFrame(cx, script, evalInFrame, thisv, *scopeChain, type);
    fp->initVarsToUndefined();

    return fp;
}

/*****************************************************************************/

/* MSVC PGO causes xpcshell startup crashes. */
#if defined(_MSC_VER)
# pragma optimize("g", off)
#endif

void
FrameIter::popActivation()
{
    ++data_.activations_;
    settleOnActivation();
}

void
FrameIter::popInterpreterFrame()
{
    JS_ASSERT(data_.state_ == INTERP);

    ++data_.interpFrames_;

    if (data_.interpFrames_.done())
        popActivation();
    else
        data_.pc_ = data_.interpFrames_.pc();
}

void
FrameIter::settleOnActivation()
{
    while (true) {
        if (data_.activations_.done()) {
            data_.state_ = DONE;
            return;
        }

        Activation *activation = data_.activations_.activation();

        // If JS_SaveFrameChain was called, stop iterating here (unless
        // GO_THROUGH_SAVED is set).
        if (data_.savedOption_ == STOP_AT_SAVED && activation->hasSavedFrameChain()) {
            data_.state_ = DONE;
            return;
        }

        // Skip activations from another context if needed.
        JS_ASSERT(activation->cx());
        JS_ASSERT(data_.cx_);
        if (data_.contextOption_ == CURRENT_CONTEXT && activation->cx() != data_.cx_) {
            ++data_.activations_;
            continue;
        }

        // If the caller supplied principals, only show activations which are subsumed (of the same
        // origin or of an origin accessible) by these principals.
        if (data_.principals_) {
            JSContext *cx = data_.cx_->asJSContext();
            if (JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes) {
                JS::AutoSuppressGCAnalysis nogc;
                if (!subsumes(data_.principals_, activation->compartment()->principals)) {
                    ++data_.activations_;
                    continue;
                }
            }
        }

#ifdef JS_ION
        if (activation->isJit()) {
            data_.jitFrames_ = jit::JitFrameIterator(data_.activations_);

            // Stop at the first scripted frame.
            while (!data_.jitFrames_.isScripted() && !data_.jitFrames_.done())
                ++data_.jitFrames_;

            // It's possible to have an JitActivation with no scripted frames,
            // for instance if we hit an over-recursion during bailout.
            if (data_.jitFrames_.done()) {
                ++data_.activations_;
                continue;
            }

            nextJitFrame();
            data_.state_ = JIT;
            return;
        }

        if (activation->isAsmJS()) {
            data_.asmJSFrames_ = AsmJSFrameIterator(data_.activations_->asAsmJS());

            if (data_.asmJSFrames_.done()) {
                ++data_.activations_;
                continue;
            }

            data_.state_ = ASMJS;
            return;
        }

        // ForkJoin activations don't contain iterable frames, so skip them.
        if (activation->isForkJoin()) {
            ++data_.activations_;
            continue;
        }
#endif

        JS_ASSERT(activation->isInterpreter());

        InterpreterActivation *interpAct = activation->asInterpreter();
        data_.interpFrames_ = InterpreterFrameIterator(interpAct);

        // If we OSR'ed into JIT code, skip the interpreter frame so that
        // the same frame is not reported twice.
        if (data_.interpFrames_.frame()->runningInJit()) {
            ++data_.interpFrames_;
            if (data_.interpFrames_.done()) {
                ++data_.activations_;
                continue;
            }
        }

        JS_ASSERT(!data_.interpFrames_.frame()->runningInJit());
        data_.pc_ = data_.interpFrames_.pc();
        data_.state_ = INTERP;
        return;
    }
}

FrameIter::Data::Data(ThreadSafeContext *cx, SavedOption savedOption,
                      ContextOption contextOption, JSPrincipals *principals)
  : cx_(cx),
    savedOption_(savedOption),
    contextOption_(contextOption),
    principals_(principals),
    pc_(nullptr),
    interpFrames_(nullptr),
    activations_(cx->perThreadData)
#ifdef JS_ION
  , jitFrames_((uint8_t *)nullptr, SequentialExecution)
  , ionInlineFrameNo_(0)
  , asmJSFrames_(nullptr)
#endif
{
}

FrameIter::Data::Data(const FrameIter::Data &other)
  : cx_(other.cx_),
    savedOption_(other.savedOption_),
    contextOption_(other.contextOption_),
    principals_(other.principals_),
    state_(other.state_),
    pc_(other.pc_),
    interpFrames_(other.interpFrames_),
    activations_(other.activations_)
#ifdef JS_ION
  , jitFrames_(other.jitFrames_)
  , ionInlineFrameNo_(other.ionInlineFrameNo_)
  , asmJSFrames_(other.asmJSFrames_)
#endif
{
}

FrameIter::FrameIter(ThreadSafeContext *cx, SavedOption savedOption)
  : data_(cx, savedOption, CURRENT_CONTEXT, nullptr)
#ifdef JS_ION
  , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
#endif
{
    // settleOnActivation can only GC if principals are given.
    JS::AutoSuppressGCAnalysis nogc;
    settleOnActivation();
}

FrameIter::FrameIter(ThreadSafeContext *cx, ContextOption contextOption,
                     SavedOption savedOption)
  : data_(cx, savedOption, contextOption, nullptr)
#ifdef JS_ION
  , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
#endif
{
    // settleOnActivation can only GC if principals are given.
    JS::AutoSuppressGCAnalysis nogc;
    settleOnActivation();
}

FrameIter::FrameIter(JSContext *cx, ContextOption contextOption,
                     SavedOption savedOption, JSPrincipals *principals)
  : data_(cx, savedOption, contextOption, principals)
#ifdef JS_ION
  , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
#endif
{
    settleOnActivation();
}

FrameIter::FrameIter(const FrameIter &other)
  : data_(other.data_)
#ifdef JS_ION
  , ionInlineFrames_(other.data_.cx_,
                     data_.jitFrames_.isIonJS() ? &other.ionInlineFrames_ : nullptr)
#endif
{
}

FrameIter::FrameIter(const Data &data)
  : data_(data)
#ifdef JS_ION
  , ionInlineFrames_(data.cx_, data_.jitFrames_.isIonJS() ? &data_.jitFrames_ : nullptr)
#endif
{
    JS_ASSERT(data.cx_);

#ifdef JS_ION
    if (data_.jitFrames_.isIonJS()) {
        while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_)
            ++ionInlineFrames_;
    }
#endif
}

#ifdef JS_ION
void
FrameIter::nextJitFrame()
{
    if (data_.jitFrames_.isIonJS()) {
        ionInlineFrames_.resetOn(&data_.jitFrames_);
        data_.pc_ = ionInlineFrames_.pc();
    } else {
        JS_ASSERT(data_.jitFrames_.isBaselineJS());
        data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
    }
}

void
FrameIter::popJitFrame()
{
    JS_ASSERT(data_.state_ == JIT);

    if (data_.jitFrames_.isIonJS() && ionInlineFrames_.more()) {
        ++ionInlineFrames_;
        data_.pc_ = ionInlineFrames_.pc();
        return;
    }

    ++data_.jitFrames_;
    while (!data_.jitFrames_.done() && !data_.jitFrames_.isScripted())
        ++data_.jitFrames_;

    if (!data_.jitFrames_.done()) {
        nextJitFrame();
        return;
    }

    popActivation();
}

void
FrameIter::popAsmJSFrame()
{
    JS_ASSERT(data_.state_ == ASMJS);

    ++data_.asmJSFrames_;
    if (data_.asmJSFrames_.done())
        popActivation();
}
#endif

FrameIter &
FrameIter::operator++()
{
    switch (data_.state_) {
      case DONE:
        MOZ_ASSUME_UNREACHABLE("Unexpected state");
      case INTERP:
        if (interpFrame()->isDebuggerFrame() && interpFrame()->evalInFramePrev()) {
            AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev();
            MOZ_ASSERT(!eifPrev.isRematerializedFrame());

            // Eval-in-frame can cross contexts and works across saved frame
            // chains.
            ContextOption prevContextOption = data_.contextOption_;
            SavedOption prevSavedOption = data_.savedOption_;
            data_.contextOption_ = ALL_CONTEXTS;
            data_.savedOption_ = GO_THROUGH_SAVED;

            popInterpreterFrame();

            while (isIon() || abstractFramePtr() != eifPrev) {
                if (data_.state_ == JIT) {
#ifdef JS_ION
                    popJitFrame();
#else
                    MOZ_ASSUME_UNREACHABLE("Invalid state");
#endif
                } else {
                    popInterpreterFrame();
                }
            }

            data_.contextOption_ = prevContextOption;
            data_.savedOption_ = prevSavedOption;
            data_.cx_ = data_.activations_->cx();
            break;
        }
        popInterpreterFrame();
        break;
#ifdef JS_ION
      case JIT:
        popJitFrame();
        break;
      case ASMJS:
        popAsmJSFrame();
        break;
#else
    default:
        MOZ_ASSUME_UNREACHABLE("Unexpected state");
#endif
    }
    return *this;
}

FrameIter::Data *
FrameIter::copyData() const
{
    Data *data = data_.cx_->new_<Data>(data_);
#ifdef JS_ION
    JS_ASSERT(data_.state_ != ASMJS);
    if (data && data_.jitFrames_.isIonJS())
        data->ionInlineFrameNo_ = ionInlineFrames_.frameNo();
#endif
    return data;
}

AbstractFramePtr
FrameIter::copyDataAsAbstractFramePtr() const
{
    AbstractFramePtr frame;
    if (Data *data = copyData())
        frame.ptr_ = uintptr_t(data);
    return frame;
}

JSCompartment *
FrameIter::compartment() const
{
    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
      case JIT:
      case ASMJS:
        return data_.activations_->compartment();
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

bool
FrameIter::isFunctionFrame() const
{
    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
        return interpFrame()->isFunctionFrame();
      case JIT:
#ifdef JS_ION
        JS_ASSERT(data_.jitFrames_.isScripted());
        if (data_.jitFrames_.isBaselineJS())
            return data_.jitFrames_.isFunctionFrame();
        return ionInlineFrames_.isFunctionFrame();
#else
        break;
#endif
      case ASMJS:
        return true;
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

bool
FrameIter::isGlobalFrame() const
{
    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
        return interpFrame()->isGlobalFrame();
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isBaselineJS())
            return data_.jitFrames_.baselineFrame()->isGlobalFrame();
        JS_ASSERT(!script()->isForEval());
        return !script()->functionNonDelazifying();
#else
        break;
#endif
      case ASMJS:
        return false;
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

bool
FrameIter::isEvalFrame() const
{
    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
        return interpFrame()->isEvalFrame();
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isBaselineJS())
            return data_.jitFrames_.baselineFrame()->isEvalFrame();
        JS_ASSERT(!script()->isForEval());
        return false;
#else
        break;
#endif
      case ASMJS:
        return false;
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

bool
FrameIter::isNonEvalFunctionFrame() const
{
    JS_ASSERT(!done());
    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
        return interpFrame()->isNonEvalFunctionFrame();
      case JIT:
        return !isEvalFrame() && isFunctionFrame();
      case ASMJS:
        return true;
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

bool
FrameIter::isGeneratorFrame() const
{
    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
        return interpFrame()->isGeneratorFrame();
      case JIT:
        return false;
      case ASMJS:
        return false;
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

JSAtom *
FrameIter::functionDisplayAtom() const
{
    JS_ASSERT(isNonEvalFunctionFrame());

    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
      case JIT:
        return callee()->displayAtom();
      case ASMJS: {
#ifdef JS_ION
        return data_.asmJSFrames_.functionDisplayAtom();
#else
        break;
#endif
      }
    }

    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

ScriptSource *
FrameIter::scriptSource() const
{
    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
      case JIT:
        return script()->scriptSource();
      case ASMJS:
#ifdef JS_ION
        return data_.activations_->asAsmJS()->module().scriptSource();
#else
        break;
#endif
    }

    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

const char *
FrameIter::scriptFilename() const
{
    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
      case JIT:
        return script()->filename();
      case ASMJS:
#ifdef JS_ION
        return data_.activations_->asAsmJS()->module().scriptSource()->filename();
#else
        break;
#endif
    }

    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

unsigned
FrameIter::computeLine(uint32_t *column) const
{
    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
      case JIT:
        return PCToLineNumber(script(), pc(), column);
      case ASMJS:
#ifdef JS_ION
        return data_.asmJSFrames_.computeLine(column);
#else
        break;
#endif
    }

    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

JSPrincipals *
FrameIter::originPrincipals() const
{
    switch (data_.state_) {
      case DONE:
        break;
      case INTERP:
      case JIT:
        return script()->originPrincipals();
      case ASMJS: {
#ifdef JS_ION
        return data_.activations_->asAsmJS()->module().scriptSource()->originPrincipals();
#else
        break;
#endif
      }
    }

    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

bool
FrameIter::isConstructing() const
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isIonJS())
            return ionInlineFrames_.isConstructing();
        JS_ASSERT(data_.jitFrames_.isBaselineJS());
        return data_.jitFrames_.isConstructing();
#else
        break;
#endif
      case INTERP:
        return interpFrame()->isConstructing();
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

bool
FrameIter::ensureHasRematerializedFrame(ThreadSafeContext *cx)
{
#ifdef JS_ION
    MOZ_ASSERT(isIon());
    return !!activation()->asJit()->getRematerializedFrame(cx, data_.jitFrames_);
#else
    return true;
#endif
}

bool
FrameIter::hasUsableAbstractFramePtr() const
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        return false;
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isBaselineJS())
            return true;

        MOZ_ASSERT(data_.jitFrames_.isIonJS());
        return !!activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
                                                                  ionInlineFrames_.frameNo());
#endif
        break;
      case INTERP:
        return true;
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

AbstractFramePtr
FrameIter::abstractFramePtr() const
{
    MOZ_ASSERT(hasUsableAbstractFramePtr());
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case JIT: {
#ifdef JS_ION
        if (data_.jitFrames_.isBaselineJS())
            return data_.jitFrames_.baselineFrame();

        MOZ_ASSERT(data_.jitFrames_.isIonJS());
        return activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
                                                                ionInlineFrames_.frameNo());
#endif
        break;
      }
      case INTERP:
        JS_ASSERT(interpFrame());
        return AbstractFramePtr(interpFrame());
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

void
FrameIter::updatePcQuadratic()
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case INTERP: {
        InterpreterFrame *frame = interpFrame();
        InterpreterActivation *activation = data_.activations_->asInterpreter();

        // Look for the current frame.
        data_.interpFrames_ = InterpreterFrameIterator(activation);
        while (data_.interpFrames_.frame() != frame)
            ++data_.interpFrames_;

        // Update the pc.
        JS_ASSERT(data_.interpFrames_.frame() == frame);
        data_.pc_ = data_.interpFrames_.pc();
        return;
      }
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isBaselineJS()) {
            jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame();
            jit::JitActivation *activation = data_.activations_->asJit();

            // ActivationIterator::jitTop_ may be invalid, so create a new
            // activation iterator.
            data_.activations_ = ActivationIterator(data_.cx_->perThreadData);
            while (data_.activations_.activation() != activation)
                ++data_.activations_;

            // Look for the current frame.
            data_.jitFrames_ = jit::JitFrameIterator(data_.activations_);
            while (!data_.jitFrames_.isBaselineJS() || data_.jitFrames_.baselineFrame() != frame)
                ++data_.jitFrames_;

            // Update the pc.
            JS_ASSERT(data_.jitFrames_.baselineFrame() == frame);
            data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
            return;
        }
#endif
        break;
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

JSFunction *
FrameIter::callee() const
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case INTERP:
        JS_ASSERT(isFunctionFrame());
        return &interpFrame()->callee();
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isBaselineJS())
            return data_.jitFrames_.callee();
        JS_ASSERT(data_.jitFrames_.isIonJS());
        return ionInlineFrames_.callee();
#else
        break;
#endif
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

Value
FrameIter::calleev() const
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case INTERP:
        JS_ASSERT(isFunctionFrame());
        return interpFrame()->calleev();
      case JIT:
#ifdef JS_ION
        return ObjectValue(*callee());
#else
        break;
#endif
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

unsigned
FrameIter::numActualArgs() const
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case INTERP:
        JS_ASSERT(isFunctionFrame());
        return interpFrame()->numActualArgs();
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isIonJS())
            return ionInlineFrames_.numActualArgs();

        JS_ASSERT(data_.jitFrames_.isBaselineJS());
        return data_.jitFrames_.numActualArgs();
#else
        break;
#endif
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

unsigned
FrameIter::numFormalArgs() const
{
    return script()->functionNonDelazifying()->nargs();
}

Value
FrameIter::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const
{
    return abstractFramePtr().unaliasedActual(i, checkAliasing);
}

JSObject *
FrameIter::scopeChain() const
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isIonJS())
            return ionInlineFrames_.scopeChain();
        return data_.jitFrames_.baselineFrame()->scopeChain();
#else
        break;
#endif
      case INTERP:
        return interpFrame()->scopeChain();
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

CallObject &
FrameIter::callObj() const
{
    JS_ASSERT(callee()->isHeavyweight());

    JSObject *pobj = scopeChain();
    while (!pobj->is<CallObject>())
        pobj = pobj->enclosingScope();
    return pobj->as<CallObject>();
}

bool
FrameIter::hasArgsObj() const
{
    return abstractFramePtr().hasArgsObj();
}

ArgumentsObject &
FrameIter::argsObj() const
{
    MOZ_ASSERT(hasArgsObj());
    return abstractFramePtr().argsObj();
}

bool
FrameIter::computeThis(JSContext *cx) const
{
    JS_ASSERT(!done() && !isAsmJS());
    assertSameCompartment(cx, scopeChain());
    return ComputeThis(cx, abstractFramePtr());
}

Value
FrameIter::computedThisValue() const
{
    return abstractFramePtr().thisValue();
}

Value
FrameIter::thisv() const
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isIonJS())
            return ionInlineFrames_.thisValue();
        return data_.jitFrames_.baselineFrame()->thisValue();
#else
        break;
#endif
      case INTERP:
        return interpFrame()->thisValue();
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

Value
FrameIter::returnValue() const
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isBaselineJS())
            return data_.jitFrames_.baselineFrame()->returnValue();
#endif
        break;
      case INTERP:
        return interpFrame()->returnValue();
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

void
FrameIter::setReturnValue(const Value &v)
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isBaselineJS()) {
            data_.jitFrames_.baselineFrame()->setReturnValue(v);
            return;
        }
#endif
        break;
      case INTERP:
        interpFrame()->setReturnValue(v);
        return;
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

size_t
FrameIter::numFrameSlots() const
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case JIT: {
#ifdef JS_ION
        if (data_.jitFrames_.isIonJS()) {
            return ionInlineFrames_.snapshotIterator().numAllocations() -
                ionInlineFrames_.script()->nfixed();
        }
        jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame();
        return frame->numValueSlots() - data_.jitFrames_.script()->nfixed();
#else
        break;
#endif
      }
      case INTERP:
        JS_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base());
        return data_.interpFrames_.sp() - interpFrame()->base();
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

Value
FrameIter::frameSlotValue(size_t index) const
{
    switch (data_.state_) {
      case DONE:
      case ASMJS:
        break;
      case JIT:
#ifdef JS_ION
        if (data_.jitFrames_.isIonJS()) {
            jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
            index += ionInlineFrames_.script()->nfixed();
            return si.maybeReadAllocByIndex(index);
        }

        index += data_.jitFrames_.script()->nfixed();
        return *data_.jitFrames_.baselineFrame()->valueSlot(index);
#else
        break;
#endif
      case INTERP:
          return interpFrame()->base()[index];
    }
    MOZ_ASSUME_UNREACHABLE("Unexpected state");
}

#if defined(_MSC_VER)
# pragma optimize("", on)
#endif

#ifdef DEBUG
bool
js::SelfHostedFramesVisible()
{
    static bool checked = false;
    static bool visible = false;
    if (!checked) {
        checked = true;
        char *env = getenv("MOZ_SHOW_ALL_JS_FRAMES");
        visible = !!env;
    }
    return visible;
}
#endif

void
NonBuiltinFrameIter::settle()
{
    if (!SelfHostedFramesVisible()) {
        while (!done() && hasScript() && script()->selfHosted())
            FrameIter::operator++();
    }
}

void
NonBuiltinScriptFrameIter::settle()
{
    if (!SelfHostedFramesVisible()) {
        while (!done() && script()->selfHosted())
            ScriptFrameIter::operator++();
    }
}

/*****************************************************************************/

JSObject *
AbstractFramePtr::evalPrevScopeChain(JSContext *cx) const
{
    // Eval frames are not compiled by Ion, though their caller might be.
    AllFramesIter iter(cx);
    while (iter.isIon() || iter.abstractFramePtr() != *this)
        ++iter;
    ++iter;
    return iter.scopeChain();
}

bool
AbstractFramePtr::hasPushedSPSFrame() const
{
    if (isInterpreterFrame())
        return asInterpreterFrame()->hasPushedSPSFrame();
#ifdef JS_ION
    return asBaselineFrame()->hasPushedSPSFrame();
#else
    MOZ_ASSUME_UNREACHABLE("Invalid frame");
#endif
}

#ifdef DEBUG
void
js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, uint32_t i)
{
    if (!checkAliasing)
        return;

    JS_ASSERT(i < script->nfixed());
    if (i < script->bindings.numVars()) {
        JS_ASSERT(!script->varIsAliased(i));
    } else {
        // FIXME: The callers of this function do not easily have the PC of the
        // current frame, and so they do not know the block scope.
    }
}
#endif

jit::JitActivation::JitActivation(JSContext *cx, bool firstFrameIsConstructing, bool active)
  : Activation(cx, Jit),
    firstFrameIsConstructing_(firstFrameIsConstructing),
    active_(active)
#ifdef JS_ION
  , rematerializedFrames_(nullptr)
#endif
{
    if (active) {
        prevJitTop_ = cx->mainThread().jitTop;
        prevJitJSContext_ = cx->mainThread().jitJSContext;
        cx->mainThread().jitJSContext = cx;
    } else {
        prevJitTop_ = nullptr;
        prevJitJSContext_ = nullptr;
    }
}

jit::JitActivation::JitActivation(ForkJoinContext *cx)
  : Activation(cx, Jit),
    firstFrameIsConstructing_(false),
    active_(true)
#ifdef JS_ION
  , rematerializedFrames_(nullptr)
#endif
{
    prevJitTop_ = cx->perThreadData->jitTop;
    prevJitJSContext_ = cx->perThreadData->jitJSContext;
    cx->perThreadData->jitJSContext = nullptr;
}

jit::JitActivation::~JitActivation()
{
    if (active_) {
        cx_->perThreadData->jitTop = prevJitTop_;
        cx_->perThreadData->jitJSContext = prevJitJSContext_;
    }

#ifdef JS_ION
    clearRematerializedFrames();
    js_delete(rematerializedFrames_);
#endif
}

// setActive() is inlined in GenerateFFIIonExit() with explicit masm instructions so
// changes to the logic here need to be reflected in GenerateFFIIonExit() in the enable
// and disable activation instruction sequences.
void
jit::JitActivation::setActive(JSContext *cx, bool active)
{
    // Only allowed to deactivate/activate if activation is top.
    // (Not tested and will probably fail in other situations.)
    JS_ASSERT(cx->mainThread().activation_ == this);
    JS_ASSERT(active != active_);
    active_ = active;

    if (active) {
        prevJitTop_ = cx->mainThread().jitTop;
        prevJitJSContext_ = cx->mainThread().jitJSContext;
        cx->mainThread().jitJSContext = cx;
    } else {
        cx->mainThread().jitTop = prevJitTop_;
        cx->mainThread().jitJSContext = prevJitJSContext_;
    }
}

#ifdef JS_ION

void
jit::JitActivation::freeRematerializedFramesInVector(RematerializedFrameVector &frames)
{
    for (size_t i = 0; i < frames.length(); i++) {
        RematerializedFrame *f = frames[i];
        f->RematerializedFrame::~RematerializedFrame();
        js_free(f);
    }
    frames.clear();
}

void
jit::JitActivation::removeRematerializedFrame(uint8_t *top)
{
    if (!rematerializedFrames_)
        return;

    if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
        freeRematerializedFramesInVector(p->value());
        rematerializedFrames_->remove(p);
    }
}

void
jit::JitActivation::clearRematerializedFrames()
{
    if (!rematerializedFrames_)
        return;

    for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront()) {
        freeRematerializedFramesInVector(e.front().value());
        e.removeFront();
    }
}

jit::RematerializedFrame *
jit::JitActivation::getRematerializedFrame(ThreadSafeContext *cx, JitFrameIterator &iter,
                                           size_t inlineDepth)
{
    // Only allow rematerializing from the same thread.
    MOZ_ASSERT(cx->perThreadData == cx_->perThreadData);
    MOZ_ASSERT(iter.activation() == this);
    MOZ_ASSERT(iter.isIonJS());

    if (!rematerializedFrames_) {
        rematerializedFrames_ = cx->new_<RematerializedFrameTable>(cx);
        if (!rematerializedFrames_ || !rematerializedFrames_->init()) {
            rematerializedFrames_ = nullptr;
            return nullptr;
        }
    }

    // The unit of rematerialization is an uninlined frame and its inlined
    // frames. Since inlined frames do not exist outside of snapshots, it is
    // impossible to synchronize their rematerialized copies to preserve
    // identity. Therefore, we always rematerialize an uninlined frame and all
    // its inlined frames at once.

    uint8_t *top = iter.fp();
    RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top);
    if (!p) {
        RematerializedFrameVector empty(cx);
        if (!rematerializedFrames_->add(p, top, Move(empty)))
            return nullptr;

        InlineFrameIterator inlineIter(cx, &iter);
        if (!p->value().resize(inlineIter.frameCount()))
            return nullptr;

        while (true) {
            size_t frameNo = inlineIter.frameNo();
            p->value()[frameNo] = RematerializedFrame::New(cx, top, inlineIter);
            if (!p->value()[frameNo])
                return nullptr;

            if (!inlineIter.more())
                break;
            ++inlineIter;
        }
    }

    return p->value()[inlineDepth];
}

jit::RematerializedFrame *
jit::JitActivation::lookupRematerializedFrame(uint8_t *top, size_t inlineDepth)
{
    if (!rematerializedFrames_)
        return nullptr;
    if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top))
        return inlineDepth < p->value().length() ? p->value()[inlineDepth] : nullptr;
    return nullptr;
}

void
jit::JitActivation::markRematerializedFrames(JSTracer *trc)
{
    if (!rematerializedFrames_)
        return;
    for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront()) {
        RematerializedFrameVector &frames = e.front().value();
        for (size_t i = 0; i < frames.length(); i++)
            frames[i]->mark(trc);
    }
}

#endif // JS_ION

AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
  : Activation(cx, AsmJS),
    module_(module),
    errorRejoinSP_(nullptr),
    profiler_(nullptr),
    resumePC_(nullptr),
    exitSP_(nullptr)
{
    if (cx->runtime()->spsProfiler.enabled()) {
        // Use a profiler string that matches jsMatch regex in
        // browser/devtools/profiler/cleopatra/js/parserWorker.js.
        // (For now use a single static string to avoid further slowing down
        // calls into asm.js.)
        profiler_ = &cx->runtime()->spsProfiler;
        profiler_->enterNative("asm.js code :0", this);
    }

    prevAsmJS_ = cx->mainThread().asmJSActivationStack_;

    JSRuntime::AutoLockForInterrupt lock(cx->runtime());
    cx->mainThread().asmJSActivationStack_ = this;

    (void) errorRejoinSP_;  // squelch GCC warning
}

AsmJSActivation::~AsmJSActivation()
{
    if (profiler_)
        profiler_->exitNative();

    JSContext *cx = cx_->asJSContext();
    JS_ASSERT(cx->mainThread().asmJSActivationStack_ == this);

    JSRuntime::AutoLockForInterrupt lock(cx->runtime());
    cx->mainThread().asmJSActivationStack_ = prevAsmJS_;
}

InterpreterFrameIterator &
InterpreterFrameIterator::operator++()
{
    JS_ASSERT(!done());
    if (fp_ != activation_->entryFrame_) {
        pc_ = fp_->prevpc();
        sp_ = fp_->prevsp();
        fp_ = fp_->prev();
    } else {
        pc_ = nullptr;
        sp_ = nullptr;
        fp_ = nullptr;
    }
    return *this;
}

ActivationIterator::ActivationIterator(JSRuntime *rt)
  : jitTop_(rt->mainThread.jitTop),
    activation_(rt->mainThread.activation_)
{
    settle();
}

ActivationIterator::ActivationIterator(PerThreadData *perThreadData)
  : jitTop_(perThreadData->jitTop),
    activation_(perThreadData->activation_)
{
    settle();
}

ActivationIterator &
ActivationIterator::operator++()
{
    JS_ASSERT(activation_);
    if (activation_->isJit() && activation_->asJit()->isActive())
        jitTop_ = activation_->asJit()->prevJitTop();
    activation_ = activation_->prev();
    settle();
    return *this;
}

void
ActivationIterator::settle()
{
    // Stop at the next active activation. No need to update jitTop_, since
    // we don't iterate over an active jit activation.
    while (!done() && activation_->isJit() && !activation_->asJit()->isActive())
        activation_ = activation_->prev();
}
