/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * 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;

    // 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();
    MOZ_MUST_USE bool tryInitWithPC(void* pc);
    MOZ_MUST_USE 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();

    MOZ_MUST_USE bool init(JSContext* cx, uint32_t numResults);
    bool isInitialized() const;
#ifdef DEBUG
    size_t length() const;
#endif

    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;
class RSimdBox;

// 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);
    MOZ_MUST_USE bool allocationReadable(const RValueAllocation& a, ReadMethod rm = RM_Normal);
    void writeAllocationValuePayload(const RValueAllocation& a, const Value& v);
    void warnUnreadableAllocation();

  private:
    friend class RSimdBox;
    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;
    inline MOZ_MUST_USE 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();
    }

  protected:
    // 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.
    MOZ_MUST_USE bool initInstructionResults(MaybeReadFallback& fallback);

    // This function is used internally for computing the result of the recover
    // instructions.
    MOZ_MUST_USE 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 */