tor-browser

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

FrameIter.h (17594B)


      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 #ifndef vm_FrameIter_h
      8 #define vm_FrameIter_h
      9 
     10 #include "mozilla/Assertions.h"  // MOZ_ASSERT
     11 #include "mozilla/Attributes.h"  // MOZ_IMPLICIT, MOZ_RAII
     12 #include "mozilla/MaybeOneOf.h"  // mozilla::MaybeOneOf
     13 
     14 #include <stddef.h>  // size_t
     15 #include <stdint.h>  // uint8_t, uint32_t, uintptr_t
     16 
     17 #include "jstypes.h"  // JS_PUBLIC_API
     18 
     19 #include "jit/JSJitFrameIter.h"  // js::jit::{InlineFrameIterator,JSJitFrameIter}
     20 #include "js/ColumnNumber.h"     // JS::TaggedColumnNumberOneOrigin
     21 #include "js/RootingAPI.h"       // JS::Handle
     22 #include "js/TypeDecls.h"  // jsbytecode, JSContext, JSAtom, JSFunction, JSObject, JSScript
     23 #include "js/Value.h"       // JS::Value
     24 #include "vm/Activation.h"  // js::InterpreterActivation
     25 #include "vm/Stack.h"       // js::{AbstractFramePtr,MaybeCheckAliasing}
     26 #include "wasm/WasmFrameIter.h"  // js::wasm::{ExitReason,RegisterState,WasmFrameIter}
     27 
     28 struct JSPrincipals;
     29 
     30 namespace JS {
     31 
     32 class JS_PUBLIC_API Compartment;
     33 class JS_PUBLIC_API Realm;
     34 
     35 }  // namespace JS
     36 
     37 namespace js {
     38 
     39 class ArgumentsObject;
     40 class CallObject;
     41 
     42 namespace jit {
     43 class CommonFrameLayout;
     44 class JitActivation;
     45 }  // namespace jit
     46 
     47 namespace wasm {
     48 class Instance;
     49 }  // namespace wasm
     50 
     51 // Iterates over the frames of a single InterpreterActivation.
     52 class InterpreterFrameIterator {
     53  InterpreterActivation* activation_;
     54  InterpreterFrame* fp_;
     55  jsbytecode* pc_;
     56  JS::Value* sp_;
     57 
     58 public:
     59  explicit InterpreterFrameIterator(InterpreterActivation* activation)
     60      : activation_(activation), fp_(nullptr), pc_(nullptr), sp_(nullptr) {
     61    if (activation) {
     62      fp_ = activation->current();
     63      pc_ = activation->regs().pc;
     64      sp_ = activation->regs().sp;
     65    }
     66  }
     67 
     68  InterpreterFrame* frame() const {
     69    MOZ_ASSERT(!done());
     70    return fp_;
     71  }
     72  jsbytecode* pc() const {
     73    MOZ_ASSERT(!done());
     74    return pc_;
     75  }
     76  JS::Value* sp() const {
     77    MOZ_ASSERT(!done());
     78    return sp_;
     79  }
     80 
     81  InterpreterFrameIterator& operator++();
     82 
     83  bool done() const { return fp_ == nullptr; }
     84 };
     85 
     86 // A JitFrameIter can iterate over all kind of frames emitted by our code
     87 // generators, be they composed of JS jit frames or wasm frames, interleaved or
     88 // not, in any order.
     89 //
     90 // In the following class:
     91 // - code generated for JS is referred to as JSJit.
     92 // - code generated for wasm is referred to as Wasm.
     93 // Also, Jit refers to any one of them.
     94 //
     95 // JitFrameIter uses JSJitFrameIter to iterate over JSJit code or a
     96 // WasmFrameIter to iterate over wasm code; only one of them is active at the
     97 // time. When a sub-iterator is done, the JitFrameIter knows how to stop, move
     98 // onto the next activation or move onto another kind of Jit code.
     99 //
    100 // For ease of use, there is also OnlyJSJitFrameIter, which skips all the
    101 // non-JSJit frames.
    102 //
    103 // Note it is allowed to get a handle to the internal frame iterator via
    104 // asJSJit() and asWasm(), but the user has to be careful not to have those be
    105 // used after JitFrameIter leaves the scope or the operator++ is called.
    106 //
    107 // In particular, this can handle the transition from wasm to jit and from jit
    108 // to wasm, since these can be interleaved in the same JitActivation.
    109 class JitFrameIter {
    110 protected:
    111  jit::JitActivation* act_ = nullptr;
    112  mozilla::MaybeOneOf<jit::JSJitFrameIter, wasm::WasmFrameIter> iter_ = {};
    113  bool mustUnwindActivation_ = false;
    114 
    115  void settle();
    116 
    117 public:
    118  JitFrameIter() = default;
    119 
    120  explicit JitFrameIter(jit::JitActivation* activation,
    121                        bool mustUnwindActivation = false);
    122 
    123  explicit JitFrameIter(const JitFrameIter& another);
    124  JitFrameIter& operator=(const JitFrameIter& another);
    125 
    126  bool isSome() const { return !iter_.empty(); }
    127  void reset() {
    128    MOZ_ASSERT(isSome());
    129    iter_.destroy();
    130  }
    131 
    132  bool isJSJit() const {
    133    return isSome() && iter_.constructed<jit::JSJitFrameIter>();
    134  }
    135  jit::JSJitFrameIter& asJSJit() { return iter_.ref<jit::JSJitFrameIter>(); }
    136  const jit::JSJitFrameIter& asJSJit() const {
    137    return iter_.ref<jit::JSJitFrameIter>();
    138  }
    139 
    140  bool isWasm() const {
    141    return isSome() && iter_.constructed<wasm::WasmFrameIter>();
    142  }
    143  wasm::WasmFrameIter& asWasm() { return iter_.ref<wasm::WasmFrameIter>(); }
    144  const wasm::WasmFrameIter& asWasm() const {
    145    return iter_.ref<wasm::WasmFrameIter>();
    146  }
    147 
    148  // Operations common to all frame iterators.
    149  const jit::JitActivation* activation() const { return act_; }
    150  bool done() const;
    151  void operator++();
    152 
    153  JS::Realm* realm() const;
    154 
    155  // Returns the address of the next instruction that will execute in this
    156  // frame, once control returns to this frame.
    157  uint8_t* resumePCinCurrentFrame() const;
    158 
    159  // Operations which have an effect only on JIT frames.
    160  void skipNonScriptedJSFrames();
    161 
    162  // Returns true iff this is a JIT frame with a self-hosted script. Note: be
    163  // careful, JitFrameIter does not consider functions inlined by Ion.
    164  bool isSelfHostedIgnoringInlining() const;
    165 };
    166 
    167 // A JitFrameIter that skips all the non-JSJit frames, skipping interleaved
    168 // frames of any another kind.
    169 
    170 class OnlyJSJitFrameIter : public JitFrameIter {
    171  void settle() {
    172    while (!done() && !isJSJit()) {
    173      JitFrameIter::operator++();
    174    }
    175  }
    176 
    177 public:
    178  explicit OnlyJSJitFrameIter(jit::JitActivation* act);
    179  explicit OnlyJSJitFrameIter(const ActivationIterator& cx);
    180 
    181  void operator++() {
    182    JitFrameIter::operator++();
    183    settle();
    184  }
    185 
    186  const jit::JSJitFrameIter& frame() const { return asJSJit(); }
    187 };
    188 
    189 class ScriptSource;
    190 
    191 // A FrameIter walks over a context's stack of JS script activations,
    192 // abstracting over whether the JS scripts were running in the interpreter or
    193 // different modes of compiled code.
    194 //
    195 // FrameIter is parameterized by what it includes in the stack iteration:
    196 //  - When provided, the optional JSPrincipal argument will cause FrameIter to
    197 //    only show frames in globals whose JSPrincipals are subsumed (via
    198 //    JSSecurityCallbacks::subsume) by the given JSPrincipal.
    199 //
    200 // Additionally, there are derived FrameIter types that automatically skip
    201 // certain frames:
    202 //  - ScriptFrameIter only shows frames that have an associated JSScript
    203 //    (currently everything other than wasm stack frames). When !hasScript(),
    204 //    clients must stick to the portion of the
    205 //    interface marked below.
    206 //  - NonBuiltinScriptFrameIter additionally filters out builtin (self-hosted)
    207 //    scripts.
    208 class FrameIter {
    209 public:
    210  enum DebuggerEvalOption {
    211    FOLLOW_DEBUGGER_EVAL_PREV_LINK,
    212    IGNORE_DEBUGGER_EVAL_PREV_LINK
    213  };
    214 
    215  enum State {
    216    DONE,    // when there are no more frames nor activations to unwind.
    217    INTERP,  // interpreter activation on the stack
    218    JIT      // jit or wasm activations on the stack
    219  };
    220 
    221  // Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on
    222  // the heap, so this structure should not contain any GC things.
    223  struct Data {
    224    JSContext* cx_;
    225    DebuggerEvalOption debuggerEvalOption_;
    226    JSPrincipals* principals_;
    227 
    228    State state_;
    229 
    230    jsbytecode* pc_;
    231 
    232    InterpreterFrameIterator interpFrames_;
    233    ActivationIterator activations_;
    234 
    235    JitFrameIter jitFrames_;
    236    unsigned ionInlineFrameNo_;
    237 
    238    Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption,
    239         JSPrincipals* principals);
    240    Data(const Data& other);
    241  };
    242 
    243  explicit FrameIter(JSContext* cx,
    244                     DebuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK);
    245  FrameIter(JSContext* cx, DebuggerEvalOption, JSPrincipals*);
    246  FrameIter(const FrameIter& iter);
    247  MOZ_IMPLICIT FrameIter(const Data& data);
    248 
    249  bool done() const { return data_.state_ == DONE; }
    250 
    251  // -------------------------------------------------------
    252  // The following functions can only be called when !done()
    253  // -------------------------------------------------------
    254 
    255  FrameIter& operator++();
    256 
    257  JS::Realm* realm() const;
    258  JS::Compartment* compartment() const;
    259  Activation* activation() const { return data_.activations_.activation(); }
    260 
    261  bool isInterp() const {
    262    MOZ_ASSERT(!done());
    263    return data_.state_ == INTERP;
    264  }
    265  bool isJSJit() const {
    266    MOZ_ASSERT(!done());
    267    return data_.state_ == JIT && data_.jitFrames_.isJSJit();
    268  }
    269  bool isWasm() const {
    270    MOZ_ASSERT(!done());
    271    return data_.state_ == JIT && data_.jitFrames_.isWasm();
    272  }
    273 
    274  inline bool isIon() const;
    275  inline bool isBaseline() const;
    276  inline bool isPhysicalJitFrame() const;
    277 
    278  bool isEvalFrame() const;
    279  bool isModuleFrame() const;
    280  bool isFunctionFrame() const;
    281  bool hasArgs() const { return isFunctionFrame(); }
    282 
    283  ScriptSource* scriptSource() const;
    284  const char* filename() const;
    285  const char16_t* displayURL() const;
    286  unsigned computeLine(JS::TaggedColumnNumberOneOrigin* column = nullptr) const;
    287  JSAtom* maybeFunctionDisplayAtom() const;
    288  bool mutedErrors() const;
    289 
    290  bool hasScript() const { return !isWasm(); }
    291 
    292  // -----------------------------------------------------------
    293  //  The following functions can only be called when isWasm()
    294  // -----------------------------------------------------------
    295 
    296  inline bool wasmDebugEnabled() const;
    297  inline wasm::Instance* wasmInstance() const;
    298  inline uint32_t wasmFuncIndex() const;
    299  inline unsigned wasmBytecodeOffset() const;
    300  void wasmUpdateBytecodeOffset();
    301 
    302  // -----------------------------------------------------------
    303  // The following functions can only be called when hasScript()
    304  // -----------------------------------------------------------
    305 
    306  inline JSScript* script() const;
    307 
    308  bool isConstructing() const;
    309  jsbytecode* pc() const {
    310    MOZ_ASSERT(!done());
    311    return data_.pc_;
    312  }
    313  void updatePcQuadratic();
    314 
    315  // The function |calleeTemplate()| returns either the function from which
    316  // the current |callee| was cloned or the |callee| if it can be read. As
    317  // long as we do not have to investigate the environment chain or build a
    318  // new frame, we should prefer to use |calleeTemplate| instead of
    319  // |callee|, as requesting the |callee| might cause the invalidation of
    320  // the frame. (see js::Lambda)
    321  JSFunction* calleeTemplate() const;
    322  JSFunction* callee(JSContext* cx) const;
    323 
    324  JSFunction* maybeCallee(JSContext* cx) const {
    325    return isFunctionFrame() ? callee(cx) : nullptr;
    326  }
    327 
    328  bool matchCallee(JSContext* cx, JS::Handle<JSFunction*> fun) const;
    329 
    330  unsigned numActualArgs() const;
    331  unsigned numFormalArgs() const;
    332  JS::Value unaliasedActual(unsigned i,
    333                            MaybeCheckAliasing = CHECK_ALIASING) const;
    334  template <class Op>
    335  inline void unaliasedForEachActual(JSContext* cx, Op op);
    336 
    337  JSObject* environmentChain(JSContext* cx) const;
    338  bool hasInitialEnvironment(JSContext* cx) const;
    339  CallObject& callObj(JSContext* cx) const;
    340 
    341  bool hasArgsObj() const;
    342  ArgumentsObject& argsObj() const;
    343 
    344  // Get the original |this| value passed to this function. May not be the
    345  // actual this-binding (for instance, derived class constructors will
    346  // change their this-value later and non-strict functions will box
    347  // primitives).
    348  JS::Value thisArgument(JSContext* cx) const;
    349 
    350  JS::Value returnValue() const;
    351  void setReturnValue(const JS::Value& v);
    352 
    353  // These are only valid for the top frame.
    354  size_t numFrameSlots() const;
    355  JS::Value frameSlotValue(size_t index) const;
    356 
    357  // Ensures that we have rematerialized the top frame and its associated
    358  // inline frames. Can only be called when isIon().
    359  bool ensureHasRematerializedFrame(JSContext* cx);
    360 
    361  // True when isInterp() or isBaseline(). True when isIon() if it
    362  // has a rematerialized frame. False otherwise.
    363  bool hasUsableAbstractFramePtr() const;
    364 
    365  // -----------------------------------------------------------
    366  // The following functions can only be called when isInterp(),
    367  // isBaseline(), isWasm() or isIon(). Further, abstractFramePtr() can
    368  // only be called when hasUsableAbstractFramePtr().
    369  // -----------------------------------------------------------
    370 
    371  AbstractFramePtr abstractFramePtr() const;
    372  Data* copyData() const;
    373 
    374  // This can only be called when isInterp():
    375  inline InterpreterFrame* interpFrame() const;
    376 
    377  // This can only be called when isPhysicalJitFrame():
    378  inline jit::CommonFrameLayout* physicalJitFrame() const;
    379 
    380  // This is used to provide a raw interface for debugging.
    381  void* rawFramePtr() const;
    382 
    383  bool inPrologue() const;
    384 
    385  const wasm::WasmFrameIter& wasmFrame() const {
    386    return data_.jitFrames_.asWasm();
    387  }
    388  wasm::WasmFrameIter& wasmFrame() { return data_.jitFrames_.asWasm(); }
    389 
    390 private:
    391  Data data_;
    392  jit::InlineFrameIterator ionInlineFrames_;
    393 
    394  const jit::JSJitFrameIter& jsJitFrame() const {
    395    return data_.jitFrames_.asJSJit();
    396  }
    397 
    398  jit::JSJitFrameIter& jsJitFrame() { return data_.jitFrames_.asJSJit(); }
    399 
    400  bool isIonScripted() const {
    401    return isJSJit() && jsJitFrame().isIonScripted();
    402  }
    403 
    404  bool principalsSubsumeFrame() const;
    405 
    406  void popActivation();
    407  void popInterpreterFrame();
    408  void nextJitFrame();
    409  void popJitFrame();
    410  void settleOnActivation();
    411 };
    412 
    413 class ScriptFrameIter : public FrameIter {
    414  void settle() {
    415    while (!done() && !hasScript()) {
    416      FrameIter::operator++();
    417    }
    418  }
    419 
    420 public:
    421  explicit ScriptFrameIter(
    422      JSContext* cx,
    423      DebuggerEvalOption debuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK)
    424      : FrameIter(cx, debuggerEvalOption) {
    425    settle();
    426  }
    427 
    428  ScriptFrameIter& operator++() {
    429    FrameIter::operator++();
    430    settle();
    431    return *this;
    432  }
    433 };
    434 
    435 #ifdef DEBUG
    436 bool SelfHostedFramesVisible();
    437 #else
    438 static inline bool SelfHostedFramesVisible() { return false; }
    439 #endif
    440 
    441 /* A filtering of the FrameIter to only stop at non-self-hosted scripts. */
    442 class NonBuiltinFrameIter : public FrameIter {
    443  void settle();
    444 
    445 public:
    446  explicit NonBuiltinFrameIter(
    447      JSContext* cx, FrameIter::DebuggerEvalOption debuggerEvalOption =
    448                         FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK)
    449      : FrameIter(cx, debuggerEvalOption) {
    450    settle();
    451  }
    452 
    453  NonBuiltinFrameIter(JSContext* cx,
    454                      FrameIter::DebuggerEvalOption debuggerEvalOption,
    455                      JSPrincipals* principals)
    456      : FrameIter(cx, debuggerEvalOption, principals) {
    457    settle();
    458  }
    459 
    460  NonBuiltinFrameIter(JSContext* cx, JSPrincipals* principals)
    461      : FrameIter(cx, FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK, principals) {
    462    settle();
    463  }
    464 
    465  NonBuiltinFrameIter& operator++() {
    466    FrameIter::operator++();
    467    settle();
    468    return *this;
    469  }
    470 };
    471 
    472 // A filtering of the ScriptFrameIter to only stop at non-self-hosted scripts.
    473 class NonBuiltinScriptFrameIter : public ScriptFrameIter {
    474  void settle();
    475 
    476 public:
    477  explicit NonBuiltinScriptFrameIter(
    478      JSContext* cx, ScriptFrameIter::DebuggerEvalOption debuggerEvalOption =
    479                         ScriptFrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK)
    480      : ScriptFrameIter(cx, debuggerEvalOption) {
    481    settle();
    482  }
    483 
    484  NonBuiltinScriptFrameIter& operator++() {
    485    ScriptFrameIter::operator++();
    486    settle();
    487    return *this;
    488  }
    489 };
    490 
    491 /*
    492 * Blindly iterate over all frames in the current thread's stack. These frames
    493 * can be from different contexts and compartments, so beware.
    494 */
    495 class AllFramesIter : public FrameIter {
    496 public:
    497  explicit AllFramesIter(JSContext* cx)
    498      : FrameIter(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK) {}
    499 };
    500 
    501 /* Iterates over all script frame in the current thread's stack.
    502 * See also AllFramesIter and ScriptFrameIter.
    503 */
    504 class AllScriptFramesIter : public ScriptFrameIter {
    505 public:
    506  explicit AllScriptFramesIter(JSContext* cx)
    507      : ScriptFrameIter(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK) {}
    508 };
    509 
    510 /* Popular inline definitions. */
    511 
    512 inline JSScript* FrameIter::script() const {
    513  MOZ_ASSERT(!done());
    514  MOZ_ASSERT(hasScript());
    515  if (data_.state_ == INTERP) {
    516    return interpFrame()->script();
    517  }
    518  if (jsJitFrame().isIonJS()) {
    519    return ionInlineFrames_.script();
    520  }
    521  return jsJitFrame().script();
    522 }
    523 
    524 inline bool FrameIter::wasmDebugEnabled() const {
    525  MOZ_ASSERT(!done());
    526  MOZ_ASSERT(isWasm());
    527  return wasmFrame().debugEnabled();
    528 }
    529 
    530 inline wasm::Instance* FrameIter::wasmInstance() const {
    531  MOZ_ASSERT(!done());
    532  MOZ_ASSERT(isWasm());
    533  return wasmFrame().instance();
    534 }
    535 
    536 inline unsigned FrameIter::wasmBytecodeOffset() const {
    537  MOZ_ASSERT(!done());
    538  MOZ_ASSERT(isWasm());
    539  return wasmFrame().lineOrBytecode();
    540 }
    541 
    542 inline uint32_t FrameIter::wasmFuncIndex() const {
    543  MOZ_ASSERT(!done());
    544  MOZ_ASSERT(isWasm());
    545  return wasmFrame().funcIndex();
    546 }
    547 
    548 inline bool FrameIter::isIon() const {
    549  return isJSJit() && jsJitFrame().isIonJS();
    550 }
    551 
    552 inline bool FrameIter::isBaseline() const {
    553  return isJSJit() && jsJitFrame().isBaselineJS();
    554 }
    555 
    556 inline InterpreterFrame* FrameIter::interpFrame() const {
    557  MOZ_ASSERT(data_.state_ == INTERP);
    558  return data_.interpFrames_.frame();
    559 }
    560 
    561 inline bool FrameIter::isPhysicalJitFrame() const {
    562  if (!isJSJit()) {
    563    return false;
    564  }
    565 
    566  auto& jitFrame = jsJitFrame();
    567 
    568  if (jitFrame.isBaselineJS()) {
    569    return true;
    570  }
    571 
    572  if (jitFrame.isIonScripted()) {
    573    // Only the bottom of a group of inlined Ion frames is a physical frame.
    574    return ionInlineFrames_.frameNo() == 0;
    575  }
    576 
    577  return false;
    578 }
    579 
    580 inline jit::CommonFrameLayout* FrameIter::physicalJitFrame() const {
    581  MOZ_ASSERT(isPhysicalJitFrame());
    582  return jsJitFrame().current();
    583 }
    584 
    585 }  // namespace js
    586 
    587 #endif  // vm_FrameIter_h