//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLanguagePch.h"
#include "Language/JavascriptFunctionArgIndex.h"
#include "Language/InterpreterStackFrame.h"

#define FAligned(VALUE, TYPE) ((((LONG_PTR)VALUE) & (sizeof(TYPE)-1)) == 0)

#define AlignIt(VALUE, TYPE) (~(~((LONG_PTR)(VALUE) + (sizeof(TYPE)-1)) | (sizeof(TYPE)-1)))

namespace Js
{
    Js::ArgumentsObject * JavascriptCallStackLayout::GetArgumentsObject() const
    {
        return (Js::ArgumentsObject *)((void **)this)[JavascriptFunctionArgIndex_ArgumentsObject];
    }

    Js::Var* JavascriptCallStackLayout::GetArgumentsObjectLocation() const
    {
        return (Js::Var *)&((void **)this)[JavascriptFunctionArgIndex_ArgumentsObject];
    }

    void JavascriptCallStackLayout::SetArgumentsObject(Js::ArgumentsObject * obj)
    {
        ((void **)this)[JavascriptFunctionArgIndex_ArgumentsObject] =  obj;
    }

    Js::Var JavascriptCallStackLayout::GetOffset(int offset) const
    {
        Js::Var *varPtr = (Js::Var *)(((char *)this) + offset);
        Assert(FAligned(varPtr, Js::Var));
        return *varPtr;
    }
    double JavascriptCallStackLayout::GetDoubleAtOffset(int offset) const
    {
        double *dblPtr = (double *)(((char *)this) + offset);
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
        if (Js::Configuration::Global.flags.IsEnabled(Js::CheckAlignmentFlag))
        {
            Assert(FAligned(dblPtr, double));
        }
#endif
        return *dblPtr;
    }

    int32 JavascriptCallStackLayout::GetInt32AtOffset(int offset) const
    {
        int32 *intPtr = (int32 *)(((char *)this) + offset);
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
        if (Js::Configuration::Global.flags.IsEnabled(Js::CheckAlignmentFlag))
        {
            Assert(FAligned(intPtr, int32));
        }
#endif
        return *intPtr;
    }

    SIMDValue JavascriptCallStackLayout::GetSimdValueAtOffset(int offset) const
    {
        return  *((SIMDValue *)(((char *)this) + offset));
    }

    char * JavascriptCallStackLayout::GetValueChangeOffset(int offset) const
    {
        Js::Var *varPtr = (Js::Var *)(((char *)this) + offset);
        Assert(FAligned(varPtr, Js::Var));
        return (char *)varPtr;
    }

    ForInObjectEnumerator * JavascriptCallStackLayout::GetForInObjectEnumeratorArrayAtOffset(int offset) const
    {
        return (ForInObjectEnumerator *)(((char *)this) + offset);
    }

    JavascriptCallStackLayout *JavascriptCallStackLayout::FromFramePointer(void *const framePointer)
    {
        return
            reinterpret_cast<JavascriptCallStackLayout *>(
                static_cast<void **>(framePointer) + (JavascriptFunctionArgIndex_Function - JavascriptFunctionArgIndex_Frame));
    }


    void* const JavascriptCallStackLayout::ToFramePointer(JavascriptCallStackLayout* callstackLayout)
    {
        return
            reinterpret_cast<void * const>(
                reinterpret_cast<void **>(callstackLayout) - (JavascriptFunctionArgIndex_Function - JavascriptFunctionArgIndex_Frame));
    }

    Js::Var* JavascriptCallStackLayout::GetArgv() const
    {
        return const_cast<Js::Var*>(&this->args[0]);
    }

    ScriptContext* JavascriptStackWalker::GetCurrentScriptContext() const
    {
        return this->GetCurrentInterpreterFrame() ? this->GetCurrentInterpreterFrame()->GetScriptContext() : this->scriptContext;
    }

    Var JavascriptStackWalker::GetCurrentArgumentsObject() const
    {
#if ENABLE_PROFILE_INFO
        if (interpreterFrame)
#else
        Assert(interpreterFrame);
#endif
        {
            return interpreterFrame->GetArgumentsObject();
        }
#if ENABLE_NATIVE_CODEGEN
        else
        {
            if (inlinedFramesBeingWalked)
            {
                return inlinedFrameWalker.GetArgumentsObject();
            }
            else
            {
                return this->GetCurrentNativeArgumentsObject();
            }
        }
#endif
    }

    void JavascriptStackWalker::SetCurrentArgumentsObject(Var args)
    {
#if ENABLE_NATIVE_CODEGEN
        if (interpreterFrame)
#else
        Assert(interpreterFrame);
#endif
        {
            interpreterFrame->SetArgumentsObject(args);
        }
#if ENABLE_NATIVE_CODEGEN
        else
        {
            if (inlinedFramesBeingWalked)
            {
                inlinedFrameWalker.SetArgumentsObject(args);
            }
            else
            {
                this->SetCurrentNativeArgumentsObject(args);
            }
        }
#endif
    }

    Var JavascriptStackWalker::GetPermanentArguments() const
    {
        Assert(IsJavascriptFrame());
        AssertMsg(this->GetCurrentFunction()->IsScriptFunction(), "GetPermanentArguments should not be called for non-script function as there is no slot allocated for it.");

        const uint32 paramCount = GetCallInfo().Count;
        if (paramCount == 0)
        {
            // glob function doesn't allocate ArgumentsObject slot on stack
            return nullptr;
        }

        // Get the heap-allocated args for this frame.
        Var args = this->GetCurrentArgumentsObject();
        if (args && ArgumentsObject::Is(args))
        {
            args = ((ArgumentsObject*)args)->GetHeapArguments();
        }
        return args;
    }

    BOOL JavascriptStackWalker::WalkToArgumentsFrame(ArgumentsObject *args)
    {
        // Move the walker up the stack until we find the given arguments object on the frame.
        while (this->Walk(/*includeInlineFrame*/ true))
        {
            if (this->IsJavascriptFrame())
            {
                Var currArgs = this->GetCurrentArgumentsObject();
                if (currArgs == args)
                {
                    return TRUE;
                }
            }
        }
        return FALSE;
    }

    void JavascriptStackWalker::GetThis(Var* pVarThis, int moduleId) const
    {
#if ENABLE_NATIVE_CODEGEN
        if (inlinedFramesBeingWalked)
        {
            if (inlinedFrameWalker.GetArgc() == 0)
            {
                *pVarThis = JavascriptOperators::OP_GetThis(this->scriptContext->GetLibrary()->GetUndefined(), moduleId, scriptContext);
            }
            else
            {
                *pVarThis = inlinedFrameWalker.GetThisObject();
                Assert(*pVarThis);
            }
        }
        else
#endif
        {
            const CallInfo callInfo = this->GetCallInfo();
            if (callInfo.Count == 0)
            {
                *pVarThis = JavascriptOperators::OP_GetThis(scriptContext->GetLibrary()->GetUndefined(), moduleId, scriptContext);
            }
            else
            {
                *pVarThis = this->GetThisFromFrame();
            }
        }

        if (*pVarThis == nullptr)
        {
            *pVarThis = this->scriptContext->GetLibrary()->GetNull();
        }
    }

    BOOL IsEval(CallInfo callInfo)
    {
        return (callInfo.Flags & CallFlags_Eval) != 0;
    }

    BOOL JavascriptStackWalker::IsCallerGlobalFunction() const
    {
        const CallInfo callInfo = this->GetCallInfo();

        JavascriptFunction* function = this->GetCurrentFunction();
        if (IsLibraryStackFrameEnabled(this->scriptContext) && !function->IsScriptFunction())
        {
            return false; // native library code can't be global function
        }

        FunctionInfo* funcInfo = function->GetFunctionInfo();
        if (funcInfo->HasParseableInfo())
        {
            return funcInfo->GetParseableFunctionInfo()->GetIsGlobalFunc() || IsEval(callInfo);
        }
        else
        {
            AssertMsg(FALSE, "Here we should only have script functions which were already parsed/deserialized.");
            return callInfo.Count == 0 || IsEval(callInfo);
        }
    }

    BOOL JavascriptStackWalker::IsEvalCaller() const
    {
        const CallInfo callInfo = this->GetCallInfo();
        return (callInfo.Flags & CallFlags_Eval) != 0;
    }

    Var JavascriptStackWalker::GetCurrentNativeArgumentsObject() const
    {
        Assert(this->IsJavascriptFrame() && this->interpreterFrame == nullptr);
        return this->GetCurrentArgv()[JavascriptFunctionArgIndex_ArgumentsObject];
    }

    void JavascriptStackWalker::SetCurrentNativeArgumentsObject(Var args)
    {
        Assert(this->IsJavascriptFrame() && this->interpreterFrame == nullptr);
        this->GetCurrentArgv()[JavascriptFunctionArgIndex_ArgumentsObject] = args;
    }

    Js::Var * JavascriptStackWalker::GetJavascriptArgs(bool boxArgsAndDeepCopy) const
    {
        Assert(this->IsJavascriptFrame());

#if ENABLE_NATIVE_CODEGEN
        if (inlinedFramesBeingWalked)
        {
            return inlinedFrameWalker.GetArgv(/* includeThis */ false, boxArgsAndDeepCopy);
        }
        else
#endif
            if (this->GetCurrentFunction()->GetFunctionInfo()->IsCoroutine())
        {
            JavascriptGenerator* gen = JavascriptGenerator::FromVar(this->GetCurrentArgv()[JavascriptFunctionArgIndex_This]);
            return gen->GetArguments().Values;
        }
        else
        {
            return &this->GetCurrentArgv()[JavascriptFunctionArgIndex_SecondScriptArg];
        }
    }

    uint32 JavascriptStackWalker::GetByteCodeOffset() const
    {
        uint32 offset = 0;
        if (this->IsJavascriptFrame())
        {
            if (this->interpreterFrame)
            {
                if (this->TryGetByteCodeOffsetFromInterpreterFrame(offset))
                {
                    return offset;
                }
            }

#if ENABLE_NATIVE_CODEGEN
            if (TryGetByteCodeOffsetFromNativeFrame(offset))
            {
                return offset;
            }
#endif
        }
        return offset;
    }

    bool JavascriptStackWalker::TryGetByteCodeOffsetFromInterpreterFrame(uint32& offset) const
    {
#if ENABLE_NATIVE_CODEGEN
        if (this->lastInternalFrameInfo.codeAddress != nullptr)
        {
            return false;
        }
#endif
        offset = this->interpreterFrame->GetReader()->GetCurrentOffset();
        if (offset == 0)
        {
            // This will be the case when we are broken on the debugger on very first statement (due to async break).
            // Or the interpreter loop can throw OOS on entrance before executing bytecode.
        }
        else
        {
            // Note : For many cases, we move the m_currentLocation of ByteCodeReader already to next available opcode.
            // This could create problem in binding the exception to proper line offset.
            // Reducing by 1 will make sure the current offset falls under, current executing opcode.
            offset--;
        }
        return true;
    }

#if ENABLE_NATIVE_CODEGEN
    bool JavascriptStackWalker::TryGetByteCodeOffsetFromNativeFrame(uint32& offset) const
    {
        DWORD_PTR pCodeAddr;
        if (this->lastInternalFrameInfo.codeAddress != nullptr)
        {
            pCodeAddr = (DWORD_PTR)this->lastInternalFrameInfo.codeAddress;
        }
        else
        {
            pCodeAddr = (DWORD_PTR)this->GetCurrentCodeAddr();
        }

        // If the current instruction's return address is the beginning of the next statement then we will show error for the next line, which would be completely wrong.
        // The quick fix would be to look the address which is at least lesser than current return address.

        // Assert to verify at what places this can happen.
        Assert(pCodeAddr);

        if (pCodeAddr)
        {
#if defined(_M_ARM)
            // Note that DWORD_PTR is not actually a pointer type (!) but is simple unsigned long/__int64 (see BaseTsd.h).
            // Thus, decrement would be by 1 byte and not 4 bytes as in pointer arithmetic. That's exactly what we need.
            // For ARM the 'return address' is always odd and is 'next instr addr' + 1 byte, so to get to the BLX instr, we need to subtract 2 bytes from it.
            AssertMsg(pCodeAddr % 2 == 1, "Got even number for pCodeAddr! It's expected to be return address, which should be odd.");
            pCodeAddr--;
#endif
            pCodeAddr--;
        }

        bool usedInternalFrameInfo = false;
        uint loopNum = GetLoopNumber(usedInternalFrameInfo);

        JavascriptFunction *function = nullptr;
        FunctionBody *inlinee = nullptr;

        function = usedInternalFrameInfo ? this->GetCachedInternalFrameInfo().function : this->GetCurrentFunctionFromPhysicalFrame();

        // If there are inlined frames on the stack, we have to be able to return the byte code offsets of those inlined calls
        // from their respective callers. But, we can use the current native address as IP for only the topmost inlined frame.
        // TryGetByteCodeOffsetOfInlinee takes care of these conditions and sets up the offset of an inlinee in 'offset', if the
        // current inlinee frame is not the topmost of the inlinee frames.
        if (HasInlinedFramesOnStack() && TryGetByteCodeOffsetOfInlinee(function, loopNum, pCodeAddr, &inlinee, offset, usedInternalFrameInfo))
        {
            return true;
        }

        StatementData data;
        if (function->GetFunctionBody() && function->GetFunctionBody()->GetMatchingStatementMapFromNativeAddress(pCodeAddr, data, loopNum, inlinee))
        {
            offset = data.bytecodeBegin;
            return true;
        }

        return false;
    }

    uint JavascriptStackWalker::GetLoopNumber(bool& usedInternalFrameInfo) const
    {
        uint loopNum = LoopHeader::NoLoop;
        if (this->lastInternalFrameInfo.codeAddress != nullptr)
        {
            if (this->lastInternalFrameInfo.frameType == InternalFrameType_LoopBody)
            {
                AnalysisAssert(this->interpreterFrame);
                loopNum = this->interpreterFrame->GetCurrentLoopNum();
                Assert(loopNum != LoopHeader::NoLoop);
                usedInternalFrameInfo = true;
            }
        }
        else
        {
            if (this->IsCurrentPhysicalFrameForLoopBody())
            {
                // Internal frame but codeAddress on lastInternalFrameInfo not set. We must be in an inlined frame in the loop body.
                Assert(this->tempInterpreterFrame);
                loopNum = this->tempInterpreterFrame->GetCurrentLoopNum();
                Assert(loopNum != LoopHeader::NoLoop);
                usedInternalFrameInfo = false;
            }
        }

        return loopNum;
    }

    bool JavascriptStackWalker::TryGetByteCodeOffsetOfInlinee(Js::JavascriptFunction* parentFunction, uint loopNum, DWORD_PTR pCodeAddr, Js::FunctionBody** inlinee, uint32& offset, bool useInternalFrameInfo) const
    {
        // For inlined frames, translation from native offset -> source code happens in two steps.
        // The native offset is first translated into a statement index using the physical frame's
        // source context info. This statement index is then looked up in the *inlinee*'s source
        // context info to get the bytecode offset.
        //
        // For all inlined frames contained within a physical frame we have only one offset == (IP - entry).
        // Since we can't use that to get the other inlined callers' IPs, we save the IP of all inlined
        // callers in their "callinfo" (See InlineeCallInfo). The top most inlined frame uses the IP
        // of the physical frame. All other inlined frames use the InlineeStartOffset stored in their call info
        // to calculate the byte code offset of the callsite of the inlinee they called.

        StatementData data;
        uint32 inlineeOffset = 0;
        *inlinee = InlinedFramesBeingWalked() ? inlinedFrameWalker.GetFunctionObject()->GetFunctionBody() : nullptr;

        InlinedFrameWalker  tmpFrameWalker;
        if (InlinedFramesBeingWalked())
        {
            // Inlined frames are being walked right now. The top most frame is where the IP is.
            if (!inlinedFrameWalker.IsTopMostFrame())
            {
                inlineeOffset = inlinedFrameWalker.GetCurrentInlineeOffset();
            }
        }
        else if (ScriptFunction::Test(parentFunction) && HasInlinedFramesOnStack())
        {
            // Inlined frames are not being walked right now. However, if there
            // are inlined frames on the stack the InlineeCallInfo of the first inlined frame
            // has the native offset of the current physical frame.
            Assert(!*inlinee);
            InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, ScriptFunction::FromVar(parentFunction), PreviousInterpreterFrameIsFromBailout(), loopNum, this, useInternalFrameInfo, false /*noAlloc*/);
            inlineeOffset = tmpFrameWalker.GetBottomMostInlineeOffset();
            tmpFrameWalker.Close();
        }

        if (inlineeOffset != 0 &&
            parentFunction->GetFunctionBody()->GetMatchingStatementMapFromNativeOffset(pCodeAddr, inlineeOffset, data, loopNum, *inlinee))
        {
            offset = data.bytecodeBegin;
            return true;
        }

        return false;
    }

    bool JavascriptStackWalker::PreviousInterpreterFrameIsFromBailout() const
    {
        if (lastInternalFrameInfo.codeAddress)
        {
            return lastInternalFrameInfo.previousInterpreterFrameIsFromBailout;
        }

        return this->previousInterpreterFrameIsFromBailout;
    }

    bool JavascriptStackWalker::InlinedFramesBeingWalked() const
    {
        if (lastInternalFrameInfo.codeAddress)
        {
            return false;
        }

        return this->inlinedFramesBeingWalked;
    }

    bool JavascriptStackWalker::HasInlinedFramesOnStack() const
    {
        if (lastInternalFrameInfo.codeAddress)
        {
            return lastInternalFrameInfo.hasInlinedFramesOnStack;
        }

        return this->hasInlinedFramesOnStack;
    }
#endif

    bool JavascriptStackWalker::GetSourcePosition(const WCHAR** sourceFileName, ULONG* line, LONG* column)
    {
        uint byteCodeoffset = this->GetByteCodeOffset();
        if(byteCodeoffset)
        {
            Js::FunctionBody* functionBody = this->GetCurrentFunction()->GetFunctionBody();
            if (functionBody->GetLineCharOffset(byteCodeoffset, line, column))
            {
                if(functionBody->GetUtf8SourceInfo()->IsDynamic())
                {
                    *sourceFileName = _u("Dynamic Code");
                }
                else
                {
                    *sourceFileName = functionBody->GetUtf8SourceInfo()->GetSrcInfo()->sourceContextInfo->url;
                }
                return true;
            }
        }
        return false;
    }

    Js::JavascriptFunction * JavascriptStackWalker::UpdateFrame(bool includeInlineFrames)
    {
        this->isJavascriptFrame = this->CheckJavascriptFrame(includeInlineFrames);

        if (this->IsJavascriptFrame())
        {
            // In case we have a cross site thunk, update the script context
            Js::JavascriptFunction *function = this->GetCurrentFunction();

#if ENABLE_NATIVE_CODEGEN
            bool isCurrentPhysicalFrameForLoopBody = this->IsCurrentPhysicalFrameForLoopBody();
#endif
            if (this->interpreterFrame)
            {
#if ENABLE_NATIVE_CODEGEN
                if (lastInternalFrameInfo.codeAddress != nullptr)
                {
                    this->previousInterpreterFrameIsForLoopBody = true;
                }
#endif

                // We might've bailed out of an inlinee, so check if there were any inlinees.
                if (this->interpreterFrame->TestFlags(InterpreterStackFrameFlags_FromBailOut))
                {
                    previousInterpreterFrameIsFromBailout = true;

#if ENABLE_NATIVE_CODEGEN
                    Assert(!inlinedFramesBeingWalked);
                    if (includeInlineFrames)
                    {
                        int loopNum = -1;
                        if (isCurrentPhysicalFrameForLoopBody)
                        {
                            loopNum = this->tempInterpreterFrame->GetCurrentLoopNum();
                        }

                        bool hasInlinedFramesOnStack = InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame,
                            ScriptFunction::FromVar(function), true /*fromBailout*/, loopNum, this, false /*useInternalFrameInfo*/, false /*noAlloc*/);

                        if (hasInlinedFramesOnStack)
                        {
                            // We're now back in the state where currentFrame == physical frame of the inliner, but
                            // since interpreterFrame != null, we'll pick values from the interpreterFrame (the bailout
                            // frame of the inliner). Set a flag to tell the stack walker that it needs to start from the
                            // inlinee frames on the stack when Walk() is called.
                            this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
                            this->hasInlinedFramesOnStack = hasInlinedFramesOnStack;
                            Assert(inlinedFramesBeingWalked);
                            Assert(StackScriptFunction::GetCurrentFunctionObject(this->interpreterFrame->GetJavascriptFunction()) == inlinedFrameWalker.GetFunctionObject());
                        }
                        else
                        {
                            Assert(!isCurrentPhysicalFrameForLoopBody);
                        }
                    }
                    else if (isCurrentPhysicalFrameForLoopBody)
                    {
                        // Getting here is only possible when the current interpreterFrame is for a function which
                        // encountered a bailout after getting inlined in a jitted loop body. If we are not including
                        // inlined frames in the stack walk, we need to set the codeAddress on lastInternalFrameInfo,
                        // which would have otherwise been set upon closing the inlinedFrameWalker, now.
                        // Note that we already have an assert in CheckJavascriptFrame to ensure this.
                        SetCachedInternalFrameInfo(InternalFrameType_LoopBody, function, false /*hasInlinedFramesOnStack*/, true /*previousInterpreterFrameIsFromBailout*/);
                    }
#else
                    // How did we bail out when JIT was disabled?
                    Assert(false);
#endif
                }
                else
                {
                    Assert(StackScriptFunction::GetCurrentFunctionObject(this->interpreterFrame->GetJavascriptFunction()) == function);
                    previousInterpreterFrameIsFromBailout = false;
                }
            }
            else if (!this->isNativeLibraryFrame)
            {
#if ENABLE_NATIVE_CODEGEN
                Assert(!HasInlinedFramesOnStack() || (includeInlineFrames && isCurrentPhysicalFrameForLoopBody));

                if (!HasInlinedFramesOnStack() && includeInlineFrames)
                {
                    // Check whether there are inlined frames nested in this native frame. The corresponding check for
                    // a jitted loop body frame should have been done in CheckJavascriptFrame
                    Assert(lastInternalFrameInfo.codeAddress == nullptr);
                    bool inlinedFramesFound = InlinedFrameWalker::FromPhysicalFrame(
                        inlinedFrameWalker,
                        currentFrame,
                        ScriptFunction::FromVar(function),
                        false,  // fromBailout
                        -1,     // loopNum
                        nullptr,// walker
                        false,  // useInternalFrameInfo
                        false   // noAlloc
                    );
                    if (inlinedFramesFound)
                    {
                        this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
                        this->hasInlinedFramesOnStack = true;
                        Assert(inlinedFramesBeingWalked);
                    }
                }
#endif
            }
            this->scriptContext = function->GetScriptContext();
            return function;
        }
        return nullptr;
    }
#if ENABLE_NATIVE_CODEGEN
    void JavascriptStackWalker::WalkAndClearInlineeFrameCallInfoOnException(void *tryHandlerAddrOfReturnAddr)
    {
        // Walk the stack and when we find the first native frame, we clear the inlinee's callinfo for this frame
        // It is sufficient we stop at the first native frame which had the enclosing try-catch
        // TODO : Revisit when we start inlining functions with try-catch/try-finally
        while (this->Walk(true))
        {
            if (JavascriptFunction::IsNativeAddress(this->scriptContext, this->currentFrame.GetInstructionPointer()))
            {
                if (HasInlinedFramesOnStack())
                {
                    for (int index = inlinedFrameWalker.GetFrameCount() - 1; index >= 0; index--)
                    {
                        auto inlinedFrame = inlinedFrameWalker.GetFrameAtIndex(index);
                        inlinedFrame->callInfo.Clear();
                    }
                }
            }
            if (this->currentFrame.GetAddressOfReturnAddress() == tryHandlerAddrOfReturnAddr)
            {
                break;
            }
        }
    }
#endif
    // Note: noinline is to make sure that when we unwind to the unwindToAddress, there is at least one frame to unwind.
    _NOINLINE
    JavascriptStackWalker::JavascriptStackWalker(ScriptContext * scriptContext, bool useEERContext, PVOID returnAddress, bool _forceFullWalk /*=false*/) :
        inlinedFrameCallInfo(CallFlags_None, 0), shouldDetectPartiallyInitializedInterpreterFrame(true), forceFullWalk(_forceFullWalk),
        previousInterpreterFrameIsFromBailout(false), previousInterpreterFrameIsForLoopBody(false), hasInlinedFramesOnStack(false)
    {
        if (scriptContext == NULL)
        {
            Throw::InternalError();
        }
        this->scriptContext = scriptContext;

        // Pull the current script state from the thread context.

        ThreadContext * threadContext = scriptContext->GetThreadContext();
        this->entryExitRecord = threadContext->GetScriptEntryExit();

        this->nativeLibraryEntry = threadContext->PeekNativeLibraryEntry();
        this->prevNativeLibraryEntry = nullptr;

        this->interpreterFrame = NULL;
        this->isJavascriptFrame = false;
        this->isNativeLibraryFrame = false;

        if (entryExitRecord->frameIdOfScriptExitFunction != NULL)
        {
            // We're currently outside the script, so grab the frame from which we left.
            this->scriptContext = entryExitRecord->scriptContext;
            this->isInitialFrame = this->currentFrame.InitializeByFrameId(entryExitRecord->frameIdOfScriptExitFunction, this->scriptContext);
        }
        else
        {
            // Just start with the caller
            this->isInitialFrame = this->currentFrame.InitializeByReturnAddress(_ReturnAddress(), this->scriptContext);
        }

        if (useEERContext)
        {
            this->tempInterpreterFrame = this->scriptContext->GetThreadContext()->GetLeafInterpreterFrame();
        }
        else
        {
            // We need to generate stack for the passed script context, so use the leaf interpreter frame for passed script context
            this->tempInterpreterFrame = scriptContext->GetThreadContext()->GetLeafInterpreterFrame();
        }

        inlinedFramesBeingWalked = false;
    }

    BOOL JavascriptStackWalker::Walk(bool includeInlineFrames)
    {
        // Walk one frame up the call stack.
        this->interpreterFrame = NULL;

#if ENABLE_NATIVE_CODEGEN
        if (lastInternalFrameInfo.codeAddress != nullptr && this->previousInterpreterFrameIsForLoopBody)
        {
            this->previousInterpreterFrameIsForLoopBody = false;
            ClearCachedInternalFrameInfo();
        }

        if (inlinedFramesBeingWalked)
        {
            Assert(includeInlineFrames);
            inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
            if (!inlinedFramesBeingWalked)
            {
                inlinedFrameWalker.Close();
                if ((this->IsCurrentPhysicalFrameForLoopBody()))
                {
                    // Done walking inlined frames in a loop body, cache the native code address now
                    // in order to skip the loop body frame.
                    this->SetCachedInternalFrameInfo(InternalFrameType_LoopBody, this->GetCurrentFunctionFromPhysicalFrame(), true /*hasInlinedFramesOnStack*/, previousInterpreterFrameIsFromBailout);
                    isJavascriptFrame = false;
                }
            }

            return true;
        }
#endif
        this->hasInlinedFramesOnStack = false;
        if (this->isInitialFrame)
        {
            this->isInitialFrame = false; // Only walk initial frame once
        }
        else if (!this->currentFrame.Next())
        {
            this->isJavascriptFrame = false;
            return false;
        }

        // If we're at the entry from a host frame, hop to the frame from which we left the script.
        if (AlignAndCheckAddressOfReturnAddressMatch(this->currentFrame.GetAddressOfInstructionPointer(), this->entryExitRecord->addrOfReturnAddrOfScriptEntryFunction))
        {
            BOOL hasCaller = this->entryExitRecord->hasCaller || this->forceFullWalk;

#ifdef CHECK_STACKWALK_EXCEPTION
            BOOL ignoreStackWalkException = this->entryExitRecord->ignoreStackWalkException;
#endif

            this->entryExitRecord = this->entryExitRecord->next;
            if (this->entryExitRecord == NULL)
            {
                this->isJavascriptFrame = false;
                return false;
            }

            if (!hasCaller && !this->scriptContext->IsDiagnosticsScriptContext())
            {
#ifdef CHECK_STACKWALK_EXCEPTION
                if (!ignoreStackWalkException)
                {
                    AssertMsg(false, "walk pass no caller frame");
                }
#endif
                this->isJavascriptFrame = false;
                return false;
            }

            this->scriptContext = this->entryExitRecord->scriptContext;
            this->currentFrame.SkipToFrame(this->entryExitRecord->frameIdOfScriptExitFunction);
        }

        this->UpdateFrame(includeInlineFrames);
        return true;
    }

    BOOL JavascriptStackWalker::GetCallerWithoutInlinedFrames(_Out_opt_ JavascriptFunction ** ppFunc)
    {
        return GetCaller(ppFunc, /*includeInlineFrames*/ false);
    }

    BOOL JavascriptStackWalker::GetCaller(_Out_opt_ JavascriptFunction ** ppFunc, bool includeInlineFrames)
    {
        while (this->Walk(includeInlineFrames))
        {
            if (this->IsJavascriptFrame())
            {
                Assert(entryExitRecord != NULL);
                if (ppFunc)
                {
                *ppFunc = this->GetCurrentFunction();
                }
                AssertMsg(!this->shouldDetectPartiallyInitializedInterpreterFrame, "must have skipped first frame if needed");
                return true;
            }
        }
        if (ppFunc)
        {
            *ppFunc = nullptr;
        }
        return false;
    }

    BOOL JavascriptStackWalker::GetNonLibraryCodeCaller(_Out_opt_ JavascriptFunction ** ppFunc)
    {
        while (this->GetCaller(ppFunc))
        {
            Assert(ppFunc != nullptr);
            __analysis_assume(ppFunc != nullptr);
            if (!(*ppFunc)->IsLibraryCode())
            {
                return true;
            }
        }
        return false;
    }

    /*static*/
    bool JavascriptStackWalker::IsLibraryStackFrameEnabled(Js::ScriptContext * scriptContext)
    {
        Assert(scriptContext != nullptr);
        return CONFIG_FLAG(LibraryStackFrame);
    }

    // Check if a function is a display caller: user code, or native library / boundary script library code
    bool JavascriptStackWalker::IsDisplayCaller(JavascriptFunction* func)
    {
        FunctionBody* body = func->GetFunctionBody();
        if (IsLibraryStackFrameEnabled(func->GetScriptContext()))
        {
            return !func->IsScriptFunction() || !body->GetUtf8SourceInfo()->GetIsLibraryCode() || body->IsPublicLibraryCode();
        }
        else
        {
            return !body->GetUtf8SourceInfo()->GetIsLibraryCode();
        }
    }

    bool JavascriptStackWalker::GetDisplayCaller(_Out_opt_ JavascriptFunction ** ppFunc)
    {
        while (this->GetCaller(ppFunc))
        {
            Assert(ppFunc != nullptr);
            __analysis_assume(ppFunc != nullptr);
            if (IsDisplayCaller(*ppFunc))
            {
                return true;
            }
        }
        return false;
    }

    PCWSTR JavascriptStackWalker::GetCurrentNativeLibraryEntryName() const
    {
        Assert(IsLibraryStackFrameEnabled(this->scriptContext)
            && this->prevNativeLibraryEntry
            && this->prevNativeLibraryEntry->next == this->nativeLibraryEntry);
        return this->prevNativeLibraryEntry->name;
    }

    // WalkToTarget skips internal frames
    BOOL JavascriptStackWalker::WalkToTarget(JavascriptFunction * funcTarget)
    {
        // Walk up the call stack until we find the frame that belongs to the given function.

        while (this->Walk(/*includeInlineFrames*/ true))
        {
            if (this->IsJavascriptFrame() && this->GetCurrentFunction() == funcTarget)
            {
                // Skip internal names
                Assert( !(this->GetCallInfo().Flags & CallFlags_InternalFrame) );
                return true;
            }
        }

        return false;
    }

    bool JavascriptStackWalker::AlignAndCheckAddressOfReturnAddressMatch(void* addressOfReturnAddress, void* nativeLibraryEntryAddress)
    {
        return addressOfReturnAddress == nativeLibraryEntryAddress
#if defined(_M_IX86)
            // Under some odd cases on x86, addressOfReturnAddress and stashed entry address need to be aligned.
            // This happens when code is generated using two stack pointers. One or both have the address of 
            // return address offset by 4, 8, or 12.
            || (((uint)nativeLibraryEntryAddress - (uint)addressOfReturnAddress < 0x10) &&
                *(void**)addressOfReturnAddress == *(void**)nativeLibraryEntryAddress
                )
#endif
            ;
    }

    void ** JavascriptStackWalker::GetCurrentArgv() const
    {
        Assert(this->IsJavascriptFrame());
        Assert(this->interpreterFrame != nullptr ||
               (this->prevNativeLibraryEntry && AlignAndCheckAddressOfReturnAddressMatch(this->currentFrame.GetAddressOfReturnAddress(), this->prevNativeLibraryEntry->addr)) ||
               JavascriptFunction::IsNativeAddress(this->scriptContext, (void*)this->currentFrame.GetInstructionPointer()));

        bool isNativeAddr = (this->interpreterFrame == nullptr) &&
                            (!this->prevNativeLibraryEntry || !AlignAndCheckAddressOfReturnAddressMatch(this->currentFrame.GetAddressOfReturnAddress(), this->prevNativeLibraryEntry->addr));
        void ** argv = currentFrame.GetArgv(isNativeAddr, false /*shouldCheckForNativeAddr*/);
        Assert(argv);
        return argv;
    }

    bool JavascriptStackWalker::CheckJavascriptFrame(bool includeInlineFrames)
    {
        this->isNativeLibraryFrame = false; // Clear previous result

        void * codeAddr = this->currentFrame.GetInstructionPointer();
        if (this->tempInterpreterFrame && codeAddr == this->tempInterpreterFrame->GetReturnAddress())
        {
            bool isBailoutInterpreter = this->tempInterpreterFrame->TestFlags(Js::InterpreterStackFrameFlags_FromBailOut);

            // We need to skip over the first interpreter frame on the stack if it is the partially initialized frame
            // otherwise it is a real frame and we should continue.
            // For fully initialized frames (PushPopHelper was called) the thunk stack addr is equal or below addressOfReturnAddress
            // as the latter one is obtained in InterpreterStackFrame::InterpreterThunk called by the thunk.
            bool isPartiallyInitializedFrame = this->shouldDetectPartiallyInitializedInterpreterFrame &&
                this->currentFrame.GetAddressOfReturnAddress(isBailoutInterpreter /*isCurrentContextNative*/, false /*shouldCheckForNativeAddr*/) < this->tempInterpreterFrame->GetAddressOfReturnAddress();
            this->shouldDetectPartiallyInitializedInterpreterFrame = false;

            if (isPartiallyInitializedFrame)
            {
                return false; // Skip it.
            }

            void ** argv = this->currentFrame.GetArgv(isBailoutInterpreter /*isCurrentContextNative*/, false /*shouldCheckForNativeAddr*/);
            if (argv == nullptr)
            {
                // NOTE: When we switch to walking the stack ourselves and skip non engine frames, this should never happen.
                return false;
            }

            this->interpreterFrame = this->tempInterpreterFrame;

            this->tempInterpreterFrame = this->interpreterFrame->GetPreviousFrame();

#if ENABLE_NATIVE_CODEGEN
#if DBG
            if (((CallInfo const *)&argv[JavascriptFunctionArgIndex_CallInfo])->Flags & CallFlags_InternalFrame)
            {
                // The return address of the interpreterFrame is the same as the entryPoint for a jitted loop body.
                // This can only ever happen when we have bailed out from a function inlined in the loop body. We
                // wouldn't have created a new interpreterFrame if the bailout were from the loop body itself.
                Assert(this->interpreterFrame->TestFlags(Js::InterpreterStackFrameFlags_FromBailOut));
                InlinedFrameWalker tmpFrameWalker;
                Assert(InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, Js::ScriptFunction::FromVar(argv[JavascriptFunctionArgIndex_Function]),
                    true /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, true /*noAlloc*/));
                tmpFrameWalker.Close();
            }
#endif //DBG
#endif //ENABLE_NATIVE_CODEGEN

            return true;
        }

        if (IsLibraryStackFrameEnabled(this->scriptContext) && this->nativeLibraryEntry)
        {
            void* addressOfReturnAddress = this->currentFrame.GetAddressOfReturnAddress();
            void* nativeLibraryEntryAddress = this->nativeLibraryEntry->addr;
            AssertMsg(addressOfReturnAddress <= nativeLibraryEntryAddress, "Missed matching native library entry?");
            if (AlignAndCheckAddressOfReturnAddressMatch(addressOfReturnAddress, nativeLibraryEntryAddress))
            {
                this->isNativeLibraryFrame = true;
                this->shouldDetectPartiallyInitializedInterpreterFrame = false;
                this->prevNativeLibraryEntry = this->nativeLibraryEntry; // Saves match in prevNativeLibraryEntry
                this->nativeLibraryEntry = this->nativeLibraryEntry->next;
                return true;
            }
        }

#if ENABLE_NATIVE_CODEGEN
        BOOL isNativeAddr = JavascriptFunction::IsNativeAddress(this->scriptContext, codeAddr);
        if (isNativeAddr)
        {
            this->shouldDetectPartiallyInitializedInterpreterFrame = false;
            void ** argv = this->currentFrame.GetArgv(true /*isCurrentContextNative*/, false /*shouldCheckForNativeAddr*/);
            if (argv == nullptr)
            {
                // NOTE: When we switch to walking the stack ourselves and skip non engine frames, this should never happen.
                return false;
            }

            ScriptFunction* funcObj = Js::ScriptFunction::FromVar(argv[JavascriptFunctionArgIndex_Function]);
            if (funcObj->GetFunctionBody()->GetIsAsmjsMode())
            {
                return false;
            }

            // Note: this check has to happen after asm.js check, because call info is not valid for asm.js
            if (((CallInfo const *)&argv[JavascriptFunctionArgIndex_CallInfo])->Flags & CallFlags_InternalFrame)
            {
                if (includeInlineFrames &&
                    InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame, Js::ScriptFunction::FromVar(argv[JavascriptFunctionArgIndex_Function]),
                        false /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, false /*noAlloc*/))
                {
                    // Found inlined frames in a jitted loop body. We dont want to skip the inlined frames; walk all of them before setting codeAddress on lastInternalFrameInfo.
                    // DeepCopy here because, if there is an inlinee in a loop body, FromPhysicalFrame won't be called from UpdateFrame
                    this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
                    this->hasInlinedFramesOnStack = true;
                    Assert(inlinedFramesBeingWalked);
                    return true;
                }

                SetCachedInternalFrameInfo(InternalFrameType_LoopBody, funcObj, false /*hasInlinedFramesOnStack*/, previousInterpreterFrameIsFromBailout);
                return false;
            }

            return true;
        }
#endif
        return false;
    }

    void * JavascriptStackWalker::GetCurrentCodeAddr() const
    {
        return this->currentFrame.GetInstructionPointer();
    }

    JavascriptFunction * JavascriptStackWalker::GetCurrentFunction(bool includeInlinedFrames /* = true */) const
    {
        Assert(this->IsJavascriptFrame());

#if ENABLE_NATIVE_CODEGEN
        if (includeInlinedFrames && inlinedFramesBeingWalked)
        {
            return inlinedFrameWalker.GetFunctionObject();
        }
        else
#endif
            if (this->isNativeLibraryFrame)
        {
            // Return saved function. Do not read from stack as compiler may stackpack/optimize args.
            return JavascriptFunction::FromVar(this->prevNativeLibraryEntry->function);
        }
        else
        {
            return StackScriptFunction::GetCurrentFunctionObject((JavascriptFunction *)this->GetCurrentArgv()[JavascriptFunctionArgIndex_Function]);
        }
    }

    void JavascriptStackWalker::SetCurrentFunction(JavascriptFunction * function)
    {
        Assert(this->IsJavascriptFrame());
#if ENABLE_NATIVE_CODEGEN
        if (inlinedFramesBeingWalked)
        {
            inlinedFrameWalker.SetFunctionObject(function);
        }
        else
#endif
        {
            this->GetCurrentArgv()[JavascriptFunctionArgIndex_Function] = function;
        }
    }

    JavascriptFunction *JavascriptStackWalker::GetCurrentFunctionFromPhysicalFrame() const
    {
        return GetCurrentFunction(false);
    }

    CallInfo JavascriptStackWalker::GetCallInfo(bool includeInlinedFrames /* = true */) const
    {
        Assert(this->IsJavascriptFrame());
        CallInfo callInfo;
        if (includeInlinedFrames && inlinedFramesBeingWalked)
        {
            // Since we don't support inlining constructors yet, its questionable if we should handle the
            // hidden frame display here?
            callInfo = inlinedFrameCallInfo;
        }
        else if (this->GetCurrentFunction()->GetFunctionInfo()->IsCoroutine())
        {
            JavascriptGenerator* gen = JavascriptGenerator::FromVar(this->GetCurrentArgv()[JavascriptFunctionArgIndex_This]);
            callInfo = gen->GetArguments().Info;
        }
        else if (this->isNativeLibraryFrame)
        {
            // Return saved callInfo. Do not read from stack as compiler may stackpack/optimize args.
            callInfo = this->prevNativeLibraryEntry->callInfo;
        }
        else
        {
            callInfo = *(CallInfo const *)&this->GetCurrentArgv()[JavascriptFunctionArgIndex_CallInfo];
        }

        if (callInfo.Flags & Js::CallFlags_ExtraArg)
        {
            callInfo.Flags = (CallFlags)(callInfo.Flags & ~Js::CallFlags_ExtraArg);
        }

        return callInfo;
    }

    CallInfo JavascriptStackWalker::GetCallInfoFromPhysicalFrame() const
    {
        return GetCallInfo(false);
    }

    Var JavascriptStackWalker::GetThisFromFrame() const
    {
        Assert(!inlinedFramesBeingWalked);
        Assert(this->IsJavascriptFrame());

        if (this->GetCurrentFunction()->GetFunctionInfo()->IsCoroutine())
        {
            JavascriptGenerator* gen = JavascriptGenerator::FromVar(this->GetCurrentArgv()[JavascriptFunctionArgIndex_This]);
            return gen->GetArguments()[0];
        }

        return this->GetCurrentArgv()[JavascriptFunctionArgIndex_This];
    }

#if ENABLE_NATIVE_CODEGEN
    void JavascriptStackWalker::ClearCachedInternalFrameInfo()
    {
        this->lastInternalFrameInfo.Clear();
    }

    void JavascriptStackWalker::SetCachedInternalFrameInfo(InternalFrameType frameType, JavascriptFunction* function, bool hasInlinedFramesOnStack, bool previousInterpreterFrameIsFromBailout)
    {
        if (!this->lastInternalFrameInfo.codeAddress)
        {
            this->lastInternalFrameInfo.Set(
                this->GetCurrentCodeAddr(),
                this->currentFrame.GetFrame(),
                this->currentFrame.GetStackCheckCodeHeight(),
                frameType,
                function,
                hasInlinedFramesOnStack,
                previousInterpreterFrameIsFromBailout);
        }
    }
#endif

    bool JavascriptStackWalker::IsCurrentPhysicalFrameForLoopBody() const
    {
        return !!(this->GetCallInfoFromPhysicalFrame().Flags & CallFlags_InternalFrame);
    }

    bool JavascriptStackWalker::IsWalkable(ScriptContext *scriptContext)
    {
        if (scriptContext == NULL)
        {
            return false;
        }

        ThreadContext *threadContext = scriptContext->GetThreadContext();
        if (threadContext == NULL)
        {
            return false;
        }

        return (threadContext->GetScriptEntryExit() != NULL);
    }

    BOOL JavascriptStackWalker::GetCaller(_Out_opt_ JavascriptFunction** ppFunc, ScriptContext* scriptContext)
    {
        if (!IsWalkable(scriptContext))
        {
            if (ppFunc)
            {
            *ppFunc = nullptr;
            }
            return FALSE;
        }

        JavascriptStackWalker walker(scriptContext);
        return walker.GetCaller(ppFunc);
    }

    BOOL JavascriptStackWalker::GetCaller(_Out_opt_ JavascriptFunction** ppFunc, uint32* byteCodeOffset, ScriptContext* scriptContext)
    {
        JavascriptStackWalker walker(scriptContext);
        if (walker.GetCaller(ppFunc))
        {
            *byteCodeOffset = walker.GetByteCodeOffset();
            return TRUE;
        }
        return FALSE;
    }

    void JavascriptStackWalker::GetThis(Var* pThis, int moduleId, ScriptContext* scriptContext)
    {
        JavascriptStackWalker walker(scriptContext);
        JavascriptFunction* caller;
        if (walker.GetCaller(&caller))
        {
            walker.GetThis(pThis, moduleId);
        }
    }

    void JavascriptStackWalker::GetThis(Var* pThis, int moduleId, JavascriptFunction* func, ScriptContext* scriptContext)
    {
        JavascriptStackWalker walker(scriptContext);
        JavascriptFunction* caller;
        while (walker.GetCaller(&caller))
        {
            if (caller == func)
            {
                walker.GetThis(pThis, moduleId);
                return;
            }
        }
    }

    // Try to see whether there is a top-most javascript frame, and if there is return true if it's native.
    // Returns true if top most frame is javascript frame, in this case the isNative parameter receives true
    // when top-most frame is native, false otherwise.
    // Returns false if top most frame is not a JavaScript frame.

    /* static */
    bool JavascriptStackWalker::TryIsTopJavaScriptFrameNative(ScriptContext* scriptContext, bool* isNative, bool ignoreLibraryCode /* = false */)
    {
        Assert(scriptContext);
        Assert(isNative);

        Js::JavascriptFunction* caller;
        Js::JavascriptStackWalker walker(scriptContext);

        BOOL isSuccess;
        if (ignoreLibraryCode)
        {
            isSuccess = walker.GetNonLibraryCodeCaller(&caller);
        }
        else
        {
            isSuccess = walker.GetCaller(&caller);
        }

        if (isSuccess)
        {
            *isNative = (walker.GetCurrentInterpreterFrame() == NULL);
            return true;
        }
        return false;
    }

#if ENABLE_NATIVE_CODEGEN
    bool InlinedFrameWalker::FromPhysicalFrame(InlinedFrameWalker& self, StackFrame& physicalFrame, Js::ScriptFunction *parent, bool fromBailout,
        int loopNum, const JavascriptStackWalker * const stackWalker, bool useInternalFrameInfo, bool noAlloc)
    {
        bool inlinedFramesFound = false;
        FunctionBody* parentFunctionBody = parent->GetFunctionBody();
        EntryPointInfo *entryPointInfo;

        if (loopNum != -1)
        {
            Assert(stackWalker);
        }

        void *nativeCodeAddress = nullptr;
        void *framePointer = nullptr;
        if (loopNum != -1 && useInternalFrameInfo)
        {
            Assert(stackWalker->GetCachedInternalFrameInfo().codeAddress != nullptr);
            InternalFrameInfo lastInternalFrameInfo = stackWalker->GetCachedInternalFrameInfo();

            nativeCodeAddress = lastInternalFrameInfo.codeAddress;
            framePointer = lastInternalFrameInfo.framePointer;
        }
        else
        {
            nativeCodeAddress = physicalFrame.GetInstructionPointer();
            framePointer = physicalFrame.GetFrame();
        }

        if (loopNum != -1)
        {
            entryPointInfo = (Js::EntryPointInfo*)parentFunctionBody->GetLoopEntryPointInfoFromNativeAddress((DWORD_PTR)nativeCodeAddress, loopNum);
        }
        else
        {
            entryPointInfo = (Js::EntryPointInfo*)parentFunctionBody->GetEntryPointFromNativeAddress((DWORD_PTR)nativeCodeAddress);
        }

        AssertMsg(entryPointInfo != nullptr, "Inlined frame should resolve to the right parent address");
        if (entryPointInfo->HasInlinees())
        {
            void *entry = reinterpret_cast<void*>(entryPointInfo->GetNativeAddress());
            InlinedFrameWalker::InlinedFrame *outerMostFrame = InlinedFrame::FromPhysicalFrame(physicalFrame, stackWalker, entry, entryPointInfo, useInternalFrameInfo);

            if (!outerMostFrame)
            {
                return inlinedFramesFound;
            }

            if (!fromBailout)
            {
                InlineeFrameRecord* record = entryPointInfo->FindInlineeFrame((void*)nativeCodeAddress);

                if (record)
                {
                    record->RestoreFrames(parent->GetFunctionBody(), outerMostFrame, JavascriptCallStackLayout::FromFramePointer(framePointer), false /* boxValues */);
                }
            }

            if (outerMostFrame->callInfo.Count)
            {
                inlinedFramesFound = true;
                if (noAlloc)
                {
                    return inlinedFramesFound;
                }
                int32 frameCount = 0;
                InlinedFrameWalker::InlinedFrame *frameIterator = outerMostFrame;
                while (frameIterator->callInfo.Count)
                {
                    frameCount++;
                    frameIterator = frameIterator->Next();
                }

                InlinedFrameWalker::InlinedFrame **frames = HeapNewArray(InlinedFrameWalker::InlinedFrame*, frameCount);

                frameIterator = outerMostFrame;
                for (int index = frameCount - 1; index >= 0; index--)
                {
                    Assert(frameIterator);
                    frames[index] = frameIterator;
                    frameIterator = frameIterator->Next();
                }

                self.Initialize(frameCount, frames, parent);
            }

        }

        return inlinedFramesFound;
    }

    void InlinedFrameWalker::Close()
    {
        parentFunction = nullptr;
        HeapDeleteArray(frameCount, frames);
        frames = nullptr;
        currentIndex = -1;
        frameCount = 0;
    }

    bool InlinedFrameWalker::Next(CallInfo& callInfo)
    {
        MoveNext();
        InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
        if (currentFrame)
        {
            callInfo.Flags = CallFlags_None;
            callInfo.Count = (currentFrame->callInfo.Count & 0xFFFF);
        }

        return currentFrame != nullptr;
    }

    size_t InlinedFrameWalker::GetArgc() const
    {
        InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
        Assert(currentFrame);

        return currentFrame->callInfo.Count;
    }

    // Note: the boxArgsAndDeepCopy parameter should be true when a copy of the JS args must be ensured to
    // be on the heap. This results in a new array of Vars with deep copied boxed values (where
    // appropriate).
    // Otherwise, this parameter should be false. For instance, if the args will only be used
    // internally to gather type info, the values are not boxed (so, some Vars may still be on
    // the stack) and the array of the current frame is returned.
    Js::Var *InlinedFrameWalker::GetArgv(bool includeThis, bool boxArgsAndDeepCopy) const
    {
        InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
        Assert(currentFrame);

        uint firstArg = includeThis ? InlinedFrameArgIndex_This : InlinedFrameArgIndex_SecondScriptArg;
        size_t argCount = this->GetArgc() - firstArg;
        Js::Var *args;
        if (!boxArgsAndDeepCopy)
        {
            args = &currentFrame->argv[firstArg];
        }
        else
        {
            args = RecyclerNewArray(parentFunction->GetScriptContext()->GetRecycler(), Js::Var, argCount);
            for (size_t i = 0; i < argCount; i++)
            {
                args[i] = currentFrame->argv[firstArg + i];
            }

            this->FinalizeStackValues(args, argCount, true /*deepCopy*/);
        }

        return args;
    }

    void InlinedFrameWalker::FinalizeStackValues(__in_ecount(argCount) Js::Var args[], size_t argCount, bool deepCopy) const
    {
        ScriptContext *scriptContext = this->GetFunctionObject()->GetScriptContext();

        for (size_t i = 0; i < argCount; i++)
        {
            args[i] = Js::JavascriptOperators::BoxStackInstance(args[i], scriptContext, false /*allowStackFunction*/, deepCopy);
        }
    }

    Js::JavascriptFunction *InlinedFrameWalker::GetFunctionObject() const
    {
        InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
        Assert(currentFrame);

        return StackScriptFunction::GetCurrentFunctionObject(currentFrame->function);
    }

    void InlinedFrameWalker::SetFunctionObject(Js::JavascriptFunction * function)
    {
        InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
        Assert(currentFrame);

        currentFrame->function = function;
    }

    Js::Var InlinedFrameWalker::GetArgumentsObject() const
    {
        InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
        Assert(currentFrame);

        return currentFrame->arguments;
    }

    void InlinedFrameWalker::SetArgumentsObject(Js::Var arguments)
    {
        InlinedFrameWalker::InlinedFrame *currentFrame = (InlinedFrameWalker::InlinedFrame *)GetCurrentFrame();
        Assert(currentFrame);

        currentFrame->arguments = arguments;
    }

    Js::Var InlinedFrameWalker::GetThisObject() const
    {
        InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
        Assert(currentFrame);

        return currentFrame->argv[InlinedFrameArgIndex_This];
    }

    bool InlinedFrameWalker::IsCallerPhysicalFrame() const
    {
        return currentIndex == (frameCount - 1);
    }

    bool InlinedFrameWalker::IsTopMostFrame() const
    {
        return currentIndex == 0;
    }

    uint32 InlinedFrameWalker::GetCurrentInlineeOffset() const
    {
        Assert(!IsTopMostFrame());
        Assert(currentIndex);

        return GetFrameAtIndex(currentIndex - 1)->callInfo.InlineeStartOffset;
    }

    uint32 InlinedFrameWalker::GetBottomMostInlineeOffset() const
    {
        Assert(frameCount);

        return GetFrameAtIndex(frameCount - 1)->callInfo.InlineeStartOffset;
    }

    Js::JavascriptFunction *InlinedFrameWalker::GetBottomMostFunctionObject() const
    {
        Assert(frameCount);

        return GetFrameAtIndex(frameCount - 1)->function;
    }

    InlinedFrameWalker::InlinedFrame *const InlinedFrameWalker::GetCurrentFrame() const
    {
        return GetFrameAtIndex(currentIndex);
    }

    InlinedFrameWalker::InlinedFrame *const InlinedFrameWalker::GetFrameAtIndex(signed index) const
    {
        Assert(frames);
        Assert(frameCount);

        InlinedFrameWalker::InlinedFrame *frame = nullptr;
        if (index < frameCount)
        {
            frame = frames[index];
        }

        return frame;
    }

    void InlinedFrameWalker::MoveNext()
    {
        currentIndex++;
    }

    void InlinedFrameWalker::Initialize(int32 frameCount, __in_ecount(frameCount) InlinedFrame **frames, Js::ScriptFunction *parent)
    {
        Assert(!parentFunction);
        Assert(!this->frames);
        Assert(!this->frameCount);
        Assert(currentIndex == -1);

        this->parentFunction = parent;
        this->frames         = frames;
        this->frameCount     = frameCount;
        this->currentIndex   = -1;
    }

    InlinedFrameWalker::InlinedFrame* InlinedFrameWalker::InlinedFrame::FromPhysicalFrame(StackFrame& currentFrame, const JavascriptStackWalker * const stackWalker, void *entry, EntryPointInfo* entryPointInfo, bool useInternalFrameInfo)
    {
        // If the current javascript frame is a native frame, get the inlined frame from it, otherwise
        // it may be possible that current frame is the interpreter frame for a jitted loop body
        // If the loop body had some inlinees in it, retrieve the inlined frame using the cached info,
        // viz. instruction pointer, frame pointer, and stackCheckCodeHeight, about the loop body frame.
        struct InlinedFrame *inlinedFrame = nullptr;
        void *codeAddr, *framePointer;
        size_t stackCheckCodeHeight;

        if (useInternalFrameInfo)
        {
            codeAddr = stackWalker->GetCachedInternalFrameInfo().codeAddress;
            framePointer = stackWalker->GetCachedInternalFrameInfo().framePointer;
            stackCheckCodeHeight = stackWalker->GetCachedInternalFrameInfo().stackCheckCodeHeight;
        }
        else
        {
            codeAddr = currentFrame.GetInstructionPointer();
            framePointer = currentFrame.GetFrame();
            stackCheckCodeHeight = currentFrame.GetStackCheckCodeHeight();
        }

        if (!StackFrame::IsInStackCheckCode(entry, codeAddr, stackCheckCodeHeight))
        {
            inlinedFrame = (struct InlinedFrame *)(((uint8 *)framePointer) - entryPointInfo->GetFrameHeight());
        }

        return inlinedFrame;
    }

    void InternalFrameInfo::Set(
        void *codeAddress,
        void *framePointer,
        size_t stackCheckCodeHeight,
        InternalFrameType frameType,
        JavascriptFunction* function,
        bool hasInlinedFramesOnStack,
        bool previousInterpreterFrameIsFromBailout)
    {
        // We skip a jitted loop body's native frame when walking the stack and refer to the loop body's interpreter frame to get the function.
        // However, if the loop body has inlinees, to retrieve inlinee frames we need to cache some info about the loop body's native frame.
        this->codeAddress = codeAddress;
        this->framePointer = framePointer;
        this->stackCheckCodeHeight = stackCheckCodeHeight;
        this->frameType = frameType;
        this->function = function;
        this->hasInlinedFramesOnStack = hasInlinedFramesOnStack;
        this->previousInterpreterFrameIsFromBailout = previousInterpreterFrameIsFromBailout;
    }

    void InternalFrameInfo::Clear()
    {
        this->codeAddress = nullptr;
        this->framePointer = nullptr;
        this->stackCheckCodeHeight = (uint)-1;
        this->frameType = InternalFrameType_None;
        this->function = nullptr;
        this->hasInlinedFramesOnStack = false;
        this->previousInterpreterFrameIsFromBailout = false;
    }
#endif

#if DBG
    // Force a stack walk which till we find an interpreter frame
    // This will ensure inlined frames are decoded.
    bool JavascriptStackWalker::ValidateTopJitFrame(Js::ScriptContext* scriptContext)
    {
        if (!Configuration::Global.flags.ValidateInlineStack)
        {
            return true;
        }
        Js::JavascriptStackWalker walker(scriptContext);
        Js::JavascriptFunction* function;
        while (walker.GetCaller(&function))
        {
            Assert(function);
            if (walker.GetCurrentInterpreterFrame() != nullptr)
            {
                break;
            }
        }
        // If no asserts have fired yet - we should have succeeded.
        return true;
    }
#endif
}
