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