#include "vm/Stack-inl.h"
#include <utility>
#include "gc/Marking.h"
#include "jit/BaselineFrame.h"
#include "jit/JitcodeMap.h"
#include "jit/JitRealm.h"
#include "jit/shared/CodeGenerator-shared.h"
#include "vm/Debugger.h"
#include "vm/JSContext.h"
#include "vm/Opcodes.h"
#include "wasm/WasmInstance.h"
#include "jit/JSJitFrameIter-inl.h"
#include "vm/Compartment-inl.h"
#include "vm/EnvironmentObject-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/Probes-inl.h"
using namespace js;
using mozilla::ArrayLength;
using mozilla::DebugOnly;
using mozilla::Maybe;
void InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script,
AbstractFramePtr evalInFramePrev,
const Value& newTargetValue,
HandleObject envChain) {
flags_ = 0;
script_ = script;
RootedValue newTarget(cx, newTargetValue);
if (script->isDirectEvalInFunction()) {
FrameIter iter(cx);
if (newTarget.isNull() && iter.hasScript() &&
iter.script()->bodyScope()->hasOnChain(ScopeKind::Function)) {
newTarget = iter.newTarget();
}
} else if (evalInFramePrev) {
if (newTarget.isNull() && evalInFramePrev.hasScript() &&
evalInFramePrev.script()->bodyScope()->hasOnChain(
ScopeKind::Function)) {
newTarget = evalInFramePrev.newTarget();
}
}
Value* dstvp = (Value*)this - 1;
dstvp[0] = newTarget;
envChain_ = envChain.get();
prev_ = nullptr;
prevpc_ = nullptr;
prevsp_ = nullptr;
evalInFramePrev_ = evalInFramePrev;
MOZ_ASSERT_IF(evalInFramePrev, isDebuggerEvalFrame());
if (script->isDebuggee()) {
setIsDebuggee();
}
#ifdef DEBUG
Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
#endif
}
bool InterpreterFrame::isNonGlobalEvalFrame() const {
return isEvalFrame() && script()->bodyScope()->as<EvalScope>().isNonGlobal();
}
ArrayObject* InterpreterFrame::createRestParameter(JSContext* cx) {
MOZ_ASSERT(script()->hasRest());
unsigned nformal = callee().nargs() - 1, nactual = numActualArgs();
unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
Value* restvp = argv() + nformal;
return ObjectGroup::newArrayObject(cx, restvp, nrest, GenericObject,
ObjectGroup::NewArrayKind::UnknownIndex);
}
static inline void AssertScopeMatchesEnvironment(Scope* scope,
JSObject* originalEnv) {
#ifdef DEBUG
JSObject* env = originalEnv;
for (ScopeIter si(scope); si; si++) {
if (si.kind() == ScopeKind::NonSyntactic) {
while (env->is<WithEnvironmentObject>() ||
env->is<NonSyntacticVariablesObject>() ||
(env->is<LexicalEnvironmentObject>() &&
!env->as<LexicalEnvironmentObject>().isSyntactic())) {
MOZ_ASSERT(!IsSyntacticEnvironment(env));
env = &env->as<EnvironmentObject>().enclosingEnvironment();
}
} else if (si.hasSyntacticEnvironment()) {
switch (si.kind()) {
case ScopeKind::Function:
MOZ_ASSERT(
env->as<CallObject>().callee().existingScriptNonDelazifying() ==
si.scope()->as<FunctionScope>().script());
env = &env->as<CallObject>().enclosingEnvironment();
break;
case ScopeKind::FunctionBodyVar:
case ScopeKind::ParameterExpressionVar:
MOZ_ASSERT(&env->as<VarEnvironmentObject>().scope() == si.scope());
env = &env->as<VarEnvironmentObject>().enclosingEnvironment();
break;
case ScopeKind::Lexical:
case ScopeKind::SimpleCatch:
case ScopeKind::Catch:
case ScopeKind::NamedLambda:
case ScopeKind::StrictNamedLambda:
MOZ_ASSERT(&env->as<LexicalEnvironmentObject>().scope() ==
si.scope());
env = &env->as<LexicalEnvironmentObject>().enclosingEnvironment();
break;
case ScopeKind::With:
MOZ_ASSERT(&env->as<WithEnvironmentObject>().scope() == si.scope());
env = &env->as<WithEnvironmentObject>().enclosingEnvironment();
break;
case ScopeKind::Eval:
case ScopeKind::StrictEval:
env = &env->as<VarEnvironmentObject>().enclosingEnvironment();
break;
case ScopeKind::Global:
MOZ_ASSERT(env->as<LexicalEnvironmentObject>().isGlobal());
env = &env->as<LexicalEnvironmentObject>().enclosingEnvironment();
MOZ_ASSERT(env->is<GlobalObject>());
break;
case ScopeKind::NonSyntactic:
MOZ_CRASH("NonSyntactic should not have a syntactic environment");
break;
case ScopeKind::Module:
MOZ_ASSERT(&env->as<ModuleEnvironmentObject>().module() ==
si.scope()->as<ModuleScope>().module());
env = &env->as<ModuleEnvironmentObject>().enclosingEnvironment();
break;
case ScopeKind::WasmInstance:
env =
&env->as<WasmInstanceEnvironmentObject>().enclosingEnvironment();
break;
case ScopeKind::WasmFunction:
env = &env->as<WasmFunctionCallObject>().enclosingEnvironment();
break;
}
}
}
MOZ_ASSERT(env->is<GlobalObject>() || IsGlobalLexicalEnvironment(env) ||
env->is<DebugEnvironmentProxy>());
#endif
}
static inline void AssertScopeMatchesEnvironment(InterpreterFrame* fp,
jsbytecode* pc) {
#ifdef DEBUG
if (fp->script()->initialEnvironmentShape() && fp->hasInitialEnvironment()) {
AssertScopeMatchesEnvironment(fp->script()->innermostScope(pc),
fp->environmentChain());
}
#endif
}
bool InterpreterFrame::initFunctionEnvironmentObjects(JSContext* cx) {
return js::InitFunctionEnvironmentObjects(cx, this);
}
bool InterpreterFrame::prologue(JSContext* cx) {
RootedScript script(cx, this->script());
MOZ_ASSERT(cx->interpreterRegs().pc == script->code());
MOZ_ASSERT(cx->realm() == script->realm());
if (isEvalFrame() || isGlobalFrame()) {
HandleObject env = environmentChain();
if (!CheckGlobalOrEvalDeclarationConflicts(cx, env, script)) {
if (script->trackRecordReplayProgress()) {
mozilla::recordreplay::AdvanceExecutionProgressCounter();
}
return false;
}
return probes::EnterScript(cx, script, nullptr, this);
}
if (isModuleFrame()) {
return probes::EnterScript(cx, script, nullptr, this);
}
MOZ_ASSERT(isFunctionFrame());
AssertScopeMatchesEnvironment(script->enclosingScope(), environmentChain());
if (callee().needsFunctionEnvironmentObjects() &&
!initFunctionEnvironmentObjects(cx)) {
return false;
}
MOZ_ASSERT_IF(isConstructing(),
thisArgument().isObject() ||
thisArgument().isMagic(JS_UNINITIALIZED_LEXICAL));
return probes::EnterScript(cx, script, script->functionNonDelazifying(),
this);
}
void InterpreterFrame::epilogue(JSContext* cx, jsbytecode* pc) {
RootedScript script(cx, this->script());
MOZ_ASSERT(cx->realm() == script->realm());
probes::ExitScript(cx, script, script->functionNonDelazifying(),
hasPushedGeckoProfilerFrame());
AssertScopeMatchesEnvironment(this, pc);
EnvironmentIter ei(cx, this, pc);
UnwindAllEnvironmentsInFrame(cx, ei);
if (isFunctionFrame()) {
if (!callee().isGenerator() && !callee().isAsync() && isConstructing() &&
thisArgument().isObject() && returnValue().isPrimitive()) {
setReturnValue(thisArgument());
}
return;
}
MOZ_ASSERT(isEvalFrame() || isGlobalFrame() || isModuleFrame());
}
bool InterpreterFrame::checkReturn(JSContext* cx, HandleValue thisv) {
MOZ_ASSERT(script()->isDerivedClassConstructor());
MOZ_ASSERT(isFunctionFrame());
MOZ_ASSERT(callee().isClassConstructor());
HandleValue retVal = returnValue();
if (retVal.isObject()) {
return true;
}
if (!retVal.isUndefined()) {
ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, retVal,
nullptr);
return false;
}
if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL)) {
return ThrowUninitializedThis(cx, this);
}
setReturnValue(thisv);
return true;
}
bool InterpreterFrame::pushVarEnvironment(JSContext* cx, HandleScope scope) {
return js::PushVarEnvironmentObject(cx, scope, this);
}
bool InterpreterFrame::pushLexicalEnvironment(JSContext* cx,
Handle<LexicalScope*> scope) {
LexicalEnvironmentObject* env =
LexicalEnvironmentObject::createForFrame(cx, scope, this);
if (!env) {
return false;
}
pushOnEnvironmentChain(*env);
return true;
}
bool InterpreterFrame::freshenLexicalEnvironment(JSContext* cx) {
Rooted<LexicalEnvironmentObject*> env(
cx, &envChain_->as<LexicalEnvironmentObject>());
LexicalEnvironmentObject* fresh = LexicalEnvironmentObject::clone(cx, env);
if (!fresh) {
return false;
}
replaceInnermostEnvironment(*fresh);
return true;
}
bool InterpreterFrame::recreateLexicalEnvironment(JSContext* cx) {
Rooted<LexicalEnvironmentObject*> env(
cx, &envChain_->as<LexicalEnvironmentObject>());
LexicalEnvironmentObject* fresh = LexicalEnvironmentObject::recreate(cx, env);
if (!fresh) {
return false;
}
replaceInnermostEnvironment(*fresh);
return true;
}
void InterpreterFrame::trace(JSTracer* trc, Value* sp, jsbytecode* pc) {
TraceRoot(trc, &envChain_, "env chain");
TraceRoot(trc, &script_, "script");
if (flags_ & HAS_ARGS_OBJ) {
TraceRoot(trc, &argsObj_, "arguments");
}
if (hasReturnValue()) {
TraceRoot(trc, &rval_, "rval");
}
MOZ_ASSERT(sp >= slots());
if (hasArgs()) {
TraceRootRange(trc, 2, argv_ - 2, "fp callee and this");
unsigned argc = Max(numActualArgs(), numFormalArgs());
TraceRootRange(trc, argc + isConstructing(), argv_, "fp argv");
} else {
TraceRoot(trc, ((Value*)this) - 1, "stack newTarget");
}
JSScript* script = this->script();
size_t nfixed = script->nfixed();
size_t nlivefixed = script->calculateLiveFixed(pc);
if (nfixed == nlivefixed) {
traceValues(trc, 0, sp - slots());
} else {
traceValues(trc, nfixed, sp - slots());
while (nfixed > nlivefixed) {
unaliasedLocal(--nfixed).setUndefined();
}
traceValues(trc, 0, nlivefixed);
}
if (auto* debugEnvs = script->realm()->debugEnvs()) {
debugEnvs->traceLiveFrame(trc, this);
}
}
void InterpreterFrame::traceValues(JSTracer* trc, unsigned start,
unsigned end) {
if (start < end) {
TraceRootRange(trc, end - start, slots() + start, "vm_stack");
}
}
static void TraceInterpreterActivation(JSTracer* trc,
InterpreterActivation* act) {
for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
InterpreterFrame* fp = frames.frame();
fp->trace(trc, frames.sp(), frames.pc());
}
}
void js::TraceInterpreterActivations(JSContext* cx, JSTracer* trc) {
for (ActivationIterator iter(cx); !iter.done(); ++iter) {
Activation* act = iter.activation();
if (act->isInterpreter()) {
TraceInterpreterActivation(trc, act->asInterpreter());
}
}
}
void InterpreterRegs::setToEndOfScript() { sp = fp()->base(); }
InterpreterFrame* InterpreterStack::pushInvokeFrame(
JSContext* cx, const CallArgs& args, MaybeConstruct constructing) {
LifoAlloc::Mark mark = allocator_.mark();
RootedFunction fun(cx, &args.callee().as<JSFunction>());
RootedScript script(cx, fun->nonLazyScript());
Value* argv;
InterpreterFrame* fp = getCallFrame(cx, args, script, constructing, &argv);
if (!fp) {
return nullptr;
}
fp->mark_ = mark;
fp->initCallFrame(nullptr, nullptr, nullptr, *fun, script, argv,
args.length(), constructing);
return fp;
}
InterpreterFrame* InterpreterStack::pushExecuteFrame(
JSContext* cx, HandleScript script, const Value& newTargetValue,
HandleObject envChain, AbstractFramePtr evalInFrame) {
LifoAlloc::Mark mark = allocator_.mark();
unsigned nvars = 1 + script->nslots();
uint8_t* buffer =
allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value));
if (!buffer) {
return nullptr;
}
InterpreterFrame* fp =
reinterpret_cast<InterpreterFrame*>(buffer + 1 * sizeof(Value));
fp->mark_ = mark;
fp->initExecuteFrame(cx, script, evalInFrame, newTargetValue, envChain);
fp->initLocals();
return fp;
}
JitFrameIter::JitFrameIter(const JitFrameIter& another) { *this = another; }
JitFrameIter& JitFrameIter::operator=(const JitFrameIter& another) {
MOZ_ASSERT(this != &another);
act_ = another.act_;
mustUnwindActivation_ = another.mustUnwindActivation_;
if (isSome()) {
iter_.destroy();
}
if (!another.isSome()) {
return *this;
}
if (another.isJSJit()) {
iter_.construct<jit::JSJitFrameIter>(another.asJSJit());
} else {
MOZ_ASSERT(another.isWasm());
iter_.construct<wasm::WasmFrameIter>(another.asWasm());
}
return *this;
}
JitFrameIter::JitFrameIter(jit::JitActivation* act, bool mustUnwindActivation) {
act_ = act;
mustUnwindActivation_ = mustUnwindActivation;
MOZ_ASSERT(act->hasExitFP(),
"packedExitFP is used to determine if JSJit or wasm");
if (act->hasJSExitFP()) {
iter_.construct<jit::JSJitFrameIter>(act);
} else {
MOZ_ASSERT(act->hasWasmExitFP());
iter_.construct<wasm::WasmFrameIter>(act);
}
settle();
}
void JitFrameIter::skipNonScriptedJSFrames() {
if (isJSJit()) {
jit::JSJitFrameIter& frames = asJSJit();
while (!frames.isScripted() && !frames.done()) {
++frames;
}
settle();
}
}
bool JitFrameIter::isSelfHostedIgnoringInlining() const {
MOZ_ASSERT(!done());
if (isWasm()) {
return false;
}
return asJSJit().script()->selfHosted();
}
JS::Realm* JitFrameIter::realm() const {
MOZ_ASSERT(!done());
if (isWasm()) {
return asWasm().instance()->realm();
}
return asJSJit().script()->realm();
}
uint8_t* JitFrameIter::resumePCinCurrentFrame() const {
if (isWasm()) {
return asWasm().resumePCinCurrentFrame();
}
return asJSJit().resumePCinCurrentFrame();
}
bool JitFrameIter::done() const {
if (!isSome()) {
return true;
}
if (isJSJit()) {
return asJSJit().done();
}
if (isWasm()) {
return asWasm().done();
}
MOZ_CRASH("unhandled case");
}
void JitFrameIter::settle() {
if (isJSJit()) {
const jit::JSJitFrameIter& jitFrame = asJSJit();
if (jitFrame.type() != jit::FrameType::WasmToJSJit) {
return;
}
wasm::Frame* prevFP = (wasm::Frame*)jitFrame.prevFp();
if (mustUnwindActivation_) {
act_->setWasmExitFP(prevFP);
}
iter_.destroy();
iter_.construct<wasm::WasmFrameIter>(act_, prevFP);
MOZ_ASSERT(!asWasm().done());
return;
}
if (isWasm()) {
const wasm::WasmFrameIter& wasmFrame = asWasm();
if (!wasmFrame.unwoundIonCallerFP()) {
return;
}
MOZ_ASSERT(wasmFrame.done());
uint8_t* prevFP = wasmFrame.unwoundIonCallerFP();
jit::FrameType prevFrameType = wasmFrame.unwoundIonFrameType();
if (mustUnwindActivation_) {
act_->setJSExitFP(prevFP);
}
iter_.destroy();
iter_.construct<jit::JSJitFrameIter>(act_, prevFrameType, prevFP);
MOZ_ASSERT(!asJSJit().done());
return;
}
}
void JitFrameIter::operator++() {
MOZ_ASSERT(isSome());
if (isJSJit()) {
const jit::JSJitFrameIter& jitFrame = asJSJit();
jit::JitFrameLayout* prevFrame = nullptr;
if (mustUnwindActivation_ && jitFrame.isScripted()) {
prevFrame = jitFrame.jsFrame();
}
++asJSJit();
if (prevFrame) {
EnsureBareExitFrame(act_, prevFrame);
}
} else if (isWasm()) {
++asWasm();
} else {
MOZ_CRASH("unhandled case");
}
settle();
}
OnlyJSJitFrameIter::OnlyJSJitFrameIter(jit::JitActivation* act)
: JitFrameIter(act) {
settle();
}
OnlyJSJitFrameIter::OnlyJSJitFrameIter(JSContext* cx)
: OnlyJSJitFrameIter(cx->activation()->asJit()) {}
OnlyJSJitFrameIter::OnlyJSJitFrameIter(const ActivationIterator& iter)
: OnlyJSJitFrameIter(iter->asJit()) {}
void FrameIter::popActivation() {
++data_.activations_;
settleOnActivation();
}
bool FrameIter::principalsSubsumeFrame() const {
MOZ_ASSERT(!done());
if (!data_.principals_) {
return true;
}
JSSubsumesOp subsumes = data_.cx_->runtime()->securityCallbacks->subsumes;
if (!subsumes) {
return true;
}
return subsumes(data_.principals_, realm()->principals());
}
void FrameIter::popInterpreterFrame() {
MOZ_ASSERT(data_.state_ == INTERP);
++data_.interpFrames_;
if (data_.interpFrames_.done()) {
popActivation();
} else {
data_.pc_ = data_.interpFrames_.pc();
}
}
void FrameIter::settleOnActivation() {
MOZ_ASSERT(!data_.cx_->inUnsafeCallWithABI);
while (true) {
if (data_.activations_.done()) {
data_.state_ = DONE;
return;
}
Activation* activation = data_.activations_.activation();
if (activation->isJit()) {
data_.jitFrames_ = JitFrameIter(activation->asJit());
data_.jitFrames_.skipNonScriptedJSFrames();
if (data_.jitFrames_.done()) {
++data_.activations_;
continue;
}
data_.state_ = JIT;
nextJitFrame();
return;
}
MOZ_ASSERT(activation->isInterpreter());
InterpreterActivation* interpAct = activation->asInterpreter();
data_.interpFrames_ = InterpreterFrameIterator(interpAct);
if (data_.interpFrames_.frame()->runningInJit()) {
++data_.interpFrames_;
if (data_.interpFrames_.done()) {
++data_.activations_;
continue;
}
}
MOZ_ASSERT(!data_.interpFrames_.frame()->runningInJit());
data_.pc_ = data_.interpFrames_.pc();
data_.state_ = INTERP;
return;
}
}
FrameIter::Data::Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption,
JSPrincipals* principals)
: cx_(cx),
debuggerEvalOption_(debuggerEvalOption),
principals_(principals),
state_(DONE),
pc_(nullptr),
interpFrames_(nullptr),
activations_(cx),
ionInlineFrameNo_(0) {}
FrameIter::Data::Data(const FrameIter::Data& other)
: cx_(other.cx_),
debuggerEvalOption_(other.debuggerEvalOption_),
principals_(other.principals_),
state_(other.state_),
pc_(other.pc_),
interpFrames_(other.interpFrames_),
activations_(other.activations_),
jitFrames_(other.jitFrames_),
ionInlineFrameNo_(other.ionInlineFrameNo_) {}
FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption)
: data_(cx, debuggerEvalOption, nullptr),
ionInlineFrames_(cx, (js::jit::JSJitFrameIter*)nullptr) {
JS::AutoSuppressGCAnalysis nogc;
settleOnActivation();
MOZ_ASSERT_IF(!done(), principalsSubsumeFrame());
}
FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption,
JSPrincipals* principals)
: data_(cx, debuggerEvalOption, principals),
ionInlineFrames_(cx, (js::jit::JSJitFrameIter*)nullptr) {
settleOnActivation();
if (!done() && !principalsSubsumeFrame()) {
++*this;
}
}
FrameIter::FrameIter(const FrameIter& other)
: data_(other.data_),
ionInlineFrames_(other.data_.cx_,
isIonScripted() ? &other.ionInlineFrames_ : nullptr) {}
FrameIter::FrameIter(const Data& data)
: data_(data),
ionInlineFrames_(data.cx_, isIonScripted() ? &jsJitFrame() : nullptr) {
MOZ_ASSERT(data.cx_);
if (isIonScripted()) {
while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_) {
++ionInlineFrames_;
}
}
}
void FrameIter::nextJitFrame() {
MOZ_ASSERT(data_.jitFrames_.isSome());
if (isJSJit()) {
if (jsJitFrame().isIonScripted()) {
ionInlineFrames_.resetOn(&jsJitFrame());
data_.pc_ = ionInlineFrames_.pc();
} else {
MOZ_ASSERT(jsJitFrame().isBaselineJS());
jsJitFrame().baselineScriptAndPc(nullptr, &data_.pc_);
}
return;
}
MOZ_ASSERT(isWasm());
data_.pc_ = nullptr;
}
void FrameIter::popJitFrame() {
MOZ_ASSERT(data_.state_ == JIT);
MOZ_ASSERT(data_.jitFrames_.isSome());
if (isJSJit() && jsJitFrame().isIonScripted() && ionInlineFrames_.more()) {
++ionInlineFrames_;
data_.pc_ = ionInlineFrames_.pc();
return;
}
++data_.jitFrames_;
data_.jitFrames_.skipNonScriptedJSFrames();
if (!data_.jitFrames_.done()) {
nextJitFrame();
} else {
data_.jitFrames_.reset();
popActivation();
}
}
FrameIter& FrameIter::operator++() {
while (true) {
switch (data_.state_) {
case DONE:
MOZ_CRASH("Unexpected state");
case INTERP:
if (interpFrame()->isDebuggerEvalFrame() &&
data_.debuggerEvalOption_ == FOLLOW_DEBUGGER_EVAL_PREV_LINK) {
AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev();
popInterpreterFrame();
while (!hasUsableAbstractFramePtr() ||
abstractFramePtr() != eifPrev) {
if (data_.state_ == JIT) {
popJitFrame();
} else {
popInterpreterFrame();
}
}
break;
}
popInterpreterFrame();
break;
case JIT:
popJitFrame();
break;
}
if (done() || principalsSubsumeFrame()) {
break;
}
}
return *this;
}
FrameIter::Data* FrameIter::copyData() const {
Data* data = data_.cx_->new_<Data>(data_);
if (!data) {
return nullptr;
}
if (data && isIonScripted()) {
data->ionInlineFrameNo_ = ionInlineFrames_.frameNo();
}
return data;
}
void* FrameIter::rawFramePtr() const {
switch (data_.state_) {
case DONE:
return nullptr;
case INTERP:
return interpFrame();
case JIT:
if (isJSJit()) {
return jsJitFrame().fp();
}
MOZ_ASSERT(isWasm());
return nullptr;
}
MOZ_CRASH("Unexpected state");
}
JS::Compartment* FrameIter::compartment() const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
case JIT:
return data_.activations_->compartment();
}
MOZ_CRASH("Unexpected state");
}
Realm* FrameIter::realm() const {
MOZ_ASSERT(!done());
if (hasScript()) {
return script()->realm();
}
return wasmInstance()->realm();
}
bool FrameIter::isEvalFrame() const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
return interpFrame()->isEvalFrame();
case JIT:
if (isJSJit()) {
if (jsJitFrame().isBaselineJS()) {
return jsJitFrame().baselineFrame()->isEvalFrame();
}
MOZ_ASSERT(!script()->isForEval());
return false;
}
MOZ_ASSERT(isWasm());
return false;
}
MOZ_CRASH("Unexpected state");
}
bool FrameIter::isFunctionFrame() const {
MOZ_ASSERT(!done());
switch (data_.state_) {
case DONE:
break;
case INTERP:
return interpFrame()->isFunctionFrame();
case JIT:
if (isJSJit()) {
if (jsJitFrame().isBaselineJS()) {
return jsJitFrame().baselineFrame()->isFunctionFrame();
}
return script()->functionNonDelazifying();
}
MOZ_ASSERT(isWasm());
return false;
}
MOZ_CRASH("Unexpected state");
}
JSAtom* FrameIter::maybeFunctionDisplayAtom() const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
case JIT:
if (isWasm()) {
return wasmFrame().functionDisplayAtom();
}
if (isFunctionFrame()) {
return calleeTemplate()->displayAtom();
}
return nullptr;
}
MOZ_CRASH("Unexpected state");
}
ScriptSource* FrameIter::scriptSource() const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
case JIT:
return script()->scriptSource();
}
MOZ_CRASH("Unexpected state");
}
const char* FrameIter::filename() const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
case JIT:
if (isWasm()) {
return wasmFrame().filename();
}
return script()->filename();
}
MOZ_CRASH("Unexpected state");
}
const char16_t* FrameIter::displayURL() const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
case JIT:
if (isWasm()) {
return wasmFrame().displayURL();
}
ScriptSource* ss = script()->scriptSource();
return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
}
MOZ_CRASH("Unexpected state");
}
unsigned FrameIter::computeLine(uint32_t* column) const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
case JIT:
if (isWasm()) {
return wasmFrame().computeLine(column);
}
return PCToLineNumber(script(), pc(), column);
}
MOZ_CRASH("Unexpected state");
}
bool FrameIter::mutedErrors() const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
case JIT:
if (isWasm()) {
return wasmFrame().mutedErrors();
}
return script()->mutedErrors();
}
MOZ_CRASH("Unexpected state");
}
bool FrameIter::isConstructing() const {
switch (data_.state_) {
case DONE:
break;
case JIT:
MOZ_ASSERT(isJSJit());
if (jsJitFrame().isIonScripted()) {
return ionInlineFrames_.isConstructing();
}
MOZ_ASSERT(jsJitFrame().isBaselineJS());
return jsJitFrame().isConstructing();
case INTERP:
return interpFrame()->isConstructing();
}
MOZ_CRASH("Unexpected state");
}
bool FrameIter::ensureHasRematerializedFrame(JSContext* cx) {
MOZ_ASSERT(isIon());
return !!activation()->asJit()->getRematerializedFrame(cx, jsJitFrame());
}
bool FrameIter::hasUsableAbstractFramePtr() const {
switch (data_.state_) {
case DONE:
return false;
case JIT:
if (isJSJit()) {
if (jsJitFrame().isBaselineJS()) {
return true;
}
MOZ_ASSERT(jsJitFrame().isIonScripted());
return !!activation()->asJit()->lookupRematerializedFrame(
jsJitFrame().fp(), ionInlineFrames_.frameNo());
}
MOZ_ASSERT(isWasm());
return wasmFrame().debugEnabled();
case INTERP:
return true;
}
MOZ_CRASH("Unexpected state");
}
AbstractFramePtr FrameIter::abstractFramePtr() const {
MOZ_ASSERT(hasUsableAbstractFramePtr());
switch (data_.state_) {
case DONE:
break;
case JIT: {
if (isJSJit()) {
if (jsJitFrame().isBaselineJS()) {
return jsJitFrame().baselineFrame();
}
MOZ_ASSERT(isIonScripted());
return activation()->asJit()->lookupRematerializedFrame(
jsJitFrame().fp(), ionInlineFrames_.frameNo());
}
MOZ_ASSERT(isWasm());
MOZ_ASSERT(wasmFrame().debugEnabled());
return wasmFrame().debugFrame();
}
case INTERP:
MOZ_ASSERT(interpFrame());
return AbstractFramePtr(interpFrame());
}
MOZ_CRASH("Unexpected state");
}
void FrameIter::updatePcQuadratic() {
switch (data_.state_) {
case DONE:
break;
case INTERP: {
InterpreterFrame* frame = interpFrame();
InterpreterActivation* activation = data_.activations_->asInterpreter();
data_.interpFrames_ = InterpreterFrameIterator(activation);
while (data_.interpFrames_.frame() != frame) {
++data_.interpFrames_;
}
MOZ_ASSERT(data_.interpFrames_.frame() == frame);
data_.pc_ = data_.interpFrames_.pc();
return;
}
case JIT:
if (jsJitFrame().isBaselineJS()) {
jit::BaselineFrame* frame = jsJitFrame().baselineFrame();
jit::JitActivation* activation = data_.activations_->asJit();
data_.activations_ = ActivationIterator(data_.cx_);
while (data_.activations_.activation() != activation) {
++data_.activations_;
}
data_.jitFrames_ = JitFrameIter(data_.activations_->asJit());
while (!isJSJit() || !jsJitFrame().isBaselineJS() ||
jsJitFrame().baselineFrame() != frame) {
++data_.jitFrames_;
}
MOZ_ASSERT(jsJitFrame().baselineFrame() == frame);
jsJitFrame().baselineScriptAndPc(nullptr, &data_.pc_);
return;
}
break;
}
MOZ_CRASH("Unexpected state");
}
void FrameIter::wasmUpdateBytecodeOffset() {
MOZ_RELEASE_ASSERT(isWasm(), "Unexpected state");
wasm::DebugFrame* frame = wasmFrame().debugFrame();
data_.jitFrames_ = JitFrameIter(data_.activations_->asJit());
while (wasmFrame().debugFrame() != frame) {
++data_.jitFrames_;
}
MOZ_ASSERT(wasmFrame().debugFrame() == frame);
}
JSFunction* FrameIter::calleeTemplate() const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
MOZ_ASSERT(isFunctionFrame());
return &interpFrame()->callee();
case JIT:
if (jsJitFrame().isBaselineJS()) {
return jsJitFrame().callee();
}
MOZ_ASSERT(jsJitFrame().isIonScripted());
return ionInlineFrames_.calleeTemplate();
}
MOZ_CRASH("Unexpected state");
}
JSFunction* FrameIter::callee(JSContext* cx) const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
return calleeTemplate();
case JIT:
if (isIonScripted()) {
jit::MaybeReadFallback recover(cx, activation()->asJit(),
&jsJitFrame());
return ionInlineFrames_.callee(recover);
}
MOZ_ASSERT(jsJitFrame().isBaselineJS());
return calleeTemplate();
}
MOZ_CRASH("Unexpected state");
}
bool FrameIter::matchCallee(JSContext* cx, HandleFunction fun) const {
RootedFunction currentCallee(cx, calleeTemplate());
if (((currentCallee->flags() ^ fun->flags()) &
JSFunction::STABLE_ACROSS_CLONES) != 0 ||
currentCallee->nargs() != fun->nargs()) {
return false;
}
RootedObject global(cx, &fun->global());
bool useSameScript =
CanReuseScriptForClone(fun->realm(), currentCallee, global);
if (useSameScript &&
(currentCallee->hasScript() != fun->hasScript() ||
currentCallee->nonLazyScript() != fun->nonLazyScript())) {
return false;
}
return callee(cx) == fun;
}
unsigned FrameIter::numActualArgs() const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
MOZ_ASSERT(isFunctionFrame());
return interpFrame()->numActualArgs();
case JIT:
if (isIonScripted()) {
return ionInlineFrames_.numActualArgs();
}
MOZ_ASSERT(jsJitFrame().isBaselineJS());
return jsJitFrame().numActualArgs();
}
MOZ_CRASH("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::environmentChain(JSContext* cx) const {
switch (data_.state_) {
case DONE:
break;
case JIT:
if (isJSJit()) {
if (isIonScripted()) {
jit::MaybeReadFallback recover(cx, activation()->asJit(),
&jsJitFrame());
return ionInlineFrames_.environmentChain(recover);
}
return jsJitFrame().baselineFrame()->environmentChain();
}
MOZ_ASSERT(isWasm());
return wasmFrame().debugFrame()->environmentChain();
case INTERP:
return interpFrame()->environmentChain();
}
MOZ_CRASH("Unexpected state");
}
bool FrameIter::hasInitialEnvironment(JSContext* cx) const {
if (hasUsableAbstractFramePtr()) {
return abstractFramePtr().hasInitialEnvironment();
}
if (isWasm()) {
return false;
}
MOZ_ASSERT(isJSJit() && isIonScripted());
bool hasInitialEnv = false;
jit::MaybeReadFallback recover(cx, activation()->asJit(), &jsJitFrame());
ionInlineFrames_.environmentChain(recover, &hasInitialEnv);
return hasInitialEnv;
}
CallObject& FrameIter::callObj(JSContext* cx) const {
MOZ_ASSERT(calleeTemplate()->needsCallObject());
MOZ_ASSERT(hasInitialEnvironment(cx));
JSObject* pobj = environmentChain(cx);
while (!pobj->is<CallObject>()) {
pobj = pobj->enclosingEnvironment();
}
return pobj->as<CallObject>();
}
bool FrameIter::hasArgsObj() const { return abstractFramePtr().hasArgsObj(); }
ArgumentsObject& FrameIter::argsObj() const {
MOZ_ASSERT(hasArgsObj());
return abstractFramePtr().argsObj();
}
Value FrameIter::thisArgument(JSContext* cx) const {
MOZ_ASSERT(isFunctionFrame());
switch (data_.state_) {
case DONE:
break;
case JIT:
if (isIonScripted()) {
jit::MaybeReadFallback recover(cx, activation()->asJit(),
&jsJitFrame());
return ionInlineFrames_.thisArgument(recover);
}
return jsJitFrame().baselineFrame()->thisArgument();
case INTERP:
return interpFrame()->thisArgument();
}
MOZ_CRASH("Unexpected state");
}
Value FrameIter::newTarget() const {
switch (data_.state_) {
case DONE:
break;
case INTERP:
return interpFrame()->newTarget();
case JIT:
MOZ_ASSERT(jsJitFrame().isBaselineJS());
return jsJitFrame().baselineFrame()->newTarget();
}
MOZ_CRASH("Unexpected state");
}
Value FrameIter::returnValue() const {
switch (data_.state_) {
case DONE:
break;
case JIT:
if (jsJitFrame().isBaselineJS()) {
return jsJitFrame().baselineFrame()->returnValue();
}
break;
case INTERP:
return interpFrame()->returnValue();
}
MOZ_CRASH("Unexpected state");
}
void FrameIter::setReturnValue(const Value& v) {
switch (data_.state_) {
case DONE:
break;
case JIT:
if (jsJitFrame().isBaselineJS()) {
jsJitFrame().baselineFrame()->setReturnValue(v);
return;
}
break;
case INTERP:
interpFrame()->setReturnValue(v);
return;
}
MOZ_CRASH("Unexpected state");
}
size_t FrameIter::numFrameSlots() const {
switch (data_.state_) {
case DONE:
break;
case JIT: {
if (isIonScripted()) {
return ionInlineFrames_.snapshotIterator().numAllocations() -
ionInlineFrames_.script()->nfixed();
}
jit::BaselineFrame* frame = jsJitFrame().baselineFrame();
return frame->numValueSlots() - jsJitFrame().script()->nfixed();
}
case INTERP:
MOZ_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base());
return data_.interpFrames_.sp() - interpFrame()->base();
}
MOZ_CRASH("Unexpected state");
}
Value FrameIter::frameSlotValue(size_t index) const {
switch (data_.state_) {
case DONE:
break;
case JIT:
if (isIonScripted()) {
jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
index += ionInlineFrames_.script()->nfixed();
return si.maybeReadAllocByIndex(index);
}
index += jsJitFrame().script()->nfixed();
return *jsJitFrame().baselineFrame()->valueSlot(index);
case INTERP:
return interpFrame()->base()[index];
}
MOZ_CRASH("Unexpected state");
}
#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++();
}
}
}
Value ActivationEntryMonitor::asyncStack(JSContext* cx) {
RootedValue stack(cx, ObjectOrNullValue(cx->asyncStackForNewActivations()));
if (!cx->compartment()->wrap(cx, &stack)) {
cx->clearPendingException();
return UndefinedValue();
}
return stack;
}
void ActivationEntryMonitor::init(JSContext* cx, InterpreterFrame* entryFrame) {
gc::AutoSuppressGC suppressGC(cx);
RootedValue stack(cx, asyncStack(cx));
const char* asyncCause = cx->asyncCauseForNewActivations;
if (entryFrame->isFunctionFrame()) {
entryMonitor_->Entry(cx, &entryFrame->callee(), stack, asyncCause);
} else {
entryMonitor_->Entry(cx, entryFrame->script(), stack, asyncCause);
}
}
void ActivationEntryMonitor::init(JSContext* cx, jit::CalleeToken entryToken) {
gc::AutoSuppressGC suppressGC(cx);
RootedValue stack(cx, asyncStack(cx));
const char* asyncCause = cx->asyncCauseForNewActivations;
if (jit::CalleeTokenIsFunction(entryToken)) {
entryMonitor_->Entry(cx_, jit::CalleeTokenToFunction(entryToken), stack,
asyncCause);
} else {
entryMonitor_->Entry(cx_, jit::CalleeTokenToScript(entryToken), stack,
asyncCause);
}
}
jit::JitActivation::JitActivation(JSContext* cx)
: Activation(cx, Jit),
packedExitFP_(nullptr),
encodedWasmExitReason_(0),
prevJitActivation_(cx->jitActivation),
rematerializedFrames_(),
ionRecovery_(cx),
bailoutData_(nullptr),
lastProfilingFrame_(nullptr),
lastProfilingCallSite_(nullptr) {
cx->jitActivation = this;
registerProfiling();
}
jit::JitActivation::~JitActivation() {
if (isProfiling()) {
unregisterProfiling();
}
cx_->jitActivation = prevJitActivation_;
MOZ_ASSERT(ionRecovery_.empty());
MOZ_ASSERT(!bailoutData_);
MOZ_ASSERT(!isWasmTrapping());
clearRematerializedFrames();
}
void jit::JitActivation::setBailoutData(jit::BailoutFrameInfo* bailoutData) {
MOZ_ASSERT(!bailoutData_);
bailoutData_ = bailoutData;
}
void jit::JitActivation::cleanBailoutData() {
MOZ_ASSERT(bailoutData_);
bailoutData_ = nullptr;
}
void jit::JitActivation::removeRematerializedFrame(uint8_t* top) {
if (!rematerializedFrames_) {
return;
}
if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
RematerializedFrame::FreeInVector(p->value());
rematerializedFrames_->remove(p);
}
}
void jit::JitActivation::clearRematerializedFrames() {
if (!rematerializedFrames_) {
return;
}
for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty();
e.popFront()) {
RematerializedFrame::FreeInVector(e.front().value());
e.removeFront();
}
}
jit::RematerializedFrame* jit::JitActivation::getRematerializedFrame(
JSContext* cx, const JSJitFrameIter& iter, size_t inlineDepth) {
MOZ_ASSERT(iter.activation() == this);
MOZ_ASSERT(iter.isIonScripted());
if (!rematerializedFrames_) {
rematerializedFrames_ = cx->make_unique<RematerializedFrameTable>(cx);
if (!rematerializedFrames_) {
return nullptr;
}
}
uint8_t* top = iter.fp();
RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top);
if (!p) {
RematerializedFrameVector frames(cx);
InlineFrameIterator inlineIter(cx, &iter);
MaybeReadFallback recover(cx, this, &iter);
AutoRealmUnchecked ar(cx, iter.script()->realm());
if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter,
recover, frames)) {
return nullptr;
}
if (!rematerializedFrames_->add(p, top, std::move(frames))) {
ReportOutOfMemory(cx);
return nullptr;
}
DebugEnvironments::unsetPrevUpToDateUntil(cx, p->value()[inlineDepth]);
}
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::removeRematerializedFramesFromDebugger(JSContext* cx,
uint8_t* top) {
if (!cx->realm()->isDebuggee() || !rematerializedFrames_) {
return;
}
if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
for (uint32_t i = 0; i < p->value().length(); i++) {
Debugger::handleUnrecoverableIonBailoutError(cx, p->value()[i]);
}
RematerializedFrame::FreeInVector(p->value());
rematerializedFrames_->remove(p);
}
}
void jit::JitActivation::traceRematerializedFrames(JSTracer* trc) {
if (!rematerializedFrames_) {
return;
}
for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty();
e.popFront()) {
e.front().value().trace(trc);
}
}
bool jit::JitActivation::registerIonFrameRecovery(
RInstructionResults&& results) {
MOZ_ASSERT(!maybeIonFrameRecovery(results.frame()));
if (!ionRecovery_.append(std::move(results))) {
return false;
}
return true;
}
jit::RInstructionResults* jit::JitActivation::maybeIonFrameRecovery(
JitFrameLayout* fp) {
for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end();
it++) {
if (it->frame() == fp) {
return it;
}
}
return nullptr;
}
void jit::JitActivation::removeIonFrameRecovery(JitFrameLayout* fp) {
RInstructionResults* elem = maybeIonFrameRecovery(fp);
if (!elem) {
return;
}
ionRecovery_.erase(elem);
}
void jit::JitActivation::traceIonRecovery(JSTracer* trc) {
for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end();
it++) {
it->trace(trc);
}
}
void jit::JitActivation::startWasmTrap(wasm::Trap trap, uint32_t bytecodeOffset,
const wasm::RegisterState& state) {
MOZ_ASSERT(!isWasmTrapping());
bool unwound;
wasm::UnwindState unwindState;
MOZ_RELEASE_ASSERT(wasm::StartUnwinding(state, &unwindState, &unwound));
MOZ_ASSERT(unwound == (trap == wasm::Trap::IndirectCallBadSig));
void* pc = unwindState.pc;
wasm::Frame* fp = unwindState.fp;
const wasm::Code& code = fp->tls->instance->code();
MOZ_RELEASE_ASSERT(&code == wasm::LookupCode(pc));
if (unwound) {
bytecodeOffset = code.lookupCallSite(pc)->lineOrBytecode();
}
setWasmExitFP(fp);
wasmTrapData_.emplace();
wasmTrapData_->resumePC =
((uint8_t*)state.pc) + jit::WasmTrapInstructionLength;
wasmTrapData_->unwoundPC = pc;
wasmTrapData_->trap = trap;
wasmTrapData_->bytecodeOffset = bytecodeOffset;
MOZ_ASSERT(isWasmTrapping());
}
void jit::JitActivation::finishWasmTrap() {
MOZ_ASSERT(isWasmTrapping());
packedExitFP_ = nullptr;
wasmTrapData_.reset();
MOZ_ASSERT(!isWasmTrapping());
}
InterpreterFrameIterator& InterpreterFrameIterator::operator++() {
MOZ_ASSERT(!done());
if (fp_ != activation_->entryFrame_) {
pc_ = fp_->prevpc();
sp_ = fp_->prevsp();
fp_ = fp_->prev();
} else {
pc_ = nullptr;
sp_ = nullptr;
fp_ = nullptr;
}
return *this;
}
void Activation::registerProfiling() {
MOZ_ASSERT(isProfiling());
cx_->profilingActivation_ = this;
}
void Activation::unregisterProfiling() {
MOZ_ASSERT(isProfiling());
MOZ_ASSERT(cx_->profilingActivation_ == this);
cx_->profilingActivation_ = prevProfiling_;
}
ActivationIterator::ActivationIterator(JSContext* cx)
: activation_(cx->activation_) {
MOZ_ASSERT(cx == TlsContext.get());
}
ActivationIterator& ActivationIterator::operator++() {
MOZ_ASSERT(activation_);
activation_ = activation_->prev();
return *this;
}
JS::ProfilingFrameIterator::ProfilingFrameIterator(
JSContext* cx, const RegisterState& state,
const Maybe<uint64_t>& samplePositionInProfilerBuffer)
: cx_(cx),
samplePositionInProfilerBuffer_(samplePositionInProfilerBuffer),
activation_(nullptr) {
if (!cx->runtime()->geckoProfiler().enabled()) {
MOZ_CRASH(
"ProfilingFrameIterator called when geckoProfiler not enabled for "
"runtime.");
}
if (!cx->profilingActivation()) {
return;
}
if (!cx->isProfilerSamplingEnabled()) {
return;
}
activation_ = cx->profilingActivation();
MOZ_ASSERT(activation_->isProfiling());
static_assert(sizeof(wasm::ProfilingFrameIterator) <= StorageSpace &&
sizeof(jit::JSJitProfilingFrameIterator) <= StorageSpace,
"ProfilingFrameIterator::storage_ is too small");
static_assert(alignof(void*) >= alignof(wasm::ProfilingFrameIterator) &&
alignof(void*) >= alignof(jit::JSJitProfilingFrameIterator),
"ProfilingFrameIterator::storage_ is too weakly aligned");
iteratorConstruct(state);
settle();
}
JS::ProfilingFrameIterator::~ProfilingFrameIterator() {
if (!done()) {
MOZ_ASSERT(activation_->isProfiling());
iteratorDestroy();
}
}
void JS::ProfilingFrameIterator::operator++() {
MOZ_ASSERT(!done());
MOZ_ASSERT(activation_->isJit());
if (isWasm()) {
++wasmIter();
} else {
++jsJitIter();
}
settle();
}
void JS::ProfilingFrameIterator::settleFrames() {
if (isJSJit() && !jsJitIter().done() &&
jsJitIter().frameType() == jit::FrameType::WasmToJSJit) {
wasm::Frame* fp = (wasm::Frame*)jsJitIter().fp();
iteratorDestroy();
new (storage()) wasm::ProfilingFrameIterator(*activation_->asJit(), fp);
kind_ = Kind::Wasm;
MOZ_ASSERT(!wasmIter().done());
return;
}
if (isWasm() && wasmIter().done() && wasmIter().unwoundIonCallerFP()) {
uint8_t* fp = wasmIter().unwoundIonCallerFP();
iteratorDestroy();
new (storage())
jit::JSJitProfilingFrameIterator((jit::CommonFrameLayout*)fp);
kind_ = Kind::JSJit;
MOZ_ASSERT(!jsJitIter().done());
return;
}
}
void JS::ProfilingFrameIterator::settle() {
settleFrames();
while (iteratorDone()) {
iteratorDestroy();
activation_ = activation_->prevProfiling();
if (!activation_) {
return;
}
iteratorConstruct();
settleFrames();
}
}
void JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state) {
MOZ_ASSERT(!done());
MOZ_ASSERT(activation_->isJit());
jit::JitActivation* activation = activation_->asJit();
if (activation->hasWasmExitFP() || wasm::InCompiledCode(state.pc)) {
new (storage()) wasm::ProfilingFrameIterator(*activation, state);
kind_ = Kind::Wasm;
return;
}
new (storage()) jit::JSJitProfilingFrameIterator(cx_, state.pc);
kind_ = Kind::JSJit;
}
void JS::ProfilingFrameIterator::iteratorConstruct() {
MOZ_ASSERT(!done());
MOZ_ASSERT(activation_->isJit());
jit::JitActivation* activation = activation_->asJit();
if (activation->hasWasmExitFP()) {
new (storage()) wasm::ProfilingFrameIterator(*activation);
kind_ = Kind::Wasm;
return;
}
auto* fp = (jit::ExitFrameLayout*)activation->jsExitFP();
new (storage()) jit::JSJitProfilingFrameIterator(fp);
kind_ = Kind::JSJit;
}
void JS::ProfilingFrameIterator::iteratorDestroy() {
MOZ_ASSERT(!done());
MOZ_ASSERT(activation_->isJit());
if (isWasm()) {
wasmIter().~ProfilingFrameIterator();
return;
}
jsJitIter().~JSJitProfilingFrameIterator();
}
bool JS::ProfilingFrameIterator::iteratorDone() {
MOZ_ASSERT(!done());
MOZ_ASSERT(activation_->isJit());
if (isWasm()) {
return wasmIter().done();
}
return jsJitIter().done();
}
void* JS::ProfilingFrameIterator::stackAddress() const {
MOZ_ASSERT(!done());
MOZ_ASSERT(activation_->isJit());
if (isWasm()) {
return wasmIter().stackAddress();
}
return jsJitIter().stackAddress();
}
Maybe<JS::ProfilingFrameIterator::Frame>
JS::ProfilingFrameIterator::getPhysicalFrameAndEntry(
jit::JitcodeGlobalEntry* entry) const {
void* stackAddr = stackAddress();
if (isWasm()) {
Frame frame;
frame.kind = Frame_Wasm;
frame.stackAddress = stackAddr;
frame.returnAddress = nullptr;
frame.activation = activation_;
frame.label = nullptr;
frame.endStackAddress = activation_->asJit()->jsOrWasmExitFP();
return mozilla::Some(frame);
}
MOZ_ASSERT(isJSJit());
void* returnAddr = jsJitIter().resumePCinCurrentFrame();
jit::JitcodeGlobalTable* table =
cx_->runtime()->jitRuntime()->getJitcodeGlobalTable();
if (samplePositionInProfilerBuffer_) {
*entry = table->lookupForSamplerInfallible(
returnAddr, cx_->runtime(), *samplePositionInProfilerBuffer_);
} else {
*entry = table->lookupInfallible(returnAddr);
}
MOZ_ASSERT(entry->isIon() || entry->isIonCache() || entry->isBaseline() ||
entry->isDummy());
if (entry->isDummy()) {
return mozilla::Nothing();
}
Frame frame;
frame.kind = entry->isBaseline() ? Frame_Baseline : Frame_Ion;
frame.stackAddress = stackAddr;
frame.returnAddress = returnAddr;
frame.activation = activation_;
frame.label = nullptr;
frame.endStackAddress = activation_->asJit()->jsOrWasmExitFP();
return mozilla::Some(frame);
}
uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames,
uint32_t offset,
uint32_t end) const {
if (offset >= end) {
return 0;
}
jit::JitcodeGlobalEntry entry;
Maybe<Frame> physicalFrame = getPhysicalFrameAndEntry(&entry);
if (physicalFrame.isNothing()) {
return 0;
}
if (isWasm()) {
frames[offset] = physicalFrame.value();
frames[offset].label = wasmIter().label();
return 1;
}
const char* labels[64];
uint32_t depth = entry.callStackAtAddr(cx_->runtime(),
jsJitIter().resumePCinCurrentFrame(),
labels, ArrayLength(labels));
MOZ_ASSERT(depth < ArrayLength(labels));
for (uint32_t i = 0; i < depth; i++) {
if (offset + i >= end) {
return i;
}
frames[offset + i] = physicalFrame.value();
frames[offset + i].label = labels[i];
}
return depth;
}
Maybe<JS::ProfilingFrameIterator::Frame>
JS::ProfilingFrameIterator::getPhysicalFrameWithoutLabel() const {
jit::JitcodeGlobalEntry unused;
return getPhysicalFrameAndEntry(&unused);
}
bool JS::ProfilingFrameIterator::isWasm() const {
MOZ_ASSERT(!done());
return kind_ == Kind::Wasm;
}
bool JS::ProfilingFrameIterator::isJSJit() const {
return kind_ == Kind::JSJit;
}