tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Stack.cpp (25079B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "vm/Stack-inl.h"
      8 
      9 #include "mozilla/Maybe.h"  // mozilla::Maybe
     10 
     11 #include <algorithm>  // std::max
     12 #include <stddef.h>   // size_t
     13 #include <stdint.h>   // uint8_t, uint32_t
     14 
     15 #include "gc/Tracer.h"  // js::TraceRoot
     16 #include "jit/InlineScriptTree.h"
     17 #include "jit/JitcodeMap.h"
     18 #include "jit/JitRuntime.h"
     19 #include "js/friend/ErrorMessages.h"  // JSMSG_*
     20 #include "js/Value.h"                 // JS::Value
     21 #include "vm/FrameIter.h"             // js::FrameIter
     22 #include "vm/JSContext.h"
     23 #include "wasm/WasmProcess.h"
     24 
     25 #include "jit/JSJitFrameIter-inl.h"
     26 #include "vm/Probes-inl.h"
     27 
     28 using namespace js;
     29 
     30 using mozilla::Maybe;
     31 
     32 using JS::Value;
     33 
     34 /*****************************************************************************/
     35 
     36 void InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script,
     37                                        AbstractFramePtr evalInFramePrev,
     38                                        HandleObject envChain) {
     39  flags_ = 0;
     40  script_ = script;
     41 
     42  envChain_ = envChain.get();
     43  prev_ = nullptr;
     44  prevpc_ = nullptr;
     45  prevsp_ = nullptr;
     46 
     47  evalInFramePrev_ = evalInFramePrev;
     48  MOZ_ASSERT_IF(evalInFramePrev, isDebuggerEvalFrame());
     49 
     50  if (script->isDebuggee()) {
     51    setIsDebuggee();
     52  }
     53 
     54 #ifdef DEBUG
     55  Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
     56 #endif
     57 }
     58 
     59 ArrayObject* InterpreterFrame::createRestParameter(JSContext* cx) {
     60  MOZ_ASSERT(script()->hasRest());
     61  unsigned nformal = callee().nargs() - 1, nactual = numActualArgs();
     62  unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
     63  Value* restvp = argv() + nformal;
     64  return NewDenseCopiedArray(cx, nrest, restvp);
     65 }
     66 
     67 static inline void AssertScopeMatchesEnvironment(Scope* scope,
     68                                                 JSObject* originalEnv) {
     69 #ifdef DEBUG
     70  JSObject* env = originalEnv;
     71  for (ScopeIter si(scope); si; si++) {
     72    if (si.kind() == ScopeKind::NonSyntactic) {
     73      while (env->is<WithEnvironmentObject>() ||
     74             env->is<NonSyntacticVariablesObject>() ||
     75             (env->is<LexicalEnvironmentObject>() &&
     76              !env->as<LexicalEnvironmentObject>().isSyntactic())) {
     77        MOZ_ASSERT(!IsSyntacticEnvironment(env));
     78        env = &env->as<EnvironmentObject>().enclosingEnvironment();
     79      }
     80    } else if (si.hasSyntacticEnvironment()) {
     81      switch (si.kind()) {
     82        case ScopeKind::Function:
     83          MOZ_ASSERT(env->as<CallObject>()
     84                         .callee()
     85                         .maybeCanonicalFunction()
     86                         ->nonLazyScript() ==
     87                     si.scope()->as<FunctionScope>().script());
     88          env = &env->as<CallObject>().enclosingEnvironment();
     89          break;
     90 
     91        case ScopeKind::FunctionBodyVar:
     92          MOZ_ASSERT(&env->as<VarEnvironmentObject>().scope() == si.scope());
     93          env = &env->as<VarEnvironmentObject>().enclosingEnvironment();
     94          break;
     95 
     96        case ScopeKind::Lexical:
     97        case ScopeKind::SimpleCatch:
     98        case ScopeKind::Catch:
     99        case ScopeKind::NamedLambda:
    100        case ScopeKind::StrictNamedLambda:
    101        case ScopeKind::FunctionLexical:
    102        case ScopeKind::ClassBody:
    103          MOZ_ASSERT(&env->as<ScopedLexicalEnvironmentObject>().scope() ==
    104                     si.scope());
    105          env =
    106              &env->as<ScopedLexicalEnvironmentObject>().enclosingEnvironment();
    107          break;
    108 
    109        case ScopeKind::With:
    110          MOZ_ASSERT(&env->as<WithEnvironmentObject>().scope() == si.scope());
    111          env = &env->as<WithEnvironmentObject>().enclosingEnvironment();
    112          break;
    113 
    114        case ScopeKind::Eval:
    115        case ScopeKind::StrictEval:
    116          env = &env->as<VarEnvironmentObject>().enclosingEnvironment();
    117          break;
    118 
    119        case ScopeKind::Global:
    120          env =
    121              &env->as<GlobalLexicalEnvironmentObject>().enclosingEnvironment();
    122          MOZ_ASSERT(env->is<GlobalObject>());
    123          break;
    124 
    125        case ScopeKind::NonSyntactic:
    126          MOZ_CRASH("NonSyntactic should not have a syntactic environment");
    127          break;
    128 
    129        case ScopeKind::Module:
    130          MOZ_ASSERT(&env->as<ModuleEnvironmentObject>().module() ==
    131                     si.scope()->as<ModuleScope>().module());
    132          env = &env->as<ModuleEnvironmentObject>().enclosingEnvironment();
    133          break;
    134 
    135        case ScopeKind::WasmInstance:
    136          env =
    137              &env->as<WasmInstanceEnvironmentObject>().enclosingEnvironment();
    138          break;
    139 
    140        case ScopeKind::WasmFunction:
    141          env = &env->as<WasmFunctionCallObject>().enclosingEnvironment();
    142          break;
    143      }
    144    }
    145  }
    146 
    147  // In the case of a non-syntactic env chain, the immediate parent of the
    148  // outermost non-syntactic env may be the global lexical env, or, if
    149  // called from Debugger, a DebugEnvironmentProxy.
    150  //
    151  // In the case of a syntactic env chain, the outermost env is always a
    152  // GlobalObject.
    153  MOZ_ASSERT(env->is<GlobalObject>() ||
    154             env->is<GlobalLexicalEnvironmentObject>() ||
    155             env->is<DebugEnvironmentProxy>());
    156 #endif
    157 }
    158 
    159 static inline void AssertScopeMatchesEnvironment(InterpreterFrame* fp,
    160                                                 jsbytecode* pc) {
    161 #ifdef DEBUG
    162  // If we OOMed before fully initializing the environment chain, the scope
    163  // and environment will definitely mismatch.
    164  if (fp->script()->initialEnvironmentShape() && fp->hasInitialEnvironment()) {
    165    AssertScopeMatchesEnvironment(fp->script()->innermostScope(pc),
    166                                  fp->environmentChain());
    167  }
    168 #endif
    169 }
    170 
    171 bool InterpreterFrame::initFunctionEnvironmentObjects(JSContext* cx) {
    172  return js::InitFunctionEnvironmentObjects(cx, this);
    173 }
    174 
    175 bool InterpreterFrame::prologue(JSContext* cx) {
    176  RootedScript script(cx, this->script());
    177 
    178  MOZ_ASSERT(cx->interpreterRegs().pc == script->code());
    179  MOZ_ASSERT(cx->realm() == script->realm());
    180 
    181  if (!isFunctionFrame()) {
    182    return probes::EnterScript(cx, script, nullptr, this);
    183  }
    184 
    185  // At this point, we've yet to push any environments. Check that they
    186  // match the enclosing scope.
    187  AssertScopeMatchesEnvironment(script->enclosingScope(), environmentChain());
    188 
    189  if (callee().needsFunctionEnvironmentObjects() &&
    190      !initFunctionEnvironmentObjects(cx)) {
    191    return false;
    192  }
    193 
    194  MOZ_ASSERT_IF(isConstructing(),
    195                thisArgument().isObject() ||
    196                    thisArgument().isMagic(JS_UNINITIALIZED_LEXICAL));
    197 
    198  return probes::EnterScript(cx, script, script->function(), this);
    199 }
    200 
    201 void InterpreterFrame::epilogue(JSContext* cx, jsbytecode* pc) {
    202  RootedScript script(cx, this->script());
    203  MOZ_ASSERT(cx->realm() == script->realm());
    204  probes::ExitScript(cx, script, script->function(),
    205                     hasPushedGeckoProfilerFrame());
    206 
    207  // Check that the scope matches the environment at the point of leaving
    208  // the frame.
    209  AssertScopeMatchesEnvironment(this, pc);
    210 
    211  EnvironmentIter ei(cx, this, pc);
    212  UnwindAllEnvironmentsInFrame(cx, ei);
    213 
    214  if (isFunctionFrame()) {
    215    if (!callee().isGenerator() && !callee().isAsync() && isConstructing() &&
    216        thisArgument().isObject() && returnValue().isPrimitive()) {
    217      setReturnValue(thisArgument());
    218    }
    219 
    220    return;
    221  }
    222 
    223  MOZ_ASSERT(isEvalFrame() || isGlobalFrame() || isModuleFrame());
    224 }
    225 
    226 bool InterpreterFrame::checkReturn(JSContext* cx, HandleValue thisv,
    227                                   MutableHandleValue result) {
    228  MOZ_ASSERT(script()->isDerivedClassConstructor());
    229  MOZ_ASSERT(isFunctionFrame());
    230  MOZ_ASSERT(callee().isClassConstructor());
    231 
    232  HandleValue retVal = returnValue();
    233  if (retVal.isObject()) {
    234    result.set(retVal);
    235    return true;
    236  }
    237 
    238  if (!retVal.isUndefined()) {
    239    ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, retVal,
    240                     nullptr);
    241    return false;
    242  }
    243 
    244  if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL)) {
    245    return ThrowUninitializedThis(cx);
    246  }
    247 
    248  result.set(thisv);
    249  return true;
    250 }
    251 
    252 bool InterpreterFrame::pushVarEnvironment(JSContext* cx, Handle<Scope*> scope) {
    253  return js::PushVarEnvironmentObject(cx, scope, this);
    254 }
    255 
    256 bool InterpreterFrame::pushLexicalEnvironment(JSContext* cx,
    257                                              Handle<LexicalScope*> scope) {
    258  BlockLexicalEnvironmentObject* env =
    259      BlockLexicalEnvironmentObject::createForFrame(cx, scope, this);
    260  if (!env) {
    261    return false;
    262  }
    263 
    264  pushOnEnvironmentChain(*env);
    265  return true;
    266 }
    267 
    268 bool InterpreterFrame::freshenLexicalEnvironment(JSContext* cx,
    269                                                 jsbytecode* pc) {
    270  Rooted<BlockLexicalEnvironmentObject*> env(
    271      cx, &envChain_->as<BlockLexicalEnvironmentObject>());
    272  BlockLexicalEnvironmentObject* fresh =
    273      BlockLexicalEnvironmentObject::clone(cx, env);
    274  if (!fresh) {
    275    return false;
    276  }
    277 
    278  if (MOZ_UNLIKELY(cx->realm()->isDebuggee())) {
    279    Rooted<BlockLexicalEnvironmentObject*> freshRoot(cx, fresh);
    280    DebugEnvironments::onPopLexical(cx, this, pc);
    281    fresh = freshRoot;
    282  }
    283 
    284  replaceInnermostEnvironment(*fresh);
    285  return true;
    286 }
    287 
    288 bool InterpreterFrame::recreateLexicalEnvironment(JSContext* cx,
    289                                                  jsbytecode* pc) {
    290  Rooted<BlockLexicalEnvironmentObject*> env(
    291      cx, &envChain_->as<BlockLexicalEnvironmentObject>());
    292  BlockLexicalEnvironmentObject* fresh =
    293      BlockLexicalEnvironmentObject::recreate(cx, env);
    294  if (!fresh) {
    295    return false;
    296  }
    297 
    298  if (MOZ_UNLIKELY(cx->realm()->isDebuggee())) {
    299    Rooted<BlockLexicalEnvironmentObject*> freshRoot(cx, fresh);
    300    DebugEnvironments::onPopLexical(cx, this, pc);
    301    fresh = freshRoot;
    302  }
    303 
    304  replaceInnermostEnvironment(*fresh);
    305  return true;
    306 }
    307 
    308 bool InterpreterFrame::pushClassBodyEnvironment(JSContext* cx,
    309                                                Handle<ClassBodyScope*> scope) {
    310  ClassBodyLexicalEnvironmentObject* env =
    311      ClassBodyLexicalEnvironmentObject::createForFrame(cx, scope, this);
    312  if (!env) {
    313    return false;
    314  }
    315 
    316  pushOnEnvironmentChain(*env);
    317  return true;
    318 }
    319 
    320 void InterpreterFrame::trace(JSTracer* trc, Value* sp, jsbytecode* pc) {
    321  TraceRoot(trc, &envChain_, "env chain");
    322  TraceRoot(trc, &script_, "script");
    323 
    324  if (flags_ & HAS_ARGS_OBJ) {
    325    TraceRoot(trc, &argsObj_, "arguments");
    326  }
    327 
    328  if (hasReturnValue()) {
    329    TraceRoot(trc, &rval_, "rval");
    330  }
    331 
    332  MOZ_ASSERT(sp >= slots());
    333 
    334  if (hasArgs()) {
    335    // Trace the callee and |this|. When we're doing a moving GC, we
    336    // need to fix up the callee pointer before we use it below, under
    337    // numFormalArgs() and script().
    338    TraceRootRange(trc, 2, argv_ - 2, "fp callee and this");
    339 
    340    // Trace arguments.
    341    unsigned argc = std::max(numActualArgs(), numFormalArgs());
    342    TraceRootRange(trc, argc + isConstructing(), argv_, "fp argv");
    343  }
    344 
    345  JSScript* script = this->script();
    346  size_t nfixed = script->nfixed();
    347  size_t nlivefixed = script->calculateLiveFixed(pc);
    348 
    349  if (nfixed == nlivefixed) {
    350    // All locals are live.
    351    traceValues(trc, 0, sp - slots());
    352  } else {
    353    // Trace operand stack.
    354    traceValues(trc, nfixed, sp - slots());
    355 
    356    // Clear dead block-scoped locals.
    357    while (nfixed > nlivefixed) {
    358      unaliasedLocal(--nfixed).setUndefined();
    359    }
    360 
    361    // Trace live locals.
    362    traceValues(trc, 0, nlivefixed);
    363  }
    364 
    365  if (auto* debugEnvs = script->realm()->debugEnvs()) {
    366    debugEnvs->traceLiveFrame(trc, this);
    367  }
    368 }
    369 
    370 void InterpreterFrame::traceValues(JSTracer* trc, unsigned start,
    371                                   unsigned end) {
    372  if (start < end) {
    373    TraceRootRange(trc, end - start, slots() + start, "vm_stack");
    374  }
    375 }
    376 
    377 static void TraceInterpreterActivation(JSTracer* trc,
    378                                       InterpreterActivation* act) {
    379  for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
    380    InterpreterFrame* fp = frames.frame();
    381    fp->trace(trc, frames.sp(), frames.pc());
    382  }
    383 }
    384 
    385 void js::TraceInterpreterActivations(JSContext* cx, JSTracer* trc) {
    386  for (ActivationIterator iter(cx); !iter.done(); ++iter) {
    387    Activation* act = iter.activation();
    388    if (act->isInterpreter()) {
    389      TraceInterpreterActivation(trc, act->asInterpreter());
    390    }
    391  }
    392 }
    393 
    394 /*****************************************************************************/
    395 
    396 // Unlike the other methods of this class, this method is defined here so that
    397 // we don't have to #include jsautooplen.h in vm/Stack.h.
    398 void InterpreterRegs::setToEndOfScript() { sp = fp()->base(); }
    399 
    400 /*****************************************************************************/
    401 
    402 InterpreterFrame* InterpreterStack::pushInvokeFrame(
    403    JSContext* cx, const CallArgs& args, MaybeConstruct constructing) {
    404  LifoAlloc::Mark mark = allocator_.mark();
    405 
    406  RootedFunction fun(cx, &args.callee().as<JSFunction>());
    407  RootedScript script(cx, fun->nonLazyScript());
    408 
    409  Value* argv;
    410  InterpreterFrame* fp = getCallFrame(cx, args, script, constructing, &argv);
    411  if (!fp) {
    412    return nullptr;
    413  }
    414 
    415  fp->mark_ = mark;
    416  fp->initCallFrame(nullptr, nullptr, nullptr, *fun, script, argv,
    417                    args.length(), constructing);
    418  return fp;
    419 }
    420 
    421 InterpreterFrame* InterpreterStack::pushExecuteFrame(
    422    JSContext* cx, HandleScript script, HandleObject envChain,
    423    AbstractFramePtr evalInFrame) {
    424  LifoAlloc::Mark mark = allocator_.mark();
    425 
    426  unsigned nvars = script->nslots();
    427  uint8_t* buffer =
    428      allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value));
    429  if (!buffer) {
    430    return nullptr;
    431  }
    432 
    433  InterpreterFrame* fp = reinterpret_cast<InterpreterFrame*>(buffer);
    434  fp->mark_ = mark;
    435  fp->initExecuteFrame(cx, script, evalInFrame, envChain);
    436  fp->initLocals();
    437 
    438  return fp;
    439 }
    440 
    441 /*****************************************************************************/
    442 
    443 InterpreterFrameIterator& InterpreterFrameIterator::operator++() {
    444  MOZ_ASSERT(!done());
    445  if (fp_ != activation_->entryFrame_) {
    446    pc_ = fp_->prevpc();
    447    sp_ = fp_->prevsp();
    448    fp_ = fp_->prev();
    449  } else {
    450    pc_ = nullptr;
    451    sp_ = nullptr;
    452    fp_ = nullptr;
    453  }
    454  return *this;
    455 }
    456 
    457 JS::ProfilingFrameIterator::ProfilingFrameIterator(
    458    JSContext* cx, const RegisterState& state,
    459    const Maybe<uint64_t>& samplePositionInProfilerBuffer)
    460    : cx_(cx),
    461      samplePositionInProfilerBuffer_(samplePositionInProfilerBuffer),
    462      activation_(nullptr) {
    463  if (!cx->runtime()->geckoProfiler().enabled()) {
    464    MOZ_CRASH(
    465        "ProfilingFrameIterator called when geckoProfiler not enabled for "
    466        "runtime.");
    467  }
    468 
    469  if (!cx->profilingActivation()) {
    470    return;
    471  }
    472 
    473  // If profiler sampling is not enabled, skip.
    474  if (!cx->isProfilerSamplingEnabled()) {
    475    return;
    476  }
    477 
    478  activation_ = cx->profilingActivation();
    479 
    480  MOZ_ASSERT(activation_->isProfiling());
    481 
    482  static_assert(sizeof(wasm::ProfilingFrameIterator) <= StorageSpace &&
    483                    sizeof(jit::JSJitProfilingFrameIterator) <= StorageSpace,
    484                "ProfilingFrameIterator::storage_ is too small");
    485  static_assert(alignof(void*) >= alignof(wasm::ProfilingFrameIterator) &&
    486                    alignof(void*) >= alignof(jit::JSJitProfilingFrameIterator),
    487                "ProfilingFrameIterator::storage_ is too weakly aligned");
    488 
    489  iteratorConstruct(state);
    490  settle();
    491 }
    492 
    493 JS::ProfilingFrameIterator::~ProfilingFrameIterator() {
    494  if (!done()) {
    495    MOZ_ASSERT(activation_->isProfiling());
    496    iteratorDestroy();
    497  }
    498 }
    499 
    500 void JS::ProfilingFrameIterator::operator++() {
    501  MOZ_ASSERT(!done());
    502  MOZ_ASSERT(activation_->isJit());
    503  if (isWasm()) {
    504    ++wasmIter();
    505  } else {
    506    ++jsJitIter();
    507  }
    508  settle();
    509 }
    510 
    511 void JS::ProfilingFrameIterator::settleFrames() {
    512  // Handle transition frames (see comment in JitFrameIter::operator++).
    513  if (isJSJit() && jsJitIter().done() && jsJitIter().wasmCallerFP()) {
    514    wasm::Frame* fp = (wasm::Frame*)jsJitIter().wasmCallerFP();
    515    iteratorDestroy();
    516    new (storage()) wasm::ProfilingFrameIterator(fp);
    517    kind_ = Kind::Wasm;
    518    MOZ_ASSERT(!wasmIter().done());
    519    maybeSetEndStackAddress(wasmIter().endStackAddress());
    520    return;
    521  }
    522 
    523  if (isWasm() && wasmIter().done() && wasmIter().unwoundJitCallerFP()) {
    524    uint8_t* fp = wasmIter().unwoundJitCallerFP();
    525    iteratorDestroy();
    526    // Using this ctor will skip the first jit->wasm frame, which is
    527    // needed because the profiling iterator doesn't know how to unwind
    528    // when the callee has no script.
    529    new (storage())
    530        jit::JSJitProfilingFrameIterator((jit::CommonFrameLayout*)fp);
    531    kind_ = Kind::JSJit;
    532    maybeSetEndStackAddress(jsJitIter().endStackAddress());
    533    return;
    534  }
    535 }
    536 
    537 void JS::ProfilingFrameIterator::settle() {
    538  settleFrames();
    539  while (iteratorDone()) {
    540    iteratorDestroy();
    541    activation_ = activation_->prevProfiling();
    542    endStackAddress_ = nullptr;
    543    if (!activation_) {
    544      return;
    545    }
    546    iteratorConstruct();
    547    settleFrames();
    548  }
    549 }
    550 
    551 void JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state) {
    552  MOZ_ASSERT(!done());
    553  MOZ_ASSERT(activation_->isJit());
    554 
    555  jit::JitActivation* activation = activation_->asJit();
    556 
    557  // We want to know if we should start with a wasm profiling frame iterator
    558  // or not. To determine this, there are three possibilities:
    559  // - we've exited to C++ from wasm, in which case the activation
    560  //   exitFP low bit is tagged and we can test hasWasmExitFP().
    561  // - we're in wasm code, so we can do a lookup on PC.
    562  // - in all the other cases, we're not in wasm or we haven't exited from
    563  //   wasm.
    564  if (activation->hasWasmExitFP() || wasm::InCompiledCode(state.pc)) {
    565    new (storage()) wasm::ProfilingFrameIterator(*activation, state);
    566    kind_ = Kind::Wasm;
    567    maybeSetEndStackAddress(wasmIter().endStackAddress());
    568    return;
    569  }
    570 
    571  new (storage()) jit::JSJitProfilingFrameIterator(cx_, state.pc, state.sp);
    572  kind_ = Kind::JSJit;
    573  maybeSetEndStackAddress(jsJitIter().endStackAddress());
    574 }
    575 
    576 void JS::ProfilingFrameIterator::iteratorConstruct() {
    577  MOZ_ASSERT(!done());
    578  MOZ_ASSERT(activation_->isJit());
    579 
    580  jit::JitActivation* activation = activation_->asJit();
    581 
    582  // The same reasoning as in the above iteratorConstruct variant applies
    583  // here, except that it's even simpler: since this activation is higher up
    584  // on the stack, it can only have exited to C++, through wasm or ion.
    585  if (activation->hasWasmExitFP()) {
    586    new (storage()) wasm::ProfilingFrameIterator(*activation);
    587    kind_ = Kind::Wasm;
    588    maybeSetEndStackAddress(wasmIter().endStackAddress());
    589    return;
    590  }
    591 
    592  auto* fp = (jit::ExitFrameLayout*)activation->jsExitFP();
    593  new (storage()) jit::JSJitProfilingFrameIterator(fp);
    594  kind_ = Kind::JSJit;
    595  maybeSetEndStackAddress(jsJitIter().endStackAddress());
    596 }
    597 
    598 void JS::ProfilingFrameIterator::iteratorDestroy() {
    599  MOZ_ASSERT(!done());
    600  MOZ_ASSERT(activation_->isJit());
    601 
    602  if (isWasm()) {
    603    wasmIter().~ProfilingFrameIterator();
    604    return;
    605  }
    606 
    607  jsJitIter().~JSJitProfilingFrameIterator();
    608 }
    609 
    610 bool JS::ProfilingFrameIterator::iteratorDone() {
    611  MOZ_ASSERT(!done());
    612  MOZ_ASSERT(activation_->isJit());
    613 
    614  if (isWasm()) {
    615    return wasmIter().done();
    616  }
    617 
    618  return jsJitIter().done();
    619 }
    620 
    621 void* JS::ProfilingFrameIterator::stackAddress() const {
    622  MOZ_ASSERT(!done());
    623  MOZ_ASSERT(activation_->isJit());
    624 
    625  if (isWasm()) {
    626    return wasmIter().stackAddress();
    627  }
    628 
    629  return jsJitIter().stackAddress();
    630 }
    631 
    632 Maybe<JS::ProfilingFrameIterator::Frame>
    633 JS::ProfilingFrameIterator::getPhysicalFrameAndEntry(
    634    const jit::JitcodeGlobalEntry** entry) const {
    635  *entry = nullptr;
    636 
    637  void* stackAddr = stackAddress();
    638 
    639  MOZ_DIAGNOSTIC_ASSERT(endStackAddress_);
    640 #ifndef ENABLE_WASM_JSPI
    641  // The stack addresses are monotonically increasing, except when
    642  // suspendable stacks are present (e.g. when JS PI is enabled).
    643  MOZ_DIAGNOSTIC_ASSERT(stackAddr >= endStackAddress_);
    644 #endif
    645 
    646  if (isWasm()) {
    647    Frame frame;
    648    switch (wasmIter().category()) {
    649      case wasm::ProfilingFrameIterator::Category::Baseline: {
    650        frame.kind = FrameKind::Frame_WasmBaseline;
    651        break;
    652      }
    653      case wasm::ProfilingFrameIterator::Category::Ion: {
    654        frame.kind = FrameKind::Frame_WasmIon;
    655        break;
    656      }
    657      default: {
    658        frame.kind = FrameKind::Frame_WasmOther;
    659        break;
    660      }
    661    }
    662    frame.stackAddress = stackAddr;
    663    frame.returnAddress_ = nullptr;
    664    frame.activation = activation_;
    665    frame.label = nullptr;
    666    frame.endStackAddress = endStackAddress_;
    667    frame.interpreterScript = nullptr;
    668    // TODO: get the realm ID of wasm frames. Bug 1596235.
    669    frame.realmID = 0;
    670    frame.sourceId = 0;
    671    return mozilla::Some(frame);
    672  }
    673 
    674  MOZ_ASSERT(isJSJit());
    675 
    676  if (jit::IsPortableBaselineInterpreterEnabled()) {
    677    return mozilla::Nothing();
    678  }
    679 
    680  // Look up an entry for the return address.
    681  void* returnAddr = jsJitIter().resumePCinCurrentFrame();
    682  jit::JitcodeGlobalTable* table =
    683      cx_->runtime()->jitRuntime()->getJitcodeGlobalTable();
    684 
    685  // NB:
    686  // The following lookups should be infallible, but the ad-hoc stackwalking
    687  // code rots easily and corner cases where frames can't be looked up
    688  // occur too often (e.g. once every day).
    689  //
    690  // The calls to `lookup*` below have been changed from infallible ones to
    691  // fallible ones.  The proper solution to this problem is to fix all
    692  // the jitcode to use frame-pointers and reliably walk the stack with those.
    693  if (samplePositionInProfilerBuffer_) {
    694    *entry = table->lookupForSampler(returnAddr, cx_->runtime(),
    695                                     *samplePositionInProfilerBuffer_);
    696  } else {
    697    *entry = table->lookup(returnAddr);
    698  }
    699 
    700  // Failed to look up a jitcode entry for the given address, ignore.
    701  if (!*entry) {
    702    return mozilla::Nothing();
    703  }
    704 
    705  // Dummy frames produce no stack frames.
    706  if ((*entry)->isDummy()) {
    707    return mozilla::Nothing();
    708  }
    709 
    710  Frame frame;
    711  if ((*entry)->isBaselineInterpreter()) {
    712    frame.kind = Frame_BaselineInterpreter;
    713  } else if ((*entry)->isBaseline() || (*entry)->isRealmIndependentShared()) {
    714    frame.kind = Frame_Baseline;
    715  } else {
    716    MOZ_ASSERT((*entry)->isIon() || (*entry)->isIonIC());
    717    frame.kind = Frame_Ion;
    718  }
    719  frame.stackAddress = stackAddr;
    720  if ((*entry)->isBaselineInterpreter()) {
    721    frame.label = jsJitIter().baselineInterpreterLabel();
    722    jsJitIter().baselineInterpreterScriptPC(&frame.interpreterScript,
    723                                            &frame.interpreterPC_,
    724                                            &frame.realmID, &frame.sourceId);
    725    MOZ_ASSERT(frame.interpreterScript);
    726    MOZ_ASSERT(frame.interpreterPC_);
    727  } else {
    728    frame.interpreterScript = nullptr;
    729    frame.returnAddress_ = returnAddr;
    730    frame.label = nullptr;
    731    frame.realmID = 0;
    732    frame.sourceId = 0;
    733  }
    734  frame.activation = activation_;
    735  frame.endStackAddress = endStackAddress_;
    736  return mozilla::Some(frame);
    737 }
    738 
    739 uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames,
    740                                                  uint32_t offset,
    741                                                  uint32_t end) const {
    742  if (offset >= end) {
    743    return 0;
    744  }
    745 
    746  const jit::JitcodeGlobalEntry* entry;
    747  Maybe<Frame> physicalFrame = getPhysicalFrameAndEntry(&entry);
    748 
    749  // Dummy frames produce no stack frames.
    750  if (physicalFrame.isNothing()) {
    751    return 0;
    752  }
    753 
    754  if (isWasm()) {
    755    frames[offset] = physicalFrame.value();
    756    frames[offset].label = wasmIter().label();
    757    return 1;
    758  }
    759 
    760  if (physicalFrame->kind == Frame_BaselineInterpreter) {
    761    frames[offset] = physicalFrame.value();
    762    return 1;
    763  }
    764 
    765  // Extract the stack for the entry.
    766  jit::CallStackFrameInfo frameInfos[jit::InlineScriptTree::MaxDepth];
    767  uint32_t depth = entry->callStackAtAddr(cx_->runtime(),
    768                                          jsJitIter().resumePCinCurrentFrame(),
    769                                          frameInfos, std::size(frameInfos));
    770  MOZ_ASSERT(depth <= std::size(frameInfos));
    771  for (uint32_t i = 0; i < depth; i++) {
    772    if (offset + i >= end) {
    773      return i;
    774    }
    775    frames[offset + i] = physicalFrame.value();
    776    frames[offset + i].label = frameInfos[i].label;
    777    frames[offset + i].sourceId = frameInfos[i].sourceId;
    778  }
    779 
    780  return depth;
    781 }
    782 
    783 Maybe<JS::ProfilingFrameIterator::Frame>
    784 JS::ProfilingFrameIterator::getPhysicalFrameWithoutLabel() const {
    785  const jit::JitcodeGlobalEntry* unused;
    786  return getPhysicalFrameAndEntry(&unused);
    787 }
    788 
    789 bool JS::ProfilingFrameIterator::isWasm() const {
    790  MOZ_ASSERT(!done());
    791  return kind_ == Kind::Wasm;
    792 }
    793 
    794 bool JS::ProfilingFrameIterator::isJSJit() const {
    795  return kind_ == Kind::JSJit;
    796 }
    797 
    798 mozilla::Maybe<JS::ProfilingFrameIterator::RegisterState>
    799 JS::ProfilingFrameIterator::getCppEntryRegisters() const {
    800  if (!isJSJit()) {
    801    return mozilla::Nothing{};
    802  }
    803  return jit::JitRuntime::getCppEntryRegisters(jsJitIter().framePtr());
    804 }