tor-browser

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

FrameIter.cpp (28478B)


      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/FrameIter-inl.h"
      8 
      9 #include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_CRASH
     10 #include "mozilla/MaybeOneOf.h"  // mozilla::MaybeOneOf
     11 
     12 #include <stddef.h>  // size_t
     13 #include <stdint.h>  // uint8_t, uint32_t
     14 #include <stdlib.h>  // getenv
     15 
     16 #include "jit/BaselineFrame.h"   // js::jit::BaselineFrame
     17 #include "jit/JitFrames.h"       // js::jit::EnsureUnwoundJitExitFrame
     18 #include "jit/JSJitFrameIter.h"  // js::jit::{FrameType,InlineFrameIterator,JSJitFrameIter,MaybeReadFallback,SnapshotIterator}
     19 #include "js/ColumnNumber.h"  // JS::LimitedColumnNumberOneOrigin, JS::TaggedColumnNumberOneOrigin
     20 #include "js/GCAPI.h"              // JS::AutoSuppressGCAnalysis
     21 #include "js/Principals.h"         // JSSubsumesOp
     22 #include "vm/Activation.h"         // js::Activation{,Iterator}
     23 #include "vm/EnvironmentObject.h"  // js::CallObject
     24 #include "vm/JitActivation.h"      // js::jit::JitActivation
     25 #include "vm/JSContext.h"          // JSContext
     26 #include "vm/JSFunction.h"         // JSFunction
     27 #include "vm/JSScript.h"  // js::PCToLineNumber, JSScript, js::ScriptSource
     28 #include "vm/Runtime.h"   // JSRuntime
     29 #include "vm/Stack.h"  // js::{AbstractFramePtr,InterpreterFrame,MaybeCheckAliasing}
     30 #include "wasm/WasmFrameIter.h"  // js::wasm::WasmFrameIter
     31 #include "wasm/WasmInstance.h"   // js::wasm::Instance
     32 
     33 #include "jit/JSJitFrameIter-inl.h"  // js::jit::JSJitFrameIter::baselineFrame{,NumValueSlots}
     34 #include "vm/Stack-inl.h"  // js::AbstractFramePtr::*
     35 
     36 namespace JS {
     37 class JS_PUBLIC_API Realm;
     38 }  // namespace JS
     39 
     40 namespace js {
     41 class ArgumentsObject;
     42 }  // namespace js
     43 
     44 using JS::Realm;
     45 using JS::Value;
     46 
     47 using js::AbstractFramePtr;
     48 using js::ArgumentsObject;
     49 using js::CallObject;
     50 using js::FrameIter;
     51 using js::JitFrameIter;
     52 using js::NonBuiltinFrameIter;
     53 using js::NonBuiltinScriptFrameIter;
     54 using js::OnlyJSJitFrameIter;
     55 using js::ScriptSource;
     56 using js::jit::JSJitFrameIter;
     57 
     58 JitFrameIter::JitFrameIter(const JitFrameIter& another) { *this = another; }
     59 
     60 JitFrameIter& JitFrameIter::operator=(const JitFrameIter& another) {
     61  MOZ_ASSERT(this != &another);
     62 
     63  act_ = another.act_;
     64  mustUnwindActivation_ = another.mustUnwindActivation_;
     65 
     66  if (isSome()) {
     67    iter_.destroy();
     68  }
     69  if (!another.isSome()) {
     70    return *this;
     71  }
     72 
     73  if (another.isJSJit()) {
     74    iter_.construct<jit::JSJitFrameIter>(another.asJSJit());
     75  } else {
     76    MOZ_ASSERT(another.isWasm());
     77    iter_.construct<wasm::WasmFrameIter>(another.asWasm());
     78  }
     79 
     80  return *this;
     81 }
     82 
     83 JitFrameIter::JitFrameIter(jit::JitActivation* act, bool mustUnwindActivation) {
     84  act_ = act;
     85  mustUnwindActivation_ = mustUnwindActivation;
     86  MOZ_ASSERT(act->hasExitFP(),
     87             "packedExitFP is used to determine if JSJit or wasm");
     88  if (act->hasJSExitFP()) {
     89    iter_.construct<jit::JSJitFrameIter>(act);
     90  } else {
     91    MOZ_ASSERT(act->hasWasmExitFP());
     92    iter_.construct<wasm::WasmFrameIter>(act);
     93  }
     94  settle();
     95 }
     96 
     97 void JitFrameIter::skipNonScriptedJSFrames() {
     98  if (isJSJit()) {
     99    // Stop at the first scripted frame.
    100    jit::JSJitFrameIter& frames = asJSJit();
    101    while (!frames.isScripted() && !frames.done()) {
    102      ++frames;
    103    }
    104    settle();
    105  }
    106 }
    107 
    108 bool JitFrameIter::isSelfHostedIgnoringInlining() const {
    109  MOZ_ASSERT(!done());
    110 
    111  if (isWasm()) {
    112    return false;
    113  }
    114 
    115  return asJSJit().script()->selfHosted();
    116 }
    117 
    118 JS::Realm* JitFrameIter::realm() const {
    119  MOZ_ASSERT(!done());
    120 
    121  if (isWasm()) {
    122    return asWasm().instance()->realm();
    123  }
    124 
    125  if (asJSJit().isScripted()) {
    126    return asJSJit().script()->realm();
    127  }
    128 
    129  MOZ_RELEASE_ASSERT(asJSJit().isTrampolineNative());
    130  return asJSJit().callee()->realm();
    131 }
    132 
    133 uint8_t* JitFrameIter::resumePCinCurrentFrame() const {
    134  if (isWasm()) {
    135    return asWasm().resumePCinCurrentFrame();
    136  }
    137  return asJSJit().resumePCinCurrentFrame();
    138 }
    139 
    140 bool JitFrameIter::done() const {
    141  if (!isSome()) {
    142    return true;
    143  }
    144  if (isJSJit()) {
    145    return asJSJit().done();
    146  }
    147  if (isWasm()) {
    148    return asWasm().done();
    149  }
    150  MOZ_CRASH("unhandled case");
    151 }
    152 
    153 void JitFrameIter::settle() {
    154  if (isJSJit()) {
    155    const jit::JSJitFrameIter& jitFrame = asJSJit();
    156    if (jitFrame.type() != jit::FrameType::WasmToJSJit) {
    157      return;
    158    }
    159 
    160    // Transition from js jit frames to wasm frames: we're on the
    161    // wasm-to-jit fast path. The current stack layout is as follows:
    162    // (stack grows downward)
    163    //
    164    // [--------------------]
    165    // [WASM FUNC           ]
    166    // [WASM JIT EXIT FRAME ]
    167    // [JIT WASM ENTRY FRAME] <-- we're here.
    168    //
    169    // So prevFP points to the wasm jit exit FP, maintaing the invariant in
    170    // WasmFrameIter that the first frame is an exit frame and can be
    171    // popped.
    172 
    173    wasm::Frame* prevFP = (wasm::Frame*)jitFrame.prevFp();
    174 
    175    if (mustUnwindActivation_) {
    176      // The JS-JIT exit frame doesn't support stack switching and will only be
    177      // used if original wasm func was on the main stack.
    178 #ifdef ENABLE_WASM_JSPI
    179      MOZ_ASSERT(!act_->cx()->wasm().findSuspenderForStackAddress(
    180          prevFP->wasmCaller()));
    181 #endif
    182      act_->setWasmExitFP(prevFP, nullptr);
    183    }
    184 
    185    iter_.destroy();
    186    iter_.construct<wasm::WasmFrameIter>(act_, prevFP);
    187    MOZ_ASSERT(!asWasm().done());
    188    return;
    189  }
    190 
    191  if (isWasm()) {
    192    const wasm::WasmFrameIter& wasmFrame = asWasm();
    193    if (!wasmFrame.done() || !wasmFrame.unwoundCallerFPIsJSJit()) {
    194      return;
    195    }
    196 
    197    // Transition from wasm frames to jit frames: we're on the
    198    // jit-to-wasm fast path. The current stack layout is as follows:
    199    // (stack grows downward)
    200    //
    201    // [--------------------]
    202    // [JIT FRAME           ]
    203    // [WASM JIT ENTRY FRAME] <-- we're here
    204    //
    205    // The wasm iterator has saved the previous jit frame pointer for us.
    206 
    207    uint8_t* prevFP = wasmFrame.unwoundCallerFP();
    208 
    209    if (mustUnwindActivation_) {
    210      act_->setJSExitFP(prevFP);
    211    }
    212 
    213    iter_.destroy();
    214    iter_.construct<jit::JSJitFrameIter>(act_, prevFP, mustUnwindActivation_);
    215    MOZ_ASSERT(!asJSJit().done());
    216    return;
    217  }
    218 }
    219 
    220 void JitFrameIter::operator++() {
    221  MOZ_ASSERT(isSome());
    222  if (isJSJit()) {
    223    const jit::JSJitFrameIter& jitFrame = asJSJit();
    224 
    225    jit::JitFrameLayout* prevFrame = nullptr;
    226    if (mustUnwindActivation_ && jitFrame.isScripted()) {
    227      prevFrame = jitFrame.jsFrame();
    228    }
    229 
    230    ++asJSJit();
    231 
    232    if (prevFrame) {
    233      // Unwind the frame by updating packedExitFP. This is necessary
    234      // so that (1) debugger exception unwind and leave frame hooks
    235      // don't see this frame when they use ScriptFrameIter, and (2)
    236      // ScriptFrameIter does not crash when accessing an IonScript
    237      // that's destroyed by the ionScript->decref call.
    238      EnsureUnwoundJitExitFrame(act_, prevFrame);
    239    }
    240  } else if (isWasm()) {
    241    ++asWasm();
    242  } else {
    243    MOZ_CRASH("unhandled case");
    244  }
    245  settle();
    246 }
    247 
    248 OnlyJSJitFrameIter::OnlyJSJitFrameIter(jit::JitActivation* act)
    249    : JitFrameIter(act) {
    250  settle();
    251 }
    252 
    253 OnlyJSJitFrameIter::OnlyJSJitFrameIter(const ActivationIterator& iter)
    254    : OnlyJSJitFrameIter(iter->asJit()) {}
    255 
    256 /*****************************************************************************/
    257 
    258 void FrameIter::popActivation() {
    259  ++data_.activations_;
    260  settleOnActivation();
    261 }
    262 
    263 bool FrameIter::principalsSubsumeFrame() const {
    264  // If the caller supplied principals, only show frames which are
    265  // subsumed (of the same origin or of an origin accessible) by these
    266  // principals.
    267 
    268  MOZ_ASSERT(!done());
    269 
    270  if (!data_.principals_) {
    271    return true;
    272  }
    273 
    274  JSSubsumesOp subsumes = data_.cx_->runtime()->securityCallbacks->subsumes;
    275  if (!subsumes) {
    276    return true;
    277  }
    278 
    279  JS::AutoSuppressGCAnalysis nogc;
    280  return subsumes(data_.principals_, realm()->principals());
    281 }
    282 
    283 void FrameIter::popInterpreterFrame() {
    284  MOZ_ASSERT(data_.state_ == INTERP);
    285 
    286  ++data_.interpFrames_;
    287 
    288  if (data_.interpFrames_.done()) {
    289    popActivation();
    290  } else {
    291    data_.pc_ = data_.interpFrames_.pc();
    292  }
    293 }
    294 
    295 void FrameIter::settleOnActivation() {
    296  MOZ_ASSERT(!data_.cx_->inUnsafeCallWithABI);
    297 
    298  while (true) {
    299    if (data_.activations_.done()) {
    300      data_.state_ = DONE;
    301      return;
    302    }
    303 
    304    Activation* activation = data_.activations_.activation();
    305 
    306    if (activation->isJit()) {
    307      data_.jitFrames_ = JitFrameIter(activation->asJit());
    308      data_.jitFrames_.skipNonScriptedJSFrames();
    309      if (data_.jitFrames_.done()) {
    310        // It's possible to have an JitActivation with no scripted
    311        // frames, for instance if we hit an over-recursion during
    312        // bailout.
    313        ++data_.activations_;
    314        continue;
    315      }
    316      data_.state_ = JIT;
    317      nextJitFrame();
    318      return;
    319    }
    320 
    321    MOZ_ASSERT(activation->isInterpreter());
    322 
    323    InterpreterActivation* interpAct = activation->asInterpreter();
    324    data_.interpFrames_ = InterpreterFrameIterator(interpAct);
    325 
    326    // If we OSR'ed into JIT code, skip the interpreter frame so that
    327    // the same frame is not reported twice.
    328    if (data_.interpFrames_.frame()->runningInJit()) {
    329      ++data_.interpFrames_;
    330      if (data_.interpFrames_.done()) {
    331        ++data_.activations_;
    332        continue;
    333      }
    334    }
    335 
    336    MOZ_ASSERT(!data_.interpFrames_.frame()->runningInJit());
    337    data_.pc_ = data_.interpFrames_.pc();
    338    data_.state_ = INTERP;
    339    return;
    340  }
    341 }
    342 
    343 FrameIter::Data::Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption,
    344                      JSPrincipals* principals)
    345    : cx_(cx),
    346      debuggerEvalOption_(debuggerEvalOption),
    347      principals_(principals),
    348      state_(DONE),
    349      pc_(nullptr),
    350      interpFrames_(nullptr),
    351      activations_(cx),
    352      ionInlineFrameNo_(0) {}
    353 
    354 FrameIter::Data::Data(const FrameIter::Data& other) = default;
    355 
    356 FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption)
    357    : data_(cx, debuggerEvalOption, nullptr),
    358      ionInlineFrames_(cx, (js::jit::JSJitFrameIter*)nullptr) {
    359  settleOnActivation();
    360 
    361  // No principals so we can see all frames.
    362  MOZ_ASSERT_IF(!done(), principalsSubsumeFrame());
    363 }
    364 
    365 FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption,
    366                     JSPrincipals* principals)
    367    : data_(cx, debuggerEvalOption, principals),
    368      ionInlineFrames_(cx, (js::jit::JSJitFrameIter*)nullptr) {
    369  settleOnActivation();
    370 
    371  // If we're not allowed to see this frame, call operator++ to skip this (and
    372  // other) cross-origin frames.
    373  if (!done() && !principalsSubsumeFrame()) {
    374    ++*this;
    375  }
    376 }
    377 
    378 FrameIter::FrameIter(const FrameIter& other)
    379    : data_(other.data_),
    380      ionInlineFrames_(other.data_.cx_,
    381                       isIonScripted() ? &other.ionInlineFrames_ : nullptr) {}
    382 
    383 FrameIter::FrameIter(const Data& data)
    384    : data_(data),
    385      ionInlineFrames_(data.cx_, isIonScripted() ? &jsJitFrame() : nullptr) {
    386  MOZ_ASSERT(data.cx_);
    387  if (isIonScripted()) {
    388    while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_) {
    389      ++ionInlineFrames_;
    390    }
    391  }
    392 }
    393 
    394 void FrameIter::nextJitFrame() {
    395  MOZ_ASSERT(data_.jitFrames_.isSome());
    396 
    397  if (isJSJit()) {
    398    if (jsJitFrame().isIonScripted()) {
    399      ionInlineFrames_.resetOn(&jsJitFrame());
    400      data_.pc_ = ionInlineFrames_.pc();
    401    } else {
    402      MOZ_ASSERT(jsJitFrame().isBaselineJS());
    403      jsJitFrame().baselineScriptAndPc(nullptr, &data_.pc_);
    404    }
    405    return;
    406  }
    407 
    408  MOZ_ASSERT(isWasm());
    409  wasmFrame().enableInlinedFrames();
    410  data_.pc_ = nullptr;
    411 }
    412 
    413 void FrameIter::popJitFrame() {
    414  MOZ_ASSERT(data_.state_ == JIT);
    415  MOZ_ASSERT(data_.jitFrames_.isSome());
    416 
    417  if (isJSJit() && jsJitFrame().isIonScripted() && ionInlineFrames_.more()) {
    418    ++ionInlineFrames_;
    419    data_.pc_ = ionInlineFrames_.pc();
    420    return;
    421  }
    422 
    423  ++data_.jitFrames_;
    424  data_.jitFrames_.skipNonScriptedJSFrames();
    425 
    426  if (!data_.jitFrames_.done()) {
    427    nextJitFrame();
    428  } else {
    429    data_.jitFrames_.reset();
    430    popActivation();
    431  }
    432 }
    433 
    434 FrameIter& FrameIter::operator++() {
    435  while (true) {
    436    switch (data_.state_) {
    437      case DONE:
    438        MOZ_CRASH("Unexpected state");
    439      case INTERP:
    440        if (interpFrame()->isDebuggerEvalFrame() &&
    441            data_.debuggerEvalOption_ == FOLLOW_DEBUGGER_EVAL_PREV_LINK) {
    442          AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev();
    443 
    444          popInterpreterFrame();
    445 
    446          while (!hasUsableAbstractFramePtr() ||
    447                 abstractFramePtr() != eifPrev) {
    448            if (data_.state_ == JIT) {
    449              popJitFrame();
    450            } else {
    451              popInterpreterFrame();
    452            }
    453          }
    454 
    455          break;
    456        }
    457        popInterpreterFrame();
    458        break;
    459      case JIT:
    460        popJitFrame();
    461        break;
    462    }
    463 
    464    if (done() || principalsSubsumeFrame()) {
    465      break;
    466    }
    467  }
    468 
    469  return *this;
    470 }
    471 
    472 FrameIter::Data* FrameIter::copyData() const {
    473  Data* data = data_.cx_->new_<Data>(data_);
    474  if (!data) {
    475    return nullptr;
    476  }
    477 
    478  if (data && isIonScripted()) {
    479    data->ionInlineFrameNo_ = ionInlineFrames_.frameNo();
    480  }
    481  return data;
    482 }
    483 
    484 void* FrameIter::rawFramePtr() const {
    485  switch (data_.state_) {
    486    case DONE:
    487      return nullptr;
    488    case INTERP:
    489      return interpFrame();
    490    case JIT:
    491      if (isJSJit()) {
    492        return jsJitFrame().fp();
    493      }
    494      MOZ_ASSERT(isWasm());
    495      return nullptr;
    496  }
    497  MOZ_CRASH("Unexpected state");
    498 }
    499 
    500 JS::Compartment* FrameIter::compartment() const {
    501  switch (data_.state_) {
    502    case DONE:
    503      break;
    504    case INTERP:
    505    case JIT:
    506      return data_.activations_->compartment();
    507  }
    508  MOZ_CRASH("Unexpected state");
    509 }
    510 
    511 Realm* FrameIter::realm() const {
    512  MOZ_ASSERT(!done());
    513 
    514  if (hasScript()) {
    515    return script()->realm();
    516  }
    517 
    518  return wasmInstance()->realm();
    519 }
    520 
    521 bool FrameIter::isEvalFrame() const {
    522  switch (data_.state_) {
    523    case DONE:
    524      break;
    525    case INTERP:
    526      return interpFrame()->isEvalFrame();
    527    case JIT:
    528      if (isJSJit()) {
    529        if (jsJitFrame().isBaselineJS()) {
    530          return jsJitFrame().baselineFrame()->isEvalFrame();
    531        }
    532        MOZ_ASSERT(!script()->isForEval());
    533        return false;
    534      }
    535      MOZ_ASSERT(isWasm());
    536      return false;
    537  }
    538  MOZ_CRASH("Unexpected state");
    539 }
    540 
    541 bool FrameIter::isModuleFrame() const {
    542  MOZ_ASSERT(!done());
    543 
    544  if (hasScript()) {
    545    return script()->isModule();
    546  }
    547  MOZ_CRASH("Unexpected state");
    548 }
    549 
    550 bool FrameIter::isFunctionFrame() const {
    551  MOZ_ASSERT(!done());
    552  switch (data_.state_) {
    553    case DONE:
    554      break;
    555    case INTERP:
    556      return interpFrame()->isFunctionFrame();
    557    case JIT:
    558      if (isJSJit()) {
    559        if (jsJitFrame().isBaselineJS()) {
    560          return jsJitFrame().baselineFrame()->isFunctionFrame();
    561        }
    562        return script()->isFunction();
    563      }
    564      MOZ_ASSERT(isWasm());
    565      return false;
    566  }
    567  MOZ_CRASH("Unexpected state");
    568 }
    569 
    570 JSAtom* FrameIter::maybeFunctionDisplayAtom() const {
    571  switch (data_.state_) {
    572    case DONE:
    573      break;
    574    case INTERP:
    575    case JIT:
    576      if (isWasm()) {
    577        return wasmFrame().functionDisplayAtom();
    578      }
    579      if (isFunctionFrame()) {
    580        return calleeTemplate()->fullDisplayAtom();
    581      }
    582      return nullptr;
    583  }
    584 
    585  MOZ_CRASH("Unexpected state");
    586 }
    587 
    588 ScriptSource* FrameIter::scriptSource() const {
    589  switch (data_.state_) {
    590    case DONE:
    591      break;
    592    case INTERP:
    593    case JIT:
    594      return script()->scriptSource();
    595  }
    596 
    597  MOZ_CRASH("Unexpected state");
    598 }
    599 
    600 const char* FrameIter::filename() const {
    601  switch (data_.state_) {
    602    case DONE:
    603      break;
    604    case INTERP:
    605    case JIT:
    606      if (isWasm()) {
    607        return wasmFrame().filename();
    608      }
    609      return script()->filename();
    610  }
    611 
    612  MOZ_CRASH("Unexpected state");
    613 }
    614 
    615 const char16_t* FrameIter::displayURL() const {
    616  switch (data_.state_) {
    617    case DONE:
    618      break;
    619    case INTERP:
    620    case JIT:
    621      if (isWasm()) {
    622        return wasmFrame().displayURL();
    623      }
    624      ScriptSource* ss = script()->scriptSource();
    625      return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
    626  }
    627  MOZ_CRASH("Unexpected state");
    628 }
    629 
    630 unsigned FrameIter::computeLine(JS::TaggedColumnNumberOneOrigin* column) const {
    631  switch (data_.state_) {
    632    case DONE:
    633      break;
    634    case INTERP:
    635    case JIT:
    636      if (isWasm()) {
    637        return wasmFrame().computeLine(column);
    638      }
    639      JS::LimitedColumnNumberOneOrigin columnNumber;
    640      unsigned lineNumber = PCToLineNumber(script(), pc(), &columnNumber);
    641      if (column) {
    642        *column = JS::TaggedColumnNumberOneOrigin(columnNumber);
    643      }
    644      return lineNumber;
    645  }
    646 
    647  MOZ_CRASH("Unexpected state");
    648 }
    649 
    650 bool FrameIter::mutedErrors() const {
    651  switch (data_.state_) {
    652    case DONE:
    653      break;
    654    case INTERP:
    655    case JIT:
    656      if (isWasm()) {
    657        return wasmFrame().mutedErrors();
    658      }
    659      return script()->mutedErrors();
    660  }
    661  MOZ_CRASH("Unexpected state");
    662 }
    663 
    664 bool FrameIter::isConstructing() const {
    665  switch (data_.state_) {
    666    case DONE:
    667      break;
    668    case JIT:
    669      MOZ_ASSERT(isJSJit());
    670      if (jsJitFrame().isIonScripted()) {
    671        return ionInlineFrames_.isConstructing();
    672      }
    673      MOZ_ASSERT(jsJitFrame().isBaselineJS());
    674      return jsJitFrame().isConstructing();
    675    case INTERP:
    676      return interpFrame()->isConstructing();
    677  }
    678 
    679  MOZ_CRASH("Unexpected state");
    680 }
    681 
    682 bool FrameIter::ensureHasRematerializedFrame(JSContext* cx) {
    683  MOZ_ASSERT(isIon());
    684  return !!activation()->asJit()->getRematerializedFrame(cx, jsJitFrame());
    685 }
    686 
    687 bool FrameIter::hasUsableAbstractFramePtr() const {
    688  switch (data_.state_) {
    689    case DONE:
    690      return false;
    691    case JIT:
    692      if (isJSJit()) {
    693        if (jsJitFrame().isBaselineJS()) {
    694          return true;
    695        }
    696 
    697        MOZ_ASSERT(jsJitFrame().isIonScripted());
    698        return !!activation()->asJit()->lookupRematerializedFrame(
    699            jsJitFrame().fp(), ionInlineFrames_.frameNo());
    700      }
    701      MOZ_ASSERT(isWasm());
    702      return wasmFrame().debugEnabled();
    703    case INTERP:
    704      return true;
    705  }
    706  MOZ_CRASH("Unexpected state");
    707 }
    708 
    709 AbstractFramePtr FrameIter::abstractFramePtr() const {
    710  MOZ_ASSERT(hasUsableAbstractFramePtr());
    711  switch (data_.state_) {
    712    case DONE:
    713      break;
    714    case JIT: {
    715      if (isJSJit()) {
    716        if (jsJitFrame().isBaselineJS()) {
    717          return jsJitFrame().baselineFrame();
    718        }
    719        MOZ_ASSERT(isIonScripted());
    720        return activation()->asJit()->lookupRematerializedFrame(
    721            jsJitFrame().fp(), ionInlineFrames_.frameNo());
    722      }
    723      MOZ_ASSERT(isWasm());
    724      MOZ_ASSERT(wasmFrame().debugEnabled());
    725      return wasmFrame().debugFrame();
    726    }
    727    case INTERP:
    728      MOZ_ASSERT(interpFrame());
    729      return AbstractFramePtr(interpFrame());
    730  }
    731  MOZ_CRASH("Unexpected state");
    732 }
    733 
    734 void FrameIter::updatePcQuadratic() {
    735  switch (data_.state_) {
    736    case DONE:
    737      break;
    738    case INTERP: {
    739      InterpreterFrame* frame = interpFrame();
    740      InterpreterActivation* activation = data_.activations_->asInterpreter();
    741 
    742      // Look for the current frame.
    743      data_.interpFrames_ = InterpreterFrameIterator(activation);
    744      while (data_.interpFrames_.frame() != frame) {
    745        ++data_.interpFrames_;
    746      }
    747 
    748      // Update the pc.
    749      MOZ_ASSERT(data_.interpFrames_.frame() == frame);
    750      data_.pc_ = data_.interpFrames_.pc();
    751      return;
    752    }
    753    case JIT:
    754      if (jsJitFrame().isBaselineJS()) {
    755        jit::BaselineFrame* frame = jsJitFrame().baselineFrame();
    756        jit::JitActivation* activation = data_.activations_->asJit();
    757 
    758        // activation's exitFP may be invalid, so create a new
    759        // activation iterator.
    760        data_.activations_ = ActivationIterator(data_.cx_);
    761        while (data_.activations_.activation() != activation) {
    762          ++data_.activations_;
    763        }
    764 
    765        // Look for the current frame.
    766        data_.jitFrames_ = JitFrameIter(data_.activations_->asJit());
    767        while (!isJSJit() || !jsJitFrame().isBaselineJS() ||
    768               jsJitFrame().baselineFrame() != frame) {
    769          ++data_.jitFrames_;
    770        }
    771 
    772        // Update the pc.
    773        MOZ_ASSERT(jsJitFrame().baselineFrame() == frame);
    774        jsJitFrame().baselineScriptAndPc(nullptr, &data_.pc_);
    775        return;
    776      }
    777      break;
    778  }
    779  MOZ_CRASH("Unexpected state");
    780 }
    781 
    782 void FrameIter::wasmUpdateBytecodeOffset() {
    783  MOZ_RELEASE_ASSERT(isWasm(), "Unexpected state");
    784 
    785  wasm::DebugFrame* frame = wasmFrame().debugFrame();
    786 
    787  // Relookup the current frame, updating the bytecode offset in the process.
    788  data_.jitFrames_ = JitFrameIter(data_.activations_->asJit());
    789  while (!isWasm() || wasmFrame().debugFrame() != frame) {
    790    ++data_.jitFrames_;
    791  }
    792 
    793  MOZ_ASSERT(wasmFrame().debugFrame() == frame);
    794 }
    795 
    796 JSFunction* FrameIter::calleeTemplate() const {
    797  switch (data_.state_) {
    798    case DONE:
    799      break;
    800    case INTERP:
    801      MOZ_ASSERT(isFunctionFrame());
    802      return &interpFrame()->callee();
    803    case JIT:
    804      if (jsJitFrame().isBaselineJS()) {
    805        return jsJitFrame().callee();
    806      }
    807      MOZ_ASSERT(jsJitFrame().isIonScripted());
    808      return ionInlineFrames_.calleeTemplate();
    809  }
    810  MOZ_CRASH("Unexpected state");
    811 }
    812 
    813 JSFunction* FrameIter::callee(JSContext* cx) const {
    814  switch (data_.state_) {
    815    case DONE:
    816      break;
    817    case INTERP:
    818      return calleeTemplate();
    819    case JIT:
    820      if (isIonScripted()) {
    821        jit::MaybeReadFallback recover(cx, activation()->asJit(),
    822                                       &jsJitFrame());
    823        return ionInlineFrames_.callee(recover);
    824      }
    825      MOZ_ASSERT(jsJitFrame().isBaselineJS());
    826      return calleeTemplate();
    827  }
    828  MOZ_CRASH("Unexpected state");
    829 }
    830 
    831 bool FrameIter::matchCallee(JSContext* cx, JS::Handle<JSFunction*> fun) const {
    832  // Use the calleeTemplate to rule out a match without needing to invalidate to
    833  // find the actual callee. The real callee my be a clone of the template which
    834  // should *not* be considered a match.
    835  JSFunction* currentCallee = calleeTemplate();
    836 
    837  if (currentCallee->nargs() != fun->nargs()) {
    838    return false;
    839  }
    840 
    841  if (currentCallee->flags().stableAcrossClones() !=
    842      fun->flags().stableAcrossClones()) {
    843    return false;
    844  }
    845 
    846  // The calleeTemplate for a callee will always have the same BaseScript. If
    847  // the script clones do not use the same script, they also have a different
    848  // group and Ion will not inline them interchangeably.
    849  //
    850  // See: js::jit::InlineFrameIterator::findNextFrame()
    851  if (currentCallee->hasBaseScript() && fun->hasBaseScript()) {
    852    if (currentCallee->baseScript() != fun->baseScript()) {
    853      return false;
    854    }
    855  }
    856 
    857  return callee(cx) == fun;
    858 }
    859 
    860 unsigned FrameIter::numActualArgs() const {
    861  switch (data_.state_) {
    862    case DONE:
    863      break;
    864    case INTERP:
    865      MOZ_ASSERT(isFunctionFrame());
    866      return interpFrame()->numActualArgs();
    867    case JIT:
    868      if (isIonScripted()) {
    869        return ionInlineFrames_.numActualArgs();
    870      }
    871      MOZ_ASSERT(jsJitFrame().isBaselineJS());
    872      return jsJitFrame().numActualArgs();
    873  }
    874  MOZ_CRASH("Unexpected state");
    875 }
    876 
    877 unsigned FrameIter::numFormalArgs() const {
    878  return script()->function()->nargs();
    879 }
    880 
    881 Value FrameIter::unaliasedActual(unsigned i,
    882                                 MaybeCheckAliasing checkAliasing) const {
    883  return abstractFramePtr().unaliasedActual(i, checkAliasing);
    884 }
    885 
    886 JSObject* FrameIter::environmentChain(JSContext* cx) const {
    887  switch (data_.state_) {
    888    case DONE:
    889      break;
    890    case JIT:
    891      if (isJSJit()) {
    892        if (isIonScripted()) {
    893          jit::MaybeReadFallback recover(cx, activation()->asJit(),
    894                                         &jsJitFrame());
    895          return ionInlineFrames_.environmentChain(recover);
    896        }
    897        return jsJitFrame().baselineFrame()->environmentChain();
    898      }
    899      MOZ_ASSERT(isWasm());
    900      return wasmFrame().debugFrame()->environmentChain();
    901    case INTERP:
    902      return interpFrame()->environmentChain();
    903  }
    904  MOZ_CRASH("Unexpected state");
    905 }
    906 
    907 bool FrameIter::hasInitialEnvironment(JSContext* cx) const {
    908  if (hasUsableAbstractFramePtr()) {
    909    return abstractFramePtr().hasInitialEnvironment();
    910  }
    911 
    912  if (isWasm()) {
    913    // See JSFunction::needsFunctionEnvironmentObjects().
    914    return false;
    915  }
    916 
    917  MOZ_ASSERT(isJSJit());
    918  MOZ_ASSERT(isIonScripted());
    919 
    920  bool hasInitialEnv = false;
    921  jit::MaybeReadFallback recover(cx, activation()->asJit(), &jsJitFrame());
    922  ionInlineFrames_.environmentChain(recover, &hasInitialEnv);
    923 
    924  return hasInitialEnv;
    925 }
    926 
    927 CallObject& FrameIter::callObj(JSContext* cx) const {
    928  MOZ_ASSERT(calleeTemplate()->needsCallObject());
    929  MOZ_ASSERT(hasInitialEnvironment(cx));
    930 
    931  JSObject* pobj = environmentChain(cx);
    932  while (!pobj->is<CallObject>()) {
    933    pobj = pobj->enclosingEnvironment();
    934  }
    935  return pobj->as<CallObject>();
    936 }
    937 
    938 bool FrameIter::hasArgsObj() const { return abstractFramePtr().hasArgsObj(); }
    939 
    940 ArgumentsObject& FrameIter::argsObj() const {
    941  MOZ_ASSERT(hasArgsObj());
    942  return abstractFramePtr().argsObj();
    943 }
    944 
    945 Value FrameIter::thisArgument(JSContext* cx) const {
    946  MOZ_ASSERT(isFunctionFrame());
    947 
    948  switch (data_.state_) {
    949    case DONE:
    950      break;
    951    case JIT:
    952      if (isIonScripted()) {
    953        jit::MaybeReadFallback recover(cx, activation()->asJit(),
    954                                       &jsJitFrame());
    955        return ionInlineFrames_.thisArgument(recover);
    956      }
    957      return jsJitFrame().baselineFrame()->thisArgument();
    958    case INTERP:
    959      return interpFrame()->thisArgument();
    960  }
    961  MOZ_CRASH("Unexpected state");
    962 }
    963 
    964 Value FrameIter::returnValue() const {
    965  switch (data_.state_) {
    966    case DONE:
    967      break;
    968    case JIT:
    969      if (jsJitFrame().isBaselineJS()) {
    970        return jsJitFrame().baselineFrame()->returnValue();
    971      }
    972      break;
    973    case INTERP:
    974      return interpFrame()->returnValue();
    975  }
    976  MOZ_CRASH("Unexpected state");
    977 }
    978 
    979 void FrameIter::setReturnValue(const Value& v) {
    980  switch (data_.state_) {
    981    case DONE:
    982      break;
    983    case JIT:
    984      if (jsJitFrame().isBaselineJS()) {
    985        jsJitFrame().baselineFrame()->setReturnValue(v);
    986        return;
    987      }
    988      break;
    989    case INTERP:
    990      interpFrame()->setReturnValue(v);
    991      return;
    992  }
    993  MOZ_CRASH("Unexpected state");
    994 }
    995 
    996 size_t FrameIter::numFrameSlots() const {
    997  switch (data_.state_) {
    998    case DONE:
    999      break;
   1000    case JIT: {
   1001      if (isIonScripted()) {
   1002        return ionInlineFrames_.snapshotIterator().numAllocations() -
   1003               ionInlineFrames_.script()->nfixed();
   1004      }
   1005      uint32_t numValueSlots = jsJitFrame().baselineFrameNumValueSlots();
   1006      return numValueSlots - jsJitFrame().script()->nfixed();
   1007    }
   1008    case INTERP:
   1009      MOZ_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base());
   1010      return data_.interpFrames_.sp() - interpFrame()->base();
   1011  }
   1012  MOZ_CRASH("Unexpected state");
   1013 }
   1014 
   1015 Value FrameIter::frameSlotValue(size_t index) const {
   1016  switch (data_.state_) {
   1017    case DONE:
   1018      break;
   1019    case JIT:
   1020      if (isIonScripted()) {
   1021        jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
   1022        index += ionInlineFrames_.script()->nfixed();
   1023        return si.maybeReadAllocByIndex(index);
   1024      }
   1025      index += jsJitFrame().script()->nfixed();
   1026      return *jsJitFrame().baselineFrame()->valueSlot(index);
   1027    case INTERP:
   1028      return interpFrame()->base()[index];
   1029  }
   1030  MOZ_CRASH("Unexpected state");
   1031 }
   1032 
   1033 #ifdef DEBUG
   1034 bool js::SelfHostedFramesVisible() {
   1035  static bool checked = false;
   1036  static bool visible = false;
   1037  if (!checked) {
   1038    checked = true;
   1039    char* env = getenv("MOZ_SHOW_ALL_JS_FRAMES");
   1040    visible = !!env;
   1041  }
   1042  return visible;
   1043 }
   1044 #endif
   1045 
   1046 void NonBuiltinFrameIter::settle() {
   1047  if (!SelfHostedFramesVisible()) {
   1048    while (!done() && hasScript() && script()->selfHosted()) {
   1049      FrameIter::operator++();
   1050    }
   1051  }
   1052 }
   1053 
   1054 void NonBuiltinScriptFrameIter::settle() {
   1055  if (!SelfHostedFramesVisible()) {
   1056    while (!done() && script()->selfHosted()) {
   1057      ScriptFrameIter::operator++();
   1058    }
   1059  }
   1060 }
   1061 
   1062 bool FrameIter::inPrologue() const {
   1063  if (pc() < script()->main()) {
   1064    return true;
   1065  }
   1066  // If we do a VM call before pushing locals in baseline, the stack frame will
   1067  // not include space for those locals.
   1068  if (pc() == script()->code() && isBaseline() &&
   1069      jsJitFrame().baselineFrameNumValueSlots() < script()->nfixed()) {
   1070    return true;
   1071  }
   1072 
   1073  return false;
   1074 }