michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/Stack-inl.h" michael@0: michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include "jscntxt.h" michael@0: michael@0: #include "gc/Marking.h" michael@0: #ifdef JS_ION michael@0: #include "jit/AsmJSModule.h" michael@0: #include "jit/BaselineFrame.h" michael@0: #include "jit/JitCompartment.h" michael@0: #endif michael@0: #include "js/GCAPI.h" michael@0: #include "vm/Opcodes.h" michael@0: michael@0: #include "jit/JitFrameIterator-inl.h" michael@0: #include "vm/Interpreter-inl.h" michael@0: #include "vm/Probes-inl.h" michael@0: #include "vm/ScopeObject-inl.h" michael@0: michael@0: using namespace js; michael@0: michael@0: using mozilla::PodCopy; michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: void michael@0: InterpreterFrame::initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr evalInFramePrev, michael@0: const Value &thisv, JSObject &scopeChain, ExecuteType type) michael@0: { michael@0: /* michael@0: * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a michael@0: * script in the context of another frame and the frame type is determined michael@0: * by the context. michael@0: */ michael@0: flags_ = type | HAS_SCOPECHAIN; michael@0: michael@0: JSObject *callee = nullptr; michael@0: if (!(flags_ & (GLOBAL))) { michael@0: if (evalInFramePrev) { michael@0: JS_ASSERT(evalInFramePrev.isFunctionFrame() || evalInFramePrev.isGlobalFrame()); michael@0: if (evalInFramePrev.isFunctionFrame()) { michael@0: callee = evalInFramePrev.callee(); michael@0: flags_ |= FUNCTION; michael@0: } else { michael@0: flags_ |= GLOBAL; michael@0: } michael@0: } else { michael@0: FrameIter iter(cx); michael@0: JS_ASSERT(iter.isFunctionFrame() || iter.isGlobalFrame()); michael@0: JS_ASSERT(!iter.isAsmJS()); michael@0: if (iter.isFunctionFrame()) { michael@0: callee = iter.callee(); michael@0: flags_ |= FUNCTION; michael@0: } else { michael@0: flags_ |= GLOBAL; michael@0: } michael@0: } michael@0: } michael@0: michael@0: Value *dstvp = (Value *)this - 2; michael@0: dstvp[1] = thisv; michael@0: michael@0: if (isFunctionFrame()) { michael@0: dstvp[0] = ObjectValue(*callee); michael@0: exec.fun = &callee->as(); michael@0: u.evalScript = script; michael@0: } else { michael@0: JS_ASSERT(isGlobalFrame()); michael@0: dstvp[0] = NullValue(); michael@0: exec.script = script; michael@0: #ifdef DEBUG michael@0: u.evalScript = (JSScript *)0xbad; michael@0: #endif michael@0: } michael@0: michael@0: scopeChain_ = &scopeChain; michael@0: prev_ = nullptr; michael@0: prevpc_ = nullptr; michael@0: prevsp_ = nullptr; michael@0: michael@0: JS_ASSERT_IF(evalInFramePrev, isDebuggerFrame()); michael@0: evalInFramePrev_ = evalInFramePrev; michael@0: michael@0: #ifdef DEBUG michael@0: Debug_SetValueRangeToCrashOnTouch(&rval_, 1); michael@0: hookData_ = (void *)0xbad; michael@0: #endif michael@0: } michael@0: michael@0: template michael@0: void michael@0: InterpreterFrame::copyFrameAndValues(JSContext *cx, Value *vp, InterpreterFrame *otherfp, michael@0: const Value *othervp, Value *othersp) michael@0: { michael@0: JS_ASSERT(othervp == otherfp->generatorArgsSnapshotBegin()); michael@0: JS_ASSERT(othersp >= otherfp->slots()); michael@0: JS_ASSERT(othersp <= otherfp->generatorSlotsSnapshotBegin() + otherfp->script()->nslots()); michael@0: michael@0: /* Copy args, InterpreterFrame, and slots. */ michael@0: const Value *srcend = otherfp->generatorArgsSnapshotEnd(); michael@0: Value *dst = vp; michael@0: for (const Value *src = othervp; src < srcend; src++, dst++) { michael@0: *dst = *src; michael@0: if (doPostBarrier) michael@0: HeapValue::writeBarrierPost(*dst, dst); michael@0: } michael@0: michael@0: *this = *otherfp; michael@0: argv_ = vp + 2; michael@0: unsetPushedSPSFrame(); michael@0: if (doPostBarrier) michael@0: writeBarrierPost(); michael@0: michael@0: srcend = othersp; michael@0: dst = slots(); michael@0: for (const Value *src = otherfp->slots(); src < srcend; src++, dst++) { michael@0: *dst = *src; michael@0: if (doPostBarrier) michael@0: HeapValue::writeBarrierPost(*dst, dst); michael@0: } michael@0: } michael@0: michael@0: /* Note: explicit instantiation for js_NewGenerator located in jsiter.cpp. */ michael@0: template michael@0: void InterpreterFrame::copyFrameAndValues( michael@0: JSContext *, Value *, InterpreterFrame *, const Value *, Value *); michael@0: template michael@0: void InterpreterFrame::copyFrameAndValues( michael@0: JSContext *, Value *, InterpreterFrame *, const Value *, Value *); michael@0: michael@0: void michael@0: InterpreterFrame::writeBarrierPost() michael@0: { michael@0: /* This needs to follow the same rules as in InterpreterFrame::mark. */ michael@0: if (scopeChain_) michael@0: JSObject::writeBarrierPost(scopeChain_, (void *)&scopeChain_); michael@0: if (flags_ & HAS_ARGS_OBJ) michael@0: JSObject::writeBarrierPost(argsObj_, (void *)&argsObj_); michael@0: if (isFunctionFrame()) { michael@0: JSFunction::writeBarrierPost(exec.fun, (void *)&exec.fun); michael@0: if (isEvalFrame()) michael@0: JSScript::writeBarrierPost(u.evalScript, (void *)&u.evalScript); michael@0: } else { michael@0: JSScript::writeBarrierPost(exec.script, (void *)&exec.script); michael@0: } michael@0: if (hasReturnValue()) michael@0: HeapValue::writeBarrierPost(rval_, &rval_); michael@0: } michael@0: michael@0: bool michael@0: InterpreterFrame::copyRawFrameSlots(AutoValueVector *vec) michael@0: { michael@0: if (!vec->resize(numFormalArgs() + script()->nfixed())) michael@0: return false; michael@0: PodCopy(vec->begin(), argv(), numFormalArgs()); michael@0: PodCopy(vec->begin() + numFormalArgs(), slots(), script()->nfixed()); michael@0: return true; michael@0: } michael@0: michael@0: JSObject * michael@0: InterpreterFrame::createRestParameter(JSContext *cx) michael@0: { michael@0: JS_ASSERT(fun()->hasRest()); michael@0: unsigned nformal = fun()->nargs() - 1, nactual = numActualArgs(); michael@0: unsigned nrest = (nactual > nformal) ? nactual - nformal : 0; michael@0: Value *restvp = argv() + nformal; michael@0: JSObject *obj = NewDenseCopiedArray(cx, nrest, restvp, nullptr); michael@0: if (!obj) michael@0: return nullptr; michael@0: types::FixRestArgumentsType(cx, obj); michael@0: return obj; michael@0: } michael@0: michael@0: static inline void michael@0: AssertDynamicScopeMatchesStaticScope(JSContext *cx, JSScript *script, JSObject *scope) michael@0: { michael@0: #ifdef DEBUG michael@0: RootedObject enclosingScope(cx, script->enclosingStaticScope()); michael@0: for (StaticScopeIter i(enclosingScope); !i.done(); i++) { michael@0: if (i.hasDynamicScopeObject()) { michael@0: switch (i.type()) { michael@0: case StaticScopeIter::BLOCK: michael@0: JS_ASSERT(&i.block() == scope->as().staticScope()); michael@0: scope = &scope->as().enclosingScope(); michael@0: break; michael@0: case StaticScopeIter::WITH: michael@0: JS_ASSERT(&i.staticWith() == scope->as().staticScope()); michael@0: scope = &scope->as().enclosingScope(); michael@0: break; michael@0: case StaticScopeIter::FUNCTION: michael@0: JS_ASSERT(scope->as().callee().nonLazyScript() == i.funScript()); michael@0: scope = &scope->as().enclosingScope(); michael@0: break; michael@0: case StaticScopeIter::NAMED_LAMBDA: michael@0: scope = &scope->as().enclosingScope(); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Ideally, we'd JS_ASSERT(!scope->is()) but the enclosing michael@0: * lexical scope chain stops at eval() boundaries. See StaticScopeIter michael@0: * comment. michael@0: */ michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: InterpreterFrame::initFunctionScopeObjects(JSContext *cx) michael@0: { michael@0: CallObject *callobj = CallObject::createForFunction(cx, this); michael@0: if (!callobj) michael@0: return false; michael@0: pushOnScopeChain(*callobj); michael@0: flags_ |= HAS_CALL_OBJ; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: InterpreterFrame::prologue(JSContext *cx) michael@0: { michael@0: RootedScript script(cx, this->script()); michael@0: michael@0: JS_ASSERT(!isGeneratorFrame()); michael@0: JS_ASSERT(cx->interpreterRegs().pc == script->code()); michael@0: michael@0: if (isEvalFrame()) { michael@0: if (script->strict()) { michael@0: CallObject *callobj = CallObject::createForStrictEval(cx, this); michael@0: if (!callobj) michael@0: return false; michael@0: pushOnScopeChain(*callobj); michael@0: flags_ |= HAS_CALL_OBJ; michael@0: } michael@0: return probes::EnterScript(cx, script, nullptr, this); michael@0: } michael@0: michael@0: if (isGlobalFrame()) michael@0: return probes::EnterScript(cx, script, nullptr, this); michael@0: michael@0: JS_ASSERT(isNonEvalFunctionFrame()); michael@0: AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain()); michael@0: michael@0: if (fun()->isHeavyweight() && !initFunctionScopeObjects(cx)) michael@0: return false; michael@0: michael@0: if (isConstructing()) { michael@0: RootedObject callee(cx, &this->callee()); michael@0: JSObject *obj = CreateThisForFunction(cx, callee, michael@0: useNewType() ? SingletonObject : GenericObject); michael@0: if (!obj) michael@0: return false; michael@0: functionThis() = ObjectValue(*obj); michael@0: } michael@0: michael@0: return probes::EnterScript(cx, script, script->functionNonDelazifying(), this); michael@0: } michael@0: michael@0: void michael@0: InterpreterFrame::epilogue(JSContext *cx) michael@0: { michael@0: JS_ASSERT(!isYielding()); michael@0: michael@0: RootedScript script(cx, this->script()); michael@0: probes::ExitScript(cx, script, script->functionNonDelazifying(), hasPushedSPSFrame()); michael@0: michael@0: if (isEvalFrame()) { michael@0: if (isStrictEvalFrame()) { michael@0: JS_ASSERT_IF(hasCallObj(), scopeChain()->as().isForEval()); michael@0: if (MOZ_UNLIKELY(cx->compartment()->debugMode())) michael@0: DebugScopes::onPopStrictEvalScope(this); michael@0: } else if (isDirectEvalFrame()) { michael@0: if (isDebuggerFrame()) michael@0: JS_ASSERT(!scopeChain()->is()); michael@0: } else { michael@0: /* michael@0: * Debugger.Object.prototype.evalInGlobal creates indirect eval michael@0: * frames scoped to the given global; michael@0: * Debugger.Object.prototype.evalInGlobalWithBindings creates michael@0: * indirect eval frames scoped to an object carrying the introduced michael@0: * bindings. michael@0: */ michael@0: if (isDebuggerFrame()) { michael@0: JS_ASSERT(scopeChain()->is() || michael@0: scopeChain()->enclosingScope()->is()); michael@0: } else { michael@0: JS_ASSERT(scopeChain()->is()); michael@0: } michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (isGlobalFrame()) { michael@0: JS_ASSERT(!scopeChain()->is()); michael@0: return; michael@0: } michael@0: michael@0: JS_ASSERT(isNonEvalFunctionFrame()); michael@0: michael@0: if (fun()->isHeavyweight()) michael@0: JS_ASSERT_IF(hasCallObj(), michael@0: scopeChain()->as().callee().nonLazyScript() == script); michael@0: else michael@0: AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain()); michael@0: michael@0: if (MOZ_UNLIKELY(cx->compartment()->debugMode())) michael@0: DebugScopes::onPopCall(this, cx); michael@0: michael@0: if (isConstructing() && thisValue().isObject() && returnValue().isPrimitive()) michael@0: setReturnValue(ObjectValue(constructorThis())); michael@0: } michael@0: michael@0: bool michael@0: InterpreterFrame::pushBlock(JSContext *cx, StaticBlockObject &block) michael@0: { michael@0: JS_ASSERT (block.needsClone()); michael@0: michael@0: Rooted blockHandle(cx, &block); michael@0: ClonedBlockObject *clone = ClonedBlockObject::create(cx, blockHandle, this); michael@0: if (!clone) michael@0: return false; michael@0: michael@0: pushOnScopeChain(*clone); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: InterpreterFrame::popBlock(JSContext *cx) michael@0: { michael@0: JS_ASSERT(scopeChain_->is()); michael@0: popOffScopeChain(); michael@0: } michael@0: michael@0: void michael@0: InterpreterFrame::popWith(JSContext *cx) michael@0: { michael@0: if (MOZ_UNLIKELY(cx->compartment()->debugMode())) michael@0: DebugScopes::onPopWith(this); michael@0: michael@0: JS_ASSERT(scopeChain()->is()); michael@0: popOffScopeChain(); michael@0: } michael@0: michael@0: void michael@0: InterpreterFrame::mark(JSTracer *trc) michael@0: { michael@0: /* michael@0: * Normally we would use MarkRoot here, except that generators also take michael@0: * this path. However, generators use a special write barrier when the stack michael@0: * frame is copied to the floating frame. Therefore, no barrier is needed. michael@0: */ michael@0: if (flags_ & HAS_SCOPECHAIN) michael@0: gc::MarkObjectUnbarriered(trc, &scopeChain_, "scope chain"); michael@0: if (flags_ & HAS_ARGS_OBJ) michael@0: gc::MarkObjectUnbarriered(trc, &argsObj_, "arguments"); michael@0: if (isFunctionFrame()) { michael@0: gc::MarkObjectUnbarriered(trc, &exec.fun, "fun"); michael@0: if (isEvalFrame()) michael@0: gc::MarkScriptUnbarriered(trc, &u.evalScript, "eval script"); michael@0: } else { michael@0: gc::MarkScriptUnbarriered(trc, &exec.script, "script"); michael@0: } michael@0: if (IS_GC_MARKING_TRACER(trc)) michael@0: script()->compartment()->zone()->active = true; michael@0: gc::MarkValueUnbarriered(trc, returnValue().address(), "rval"); michael@0: } michael@0: michael@0: void michael@0: InterpreterFrame::markValues(JSTracer *trc, unsigned start, unsigned end) michael@0: { michael@0: if (start < end) michael@0: gc::MarkValueRootRange(trc, end - start, slots() + start, "vm_stack"); michael@0: } michael@0: michael@0: void michael@0: InterpreterFrame::markValues(JSTracer *trc, Value *sp, jsbytecode *pc) michael@0: { michael@0: JS_ASSERT(sp >= slots()); michael@0: michael@0: NestedScopeObject *staticScope; michael@0: michael@0: staticScope = script()->getStaticScope(pc); michael@0: while (staticScope && !staticScope->is()) michael@0: staticScope = staticScope->enclosingNestedScope(); michael@0: michael@0: size_t nfixed = script()->nfixed(); michael@0: size_t nlivefixed; michael@0: michael@0: if (staticScope) { michael@0: StaticBlockObject &blockObj = staticScope->as(); michael@0: nlivefixed = blockObj.localOffset() + blockObj.numVariables(); michael@0: } else { michael@0: nlivefixed = script()->nfixedvars(); michael@0: } michael@0: michael@0: if (nfixed == nlivefixed) { michael@0: // All locals are live. michael@0: markValues(trc, 0, sp - slots()); michael@0: } else { michael@0: // Mark operand stack. michael@0: markValues(trc, nfixed, sp - slots()); michael@0: michael@0: // Clear dead locals. michael@0: while (nfixed > nlivefixed) michael@0: unaliasedLocal(--nfixed, DONT_CHECK_ALIASING).setUndefined(); michael@0: michael@0: // Mark live locals. michael@0: markValues(trc, 0, nlivefixed); michael@0: } michael@0: michael@0: if (hasArgs()) { michael@0: // Mark callee, |this| and arguments. michael@0: unsigned argc = Max(numActualArgs(), numFormalArgs()); michael@0: gc::MarkValueRootRange(trc, argc + 2, argv_ - 2, "fp argv"); michael@0: } else { michael@0: // Mark callee and |this| michael@0: gc::MarkValueRootRange(trc, 2, ((Value *)this) - 2, "stack callee and this"); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: MarkInterpreterActivation(JSTracer *trc, InterpreterActivation *act) michael@0: { michael@0: for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) { michael@0: InterpreterFrame *fp = frames.frame(); michael@0: fp->markValues(trc, frames.sp(), frames.pc()); michael@0: fp->mark(trc); michael@0: } michael@0: } michael@0: michael@0: void michael@0: js::MarkInterpreterActivations(JSRuntime *rt, JSTracer *trc) michael@0: { michael@0: for (ActivationIterator iter(rt); !iter.done(); ++iter) { michael@0: Activation *act = iter.activation(); michael@0: if (act->isInterpreter()) michael@0: MarkInterpreterActivation(trc, act->asInterpreter()); michael@0: } michael@0: michael@0: } michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: // Unlike the other methods of this calss, this method is defined here so that michael@0: // we don't have to #include jsautooplen.h in vm/Stack.h. michael@0: void michael@0: InterpreterRegs::setToEndOfScript() michael@0: { michael@0: JSScript *script = fp()->script(); michael@0: sp = fp()->base(); michael@0: pc = script->codeEnd() - JSOP_RETRVAL_LENGTH; michael@0: JS_ASSERT(*pc == JSOP_RETRVAL); michael@0: } michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: InterpreterFrame * michael@0: InterpreterStack::pushInvokeFrame(JSContext *cx, const CallArgs &args, InitialFrameFlags initial) michael@0: { michael@0: LifoAlloc::Mark mark = allocator_.mark(); michael@0: michael@0: RootedFunction fun(cx, &args.callee().as()); michael@0: RootedScript script(cx, fun->nonLazyScript()); michael@0: michael@0: InterpreterFrame::Flags flags = ToFrameFlags(initial); michael@0: Value *argv; michael@0: InterpreterFrame *fp = getCallFrame(cx, args, script, &flags, &argv); michael@0: if (!fp) michael@0: return nullptr; michael@0: michael@0: fp->mark_ = mark; michael@0: fp->initCallFrame(cx, nullptr, nullptr, nullptr, *fun, script, argv, args.length(), flags); michael@0: return fp; michael@0: } michael@0: michael@0: InterpreterFrame * michael@0: InterpreterStack::pushExecuteFrame(JSContext *cx, HandleScript script, const Value &thisv, michael@0: HandleObject scopeChain, ExecuteType type, michael@0: AbstractFramePtr evalInFrame) michael@0: { michael@0: LifoAlloc::Mark mark = allocator_.mark(); michael@0: michael@0: unsigned nvars = 2 /* callee, this */ + script->nslots(); michael@0: uint8_t *buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value)); michael@0: if (!buffer) michael@0: return nullptr; michael@0: michael@0: InterpreterFrame *fp = reinterpret_cast(buffer + 2 * sizeof(Value)); michael@0: fp->mark_ = mark; michael@0: fp->initExecuteFrame(cx, script, evalInFrame, thisv, *scopeChain, type); michael@0: fp->initVarsToUndefined(); michael@0: michael@0: return fp; michael@0: } michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: /* MSVC PGO causes xpcshell startup crashes. */ michael@0: #if defined(_MSC_VER) michael@0: # pragma optimize("g", off) michael@0: #endif michael@0: michael@0: void michael@0: FrameIter::popActivation() michael@0: { michael@0: ++data_.activations_; michael@0: settleOnActivation(); michael@0: } michael@0: michael@0: void michael@0: FrameIter::popInterpreterFrame() michael@0: { michael@0: JS_ASSERT(data_.state_ == INTERP); michael@0: michael@0: ++data_.interpFrames_; michael@0: michael@0: if (data_.interpFrames_.done()) michael@0: popActivation(); michael@0: else michael@0: data_.pc_ = data_.interpFrames_.pc(); michael@0: } michael@0: michael@0: void michael@0: FrameIter::settleOnActivation() michael@0: { michael@0: while (true) { michael@0: if (data_.activations_.done()) { michael@0: data_.state_ = DONE; michael@0: return; michael@0: } michael@0: michael@0: Activation *activation = data_.activations_.activation(); michael@0: michael@0: // If JS_SaveFrameChain was called, stop iterating here (unless michael@0: // GO_THROUGH_SAVED is set). michael@0: if (data_.savedOption_ == STOP_AT_SAVED && activation->hasSavedFrameChain()) { michael@0: data_.state_ = DONE; michael@0: return; michael@0: } michael@0: michael@0: // Skip activations from another context if needed. michael@0: JS_ASSERT(activation->cx()); michael@0: JS_ASSERT(data_.cx_); michael@0: if (data_.contextOption_ == CURRENT_CONTEXT && activation->cx() != data_.cx_) { michael@0: ++data_.activations_; michael@0: continue; michael@0: } michael@0: michael@0: // If the caller supplied principals, only show activations which are subsumed (of the same michael@0: // origin or of an origin accessible) by these principals. michael@0: if (data_.principals_) { michael@0: if (JSSubsumesOp subsumes = data_.cx_->runtime()->securityCallbacks->subsumes) { michael@0: JS::AutoAssertNoGC nogc; michael@0: if (!subsumes(data_.principals_, activation->compartment()->principals)) { michael@0: ++data_.activations_; michael@0: continue; michael@0: } michael@0: } michael@0: } michael@0: michael@0: #ifdef JS_ION michael@0: if (activation->isJit()) { michael@0: data_.jitFrames_ = jit::JitFrameIterator(data_.activations_); michael@0: michael@0: // Stop at the first scripted frame. michael@0: while (!data_.jitFrames_.isScripted() && !data_.jitFrames_.done()) michael@0: ++data_.jitFrames_; michael@0: michael@0: // It's possible to have an JitActivation with no scripted frames, michael@0: // for instance if we hit an over-recursion during bailout. michael@0: if (data_.jitFrames_.done()) { michael@0: ++data_.activations_; michael@0: continue; michael@0: } michael@0: michael@0: nextJitFrame(); michael@0: data_.state_ = JIT; michael@0: return; michael@0: } michael@0: michael@0: if (activation->isAsmJS()) { michael@0: data_.asmJSFrames_ = AsmJSFrameIterator(data_.activations_->asAsmJS()); michael@0: michael@0: if (data_.asmJSFrames_.done()) { michael@0: ++data_.activations_; michael@0: continue; michael@0: } michael@0: michael@0: data_.state_ = ASMJS; michael@0: return; michael@0: } michael@0: michael@0: // ForkJoin activations don't contain iterable frames, so skip them. michael@0: if (activation->isForkJoin()) { michael@0: ++data_.activations_; michael@0: continue; michael@0: } michael@0: #endif michael@0: michael@0: JS_ASSERT(activation->isInterpreter()); michael@0: michael@0: InterpreterActivation *interpAct = activation->asInterpreter(); michael@0: data_.interpFrames_ = InterpreterFrameIterator(interpAct); michael@0: michael@0: // If we OSR'ed into JIT code, skip the interpreter frame so that michael@0: // the same frame is not reported twice. michael@0: if (data_.interpFrames_.frame()->runningInJit()) { michael@0: ++data_.interpFrames_; michael@0: if (data_.interpFrames_.done()) { michael@0: ++data_.activations_; michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: JS_ASSERT(!data_.interpFrames_.frame()->runningInJit()); michael@0: data_.pc_ = data_.interpFrames_.pc(); michael@0: data_.state_ = INTERP; michael@0: return; michael@0: } michael@0: } michael@0: michael@0: FrameIter::Data::Data(JSContext *cx, SavedOption savedOption, ContextOption contextOption, michael@0: JSPrincipals *principals) michael@0: : cx_(cx), michael@0: savedOption_(savedOption), michael@0: contextOption_(contextOption), michael@0: principals_(principals), michael@0: pc_(nullptr), michael@0: interpFrames_(nullptr), michael@0: activations_(cx->runtime()) michael@0: #ifdef JS_ION michael@0: , jitFrames_((uint8_t *)nullptr, SequentialExecution) michael@0: , ionInlineFrameNo_(0) michael@0: , asmJSFrames_(nullptr) michael@0: #endif michael@0: { michael@0: } michael@0: michael@0: FrameIter::Data::Data(const FrameIter::Data &other) michael@0: : cx_(other.cx_), michael@0: savedOption_(other.savedOption_), michael@0: contextOption_(other.contextOption_), michael@0: principals_(other.principals_), michael@0: state_(other.state_), michael@0: pc_(other.pc_), michael@0: interpFrames_(other.interpFrames_), michael@0: activations_(other.activations_) michael@0: #ifdef JS_ION michael@0: , jitFrames_(other.jitFrames_) michael@0: , ionInlineFrameNo_(other.ionInlineFrameNo_) michael@0: , asmJSFrames_(other.asmJSFrames_) michael@0: #endif michael@0: { michael@0: } michael@0: michael@0: FrameIter::FrameIter(JSContext *cx, SavedOption savedOption) michael@0: : data_(cx, savedOption, CURRENT_CONTEXT, nullptr) michael@0: #ifdef JS_ION michael@0: , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr) michael@0: #endif michael@0: { michael@0: // settleOnActivation can only GC if principals are given. michael@0: JS::AutoAssertNoGC nogc; michael@0: settleOnActivation(); michael@0: } michael@0: michael@0: FrameIter::FrameIter(JSContext *cx, ContextOption contextOption, michael@0: SavedOption savedOption, JSPrincipals *principals) michael@0: : data_(cx, savedOption, contextOption, principals) michael@0: #ifdef JS_ION michael@0: , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr) michael@0: #endif michael@0: { michael@0: settleOnActivation(); michael@0: } michael@0: michael@0: FrameIter::FrameIter(const FrameIter &other) michael@0: : data_(other.data_) michael@0: #ifdef JS_ION michael@0: , ionInlineFrames_(other.data_.cx_, michael@0: data_.jitFrames_.isIonJS() ? &other.ionInlineFrames_ : nullptr) michael@0: #endif michael@0: { michael@0: } michael@0: michael@0: FrameIter::FrameIter(const Data &data) michael@0: : data_(data) michael@0: #ifdef JS_ION michael@0: , ionInlineFrames_(data.cx_, data_.jitFrames_.isIonJS() ? &data_.jitFrames_ : nullptr) michael@0: #endif michael@0: { michael@0: JS_ASSERT(data.cx_); michael@0: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isIonJS()) { michael@0: while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_) michael@0: ++ionInlineFrames_; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: #ifdef JS_ION michael@0: void michael@0: FrameIter::nextJitFrame() michael@0: { michael@0: if (data_.jitFrames_.isIonJS()) { michael@0: ionInlineFrames_.resetOn(&data_.jitFrames_); michael@0: data_.pc_ = ionInlineFrames_.pc(); michael@0: } else { michael@0: JS_ASSERT(data_.jitFrames_.isBaselineJS()); michael@0: data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_); michael@0: } michael@0: } michael@0: michael@0: void michael@0: FrameIter::popJitFrame() michael@0: { michael@0: JS_ASSERT(data_.state_ == JIT); michael@0: michael@0: if (data_.jitFrames_.isIonJS() && ionInlineFrames_.more()) { michael@0: ++ionInlineFrames_; michael@0: data_.pc_ = ionInlineFrames_.pc(); michael@0: return; michael@0: } michael@0: michael@0: ++data_.jitFrames_; michael@0: while (!data_.jitFrames_.done() && !data_.jitFrames_.isScripted()) michael@0: ++data_.jitFrames_; michael@0: michael@0: if (!data_.jitFrames_.done()) { michael@0: nextJitFrame(); michael@0: return; michael@0: } michael@0: michael@0: popActivation(); michael@0: } michael@0: michael@0: void michael@0: FrameIter::popAsmJSFrame() michael@0: { michael@0: JS_ASSERT(data_.state_ == ASMJS); michael@0: michael@0: ++data_.asmJSFrames_; michael@0: if (data_.asmJSFrames_.done()) michael@0: popActivation(); michael@0: } michael@0: #endif michael@0: michael@0: FrameIter & michael@0: FrameIter::operator++() michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: case INTERP: michael@0: if (interpFrame()->isDebuggerFrame() && interpFrame()->evalInFramePrev()) { michael@0: AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev(); michael@0: MOZ_ASSERT(!eifPrev.isRematerializedFrame()); michael@0: michael@0: // Eval-in-frame can cross contexts and works across saved frame michael@0: // chains. michael@0: ContextOption prevContextOption = data_.contextOption_; michael@0: SavedOption prevSavedOption = data_.savedOption_; michael@0: data_.contextOption_ = ALL_CONTEXTS; michael@0: data_.savedOption_ = GO_THROUGH_SAVED; michael@0: michael@0: popInterpreterFrame(); michael@0: michael@0: while (isIon() || abstractFramePtr() != eifPrev) { michael@0: if (data_.state_ == JIT) { michael@0: #ifdef JS_ION michael@0: popJitFrame(); michael@0: #else michael@0: MOZ_ASSUME_UNREACHABLE("Invalid state"); michael@0: #endif michael@0: } else { michael@0: popInterpreterFrame(); michael@0: } michael@0: } michael@0: michael@0: data_.contextOption_ = prevContextOption; michael@0: data_.savedOption_ = prevSavedOption; michael@0: data_.cx_ = data_.activations_->cx(); michael@0: break; michael@0: } michael@0: popInterpreterFrame(); michael@0: break; michael@0: #ifdef JS_ION michael@0: case JIT: michael@0: popJitFrame(); michael@0: break; michael@0: case ASMJS: michael@0: popAsmJSFrame(); michael@0: break; michael@0: #else michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: #endif michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: FrameIter::Data * michael@0: FrameIter::copyData() const michael@0: { michael@0: Data *data = data_.cx_->new_(data_); michael@0: #ifdef JS_ION michael@0: JS_ASSERT(data_.state_ != ASMJS); michael@0: if (data && data_.jitFrames_.isIonJS()) michael@0: data->ionInlineFrameNo_ = ionInlineFrames_.frameNo(); michael@0: #endif michael@0: return data; michael@0: } michael@0: michael@0: AbstractFramePtr michael@0: FrameIter::copyDataAsAbstractFramePtr() const michael@0: { michael@0: AbstractFramePtr frame; michael@0: if (Data *data = copyData()) michael@0: frame.ptr_ = uintptr_t(data); michael@0: return frame; michael@0: } michael@0: michael@0: JSCompartment * michael@0: FrameIter::compartment() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: case JIT: michael@0: case ASMJS: michael@0: return data_.activations_->compartment(); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: bool michael@0: FrameIter::isFunctionFrame() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: return interpFrame()->isFunctionFrame(); michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: JS_ASSERT(data_.jitFrames_.isScripted()); michael@0: if (data_.jitFrames_.isBaselineJS()) michael@0: return data_.jitFrames_.isFunctionFrame(); michael@0: return ionInlineFrames_.isFunctionFrame(); michael@0: #else michael@0: break; michael@0: #endif michael@0: case ASMJS: michael@0: return true; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: bool michael@0: FrameIter::isGlobalFrame() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: return interpFrame()->isGlobalFrame(); michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isBaselineJS()) michael@0: return data_.jitFrames_.baselineFrame()->isGlobalFrame(); michael@0: JS_ASSERT(!script()->isForEval()); michael@0: return !script()->functionNonDelazifying(); michael@0: #else michael@0: break; michael@0: #endif michael@0: case ASMJS: michael@0: return false; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: bool michael@0: FrameIter::isEvalFrame() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: return interpFrame()->isEvalFrame(); michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isBaselineJS()) michael@0: return data_.jitFrames_.baselineFrame()->isEvalFrame(); michael@0: JS_ASSERT(!script()->isForEval()); michael@0: return false; michael@0: #else michael@0: break; michael@0: #endif michael@0: case ASMJS: michael@0: return false; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: bool michael@0: FrameIter::isNonEvalFunctionFrame() const michael@0: { michael@0: JS_ASSERT(!done()); michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: return interpFrame()->isNonEvalFunctionFrame(); michael@0: case JIT: michael@0: return !isEvalFrame() && isFunctionFrame(); michael@0: case ASMJS: michael@0: return true; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: bool michael@0: FrameIter::isGeneratorFrame() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: return interpFrame()->isGeneratorFrame(); michael@0: case JIT: michael@0: return false; michael@0: case ASMJS: michael@0: return false; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: JSAtom * michael@0: FrameIter::functionDisplayAtom() const michael@0: { michael@0: JS_ASSERT(isNonEvalFunctionFrame()); michael@0: michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: case JIT: michael@0: return callee()->displayAtom(); michael@0: case ASMJS: { michael@0: #ifdef JS_ION michael@0: return data_.asmJSFrames_.functionDisplayAtom(); michael@0: #else michael@0: break; michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: ScriptSource * michael@0: FrameIter::scriptSource() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: case JIT: michael@0: return script()->scriptSource(); michael@0: case ASMJS: michael@0: #ifdef JS_ION michael@0: return data_.activations_->asAsmJS()->module().scriptSource(); michael@0: #else michael@0: break; michael@0: #endif michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: const char * michael@0: FrameIter::scriptFilename() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: case JIT: michael@0: return script()->filename(); michael@0: case ASMJS: michael@0: #ifdef JS_ION michael@0: return data_.activations_->asAsmJS()->module().scriptSource()->filename(); michael@0: #else michael@0: break; michael@0: #endif michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: unsigned michael@0: FrameIter::computeLine(uint32_t *column) const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: case JIT: michael@0: return PCToLineNumber(script(), pc(), column); michael@0: case ASMJS: michael@0: #ifdef JS_ION michael@0: return data_.asmJSFrames_.computeLine(column); michael@0: #else michael@0: break; michael@0: #endif michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: JSPrincipals * michael@0: FrameIter::originPrincipals() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: break; michael@0: case INTERP: michael@0: case JIT: michael@0: return script()->originPrincipals(); michael@0: case ASMJS: { michael@0: #ifdef JS_ION michael@0: return data_.activations_->asAsmJS()->module().scriptSource()->originPrincipals(); michael@0: #else michael@0: break; michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: bool michael@0: FrameIter::isConstructing() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isIonJS()) michael@0: return ionInlineFrames_.isConstructing(); michael@0: JS_ASSERT(data_.jitFrames_.isBaselineJS()); michael@0: return data_.jitFrames_.isConstructing(); michael@0: #else michael@0: break; michael@0: #endif michael@0: case INTERP: michael@0: return interpFrame()->isConstructing(); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: bool michael@0: FrameIter::ensureHasRematerializedFrame() michael@0: { michael@0: #ifdef JS_ION michael@0: MOZ_ASSERT(isIon()); michael@0: return !!activation()->asJit()->getRematerializedFrame(activation()->cx(), data_.jitFrames_); michael@0: #else michael@0: return true; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: FrameIter::hasUsableAbstractFramePtr() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: return false; michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isBaselineJS()) michael@0: return true; michael@0: michael@0: MOZ_ASSERT(data_.jitFrames_.isIonJS()); michael@0: return !!activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(), michael@0: ionInlineFrames_.frameNo()); michael@0: #endif michael@0: break; michael@0: case INTERP: michael@0: return true; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: AbstractFramePtr michael@0: FrameIter::abstractFramePtr() const michael@0: { michael@0: MOZ_ASSERT(hasUsableAbstractFramePtr()); michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case JIT: { michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isBaselineJS()) michael@0: return data_.jitFrames_.baselineFrame(); michael@0: michael@0: MOZ_ASSERT(data_.jitFrames_.isIonJS()); michael@0: return activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(), michael@0: ionInlineFrames_.frameNo()); michael@0: #endif michael@0: break; michael@0: } michael@0: case INTERP: michael@0: JS_ASSERT(interpFrame()); michael@0: return AbstractFramePtr(interpFrame()); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: void michael@0: FrameIter::updatePcQuadratic() michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case INTERP: { michael@0: InterpreterFrame *frame = interpFrame(); michael@0: InterpreterActivation *activation = data_.activations_->asInterpreter(); michael@0: michael@0: // Look for the current frame. michael@0: data_.interpFrames_ = InterpreterFrameIterator(activation); michael@0: while (data_.interpFrames_.frame() != frame) michael@0: ++data_.interpFrames_; michael@0: michael@0: // Update the pc. michael@0: JS_ASSERT(data_.interpFrames_.frame() == frame); michael@0: data_.pc_ = data_.interpFrames_.pc(); michael@0: return; michael@0: } michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isBaselineJS()) { michael@0: jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame(); michael@0: jit::JitActivation *activation = data_.activations_->asJit(); michael@0: michael@0: // ActivationIterator::ionTop_ may be invalid, so create a new michael@0: // activation iterator. michael@0: data_.activations_ = ActivationIterator(data_.cx_->runtime()); michael@0: while (data_.activations_.activation() != activation) michael@0: ++data_.activations_; michael@0: michael@0: // Look for the current frame. michael@0: data_.jitFrames_ = jit::JitFrameIterator(data_.activations_); michael@0: while (!data_.jitFrames_.isBaselineJS() || data_.jitFrames_.baselineFrame() != frame) michael@0: ++data_.jitFrames_; michael@0: michael@0: // Update the pc. michael@0: JS_ASSERT(data_.jitFrames_.baselineFrame() == frame); michael@0: data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_); michael@0: return; michael@0: } michael@0: #endif michael@0: break; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: JSFunction * michael@0: FrameIter::callee() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case INTERP: michael@0: JS_ASSERT(isFunctionFrame()); michael@0: return &interpFrame()->callee(); michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isBaselineJS()) michael@0: return data_.jitFrames_.callee(); michael@0: JS_ASSERT(data_.jitFrames_.isIonJS()); michael@0: return ionInlineFrames_.callee(); michael@0: #else michael@0: break; michael@0: #endif michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: Value michael@0: FrameIter::calleev() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case INTERP: michael@0: JS_ASSERT(isFunctionFrame()); michael@0: return interpFrame()->calleev(); michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: return ObjectValue(*callee()); michael@0: #else michael@0: break; michael@0: #endif michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: unsigned michael@0: FrameIter::numActualArgs() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case INTERP: michael@0: JS_ASSERT(isFunctionFrame()); michael@0: return interpFrame()->numActualArgs(); michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isIonJS()) michael@0: return ionInlineFrames_.numActualArgs(); michael@0: michael@0: JS_ASSERT(data_.jitFrames_.isBaselineJS()); michael@0: return data_.jitFrames_.numActualArgs(); michael@0: #else michael@0: break; michael@0: #endif michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: unsigned michael@0: FrameIter::numFormalArgs() const michael@0: { michael@0: return script()->functionNonDelazifying()->nargs(); michael@0: } michael@0: michael@0: Value michael@0: FrameIter::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const michael@0: { michael@0: return abstractFramePtr().unaliasedActual(i, checkAliasing); michael@0: } michael@0: michael@0: JSObject * michael@0: FrameIter::scopeChain() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isIonJS()) michael@0: return ionInlineFrames_.scopeChain(); michael@0: return data_.jitFrames_.baselineFrame()->scopeChain(); michael@0: #else michael@0: break; michael@0: #endif michael@0: case INTERP: michael@0: return interpFrame()->scopeChain(); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: CallObject & michael@0: FrameIter::callObj() const michael@0: { michael@0: JS_ASSERT(callee()->isHeavyweight()); michael@0: michael@0: JSObject *pobj = scopeChain(); michael@0: while (!pobj->is()) michael@0: pobj = pobj->enclosingScope(); michael@0: return pobj->as(); michael@0: } michael@0: michael@0: bool michael@0: FrameIter::hasArgsObj() const michael@0: { michael@0: return abstractFramePtr().hasArgsObj(); michael@0: } michael@0: michael@0: ArgumentsObject & michael@0: FrameIter::argsObj() const michael@0: { michael@0: MOZ_ASSERT(hasArgsObj()); michael@0: return abstractFramePtr().argsObj(); michael@0: } michael@0: michael@0: bool michael@0: FrameIter::computeThis(JSContext *cx) const michael@0: { michael@0: JS_ASSERT(!done() && !isAsmJS()); michael@0: assertSameCompartment(cx, scopeChain()); michael@0: return ComputeThis(cx, abstractFramePtr()); michael@0: } michael@0: michael@0: Value michael@0: FrameIter::computedThisValue() const michael@0: { michael@0: return abstractFramePtr().thisValue(); michael@0: } michael@0: michael@0: Value michael@0: FrameIter::thisv() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isIonJS()) michael@0: return ionInlineFrames_.thisValue(); michael@0: return data_.jitFrames_.baselineFrame()->thisValue(); michael@0: #else michael@0: break; michael@0: #endif michael@0: case INTERP: michael@0: return interpFrame()->thisValue(); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: Value michael@0: FrameIter::returnValue() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isBaselineJS()) michael@0: return data_.jitFrames_.baselineFrame()->returnValue(); michael@0: #endif michael@0: break; michael@0: case INTERP: michael@0: return interpFrame()->returnValue(); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: void michael@0: FrameIter::setReturnValue(const Value &v) michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isBaselineJS()) { michael@0: data_.jitFrames_.baselineFrame()->setReturnValue(v); michael@0: return; michael@0: } michael@0: #endif michael@0: break; michael@0: case INTERP: michael@0: interpFrame()->setReturnValue(v); michael@0: return; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: size_t michael@0: FrameIter::numFrameSlots() const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case JIT: { michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isIonJS()) { michael@0: return ionInlineFrames_.snapshotIterator().numAllocations() - michael@0: ionInlineFrames_.script()->nfixed(); michael@0: } michael@0: jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame(); michael@0: return frame->numValueSlots() - data_.jitFrames_.script()->nfixed(); michael@0: #else michael@0: break; michael@0: #endif michael@0: } michael@0: case INTERP: michael@0: JS_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base()); michael@0: return data_.interpFrames_.sp() - interpFrame()->base(); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: Value michael@0: FrameIter::frameSlotValue(size_t index) const michael@0: { michael@0: switch (data_.state_) { michael@0: case DONE: michael@0: case ASMJS: michael@0: break; michael@0: case JIT: michael@0: #ifdef JS_ION michael@0: if (data_.jitFrames_.isIonJS()) { michael@0: jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator()); michael@0: index += ionInlineFrames_.script()->nfixed(); michael@0: return si.maybeReadAllocByIndex(index); michael@0: } michael@0: michael@0: index += data_.jitFrames_.script()->nfixed(); michael@0: return *data_.jitFrames_.baselineFrame()->valueSlot(index); michael@0: #else michael@0: break; michael@0: #endif michael@0: case INTERP: michael@0: return interpFrame()->base()[index]; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected state"); michael@0: } michael@0: michael@0: #if defined(_MSC_VER) michael@0: # pragma optimize("", on) michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: bool michael@0: js::SelfHostedFramesVisible() michael@0: { michael@0: static bool checked = false; michael@0: static bool visible = false; michael@0: if (!checked) { michael@0: checked = true; michael@0: char *env = getenv("MOZ_SHOW_ALL_JS_FRAMES"); michael@0: visible = !!env; michael@0: } michael@0: return visible; michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: NonBuiltinFrameIter::settle() michael@0: { michael@0: if (!SelfHostedFramesVisible()) { michael@0: while (!done() && hasScript() && script()->selfHosted()) michael@0: FrameIter::operator++(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: NonBuiltinScriptFrameIter::settle() michael@0: { michael@0: if (!SelfHostedFramesVisible()) { michael@0: while (!done() && script()->selfHosted()) michael@0: ScriptFrameIter::operator++(); michael@0: } michael@0: } michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: JSObject * michael@0: AbstractFramePtr::evalPrevScopeChain(JSContext *cx) const michael@0: { michael@0: // Eval frames are not compiled by Ion, though their caller might be. michael@0: AllFramesIter iter(cx); michael@0: while (iter.isIon() || iter.abstractFramePtr() != *this) michael@0: ++iter; michael@0: ++iter; michael@0: return iter.scopeChain(); michael@0: } michael@0: michael@0: bool michael@0: AbstractFramePtr::hasPushedSPSFrame() const michael@0: { michael@0: if (isInterpreterFrame()) michael@0: return asInterpreterFrame()->hasPushedSPSFrame(); michael@0: #ifdef JS_ION michael@0: return asBaselineFrame()->hasPushedSPSFrame(); michael@0: #else michael@0: MOZ_ASSUME_UNREACHABLE("Invalid frame"); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, uint32_t i) michael@0: { michael@0: if (!checkAliasing) michael@0: return; michael@0: michael@0: JS_ASSERT(i < script->nfixed()); michael@0: if (i < script->bindings.numVars()) { michael@0: JS_ASSERT(!script->varIsAliased(i)); michael@0: } else { michael@0: // FIXME: The callers of this function do not easily have the PC of the michael@0: // current frame, and so they do not know the block scope. michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: jit::JitActivation::JitActivation(JSContext *cx, bool firstFrameIsConstructing, bool active) michael@0: : Activation(cx, Jit), michael@0: firstFrameIsConstructing_(firstFrameIsConstructing), michael@0: active_(active) michael@0: #ifdef JS_ION michael@0: , rematerializedFrames_(cx) michael@0: #endif michael@0: { michael@0: if (active) { michael@0: prevIonTop_ = cx->mainThread().ionTop; michael@0: prevJitJSContext_ = cx->mainThread().jitJSContext; michael@0: cx->mainThread().jitJSContext = cx; michael@0: } else { michael@0: prevIonTop_ = nullptr; michael@0: prevJitJSContext_ = nullptr; michael@0: } michael@0: } michael@0: michael@0: jit::JitActivation::~JitActivation() michael@0: { michael@0: if (active_) { michael@0: cx_->mainThread().ionTop = prevIonTop_; michael@0: cx_->mainThread().jitJSContext = prevJitJSContext_; michael@0: } michael@0: michael@0: #ifdef JS_ION michael@0: clearRematerializedFrames(); michael@0: #endif michael@0: } michael@0: michael@0: // setActive() is inlined in GenerateFFIIonExit() with explicit masm instructions so michael@0: // changes to the logic here need to be reflected in GenerateFFIIonExit() in the enable michael@0: // and disable activation instruction sequences. michael@0: void michael@0: jit::JitActivation::setActive(JSContext *cx, bool active) michael@0: { michael@0: // Only allowed to deactivate/activate if activation is top. michael@0: // (Not tested and will probably fail in other situations.) michael@0: JS_ASSERT(cx->mainThread().activation_ == this); michael@0: JS_ASSERT(active != active_); michael@0: active_ = active; michael@0: michael@0: if (active) { michael@0: prevIonTop_ = cx->mainThread().ionTop; michael@0: prevJitJSContext_ = cx->mainThread().jitJSContext; michael@0: cx->mainThread().jitJSContext = cx; michael@0: } else { michael@0: cx->mainThread().ionTop = prevIonTop_; michael@0: cx->mainThread().jitJSContext = prevJitJSContext_; michael@0: } michael@0: } michael@0: michael@0: #ifdef JS_ION michael@0: michael@0: void michael@0: jit::JitActivation::freeRematerializedFramesInVector(RematerializedFrameVector &frames) michael@0: { michael@0: for (size_t i = 0; i < frames.length(); i++) { michael@0: RematerializedFrame *f = frames[i]; michael@0: f->RematerializedFrame::~RematerializedFrame(); michael@0: js_free(f); michael@0: } michael@0: frames.clear(); michael@0: } michael@0: michael@0: void michael@0: jit::JitActivation::removeRematerializedFrame(uint8_t *top) michael@0: { michael@0: if (!rematerializedFrames_.initialized()) michael@0: return; michael@0: michael@0: if (RematerializedFrameTable::Ptr p = rematerializedFrames_.lookup(top)) { michael@0: freeRematerializedFramesInVector(p->value()); michael@0: rematerializedFrames_.remove(p); michael@0: } michael@0: } michael@0: michael@0: void michael@0: jit::JitActivation::clearRematerializedFrames() michael@0: { michael@0: if (!rematerializedFrames_.initialized()) michael@0: return; michael@0: michael@0: for (RematerializedFrameTable::Enum e(rematerializedFrames_); !e.empty(); e.popFront()) { michael@0: freeRematerializedFramesInVector(e.front().value()); michael@0: e.removeFront(); michael@0: } michael@0: } michael@0: michael@0: jit::RematerializedFrame * michael@0: jit::JitActivation::getRematerializedFrame(JSContext *cx, JitFrameIterator &iter, michael@0: size_t inlineDepth) michael@0: { michael@0: MOZ_ASSERT(iter.activation() == this); michael@0: MOZ_ASSERT(iter.isIonJS()); michael@0: michael@0: if (!rematerializedFrames_.initialized() && !rematerializedFrames_.init()) michael@0: return nullptr; michael@0: michael@0: // The unit of rematerialization is an uninlined frame and its inlined michael@0: // frames. Since inlined frames do not exist outside of snapshots, it is michael@0: // impossible to synchronize their rematerialized copies to preserve michael@0: // identity. Therefore, we always rematerialize an uninlined frame and all michael@0: // its inlined frames at once. michael@0: michael@0: uint8_t *top = iter.fp(); michael@0: RematerializedFrameTable::AddPtr p = rematerializedFrames_.lookupForAdd(top); michael@0: if (!p) { michael@0: RematerializedFrameVector empty(cx); michael@0: if (!rematerializedFrames_.add(p, top, Move(empty))) michael@0: return nullptr; michael@0: michael@0: InlineFrameIterator inlineIter(cx, &iter); michael@0: if (!p->value().resize(inlineIter.frameCount())) michael@0: return nullptr; michael@0: michael@0: while (true) { michael@0: size_t frameNo = inlineIter.frameNo(); michael@0: p->value()[frameNo] = RematerializedFrame::New(cx, top, inlineIter); michael@0: if (!p->value()[frameNo]) michael@0: return nullptr; michael@0: michael@0: if (!inlineIter.more()) michael@0: break; michael@0: ++inlineIter; michael@0: } michael@0: } michael@0: michael@0: return p->value()[inlineDepth]; michael@0: } michael@0: michael@0: jit::RematerializedFrame * michael@0: jit::JitActivation::lookupRematerializedFrame(uint8_t *top, size_t inlineDepth) michael@0: { michael@0: if (!rematerializedFrames_.initialized()) michael@0: return nullptr; michael@0: if (RematerializedFrameTable::Ptr p = rematerializedFrames_.lookup(top)) michael@0: return inlineDepth < p->value().length() ? p->value()[inlineDepth] : nullptr; michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: jit::JitActivation::markRematerializedFrames(JSTracer *trc) michael@0: { michael@0: if (!rematerializedFrames_.initialized()) michael@0: return; michael@0: for (RematerializedFrameTable::Enum e(rematerializedFrames_); !e.empty(); e.popFront()) { michael@0: RematerializedFrameVector &frames = e.front().value(); michael@0: for (size_t i = 0; i < frames.length(); i++) michael@0: frames[i]->mark(trc); michael@0: } michael@0: } michael@0: michael@0: #endif // JS_ION michael@0: michael@0: AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module) michael@0: : Activation(cx, AsmJS), michael@0: module_(module), michael@0: errorRejoinSP_(nullptr), michael@0: profiler_(nullptr), michael@0: resumePC_(nullptr), michael@0: exitSP_(nullptr) michael@0: { michael@0: if (cx->runtime()->spsProfiler.enabled()) { michael@0: // Use a profiler string that matches jsMatch regex in michael@0: // browser/devtools/profiler/cleopatra/js/parserWorker.js. michael@0: // (For now use a single static string to avoid further slowing down michael@0: // calls into asm.js.) michael@0: profiler_ = &cx->runtime()->spsProfiler; michael@0: profiler_->enterNative("asm.js code :0", this); michael@0: } michael@0: michael@0: prevAsmJS_ = cx_->runtime()->mainThread.asmJSActivationStack_; michael@0: michael@0: JSRuntime::AutoLockForInterrupt lock(cx_->runtime()); michael@0: cx_->runtime()->mainThread.asmJSActivationStack_ = this; michael@0: michael@0: (void) errorRejoinSP_; // squelch GCC warning michael@0: } michael@0: michael@0: AsmJSActivation::~AsmJSActivation() michael@0: { michael@0: if (profiler_) michael@0: profiler_->exitNative(); michael@0: michael@0: JS_ASSERT(cx_->runtime()->mainThread.asmJSActivationStack_ == this); michael@0: michael@0: JSRuntime::AutoLockForInterrupt lock(cx_->runtime()); michael@0: cx_->runtime()->mainThread.asmJSActivationStack_ = prevAsmJS_; michael@0: } michael@0: michael@0: InterpreterFrameIterator & michael@0: InterpreterFrameIterator::operator++() michael@0: { michael@0: JS_ASSERT(!done()); michael@0: if (fp_ != activation_->entryFrame_) { michael@0: pc_ = fp_->prevpc(); michael@0: sp_ = fp_->prevsp(); michael@0: fp_ = fp_->prev(); michael@0: } else { michael@0: pc_ = nullptr; michael@0: sp_ = nullptr; michael@0: fp_ = nullptr; michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: ActivationIterator::ActivationIterator(JSRuntime *rt) michael@0: : jitTop_(rt->mainThread.ionTop), michael@0: activation_(rt->mainThread.activation_) michael@0: { michael@0: settle(); michael@0: } michael@0: michael@0: ActivationIterator & michael@0: ActivationIterator::operator++() michael@0: { michael@0: JS_ASSERT(activation_); michael@0: if (activation_->isJit() && activation_->asJit()->isActive()) michael@0: jitTop_ = activation_->asJit()->prevIonTop(); michael@0: activation_ = activation_->prev(); michael@0: settle(); michael@0: return *this; michael@0: } michael@0: michael@0: void michael@0: ActivationIterator::settle() michael@0: { michael@0: // Stop at the next active activation. No need to update jitTop_, since michael@0: // we don't iterate over an active jit activation. michael@0: while (!done() && activation_->isJit() && !activation_->asJit()->isActive()) michael@0: activation_ = activation_->prev(); michael@0: }