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