tor-browser

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

JSJitFrameIter.h (27232B)


      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 jit_JSJitFrameIter_h
      8 #define jit_JSJitFrameIter_h
      9 
     10 #include "mozilla/Maybe.h"
     11 
     12 #include "jstypes.h"
     13 
     14 #include "jit/JitCode.h"
     15 #include "jit/MachineState.h"
     16 #include "jit/Snapshots.h"
     17 #include "js/ProfilingFrameIterator.h"
     18 #include "vm/JSFunction.h"
     19 #include "vm/JSScript.h"
     20 
     21 namespace js {
     22 
     23 class ArgumentsObject;
     24 
     25 namespace jit {
     26 
     27 enum class FrameType {
     28  // A JS frame is analogous to a js::InterpreterFrame, representing one
     29  // scripted function activation. IonJS frames are used by the optimizing
     30  // compiler.
     31  IonJS,
     32 
     33  // JS frame used by the Baseline Interpreter and Baseline JIT.
     34  BaselineJS,
     35 
     36  // Frame pushed by Baseline stubs that make non-tail calls, so that the
     37  // return address -> ICEntry mapping works.
     38  BaselineStub,
     39 
     40  // The entry frame is the initial prologue block transitioning from the VM
     41  // into the Ion world.
     42  CppToJSJit,
     43 
     44  // This entry frame sits right before the baseline interpreter
     45  // so that external profilers can identify which function is being
     46  // interpreted. Only used under the --emit-interpreter-entry option.
     47  BaselineInterpreterEntry,
     48 
     49  // Ion IC calling a scripted getter/setter or a VMFunction.
     50  IonICCall,
     51 
     52  // An exit frame is necessary for transitioning from a JS frame into C++.
     53  // From within C++, an exit frame is always the last frame in any
     54  // JitActivation.
     55  Exit,
     56 
     57  // A bailout frame is a special IonJS jit frame after a bailout, and before
     58  // the reconstruction of the BaselineJS frame. From within C++, a bailout
     59  // frame is always the last frame in a JitActivation iff the bailout frame
     60  // information is recorded on the JitActivation.
     61  Bailout,
     62 
     63  // A wasm to JS frame is constructed during fast calls from wasm to the JS
     64  // jits, used as a marker to interleave JS jit and wasm frames. From the
     65  // point of view of JS JITs, this is just another kind of entry frame.
     66  WasmToJSJit,
     67 
     68  // Frame for a TrampolineNative, a JS builtin implemented with a JIT
     69  // trampoline. See jit/TrampolineNatives.h.
     70  TrampolineNative,
     71 };
     72 
     73 enum class ReadFrameArgsBehavior {
     74  // Read all actual arguments. Will invoke the callback numActualArgs times.
     75  Actuals,
     76 
     77  // Read all argument values in the stack frame. Will invoke the callback
     78  // max(numFormalArgs, numActualArgs) times.
     79  ActualsAndFormals,
     80 };
     81 
     82 class CommonFrameLayout;
     83 class JitFrameLayout;
     84 class ExitFrameLayout;
     85 
     86 class BaselineFrame;
     87 class JitActivation;
     88 class SafepointIndex;
     89 class OsiIndex;
     90 
     91 // Iterate over the JIT stack to assert that all invariants are respected.
     92 //  - Check that all entry frames are aligned on JitStackAlignment.
     93 
     94 void AssertJitStackInvariants(JSContext* cx);
     95 
     96 // A JSJitFrameIter can iterate over a linear frame group of JS jit frames
     97 // only. It will stop at the first frame that is not of the same kind, or at
     98 // the end of an activation.
     99 //
    100 // If you want to handle every kind of frames (including wasm frames), use
    101 // JitFrameIter. If you want to skip interleaved frames of other kinds, use
    102 // OnlyJSJitFrameIter.
    103 
    104 class JSJitFrameIter {
    105 protected:
    106  uint8_t* current_;
    107  FrameType type_;
    108  uint8_t* resumePCinCurrentFrame_ = nullptr;
    109 
    110  // Size of the current Baseline frame. Equivalent to
    111  // BaselineFrame::debugFrameSize_ in debug builds.
    112  mozilla::Maybe<uint32_t> baselineFrameSize_;
    113 
    114 private:
    115  mutable const SafepointIndex* cachedSafepointIndex_ = nullptr;
    116  const JitActivation* activation_;
    117 
    118  void dumpBaseline() const;
    119 
    120 public:
    121  // See comment above the class.
    122  explicit JSJitFrameIter(const JitActivation* activation);
    123 
    124  // A constructor specialized for jit->wasm frames, which starts at a
    125  // specific FP.
    126  JSJitFrameIter(const JitActivation* activation, uint8_t* fp, bool unwinding);
    127 
    128  void setResumePCInCurrentFrame(uint8_t* newAddr) {
    129    resumePCinCurrentFrame_ = newAddr;
    130  }
    131 
    132  // Current frame information.
    133  FrameType type() const { return type_; }
    134  uint8_t* fp() const { return current_; }
    135  const JitActivation* activation() const { return activation_; }
    136 
    137  CommonFrameLayout* current() const { return (CommonFrameLayout*)current_; }
    138 
    139  inline uint8_t* returnAddress() const;
    140 
    141  // Return the pointer of the JitFrame, the iterator is assumed to be settled
    142  // on a scripted frame.
    143  JitFrameLayout* jsFrame() const;
    144 
    145  inline ExitFrameLayout* exitFrame() const;
    146 
    147  // Returns whether the JS frame has been invalidated and, if so,
    148  // places the invalidated Ion script in |ionScript|.
    149  bool checkInvalidation(IonScript** ionScript) const;
    150  bool checkInvalidation() const;
    151 
    152  bool isExitFrame() const { return type_ == FrameType::Exit; }
    153  bool isScripted() const {
    154    return type_ == FrameType::BaselineJS || type_ == FrameType::IonJS ||
    155           type_ == FrameType::Bailout;
    156  }
    157  bool isBaselineJS() const { return type_ == FrameType::BaselineJS; }
    158  bool isIonScripted() const {
    159    return type_ == FrameType::IonJS || type_ == FrameType::Bailout;
    160  }
    161  bool isIonJS() const { return type_ == FrameType::IonJS; }
    162  bool isIonICCall() const { return type_ == FrameType::IonICCall; }
    163  bool isBailoutJS() const { return type_ == FrameType::Bailout; }
    164  bool isBaselineStub() const { return type_ == FrameType::BaselineStub; }
    165  bool isBaselineInterpreterEntry() const {
    166    return type_ == FrameType::BaselineInterpreterEntry;
    167  }
    168  bool isTrampolineNative() const {
    169    return type_ == FrameType::TrampolineNative;
    170  }
    171  bool isBareExit() const;
    172  bool isUnwoundJitExit() const;
    173  template <typename T>
    174  bool isExitFrameLayout() const;
    175 
    176  static bool isEntry(FrameType type) {
    177    return type == FrameType::CppToJSJit || type == FrameType::WasmToJSJit;
    178  }
    179  bool isEntry() const { return isEntry(type_); }
    180 
    181  bool isFunctionFrame() const;
    182 
    183  bool isConstructing() const;
    184 
    185  void* calleeToken() const;
    186  JSFunction* callee() const;
    187  JSFunction* maybeCallee() const;
    188  unsigned numActualArgs() const;
    189  JSScript* script() const;
    190  JSScript* maybeForwardedScript() const;
    191  void baselineScriptAndPc(JSScript** scriptRes, jsbytecode** pcRes) const;
    192  Value* actualArgs() const;
    193 
    194  // Returns the address of the next instruction that will execute in this
    195  // frame, once control returns to this frame.
    196  uint8_t* resumePCinCurrentFrame() const { return resumePCinCurrentFrame_; }
    197 
    198  // Previous frame information extracted from the current frame.
    199  inline FrameType prevType() const;
    200  uint8_t* prevFp() const;
    201 
    202  // Functions used to iterate on frames. When prevType is an entry,
    203  // the current frame is the last JS Jit frame.
    204  bool done() const { return isEntry(); }
    205  void operator++();
    206 
    207  // Returns the IonScript associated with this JS frame.
    208  IonScript* ionScript() const;
    209 
    210  // Returns the IonScript associated with this JS frame; the frame must
    211  // not be invalidated.
    212  IonScript* ionScriptFromCalleeToken() const;
    213 
    214  // Returns the Safepoint associated with this JS frame. Incurs a lookup
    215  // overhead.
    216  const SafepointIndex* safepoint() const;
    217 
    218  // Returns the OSI index associated with this JS frame. Incurs a lookup
    219  // overhead.
    220  const OsiIndex* osiIndex() const;
    221 
    222  // Returns the Snapshot offset associated with this JS frame. Incurs a
    223  // lookup overhead.
    224  SnapshotOffset snapshotOffset() const;
    225 
    226  uintptr_t* spillBase() const;
    227  MachineState machineState() const;
    228 
    229  template <class Op>
    230  void unaliasedForEachActual(Op op) const {
    231    MOZ_ASSERT(isBaselineJS());
    232 
    233    unsigned nactual = numActualArgs();
    234    Value* argv = actualArgs();
    235    for (unsigned i = 0; i < nactual; i++) {
    236      op(argv[i]);
    237    }
    238  }
    239 
    240  void dump() const;
    241 
    242  inline BaselineFrame* baselineFrame() const;
    243 
    244  // Returns the number of local and expression stack Values for the current
    245  // Baseline frame.
    246  inline uint32_t baselineFrameNumValueSlots() const;
    247 };
    248 
    249 class JitcodeGlobalTable;
    250 
    251 class JSJitProfilingFrameIterator {
    252  uint8_t* fp_;
    253  uint8_t* wasmCallerFP_ = nullptr;
    254  // See JS::ProfilingFrameIterator::endStackAddress_ comment.
    255  void* endStackAddress_ = nullptr;
    256  FrameType type_;
    257  void* resumePCinCurrentFrame_;
    258 
    259  inline JSScript* frameScript() const;
    260  [[nodiscard]] bool tryInitWithPC(void* pc);
    261  [[nodiscard]] bool tryInitWithTable(JitcodeGlobalTable* table, void* pc,
    262                                      bool forLastCallSite);
    263 
    264  void moveToNextFrame(CommonFrameLayout* frame);
    265 
    266 public:
    267  JSJitProfilingFrameIterator(JSContext* cx, void* pc, void* sp);
    268  explicit JSJitProfilingFrameIterator(CommonFrameLayout* exitFP);
    269 
    270  void operator++();
    271  bool done() const { return fp_ == nullptr; }
    272 
    273  const char* baselineInterpreterLabel() const;
    274  void baselineInterpreterScriptPC(JSScript** script, jsbytecode** pc,
    275                                   uint64_t* realmID, uint32_t* sourceId) const;
    276 
    277  void* fp() const {
    278    MOZ_ASSERT(!done());
    279    return fp_;
    280  }
    281  void* wasmCallerFP() const {
    282    MOZ_ASSERT(done());
    283    MOZ_ASSERT(bool(wasmCallerFP_) == (type_ == FrameType::WasmToJSJit));
    284    return wasmCallerFP_;
    285  }
    286  inline JitFrameLayout* framePtr() const;
    287  void* stackAddress() const { return fp(); }
    288  FrameType frameType() const {
    289    MOZ_ASSERT(!done());
    290    return type_;
    291  }
    292  void* resumePCinCurrentFrame() const {
    293    MOZ_ASSERT(!done());
    294    return resumePCinCurrentFrame_;
    295  }
    296 
    297  void* endStackAddress() const { return endStackAddress_; }
    298 };
    299 
    300 class RInstructionResults {
    301  // Vector of results of recover instructions.
    302  using Values = mozilla::Vector<HeapPtr<Value>, 1, SystemAllocPolicy>;
    303  UniquePtr<Values> results_;
    304 
    305  // The frame pointer is used as a key to check if the current frame already
    306  // bailed out.
    307  JitFrameLayout* fp_;
    308 
    309  // Record if we tried and succeed at allocating and filling the vector of
    310  // recover instruction results, if needed.  This flag is needed in order to
    311  // avoid evaluating the recover instruction twice.
    312  bool initialized_;
    313 
    314 public:
    315  explicit RInstructionResults(JitFrameLayout* fp);
    316  RInstructionResults(RInstructionResults&& src);
    317 
    318  RInstructionResults& operator=(RInstructionResults&& rhs);
    319 
    320  ~RInstructionResults();
    321 
    322  [[nodiscard]] bool init(JSContext* cx, uint32_t numResults);
    323  bool isInitialized() const;
    324  size_t length() const;
    325 
    326  JitFrameLayout* frame() const;
    327 
    328  HeapPtr<Value>& operator[](size_t index);
    329 
    330  void trace(JSTracer* trc);
    331 };
    332 
    333 struct MaybeReadFallback {
    334  enum FallbackConsequence { Fallback_Invalidate, Fallback_DoNothing };
    335 
    336  JSContext* maybeCx = nullptr;
    337  JitActivation* activation = nullptr;
    338  const JSJitFrameIter* frame = nullptr;
    339  const FallbackConsequence consequence = Fallback_Invalidate;
    340 
    341  MaybeReadFallback() = default;
    342 
    343  MaybeReadFallback(JSContext* cx, JitActivation* activation,
    344                    const JSJitFrameIter* frame,
    345                    FallbackConsequence consequence = Fallback_Invalidate)
    346      : maybeCx(cx),
    347        activation(activation),
    348        frame(frame),
    349        consequence(consequence) {}
    350 
    351  bool canRecoverResults() { return maybeCx; }
    352 };
    353 
    354 class RResumePoint;
    355 
    356 // Reads frame information in snapshot-encoding order (that is, outermost frame
    357 // to innermost frame).
    358 class SnapshotIterator {
    359 protected:
    360  SnapshotReader snapshot_;
    361  RecoverReader recover_;
    362  JitFrameLayout* fp_;
    363  const MachineState* machine_;
    364  IonScript* ionScript_;
    365  RInstructionResults* instructionResults_;
    366 
    367  enum class ReadMethod : bool {
    368    // Read the normal value.
    369    Normal,
    370 
    371    // Read the default value, or the normal value if there is no default.
    372    AlwaysDefault,
    373  };
    374 
    375 private:
    376  // Read a spilled register from the machine state.
    377  bool hasRegister(Register reg) const { return machine_->has(reg); }
    378  uintptr_t fromRegister(Register reg) const { return machine_->read(reg); }
    379 
    380  bool hasRegister(FloatRegister reg) const { return machine_->has(reg); }
    381  template <typename T>
    382  T fromRegister(FloatRegister reg) const {
    383    return machine_->read<T>(reg);
    384  }
    385 
    386  // Read an uintptr_t from the stack.
    387  bool hasStack(int32_t offset) const { return true; }
    388  uintptr_t fromStack(int32_t offset) const;
    389 
    390  bool hasInstructionResult(uint32_t index) const {
    391    return instructionResults_;
    392  }
    393  bool hasInstructionResults() const { return instructionResults_; }
    394  Value fromInstructionResult(uint32_t index) const;
    395 
    396  Value allocationValue(const RValueAllocation& a,
    397                        ReadMethod rm = ReadMethod::Normal);
    398  [[nodiscard]] bool allocationReadable(const RValueAllocation& a,
    399                                        ReadMethod rm = ReadMethod::Normal);
    400  void writeAllocationValuePayload(const RValueAllocation& a, const Value& v);
    401  void warnUnreadableAllocation();
    402 
    403 public:
    404  // Handle iterating over RValueAllocations of the snapshots.
    405  inline RValueAllocation readAllocation() {
    406    MOZ_ASSERT(moreAllocations());
    407    return snapshot_.readAllocation();
    408  }
    409  void skip() { snapshot_.skipAllocation(); }
    410 
    411  const RResumePoint* resumePoint() const;
    412  const RInstruction* instruction() const { return recover_.instruction(); }
    413 
    414  uint32_t numAllocations() const;
    415  inline bool moreAllocations() const {
    416    return snapshot_.numAllocationsRead() < numAllocations();
    417  }
    418 
    419  JitFrameLayout* frame() { return fp_; };
    420 
    421  // Used by recover instruction to store the value back into the instruction
    422  // results array.
    423  void storeInstructionResult(const Value& v);
    424 
    425 public:
    426  // Exhibits frame properties contained in the snapshot.
    427  uint32_t pcOffset() const;
    428  ResumeMode resumeMode() const;
    429 
    430  bool resumeAfter() const {
    431    // Calls in outer frames are never considered resume-after.
    432    MOZ_ASSERT_IF(moreFrames(), !IsResumeAfter(resumeMode()));
    433    return IsResumeAfter(resumeMode());
    434  }
    435  inline BailoutKind bailoutKind() const { return snapshot_.bailoutKind(); }
    436 
    437  IonScript* ionScript() const { return ionScript_; }
    438 
    439 public:
    440  // Read the next instruction available and get ready to either skip it or
    441  // evaluate it.
    442  inline void nextInstruction() {
    443    MOZ_ASSERT(snapshot_.numAllocationsRead() == numAllocations());
    444    recover_.nextInstruction();
    445    snapshot_.resetNumAllocationsRead();
    446  }
    447 
    448  // Skip an Instruction by walking to the next instruction and by skipping
    449  // all the allocations corresponding to this instruction.
    450  void skipInstruction();
    451 
    452  inline bool moreInstructions() const { return recover_.moreInstructions(); }
    453 
    454  // Register a vector used for storing the results of the evaluation of
    455  // recover instructions. This vector should be registered before the
    456  // beginning of the iteration. This function is in charge of allocating
    457  // enough space for all instructions results, and return false iff it fails.
    458  [[nodiscard]] bool initInstructionResults(MaybeReadFallback& fallback);
    459 
    460 protected:
    461  // This function is used internally for computing the result of the recover
    462  // instructions.
    463  [[nodiscard]] bool computeInstructionResults(
    464      JSContext* cx, RInstructionResults* results) const;
    465 
    466 public:
    467  // Handle iterating over frames of the snapshots.
    468  void nextFrame();
    469  void settleOnFrame();
    470 
    471  inline bool moreFrames() const {
    472    // The last instruction is recovering the innermost frame, so as long as
    473    // there is more instruction there is necesseray more frames.
    474    return moreInstructions();
    475  }
    476 
    477 public:
    478  // Connect all informations about the current script in order to recover the
    479  // content of baseline frames.
    480 
    481  SnapshotIterator(const JSJitFrameIter& iter,
    482                   const MachineState* machineState);
    483  SnapshotIterator();
    484 
    485  Value read() { return allocationValue(readAllocation()); }
    486 
    487  // Like |read()| but also supports IntPtr and Int64 allocations, which are
    488  // returned as BigInt values.
    489  bool readMaybeUnpackedBigInt(JSContext* cx, MutableHandle<Value> result);
    490 
    491  int32_t readInt32() {
    492    Value val = read();
    493    MOZ_RELEASE_ASSERT(val.isInt32());
    494    return val.toInt32();
    495  }
    496 
    497  double readNumber() {
    498    Value val = read();
    499    MOZ_RELEASE_ASSERT(val.isNumber());
    500    return val.toNumber();
    501  }
    502 
    503  JSString* readString() {
    504    Value val = read();
    505    MOZ_RELEASE_ASSERT(val.isString());
    506    return val.toString();
    507  }
    508 
    509  JS::BigInt* readBigInt() {
    510    Value val = read();
    511    MOZ_RELEASE_ASSERT(val.isBigInt());
    512    return val.toBigInt();
    513  }
    514 
    515  JSObject* readObject() {
    516    Value val = read();
    517    MOZ_RELEASE_ASSERT(val.isObject());
    518    return &val.toObject();
    519  }
    520 
    521  JS::GCCellPtr readGCCellPtr() {
    522    Value val = read();
    523    MOZ_RELEASE_ASSERT(val.isGCThing());
    524    return val.toGCCellPtr();
    525  }
    526 
    527  // Read the |Normal| value unless it is not available and that the snapshot
    528  // provides a |Default| value. This is useful to avoid invalidations of the
    529  // frame while we are only interested in a few properties which are provided
    530  // by the |Default| value.
    531  Value readWithDefault(RValueAllocation* alloc) {
    532    *alloc = RValueAllocation();
    533    RValueAllocation a = readAllocation();
    534    if (allocationReadable(a)) {
    535      return allocationValue(a);
    536    }
    537 
    538    *alloc = a;
    539    return allocationValue(a, ReadMethod::AlwaysDefault);
    540  }
    541 
    542  Value maybeRead(const RValueAllocation& a, MaybeReadFallback& fallback);
    543  Value maybeRead(MaybeReadFallback& fallback) {
    544    RValueAllocation a = readAllocation();
    545    return maybeRead(a, fallback);
    546  }
    547 
    548  bool tryRead(Value* result);
    549 
    550 private:
    551  int64_t allocationInt64(const RValueAllocation& alloc);
    552  intptr_t allocationIntPtr(const RValueAllocation& alloc);
    553 
    554 public:
    555  int64_t readInt64() { return allocationInt64(readAllocation()); }
    556 
    557  intptr_t readIntPtr() { return allocationIntPtr(readAllocation()); }
    558 
    559  // Read either a BigInt or unpacked BigInt.
    560  JS::BigInt* readBigInt(JSContext* cx);
    561 
    562  void traceAllocation(JSTracer* trc);
    563 
    564  template <class Op>
    565  void readFunctionFrameArgs(Op& op, ArgumentsObject** argsObj, Value* thisv,
    566                             unsigned start, unsigned end, JSScript* script,
    567                             MaybeReadFallback& fallback) {
    568    // Assumes that the common frame arguments have already been read.
    569    if (script->needsArgsObj()) {
    570      if (argsObj) {
    571        Value v = maybeRead(fallback);
    572        if (v.isObject()) {
    573          *argsObj = &v.toObject().as<ArgumentsObject>();
    574        }
    575      } else {
    576        skip();
    577      }
    578    }
    579 
    580    if (thisv) {
    581      *thisv = maybeRead(fallback);
    582    } else {
    583      skip();
    584    }
    585 
    586    unsigned i = 0;
    587    if (end < start) {
    588      i = start;
    589    }
    590 
    591    for (; i < start; i++) {
    592      skip();
    593    }
    594    for (; i < end; i++) {
    595      // We are not always able to read values from the snapshots, some values
    596      // such as non-gc things may still be live in registers and cause an
    597      // error while reading the machine state.
    598      Value v = maybeRead(fallback);
    599      op(v);
    600    }
    601  }
    602 
    603  // Iterate over all the allocations and return only the value of the
    604  // allocation located at one index.
    605  Value maybeReadAllocByIndex(size_t index);
    606 
    607 #ifdef TRACK_SNAPSHOTS
    608  void spewBailingFrom() const { snapshot_.spewBailingFrom(); }
    609 #endif
    610 };
    611 
    612 // Reads frame information in callstack order (that is, innermost frame to
    613 // outermost frame).
    614 class InlineFrameIterator {
    615  const JSJitFrameIter* frame_;
    616  SnapshotIterator start_;
    617  SnapshotIterator si_;
    618  uint32_t framesRead_;
    619 
    620  // When the inline-frame-iterator is created, this variable is defined to
    621  // UINT32_MAX. Then the first iteration of findNextFrame, which settle on
    622  // the innermost frame, is used to update this counter to the number of
    623  // frames contained in the recover buffer.
    624  uint32_t frameCount_;
    625 
    626  // The |calleeTemplate_| fields contains either the JSFunction or the
    627  // template from which it is supposed to be cloned. The |calleeRVA_| is an
    628  // Invalid value allocation, if the |calleeTemplate_| field is the effective
    629  // JSFunction, and not its template. On the other hand, any other value
    630  // allocation implies that the |calleeTemplate_| is the template JSFunction
    631  // from which the effective one would be derived and cached by the Recover
    632  // instruction result.
    633  RootedFunction calleeTemplate_;
    634  RValueAllocation calleeRVA_;
    635 
    636  RootedScript script_;
    637  jsbytecode* pc_;
    638  uint32_t numActualArgs_;
    639 
    640  // Register state, used by all snapshot iterators.
    641  MachineState machine_;
    642 
    643  struct Nop {
    644    void operator()(const Value& v) {}
    645  };
    646 
    647 private:
    648  void findNextFrame();
    649  JSObject* computeEnvironmentChain(const Value& envChainValue,
    650                                    MaybeReadFallback& fallback,
    651                                    bool* hasInitialEnv = nullptr) const;
    652 
    653 public:
    654  InlineFrameIterator(JSContext* cx, const JSJitFrameIter* iter);
    655  InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter);
    656 
    657  bool more() const { return frame_ && framesRead_ < frameCount_; }
    658 
    659  // Due to optimizations, we are not always capable of reading the callee of
    660  // inlined frames without invalidating the IonCode. This function might
    661  // return either the effective callee of the JSFunction which might be used
    662  // to create it.
    663  //
    664  // As such, the |calleeTemplate()| can be used to read most of the metadata
    665  // which are conserved across clones.
    666  JSFunction* calleeTemplate() const {
    667    MOZ_ASSERT(isFunctionFrame());
    668    return calleeTemplate_;
    669  }
    670  JSFunction* maybeCalleeTemplate() const { return calleeTemplate_; }
    671 
    672  JSFunction* callee(MaybeReadFallback& fallback) const;
    673 
    674  unsigned numActualArgs() const {
    675    // The number of actual arguments for inline frames is determined by this
    676    // iterator based on the caller's bytecode instruction (Call, FunCall,
    677    // GetProp/SetProp, etc). For the outer function it's stored in the stack
    678    // frame.
    679    if (more()) {
    680      return numActualArgs_;
    681    }
    682 
    683    return frame_->numActualArgs();
    684  }
    685 
    686  template <class ArgOp, class LocalOp>
    687  void readFrameArgsAndLocals(JSContext* cx, ArgOp& argOp, LocalOp& localOp,
    688                              JSObject** envChain, bool* hasInitialEnv,
    689                              Value* rval, ArgumentsObject** argsObj,
    690                              Value* thisv, ReadFrameArgsBehavior behavior,
    691                              MaybeReadFallback& fallback) const {
    692    SnapshotIterator s(si_);
    693 
    694    // Read the env chain.
    695    if (envChain) {
    696      Value envChainValue = s.maybeRead(fallback);
    697      *envChain =
    698          computeEnvironmentChain(envChainValue, fallback, hasInitialEnv);
    699    } else {
    700      s.skip();
    701    }
    702 
    703    // Read return value.
    704    if (rval) {
    705      *rval = s.maybeRead(fallback);
    706    } else {
    707      s.skip();
    708    }
    709 
    710    // Read arguments, which only function frames have.
    711    if (isFunctionFrame()) {
    712      unsigned nactual = numActualArgs();
    713      unsigned nformal = calleeTemplate()->nargs();
    714 
    715      // Read the formal arguments, which are taken from the inlined frame,
    716      // because it will have the updated value when JSOp::SetArg is used.
    717      unsigned numFormalsToRead;
    718      if (behavior == ReadFrameArgsBehavior::Actuals) {
    719        numFormalsToRead = std::min(nactual, nformal);
    720      } else {
    721        MOZ_ASSERT(behavior == ReadFrameArgsBehavior::ActualsAndFormals);
    722        numFormalsToRead = nformal;
    723      }
    724      s.readFunctionFrameArgs(argOp, argsObj, thisv, 0, numFormalsToRead,
    725                              script(), fallback);
    726 
    727      // Skip formals we didn't read.
    728      for (unsigned i = numFormalsToRead; i < nformal; i++) {
    729        s.skip();
    730      }
    731 
    732      if (nactual > nformal) {
    733        if (more()) {
    734          // There is still a parent frame of this inlined frame.  All
    735          // arguments (also the overflown) are the last pushed values
    736          // in the parent frame.  To get the overflown arguments, we
    737          // need to take them from there.
    738 
    739          // The overflown arguments are not available in current frame.
    740          // They are the last pushed arguments in the parent frame of
    741          // this inlined frame.
    742          InlineFrameIterator it(cx, this);
    743          ++it;
    744          unsigned argsObjAdj = it.script()->needsArgsObj() ? 1 : 0;
    745          bool hasNewTarget = isConstructing();
    746          SnapshotIterator parent_s(it.snapshotIterator());
    747 
    748          // Skip over all slots until we get to the last slots
    749          // (= arguments slots of callee) the +3 is for [this], [returnvalue],
    750          // [envchain], and maybe +1 for [argsObj]
    751          MOZ_ASSERT(parent_s.numAllocations() >=
    752                     nactual + 3 + argsObjAdj + hasNewTarget);
    753          unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj -
    754                          hasNewTarget;
    755          for (unsigned j = 0; j < skip; j++) {
    756            parent_s.skip();
    757          }
    758 
    759          // Get the overflown arguments
    760          parent_s.skip();  // env chain
    761          parent_s.skip();  // return value
    762          parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr, nformal,
    763                                         nactual, it.script(), fallback);
    764        } else {
    765          // There is no parent frame to this inlined frame, we can read
    766          // from the frame's Value vector directly.
    767          Value* argv = frame_->actualArgs();
    768          for (unsigned i = nformal; i < nactual; i++) {
    769            argOp(argv[i]);
    770          }
    771        }
    772      }
    773    }
    774 
    775    // At this point we've read all the formals in s, and can read the
    776    // locals.
    777    for (unsigned i = 0; i < script()->nfixed(); i++) {
    778      localOp(s.maybeRead(fallback));
    779    }
    780  }
    781 
    782  template <class Op>
    783  void unaliasedForEachActual(JSContext* cx, Op op,
    784                              MaybeReadFallback& fallback) const {
    785    Nop nop;
    786    readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr,
    787                           nullptr, ReadFrameArgsBehavior::Actuals, fallback);
    788  }
    789 
    790  JSScript* script() const { return script_; }
    791  jsbytecode* pc() const { return pc_; }
    792  SnapshotIterator snapshotIterator() const { return si_; }
    793  bool isFunctionFrame() const;
    794  bool isModuleFrame() const;
    795  bool isConstructing() const;
    796 
    797  JSObject* environmentChain(MaybeReadFallback& fallback,
    798                             bool* hasInitialEnvironment = nullptr) const {
    799    SnapshotIterator s(si_);
    800 
    801    // envChain
    802    Value v = s.maybeRead(fallback);
    803    return computeEnvironmentChain(v, fallback, hasInitialEnvironment);
    804  }
    805 
    806  Value thisArgument(MaybeReadFallback& fallback) const {
    807    SnapshotIterator s(si_);
    808 
    809    // envChain
    810    s.skip();
    811 
    812    // return value
    813    s.skip();
    814 
    815    // Arguments object.
    816    if (script()->needsArgsObj()) {
    817      s.skip();
    818    }
    819 
    820    return s.maybeRead(fallback);
    821  }
    822 
    823  InlineFrameIterator& operator++() {
    824    findNextFrame();
    825    return *this;
    826  }
    827 
    828  void dump() const;
    829 
    830  void resetOn(const JSJitFrameIter* iter);
    831 
    832  const JSJitFrameIter& frame() const { return *frame_; }
    833 
    834  // Inline frame number, 0 for the outermost (non-inlined) frame.
    835  size_t frameNo() const { return frameCount() - framesRead_; }
    836  size_t frameCount() const {
    837    MOZ_ASSERT(frameCount_ != UINT32_MAX);
    838    return frameCount_;
    839  }
    840 
    841 private:
    842  InlineFrameIterator() = delete;
    843  InlineFrameIterator(const InlineFrameIterator& iter) = delete;
    844 };
    845 
    846 }  // namespace jit
    847 }  // namespace js
    848 
    849 #endif /* jit_JSJitFrameIter_h */