summaryrefslogtreecommitdiff
path: root/js/src/jit/JitFrameIterator.h
blob: bff72c844d961846e8691bde11d8563ebca1bf93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_JitFrameIterator_h
#define jit_JitFrameIterator_h

#include "jsfun.h"
#include "jsscript.h"
#include "jstypes.h"

#include "jit/IonCode.h"
#include "jit/Snapshots.h"

#include "js/ProfilingFrameIterator.h"

namespace js {
    class ActivationIterator;
} // namespace js

namespace js {
namespace jit {

typedef void * CalleeToken;

enum FrameType
{
    // A JS frame is analogous to a js::InterpreterFrame, representing one scripted
    // function activation. IonJS frames are used by the optimizing compiler.
    JitFrame_IonJS,

    // JS frame used by the baseline JIT.
    JitFrame_BaselineJS,

    // Frame pushed for JIT stubs that make non-tail calls, so that the
    // return address -> ICEntry mapping works.
    JitFrame_BaselineStub,
    JitFrame_IonStub,

    // The entry frame is the initial prologue block transitioning from the VM
    // into the Ion world.
    JitFrame_Entry,

    // A rectifier frame sits in between two JS frames, adapting argc != nargs
    // mismatches in calls.
    JitFrame_Rectifier,

    // Ion IC calling a scripted getter/setter.
    JitFrame_IonAccessorIC,

    // An exit frame is necessary for transitioning from a JS frame into C++.
    // From within C++, an exit frame is always the last frame in any
    // JitActivation.
    JitFrame_Exit,

    // A bailout frame is a special IonJS jit frame after a bailout, and before
    // the reconstruction of the BaselineJS frame. From within C++, a bailout
    // frame is always the last frame in a JitActivation iff the bailout frame
    // information is recorded on the JitActivation.
    JitFrame_Bailout,
};

enum ReadFrameArgsBehavior {
    // Only read formals (i.e. [0 ... callee()->nargs]
    ReadFrame_Formals,

    // Only read overflown args (i.e. [callee()->nargs ... numActuals()]
    ReadFrame_Overflown,

    // Read all args (i.e. [0 ... numActuals()])
    ReadFrame_Actuals
};

class CommonFrameLayout;
class JitFrameLayout;
class ExitFrameLayout;

class BaselineFrame;

class JitActivation;

// Iterate over the JIT stack to assert that all invariants are respected.
//  - Check that all entry frames are aligned on JitStackAlignment.
//  - Check that all rectifier frames keep the JitStackAlignment.
void AssertJitStackInvariants(JSContext* cx);

class JitFrameIterator
{
  protected:
    uint8_t* current_;
    FrameType type_;
    uint8_t* returnAddressToFp_;
    size_t frameSize_;

  private:
    mutable const SafepointIndex* cachedSafepointIndex_;
    const JitActivation* activation_;

    void dumpBaseline() const;

  public:
    explicit JitFrameIterator();
    explicit JitFrameIterator(JSContext* cx);
    explicit JitFrameIterator(const ActivationIterator& activations);

    // Current frame information.
    FrameType type() const {
        return type_;
    }
    uint8_t* fp() const {
        return current_;
    }
    const JitActivation* activation() const {
        return activation_;
    }

    CommonFrameLayout* current() const {
        return (CommonFrameLayout*)current_;
    }

    inline uint8_t* returnAddress() const;

    // Return the pointer of the JitFrame, the iterator is assumed to be settled
    // on a scripted frame.
    JitFrameLayout* jsFrame() const;

    inline ExitFrameLayout* exitFrame() const;

    // Returns whether the JS frame has been invalidated and, if so,
    // places the invalidated Ion script in |ionScript|.
    bool checkInvalidation(IonScript** ionScript) const;
    bool checkInvalidation() const;

    bool isExitFrame() const {
        return type_ == JitFrame_Exit;
    }
    bool isScripted() const {
        return type_ == JitFrame_BaselineJS || type_ == JitFrame_IonJS || type_ == JitFrame_Bailout;
    }
    bool isBaselineJS() const {
        return type_ == JitFrame_BaselineJS;
    }
    bool isIonScripted() const {
        return type_ == JitFrame_IonJS || type_ == JitFrame_Bailout;
    }
    bool isIonJS() const {
        return type_ == JitFrame_IonJS;
    }
    bool isIonStub() const {
        return type_ == JitFrame_IonStub;
    }
    bool isIonAccessorIC() const {
        return type_ == JitFrame_IonAccessorIC;
    }
    bool isBailoutJS() const {
        return type_ == JitFrame_Bailout;
    }
    bool isBaselineStub() const {
        return type_ == JitFrame_BaselineStub;
    }
    bool isRectifier() const {
        return type_ == JitFrame_Rectifier;
    }
    bool isBareExit() const;
    template <typename T> bool isExitFrameLayout() const;

    bool isEntry() const {
        return type_ == JitFrame_Entry;
    }
    bool isFunctionFrame() const;

    bool isConstructing() const;

    void* calleeToken() const;
    JSFunction* callee() const;
    JSFunction* maybeCallee() const;
    unsigned numActualArgs() const;
    JSScript* script() const;
    void baselineScriptAndPc(JSScript** scriptRes, jsbytecode** pcRes) const;
    Value* actualArgs() const;

    // Returns the return address of the frame above this one (that is, the
    // return address that returns back to the current frame).
    uint8_t* returnAddressToFp() const {
        return returnAddressToFp_;
    }

    // Previous frame information extracted from the current frame.
    inline size_t prevFrameLocalSize() const;
    inline FrameType prevType() const;
    uint8_t* prevFp() const;

    // Returns the stack space used by the current frame, in bytes. This does
    // not include the size of its fixed header.
    size_t frameSize() const {
        MOZ_ASSERT(!isExitFrame());
        return frameSize_;
    }

    // Functions used to iterate on frames. When prevType is JitFrame_Entry,
    // the current frame is the last frame.
    inline bool done() const {
        return type_ == JitFrame_Entry;
    }
    JitFrameIterator& operator++();

    // Returns the IonScript associated with this JS frame.
    IonScript* ionScript() const;

    // Returns the IonScript associated with this JS frame; the frame must
    // not be invalidated.
    IonScript* ionScriptFromCalleeToken() const;

    // Returns the Safepoint associated with this JS frame. Incurs a lookup
    // overhead.
    const SafepointIndex* safepoint() const;

    // Returns the OSI index associated with this JS frame. Incurs a lookup
    // overhead.
    const OsiIndex* osiIndex() const;

    // Returns the Snapshot offset associated with this JS frame. Incurs a
    // lookup overhead.
    SnapshotOffset snapshotOffset() const;

    uintptr_t* spillBase() const;
    MachineState machineState() const;

    template <class Op>
    void unaliasedForEachActual(Op op, ReadFrameArgsBehavior behavior) const {
        MOZ_ASSERT(isBaselineJS());

        unsigned nactual = numActualArgs();
        unsigned start, end;
        switch (behavior) {
          case ReadFrame_Formals:
            start = 0;
            end = callee()->nargs();
            break;
          case ReadFrame_Overflown:
            start = callee()->nargs();
            end = nactual;
            break;
          case ReadFrame_Actuals:
            start = 0;
            end = nactual;
        }

        Value* argv = actualArgs();
        for (unsigned i = start; i < end; i++)
            op(argv[i]);
    }

    void dump() const;

    inline BaselineFrame* baselineFrame() const;

    // Returns the number of local and expression stack Values for the current
    // Baseline frame.
    inline uint32_t baselineFrameNumValueSlots() const;

    // This function isn't used, but we keep it here (debug-only) because it is
    // helpful when chasing issues with the jitcode map.
#ifdef DEBUG
    bool verifyReturnAddressUsingNativeToBytecodeMap();
#else
    inline bool verifyReturnAddressUsingNativeToBytecodeMap() { return true; }
#endif
};

class JitcodeGlobalTable;

class JitProfilingFrameIterator
{
    uint8_t* fp_;
    FrameType type_;
    void* returnAddressToFp_;

    inline JitFrameLayout* framePtr();
    inline JSScript* frameScript();
    [[nodiscard]] bool tryInitWithPC(void* pc);
    [[nodiscard]] bool tryInitWithTable(JitcodeGlobalTable* table, void* pc, JSRuntime* rt,
                                        bool forLastCallSite);
    void fixBaselineReturnAddress();

    void moveToNextFrame(CommonFrameLayout* frame);

  public:
    JitProfilingFrameIterator(JSRuntime* rt,
                              const JS::ProfilingFrameIterator::RegisterState& state);
    explicit JitProfilingFrameIterator(void* exitFrame);

    void operator++();
    bool done() const { return fp_ == nullptr; }

    void* fp() const { MOZ_ASSERT(!done()); return fp_; }
    void* stackAddress() const { return fp(); }
    FrameType frameType() const { MOZ_ASSERT(!done()); return type_; }
    void* returnAddressToFp() const { MOZ_ASSERT(!done()); return returnAddressToFp_; }
};

class RInstructionResults
{
    // Vector of results of recover instructions.
    typedef mozilla::Vector<HeapPtr<Value>, 1, SystemAllocPolicy> Values;
    UniquePtr<Values> results_;

    // The frame pointer is used as a key to check if the current frame already
    // bailed out.
    JitFrameLayout* fp_;

    // Record if we tried and succeed at allocating and filling the vector of
    // recover instruction results, if needed.  This flag is needed in order to
    // avoid evaluating the recover instruction twice.
    bool initialized_;

  public:
    explicit RInstructionResults(JitFrameLayout* fp);
    RInstructionResults(RInstructionResults&& src);

    RInstructionResults& operator=(RInstructionResults&& rhs);

    ~RInstructionResults();

    [[nodiscard]] bool init(JSContext* cx, uint32_t numResults);
    bool isInitialized() const;
    size_t length() const;

    JitFrameLayout* frame() const;

    HeapPtr<Value>& operator[](size_t index);

    void trace(JSTracer* trc);
};

struct MaybeReadFallback
{
    enum NoGCValue {
        NoGC_UndefinedValue,
        NoGC_MagicOptimizedOut
    };

    enum FallbackConsequence {
        Fallback_Invalidate,
        Fallback_DoNothing
    };

    JSContext* maybeCx;
    JitActivation* activation;
    const JitFrameIterator* frame;
    const NoGCValue unreadablePlaceholder_;
    const FallbackConsequence consequence;

    explicit MaybeReadFallback(const Value& placeholder = UndefinedValue())
      : maybeCx(nullptr),
        activation(nullptr),
        frame(nullptr),
        unreadablePlaceholder_(noGCPlaceholder(placeholder)),
        consequence(Fallback_Invalidate)
    {
    }

    MaybeReadFallback(JSContext* cx, JitActivation* activation, const JitFrameIterator* frame,
                      FallbackConsequence consequence = Fallback_Invalidate)
      : maybeCx(cx),
        activation(activation),
        frame(frame),
        unreadablePlaceholder_(NoGC_UndefinedValue),
        consequence(consequence)
    {
    }

    bool canRecoverResults() { return maybeCx; }

    Value unreadablePlaceholder() const {
        if (unreadablePlaceholder_ == NoGC_MagicOptimizedOut)
            return MagicValue(JS_OPTIMIZED_OUT);
        return UndefinedValue();
    }

    NoGCValue noGCPlaceholder(const Value& v) const {
        if (v.isMagic(JS_OPTIMIZED_OUT))
            return NoGC_MagicOptimizedOut;
        return NoGC_UndefinedValue;
    }
};


class RResumePoint;

// Reads frame information in snapshot-encoding order (that is, outermost frame
// to innermost frame).
class SnapshotIterator
{
  protected:
    SnapshotReader snapshot_;
    RecoverReader recover_;
    JitFrameLayout* fp_;
    const MachineState* machine_;
    IonScript* ionScript_;
    RInstructionResults* instructionResults_;

    enum ReadMethod {
        // Read the normal value.
        RM_Normal          = 1 << 0,

        // Read the default value, or the normal value if there is no default.
        RM_AlwaysDefault   = 1 << 1,

        // Try to read the normal value if it is readable, otherwise default to
        // the Default value.
        RM_NormalOrDefault = RM_Normal | RM_AlwaysDefault,
    };

  private:
    // Read a spilled register from the machine state.
    bool hasRegister(Register reg) const {
        return machine_->has(reg);
    }
    uintptr_t fromRegister(Register reg) const {
        return machine_->read(reg);
    }

    bool hasRegister(FloatRegister reg) const {
        return machine_->has(reg);
    }
    double fromRegister(FloatRegister reg) const {
        return machine_->read(reg);
    }

    // Read an uintptr_t from the stack.
    bool hasStack(int32_t offset) const {
        return true;
    }
    uintptr_t fromStack(int32_t offset) const;

    bool hasInstructionResult(uint32_t index) const {
        return instructionResults_;
    }
    bool hasInstructionResults() const {
        return instructionResults_;
    }
    Value fromInstructionResult(uint32_t index) const;

    Value allocationValue(const RValueAllocation& a, ReadMethod rm = RM_Normal);
    [[nodiscard]] bool allocationReadable(const RValueAllocation& a, ReadMethod rm = RM_Normal);
    void writeAllocationValuePayload(const RValueAllocation& a, const Value& v);
    void warnUnreadableAllocation();

  private:
    const FloatRegisters::RegisterContent* floatAllocationPointer(const RValueAllocation& a) const;

  public:
    // Handle iterating over RValueAllocations of the snapshots.
    inline RValueAllocation readAllocation() {
        MOZ_ASSERT(moreAllocations());
        return snapshot_.readAllocation();
    }
    Value skip() {
        snapshot_.skipAllocation();
        return UndefinedValue();
    }

    const RResumePoint* resumePoint() const;
    const RInstruction* instruction() const {
        return recover_.instruction();
    }

    uint32_t numAllocations() const;
    inline bool moreAllocations() const {
        return snapshot_.numAllocationsRead() < numAllocations();
    }

    int32_t readOuterNumActualArgs() const;

    // Used by recover instruction to store the value back into the instruction
    // results array.
    void storeInstructionResult(const Value& v);

  public:
    // Exhibits frame properties contained in the snapshot.
    uint32_t pcOffset() const;
    [[nodiscard]] inline bool resumeAfter() const {
        // Inline frames are inlined on calls, which are considered as being
        // resumed on the Call as baseline will push the pc once we return from
        // the call.
        if (moreFrames())
            return false;
        return recover_.resumeAfter();
    }
    inline BailoutKind bailoutKind() const {
        return snapshot_.bailoutKind();
    }

  public:
    // Read the next instruction available and get ready to either skip it or
    // evaluate it.
    inline void nextInstruction() {
        MOZ_ASSERT(snapshot_.numAllocationsRead() == numAllocations());
        recover_.nextInstruction();
        snapshot_.resetNumAllocationsRead();
    }

    // Skip an Instruction by walking to the next instruction and by skipping
    // all the allocations corresponding to this instruction.
    void skipInstruction();

    inline bool moreInstructions() const {
        return recover_.moreInstructions();
    }

    // Register a vector used for storing the results of the evaluation of
    // recover instructions. This vector should be registered before the
    // beginning of the iteration. This function is in charge of allocating
    // enough space for all instructions results, and return false iff it fails.
    [[nodiscard]] bool initInstructionResults(MaybeReadFallback& fallback);

  protected:
    // This function is used internally for computing the result of the recover
    // instructions.
    [[nodiscard]] bool computeInstructionResults(JSContext* cx, RInstructionResults* results) const;

  public:
    // Handle iterating over frames of the snapshots.
    void nextFrame();
    void settleOnFrame();

    inline bool moreFrames() const {
        // The last instruction is recovering the innermost frame, so as long as
        // there is more instruction there is necesseray more frames.
        return moreInstructions();
    }

  public:
    // Connect all informations about the current script in order to recover the
    // content of baseline frames.

    SnapshotIterator(const JitFrameIterator& iter, const MachineState* machineState);
    SnapshotIterator();

    Value read() {
        return allocationValue(readAllocation());
    }

    // Read the |Normal| value unless it is not available and that the snapshot
    // provides a |Default| value. This is useful to avoid invalidations of the
    // frame while we are only interested in a few properties which are provided
    // by the |Default| value.
    Value readWithDefault(RValueAllocation* alloc) {
        *alloc = RValueAllocation();
        RValueAllocation a = readAllocation();
        if (allocationReadable(a))
            return allocationValue(a);

        *alloc = a;
        return allocationValue(a, RM_AlwaysDefault);
    }

    Value maybeRead(const RValueAllocation& a, MaybeReadFallback& fallback);
    Value maybeRead(MaybeReadFallback& fallback) {
        RValueAllocation a = readAllocation();
        return maybeRead(a, fallback);
    }

    void traceAllocation(JSTracer* trc);

    template <class Op>
    void readFunctionFrameArgs(Op& op, ArgumentsObject** argsObj, Value* thisv,
                               unsigned start, unsigned end, JSScript* script,
                               MaybeReadFallback& fallback)
    {
        // Assumes that the common frame arguments have already been read.
        if (script->argumentsHasVarBinding()) {
            if (argsObj) {
                Value v = read();
                if (v.isObject())
                    *argsObj = &v.toObject().as<ArgumentsObject>();
            } else {
                skip();
            }
        }

        if (thisv)
            *thisv = maybeRead(fallback);
        else
            skip();

        unsigned i = 0;
        if (end < start)
            i = start;

        for (; i < start; i++)
            skip();
        for (; i < end; i++) {
            // We are not always able to read values from the snapshots, some values
            // such as non-gc things may still be live in registers and cause an
            // error while reading the machine state.
            Value v = maybeRead(fallback);
            op(v);
        }
    }

    // Iterate over all the allocations and return only the value of the
    // allocation located at one index.
    Value maybeReadAllocByIndex(size_t index);

#ifdef TRACK_SNAPSHOTS
    void spewBailingFrom() const {
        snapshot_.spewBailingFrom();
    }
#endif
};

// Reads frame information in callstack order (that is, innermost frame to
// outermost frame).
class InlineFrameIterator
{
    const JitFrameIterator* frame_;
    SnapshotIterator start_;
    SnapshotIterator si_;
    uint32_t framesRead_;

    // When the inline-frame-iterator is created, this variable is defined to
    // UINT32_MAX. Then the first iteration of findNextFrame, which settle on
    // the innermost frame, is used to update this counter to the number of
    // frames contained in the recover buffer.
    uint32_t frameCount_;

    // The |calleeTemplate_| fields contains either the JSFunction or the
    // template from which it is supposed to be cloned. The |calleeRVA_| is an
    // Invalid value allocation, if the |calleeTemplate_| field is the effective
    // JSFunction, and not its template. On the other hand, any other value
    // allocation implies that the |calleeTemplate_| is the template JSFunction
    // from which the effective one would be derived and cached by the Recover
    // instruction result.
    RootedFunction calleeTemplate_;
    RValueAllocation calleeRVA_;

    RootedScript script_;
    jsbytecode* pc_;
    uint32_t numActualArgs_;

    // Register state, used by all snapshot iterators.
    MachineState machine_;

    struct Nop {
        void operator()(const Value& v) { }
    };

  private:
    void findNextFrame();
    JSObject* computeEnvironmentChain(const Value& envChainValue, MaybeReadFallback& fallback,
                                      bool* hasInitialEnv = nullptr) const;

  public:
    InlineFrameIterator(JSContext* cx, const JitFrameIterator* iter);
    InlineFrameIterator(JSRuntime* rt, const JitFrameIterator* iter);
    InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter);

    bool more() const {
        return frame_ && framesRead_ < frameCount_;
    }

    // Due to optimizations, we are not always capable of reading the callee of
    // inlined frames without invalidating the IonCode. This function might
    // return either the effective callee of the JSFunction which might be used
    // to create it.
    //
    // As such, the |calleeTemplate()| can be used to read most of the metadata
    // which are conserved across clones.
    JSFunction* calleeTemplate() const {
        MOZ_ASSERT(isFunctionFrame());
        return calleeTemplate_;
    }
    JSFunction* maybeCalleeTemplate() const {
        return calleeTemplate_;
    }

    JSFunction* callee(MaybeReadFallback& fallback) const;

    unsigned numActualArgs() const {
        // The number of actual arguments of inline frames is recovered by the
        // iteration process. It is recovered from the bytecode because this
        // property still hold since the for inlined frames. This property does not
        // hold for the parent frame because it can have optimize a call to
        // js_fun_call or js_fun_apply.
        if (more())
            return numActualArgs_;

        return frame_->numActualArgs();
    }

    template <class ArgOp, class LocalOp>
    void readFrameArgsAndLocals(JSContext* cx, ArgOp& argOp, LocalOp& localOp,
                                JSObject** envChain, bool* hasInitialEnv,
                                Value* rval, ArgumentsObject** argsObj,
                                Value* thisv, Value* newTarget,
                                ReadFrameArgsBehavior behavior,
                                MaybeReadFallback& fallback) const
    {
        SnapshotIterator s(si_);

        // Read the env chain.
        if (envChain) {
            Value envChainValue = s.maybeRead(fallback);
            *envChain = computeEnvironmentChain(envChainValue, fallback, hasInitialEnv);
        } else {
            s.skip();
        }

        // Read return value.
        if (rval)
            *rval = s.maybeRead(fallback);
        else
            s.skip();

        if (newTarget) {
            // For now, only support reading new.target when we are reading
            // overflown arguments.
            MOZ_ASSERT(behavior != ReadFrame_Formals);
            newTarget->setUndefined();
        }

        // Read arguments, which only function frames have.
        if (isFunctionFrame()) {
            unsigned nactual = numActualArgs();
            unsigned nformal = calleeTemplate()->nargs();

            // Get the non overflown arguments, which are taken from the inlined
            // frame, because it will have the updated value when JSOP_SETARG is
            // done.
            if (behavior != ReadFrame_Overflown)
                s.readFunctionFrameArgs(argOp, argsObj, thisv, 0, nformal, script(), fallback);

            if (behavior != ReadFrame_Formals) {
                if (more()) {
                    // There is still a parent frame of this inlined frame.  All
                    // arguments (also the overflown) are the last pushed values
                    // in the parent frame.  To get the overflown arguments, we
                    // need to take them from there.

                    // The overflown arguments are not available in current frame.
                    // They are the last pushed arguments in the parent frame of
                    // this inlined frame.
                    InlineFrameIterator it(cx, this);
                    ++it;
                    unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0;
                    bool hasNewTarget = isConstructing();
                    SnapshotIterator parent_s(it.snapshotIterator());

                    // Skip over all slots until we get to the last slots
                    // (= arguments slots of callee) the +3 is for [this], [returnvalue],
                    // [envchain], and maybe +1 for [argsObj]
                    MOZ_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj + hasNewTarget);
                    unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj - hasNewTarget;
                    for (unsigned j = 0; j < skip; j++)
                        parent_s.skip();

                    // Get the overflown arguments
                    MaybeReadFallback unusedFallback;
                    parent_s.skip(); // env chain
                    parent_s.skip(); // return value
                    parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr,
                                                   nformal, nactual, it.script(),
                                                   fallback);
                    if (newTarget && isConstructing())
                        *newTarget = parent_s.maybeRead(fallback);
                } else {
                    // There is no parent frame to this inlined frame, we can read
                    // from the frame's Value vector directly.
                    Value* argv = frame_->actualArgs();
                    for (unsigned i = nformal; i < nactual; i++)
                        argOp(argv[i]);
                    if (newTarget && isConstructing())
                        *newTarget = argv[nactual];
                }
            }
        }

        // At this point we've read all the formals in s, and can read the
        // locals.
        for (unsigned i = 0; i < script()->nfixed(); i++)
            localOp(s.maybeRead(fallback));
    }

    template <class Op>
    void unaliasedForEachActual(JSContext* cx, Op op,
                                ReadFrameArgsBehavior behavior,
                                MaybeReadFallback& fallback) const
    {
        Nop nop;
        readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr,
                               nullptr, nullptr, behavior, fallback);
    }

    JSScript* script() const {
        return script_;
    }
    jsbytecode* pc() const {
        return pc_;
    }
    SnapshotIterator snapshotIterator() const {
        return si_;
    }
    bool isFunctionFrame() const;
    bool isConstructing() const;

    JSObject* environmentChain(MaybeReadFallback& fallback) const {
        SnapshotIterator s(si_);

        // envChain
        Value v = s.maybeRead(fallback);
        return computeEnvironmentChain(v, fallback);
    }

    Value thisArgument(MaybeReadFallback& fallback) const {
        SnapshotIterator s(si_);

        // envChain
        s.skip();

        // return value
        s.skip();

        // Arguments object.
        if (script()->argumentsHasVarBinding())
            s.skip();

        return s.maybeRead(fallback);
    }

    InlineFrameIterator& operator++() {
        findNextFrame();
        return *this;
    }

    void dump() const;

    void resetOn(const JitFrameIterator* iter);

    const JitFrameIterator& frame() const {
        return *frame_;
    }

    // Inline frame number, 0 for the outermost (non-inlined) frame.
    size_t frameNo() const {
        return frameCount() - framesRead_;
    }
    size_t frameCount() const {
        MOZ_ASSERT(frameCount_ != UINT32_MAX);
        return frameCount_;
    }

  private:
    InlineFrameIterator() = delete;
    InlineFrameIterator(const InlineFrameIterator& iter) = delete;
};

} // namespace jit
} // namespace js

#endif /* jit_JitFrameIterator_h */