/*
 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ecmascript/frames.h"
#include <mutex>

#if ECMASCRIPT_ENABLE_ARK_STEED
#include "ecmascript/arksteed/arksteed_safepoint_table.h"
#endif
#include "ecmascript/dfx/stackinfo/js_stackinfo.h"
#include "ecmascript/stackmap/ark_stackmap_parser.h"

namespace panda::ecmascript {

__attribute__((retain)) uintptr_t FrameIterator::rdoAddrMapBegin_ = 0;
__attribute__((retain)) uintptr_t FrameIterator::rdoAddrMapEnd_ = 0;

FrameIterator::FrameIterator(JSTaggedType *sp, const JSThread *thread) : current_(sp), thread_(thread)
{
    if (thread != nullptr) {
        arkStackMapParser_ =
            const_cast<JSThread *>(thread)->GetEcmaVM()->GetAOTFileManager()->GetStackMapParser();
    }
}

int FrameIterator::ComputeDelta(const Method *method) const
{
    if (method == nullptr || isJITFrame_) {
        return fpDeltaPrevFrameSp_;
    }
    return method->GetFpDelta();
}

Method *FrameIterator::CheckAndGetMethod() const
{
    auto function = GetFunction();
    if (function.CheckIsJSFunctionBase() || function.CheckIsJSProxy()) {
        return ECMAObject::Cast(function.GetTaggedObject())->GetCallTarget(thread_);
    }
    return nullptr;
}

JSTaggedValue FrameIterator::GetFunction() const
{
    FrameType type = GetFrameType();
    switch (type) {
        case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
            auto frame = GetFrame<OptimizedJSFunctionFrame>();
            return frame->GetFunction();
        }
        case FrameType::ASM_INTERPRETER_FRAME:
        case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
            auto frame = GetFrame<AsmInterpretedFrame>();
            return frame->function;
        }
        case FrameType::INTERPRETER_FRAME:
        case FrameType::INTERPRETER_FAST_NEW_FRAME: {
            auto frame = GetFrame<InterpretedFrame>();
            return frame->function;
        }
        case FrameType::INTERPRETER_BUILTIN_FRAME: {
            auto frame = GetFrame<InterpretedBuiltinFrame>();
            return frame->function;
        }
        case FrameType::BUILTIN_FRAME_WITH_ARGV: {
            auto *frame = BuiltinWithArgvFrame::GetFrameFromSp(GetSp());
            return frame->GetFunction();
        }
        case FrameType::BUILTIN_ENTRY_FRAME:
        case FrameType::BUILTIN_FRAME: {
            auto *frame = BuiltinFrame::GetFrameFromSp(GetSp());
            return frame->GetFunction();
        }
        case FrameType::BUILTIN_CALL_LEAVE_FRAME: {
            auto *frame = OptimizedBuiltinLeaveFrame::GetFrameFromSp(GetSp());
            return JSTaggedValue(*(frame->GetArgv()));
        }
        case FrameType::FASTJIT_FUNCTION_FRAME:
        case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
            auto frame = FASTJITFunctionFrame::GetFrameFromSp(GetSp());
            return frame->GetFunction();
        }
        case FrameType::STEED_FUNCTION_FRAME: {
            auto frame = SteedFunctionFrame::GetFrameFromSp(GetSp());
            return frame->GetFunction();
        }
        case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME :
        case FrameType::OPTIMIZED_FRAME:
        case FrameType::OPTIMIZED_ENTRY_FRAME:
        case FrameType::ASM_BRIDGE_FRAME:
        case FrameType::LEAVE_FRAME:
        case FrameType::BASELINE_BUILTIN_FRAME:
        case FrameType::LEAVE_FRAME_WITH_ARGV:
        case FrameType::INTERPRETER_ENTRY_FRAME:
        case FrameType::ASM_INTERPRETER_ENTRY_FRAME:
        case FrameType::ASM_INTERPRETER_BRIDGE_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME: {
            return JSTaggedValue::Undefined();
        }
        default: {
            LOG_FULL(FATAL) << "Unknown frame type: " << static_cast<uintptr_t>(type);
            UNREACHABLE();
        }
    }
}

void FrameIterator::PrintText(uint32_t *jitTextBegin, uint32_t jitTextSize, std::string message)
{
    LOG_FULL(INFO) << "TextBegin: " << std::hex << reinterpret_cast<uintptr_t>(jitTextBegin)
                   << ", TextSize: " << jitTextSize << ", " << message;
    std::ostringstream oss;
    for (size_t point = 0; point < jitTextSize; ++point) {
        oss << std::hex << jitTextBegin[point] << " ";
        if (point % PRINT_STEP == 0 && point != 0) {
            LOG_FULL(INFO) << oss.str();
            oss.str("");
        }
    }
    LOG_FULL(INFO) << oss.str();
}

AOTFileInfo::CallSiteInfo FrameIterator::TryCalCallSiteInfoFromMachineCode(uintptr_t retAddr, bool isDeopt) const
{
    FrameType type = GetFrameType();
    if (type == FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME || type == FrameType::OPTIMIZED_JS_FUNCTION_FRAME ||
        type == FrameType::FASTJIT_FUNCTION_FRAME || type == FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME ||
        type == FrameType::STEED_FUNCTION_FRAME) {
        MachineCode* machineCode = thread_->GetEcmaVM()->GetHeap()->GetMachineCodeObject(retAddr);
        if (machineCode == nullptr) {
            std::ostringstream oss;
            oss << "machine code is nullptr. deopt type: " << std::hex << GetDeoptType() << ", retAddr: " << retAddr;
            if (!isDeopt) {
                LOG_FULL(FATAL) << oss.str();
            }
            oss << ", isDeopt";
            JSTaggedValue jsFunction = GetFunction();
            if (!jsFunction.IsJSFunction()) {
                oss << ", not js function object. addr: " << jsFunction.GetRawData();
                LOG_FULL(FATAL) << oss.str();
            }
            std::string methodName =
                Method::Cast(JSFunction::Cast(jsFunction)->GetMethod(thread_))->GetMethodName(thread_);
            oss << ", method name: " << methodName << ", function addr: " << jsFunction.GetRawData();
            JSTaggedValue machineCodeObj = JSFunction::Cast(jsFunction.GetTaggedObject())->GetMachineCode(thread_);
            if (!machineCodeObj.IsMachineCodeObject()) {
                oss << ", not machine code object. addr: " << machineCodeObj.GetRawData();
                LOG_FULL(FATAL) << oss.str();
            }
            machineCode = MachineCode::Cast(machineCodeObj.GetTaggedObject());
            uint32_t *textBegin = reinterpret_cast<uint32_t *>(retAddr - machineCode->GetInstructionsSize());
            uint32_t textSize = machineCode->GetInstructionsSize() * INSTRUCTION_CONTEXT / INSTRUCTION_LENGTH;
            PrintText(textBegin, textSize, "print text begin:");
            textBegin = reinterpret_cast<uint32_t *>(machineCode->GetInstructionsAddr());
            textSize = machineCode->GetInstructionsSize() / INSTRUCTION_LENGTH;
            PrintText(textBegin, textSize, "print machine code begin:");
            oss << ", machine code addr: " << machineCode << ", text begin: " << machineCode->GetText()
                << ", text size: " << machineCode->GetTextSize();
            LOG_FULL(FATAL) << oss.str();
        }
        const_cast<FrameIterator*>(this)->machineCode_ = reinterpret_cast<JSTaggedType>(machineCode);
        return reinterpret_cast<MachineCode*>(machineCode_)->CalCallSiteInfo();
    }
    return {};
}

std::pair<AOTFileInfo::CallSiteInfo, bool> FrameIterator::CalCallSiteInfo(uintptr_t retAddr, bool isDeopt) const
{
    auto callSiteInfo = const_cast<JSThread*>(thread_)->GetEcmaVM()->CalCallSiteInfo(retAddr, isDeopt);
    if (std::get<1>(callSiteInfo) != nullptr) { // 1 : stackMapAddr
        return std::make_pair(callSiteInfo, false);
    }
    // try get jit code
    callSiteInfo = TryCalCallSiteInfoFromMachineCode(retAddr, isDeopt);
    return std::make_pair(callSiteInfo, true);
}

uintptr_t FrameIterator::RDORetAddrConvert(uintptr_t retAddr)
{
    if (retAddr == 0 || rdoAddrMapBegin_ == 0 || rdoAddrMapEnd_ == 0) {
        return retAddr;
    }

    static std::once_flag rdoInitFlag;
    static std::unordered_map<uintptr_t, uintptr_t> retAddrMap;

    std::call_once(rdoInitFlag, [&]() {
        for (uintptr_t curAddr = rdoAddrMapBegin_;
            curAddr < rdoAddrMapEnd_;
            curAddr += sizeof(RdoAddrConvertPair)) {
            RdoAddrConvertPair* curPtr =
                reinterpret_cast<RdoAddrConvertPair*>(curAddr);
            retAddrMap[curPtr->ccRetAddr] = curPtr->originRetAddr;
        }
    });

    auto it = retAddrMap.find(retAddr);
    if (it != retAddrMap.end()) {
        return it->second;
    }

    return retAddr;
}

template <GCVisitedFlag GCVisit>
void FrameIterator::Advance()
{
    ASSERT(!Done());
    FrameType rawT = GetRawFrameType();
    FrameType t = GetFrameType();
    bool needCalCallSiteInfo = false;
    // If need to get return addr from frame, the flag euqal true.
    bool needCheckLazyDeoptFrame = false;
    constexpr bool GCVisitFlag = (GCVisit == GCVisitedFlag::VISITED ||
                                 GCVisit == GCVisitedFlag::HYBRID_STACK ||
                                 GCVisit == GCVisitedFlag::DEOPT);
    switch (t) {
        case FrameType::OPTIMIZED_FRAME : {
            auto frame = GetFrame<OptimizedFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::OPTIMIZED_ENTRY_FRAME : {
            auto frame = GetFrame<OptimizedEntryFrame>();
            if constexpr (GCVisitFlag) {
                optimizedReturnAddr_ = 0;
                optimizedCallSiteSp_ = 0;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::BASELINE_BUILTIN_FRAME: {
            auto frame = GetFrame<BaselineBuiltinFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = 0;
                optimizedReturnAddr_ = 0;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::ASM_BRIDGE_FRAME : {
            auto frame = GetFrame<AsmBridgeFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME: {
            auto frame = GetFrame<OptimizedJSFunctionUnfoldArgVFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = frame->GetPrevFrameSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME: {
            auto frame = GetFrame<OptimizedJSFunctionFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
            auto frame = GetFrame<OptimizedJSFunctionFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::LEAVE_FRAME : {
            auto frame = GetFrame<OptimizedLeaveFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::LEAVE_FRAME_WITH_ARGV : {
            auto frame = GetFrame<OptimizedWithArgvLeaveFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::BUILTIN_CALL_LEAVE_FRAME : {
            auto frame = GetFrame<OptimizedBuiltinLeaveFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::INTERPRETER_FRAME:
        case FrameType::INTERPRETER_FAST_NEW_FRAME : {
            auto frame = GetFrame<InterpretedFrame>();
            if constexpr (GCVisitFlag) {
                optimizedReturnAddr_ = 0;
                optimizedCallSiteSp_ = 0;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::INTERPRETER_BUILTIN_FRAME: {
            auto frame = GetFrame<InterpretedBuiltinFrame>();
            if constexpr (GCVisitFlag) {
                optimizedReturnAddr_ = 0;
                optimizedCallSiteSp_ = 0;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::INTERPRETER_CONSTRUCTOR_FRAME:
        case FrameType::ASM_INTERPRETER_FRAME : {
            auto frame = GetFrame<AsmInterpretedFrame>();
            if constexpr (GCVisitFlag) {
                optimizedReturnAddr_ = 0;
                optimizedCallSiteSp_ = 0;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::BUILTIN_FRAME : {
            auto frame = GetFrame<BuiltinFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::BUILTIN_ENTRY_FRAME : {
            auto frame = GetFrame<BuiltinFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = false;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::BUILTIN_FRAME_WITH_ARGV : {
            auto frame = GetFrame<BuiltinWithArgvFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME : {
            auto frame = GetFrame<BuiltinWithArgvFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::INTERPRETER_ENTRY_FRAME : {
            auto frame = GetFrame<InterpretedEntryFrame>();
            if constexpr (GCVisitFlag) {
                optimizedReturnAddr_ = 0;
                optimizedCallSiteSp_ = 0;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::ASM_INTERPRETER_ENTRY_FRAME : {
            auto frame = GetFrame<AsmInterpretedEntryFrame>();
            if constexpr (GCVisitFlag) {
                optimizedReturnAddr_ = 0;
                optimizedCallSiteSp_ = 0;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::ASM_INTERPRETER_BRIDGE_FRAME : {
            auto frame = GetFrame<AsmInterpretedBridgeFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::FASTJIT_FUNCTION_FRAME:
        case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
            auto frame = GetFrame<FASTJITFunctionFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
                needCheckLazyDeoptFrame = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        case FrameType::STEED_FUNCTION_FRAME: {
            auto frame = GetFrame<SteedFunctionFrame>();
            if constexpr (GCVisitFlag) {
                optimizedCallSiteSp_ = GetPrevFrameCallSiteSp();
                optimizedReturnAddr_ = frame->GetReturnAddr();
                needCalCallSiteInfo = true;
            }
            current_ = frame->GetPrevFrameFp();
            break;
        }
        default: {
            if constexpr (GCVisit == GCVisitedFlag::HYBRID_STACK) {
                current_ = nullptr;
                break;
            }
            LOG_ECMA(FATAL) << "this branch is unreachable";
            UNREACHABLE();
        }
    }

    needCheckLazyDeoptFrame &= IsLazyDeoptFrameType(rawT);
    if (UNLIKELY(needCheckLazyDeoptFrame)) {
        optimizedReturnAddr_ = const_cast<JSThread *>(thread_)->GetCallSiteReturnAddr(optimizedCallSiteSp_);
    }
    
    optimizedReturnAddr_ = RDORetAddrConvert(optimizedReturnAddr_);

    if constexpr (GCVisitFlag) {
        if (!needCalCallSiteInfo) {
            return;
        }
        uint64_t textStart = 0;
        AOTFileInfo::CallSiteInfo callSiteInfo;
        if constexpr (GCVisit == GCVisitedFlag::DEOPT) {
            std::tie(callSiteInfo, isJITFrame_) = CalCallSiteInfo(optimizedReturnAddr_, true);
        } else {
            std::tie(callSiteInfo, isJITFrame_) = CalCallSiteInfo(optimizedReturnAddr_, false);
        }
        std::tie(textStart, stackMapAddr_, fpDeltaPrevFrameSp_, calleeRegInfo_) = callSiteInfo;
        ASSERT(optimizedReturnAddr_ >= textStart);
        optimizedReturnAddr_ = optimizedReturnAddr_ - textStart;
    }
}
template void FrameIterator::Advance<GCVisitedFlag::VISITED>();
template void FrameIterator::Advance<GCVisitedFlag::IGNORED>();
template void FrameIterator::Advance<GCVisitedFlag::HYBRID_STACK>();
template void FrameIterator::Advance<GCVisitedFlag::DEOPT>();

uintptr_t *FrameIterator::GetReturnAddrAddress() const
{
    FrameType type = GetFrameType();
    switch (type) {
        case FrameType::OPTIMIZED_FRAME : {
            auto frame = GetFrame<OptimizedFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        case FrameType::BASELINE_BUILTIN_FRAME: {
            auto frame = GetFrame<BaselineBuiltinFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        case FrameType::ASM_BRIDGE_FRAME : {
            auto frame = GetFrame<AsmBridgeFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME: {
            auto frame = GetFrame<OptimizedJSFunctionUnfoldArgVFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME:
        case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
            auto frame = GetFrame<OptimizedJSFunctionFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        case FrameType::LEAVE_FRAME : {
            auto frame = GetFrame<OptimizedLeaveFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        case FrameType::LEAVE_FRAME_WITH_ARGV : {
            auto frame = GetFrame<OptimizedWithArgvLeaveFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        case FrameType::BUILTIN_CALL_LEAVE_FRAME : {
            auto frame = GetFrame<OptimizedBuiltinLeaveFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        case FrameType::BUILTIN_FRAME:
        case FrameType::BUILTIN_ENTRY_FRAME : {
            auto frame = GetFrame<BuiltinFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        case FrameType::BUILTIN_FRAME_WITH_ARGV :
        case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME : {
            auto frame = GetFrame<BuiltinWithArgvFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        case FrameType::ASM_INTERPRETER_BRIDGE_FRAME : {
            auto frame = GetFrame<AsmInterpretedBridgeFrame>();
            return const_cast<uintptr_t *>(frame->GetReturnAddrAddress());
        }
        default:
            break;
    }
    return nullptr;
}

uintptr_t FrameIterator::GetPrevFrameCallSiteSp() const
{
    if (Done()) {
        return 0;
    }
    auto type = GetFrameType();
    switch (type) {
        case FrameType::LEAVE_FRAME: {
            auto frame = GetFrame<OptimizedLeaveFrame>();
            return frame->GetCallSiteSp();
        }
        case FrameType::LEAVE_FRAME_WITH_ARGV: {
            auto frame = GetFrame<OptimizedWithArgvLeaveFrame>();
            return frame->GetCallSiteSp();
        }
        case FrameType::BUILTIN_CALL_LEAVE_FRAME: {
            auto frame = GetFrame<OptimizedBuiltinLeaveFrame>();
            return frame->GetCallSiteSp();
        }
        case FrameType::BUILTIN_FRAME_WITH_ARGV: {
            auto frame = GetFrame<BuiltinWithArgvFrame>();
            return frame->GetCallSiteSp();
        }
        case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME: {
            auto frame = GetFrame<BuiltinWithArgvFrame>();
            return frame->GetCallSiteSp();
        }
        case FrameType::BUILTIN_FRAME: {
            auto frame = GetFrame<BuiltinFrame>();
            return frame->GetCallSiteSp();
        }
        case FrameType::ASM_INTERPRETER_BRIDGE_FRAME: {
            auto frame = GetFrame<AsmInterpretedBridgeFrame>();
            return frame->GetCallSiteSp();
        }
        case FrameType::OPTIMIZED_FRAME:
        case FrameType::BASELINE_BUILTIN_FRAME:  // maybe we can store fpDelta somewhere else for these 2 cases
        case FrameType::FASTJIT_FUNCTION_FRAME:
        case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME:
        case FrameType::STEED_FUNCTION_FRAME: {
            ASSERT(thread_ != nullptr);
            auto callSiteSp = reinterpret_cast<uintptr_t>(current_) + fpDeltaPrevFrameSp_;
            return callSiteSp;
        }
        case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
            ASSERT(thread_ != nullptr);
            auto method = CheckAndGetMethod();
            ASSERT(method != nullptr);
            int delta = ComputeDelta(method);
            auto callSiteSp = reinterpret_cast<uintptr_t>(current_) + delta;
            return callSiteSp;
        }
        case FrameType::ASM_BRIDGE_FRAME: {
            auto frame = GetFrame<AsmBridgeFrame>();
            return frame->GetCallSiteSp();
        }
        case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME: {
            auto frame = GetFrame<OptimizedJSFunctionUnfoldArgVFrame>();
            return frame->GetPrevFrameSp();
        }
        case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME : {
            auto callSiteSp = OptimizedJSFunctionFrame::ComputeArgsConfigFrameSp(current_);
            return callSiteSp;
        }
        case FrameType::BUILTIN_ENTRY_FRAME:
        case FrameType::ASM_INTERPRETER_FRAME:
        case FrameType::INTERPRETER_CONSTRUCTOR_FRAME:
        case FrameType::INTERPRETER_FRAME:
        case FrameType::INTERPRETER_FAST_NEW_FRAME:
        case FrameType::OPTIMIZED_ENTRY_FRAME:
        case FrameType::INTERPRETER_BUILTIN_FRAME:
        case FrameType::INTERPRETER_ENTRY_FRAME:
        case FrameType::ASM_INTERPRETER_ENTRY_FRAME: {
            break;
        }
        default: {
            LOG_FULL(FATAL) << "Unknown frame type: " << static_cast<uintptr_t>(type);
        }
    }
    return 0;
}

uint32_t FrameIterator::GetBytecodeOffset() const
{
    FrameType type = this->GetFrameType();
    switch (type) {
        case FrameType::ASM_INTERPRETER_FRAME:
        case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
            auto *frame = this->GetFrame<AsmInterpretedFrame>();
            Method *method = ECMAObject::Cast(frame->function.GetTaggedObject())->GetCallTarget(thread_);
            auto offset = frame->GetPc() - method->GetBytecodeArray();
            return static_cast<uint32_t>(offset);
        }
        case FrameType::INTERPRETER_FRAME:
        case FrameType::INTERPRETER_FAST_NEW_FRAME: {
            auto *frame = this->GetFrame<InterpretedFrame>();
            Method *method = ECMAObject::Cast(frame->function.GetTaggedObject())->GetCallTarget(thread_);
            auto offset = frame->GetPc() - method->GetBytecodeArray();
            return static_cast<uint32_t>(offset);
        }
        case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
            [[fallthrough]];
        }
        case FrameType::FASTJIT_FUNCTION_FRAME:
        case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME:
        case FrameType::STEED_FUNCTION_FRAME: {
            [[fallthrough]];
        }
        default: {
            break;
        }
    }
    return 0;
}

void FrameIterator::GetStackTraceInfos(std::vector<std::pair<JSTaggedType, uint32_t>> &stackTraceInfos,
                                       bool needBaselineSpecialHandling, uint32_t pcOffset) const
{
    FrameType type = this->GetFrameType();
    switch (type) {
        case FrameType::ASM_INTERPRETER_FRAME:
        case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
            auto *frame = this->GetFrame<AsmInterpretedFrame>();
            uint32_t offset;
            if (UNLIKELY(needBaselineSpecialHandling)) {
                offset = pcOffset;
            } else {
                Method *method = ECMAObject::Cast(frame->function.GetTaggedObject())->GetCallTarget(thread_);
                offset = frame->GetPc() - method->GetBytecodeArray();
            }
            stackTraceInfos.push_back(std::make_pair(frame->function.GetRawData(), static_cast<uint32_t>(offset)));
            break;
        }
        case FrameType::INTERPRETER_FRAME:
        case FrameType::INTERPRETER_FAST_NEW_FRAME: {
            auto *frame = this->GetFrame<InterpretedFrame>();
            Method *method = ECMAObject::Cast(frame->function.GetTaggedObject())->GetCallTarget(thread_);
            auto offset = frame->GetPc() - method->GetBytecodeArray();
            stackTraceInfos.push_back(std::make_pair(frame->function.GetRawData(), static_cast<uint32_t>(offset)));
            break;
        }
        case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_FRAME:
        case FrameType::FASTJIT_FUNCTION_FRAME:
        case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
            CollectStackTraceInfos(stackTraceInfos);
            break;
        }
        case FrameType::STEED_FUNCTION_FRAME: {
            auto *frame = this->GetFrame<SteedFunctionFrame>();
            // to do: Fix offset
            stackTraceInfos.push_back(std::make_pair(frame->GetFunction().GetRawData(), 0));
            break;
        }
        default: {
            break;
        }
    }
}

void FrameIterator::CollectStackTraceInfos(std::vector<std::pair<JSTaggedType, uint32_t>> &info) const
{
    uintptr_t callsiteSp = GetCallSiteSp();
    uintptr_t callsiteFp = reinterpret_cast<uintptr_t>(GetSp());
    arkStackMapParser_->CollectStackTraceInfos(optimizedReturnAddr_, info, callsiteSp, callsiteFp, stackMapAddr_);
}

size_t FrameIterator::GetInlineDepth() const
{
    return arkStackMapParser_->GetInlineDepth(optimizedReturnAddr_, stackMapAddr_);
}

uintptr_t FrameIterator::GetPrevFrame() const
{
    FrameType type = GetFrameType();
    uintptr_t end = 0U;
    switch (type) {
        case FrameType::INTERPRETER_FRAME:
        case FrameType::INTERPRETER_FAST_NEW_FRAME: {
            auto prevFrame = GetFrame<InterpretedFrame>();
            end = ToUintPtr(prevFrame);
            break;
        }
        case FrameType::INTERPRETER_ENTRY_FRAME: {
            auto prevFrame = GetFrame<InterpretedEntryFrame>();
            end = ToUintPtr(prevFrame);
            break;
        }
        case FrameType::INTERPRETER_BUILTIN_FRAME: {
            auto prevFrame = GetFrame<InterpretedBuiltinFrame>();
            end = ToUintPtr(prevFrame);
            break;
        }
        default: {
            LOG_FULL(FATAL) << "Unknown frame type: " << static_cast<uintptr_t>(type);
        }
    }
    return end;
}

bool FrameIterator::IteratorStackMapAndDeopt(RootVisitor &visitor) const
{
    ASSERT(arkStackMapParser_ != nullptr);
    if (!stackMapAddr_) {  // enter by assembler, no stack map
        return true;
    }
    // ark steed reuse StackMap addr to store safe point table
    if (IsSteedFunctionFrame()) {
        return true;
    }
    return arkStackMapParser_->IteratorStackMapAndDeopt(visitor, optimizedReturnAddr_,
        reinterpret_cast<uintptr_t>(current_), optimizedCallSiteSp_, stackMapAddr_);
}

ARK_INLINE void OptimizedFrame::GCIterate(const FrameIterator &it, RootVisitor &visitor) const
{
    bool ret = it.IteratorStackMapAndDeopt(visitor);
    if (!ret) {
#ifndef NDEBUG
        LOG_ECMA(DEBUG) << " stackmap don't found returnAddr " << it.GetOptimizedReturnAddr();
#endif
    }
}

ARK_INLINE void BaselineBuiltinFrame::GCIterate(const FrameIterator &it, RootVisitor &visitor) const
{
    bool ret = it.IteratorStackMapAndDeopt(visitor);
    if (!ret) {
#ifndef NDEBUG
        LOG_ECMA(DEBUG) << " stackmap don't found returnAddr " << it.GetOptimizedReturnAddr();
#endif
    }
}

void FrameIterator::CollectArkDeopt(std::vector<kungfu::ARKDeopt>& deopts) const
{
    arkStackMapParser_->GetArkDeopt(optimizedReturnAddr_, stackMapAddr_, deopts);
}

ARK_INLINE JSTaggedType* OptimizedJSFunctionFrame::GetArgv(const FrameIterator &it) const
{
    uintptr_t *preFrameSp = ComputePrevFrameSp(it);
    return GetArgv(preFrameSp);
}

ARK_INLINE uintptr_t* OptimizedJSFunctionFrame::ComputePrevFrameSp(const FrameIterator &it) const
{
    const JSTaggedType *sp = it.GetSp();
    Method *method = it.CheckAndGetMethod();
    ASSERT(method != nullptr);
    int delta = it.ComputeDelta(method);
    ASSERT((delta > 0) && (delta % sizeof(uintptr_t) == 0));
    uintptr_t *preFrameSp = reinterpret_cast<uintptr_t *>(const_cast<JSTaggedType *>(sp)) + delta / sizeof(uintptr_t);
    return preFrameSp;
}

ARK_INLINE void OptimizedJSFunctionFrame::GCIterate(const FrameIterator &it,
    RootVisitor &visitor, FrameType frameType) const
{
    OptimizedJSFunctionFrame *frame = OptimizedJSFunctionFrame::GetFrameFromSp(it.GetSp());
    uintptr_t *jsFuncPtr = reinterpret_cast<uintptr_t *>(frame);
    uintptr_t jsFuncSlot = ToUintPtr(jsFuncPtr);
    visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(jsFuncSlot));
    if (frameType == FrameType::OPTIMIZED_JS_FUNCTION_FRAME) {
        uintptr_t *preFrameSp = frame->ComputePrevFrameSp(it);
        auto argc = frame->GetArgc(preFrameSp);
        JSTaggedType *argv = frame->GetArgv(reinterpret_cast<uintptr_t *>(preFrameSp));
        if (argc > 0) {
            uintptr_t start = ToUintPtr(argv); // argv
            uintptr_t end = ToUintPtr(argv + argc);
            visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
        }
    }

    auto machineCodeSlot = ObjectSlot(ToUintPtr(it.GetMachineCodeSlot()));
    if (machineCodeSlot.GetTaggedType() != JSTaggedValue::VALUE_UNDEFINED) {
        MachineCode *machineCode = MachineCode::Cast(JSTaggedValue(machineCodeSlot.GetTaggedType()).GetTaggedObject());
        LOG_JIT(INFO) << "OptimizedJSFunctionFrame::GCIterate function addr: " << reinterpret_cast<void *>(*jsFuncPtr)
                      << ", machine code addr: " << machineCode
                      << ", text begin: " << std::hex << machineCode->GetText()
                      << ", text size: " << machineCode->GetTextSize()
                      << ", visitor addr: " << reinterpret_cast<void *>(&visitor);
        visitor.VisitRoot(Root::ROOT_FRAME, machineCodeSlot);
    }

    bool ret = it.IteratorStackMapAndDeopt(visitor);
    if (!ret) {
#ifndef NDEBUG
        LOG_ECMA(DEBUG) << " stackmap don't found returnAddr " << it.GetOptimizedReturnAddr();
#endif
    }
}

void OptimizedJSFunctionFrame::GetDeoptBundleInfo(const FrameIterator &it, std::vector<kungfu::ARKDeopt>& deopts) const
{
    it.CollectArkDeopt(deopts);
}

void OptimizedJSFunctionFrame::GetFuncCalleeRegAndOffset(
    const FrameIterator &it, kungfu::CalleeRegAndOffsetVec &ret) const
{
    it.GetCalleeRegAndOffsetVec(ret);
}

ARK_INLINE JSTaggedType* FASTJITFunctionFrame::GetArgv(const FrameIterator &it) const
{
    uintptr_t *preFrameSp = ComputePrevFrameSp(it);
    return GetArgv(preFrameSp);
}

ARK_INLINE uintptr_t* FASTJITFunctionFrame::ComputePrevFrameSp(const FrameIterator &it) const
{
    const JSTaggedType *sp = it.GetSp();
    int delta = it.ComputeDelta();
    ASSERT((delta > 0) && (delta % sizeof(uintptr_t) == 0));
    uintptr_t *preFrameSp = reinterpret_cast<uintptr_t *>(const_cast<JSTaggedType *>(sp)) + delta / sizeof(uintptr_t);
    return preFrameSp;
}

ARK_INLINE void FASTJITFunctionFrame::GCIterate(const FrameIterator &it,
    RootVisitor &visitor, FrameType frameType) const
{
    FASTJITFunctionFrame *frame = FASTJITFunctionFrame::GetFrameFromSp(it.GetSp());
    uintptr_t jsFuncSlot = GetFuncAddrFromSp(it.GetSp());
    visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(jsFuncSlot));
    if (frameType == FrameType::FASTJIT_FUNCTION_FRAME) {
        uintptr_t *preFrameSp = frame->ComputePrevFrameSp(it);
        auto argc = frame->GetArgc(preFrameSp);
        JSTaggedType *argv = frame->GetArgv(reinterpret_cast<uintptr_t *>(preFrameSp));
        if (argc > 0) {
            uintptr_t start = ToUintPtr(argv); // argv
            uintptr_t end = ToUintPtr(argv + argc);
            visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
        }
    }

    auto machineCodeSlot = ObjectSlot(ToUintPtr(it.GetMachineCodeSlot()));
    if (machineCodeSlot.GetTaggedType() != JSTaggedValue::VALUE_UNDEFINED) {
        visitor.VisitRoot(Root::ROOT_FRAME, machineCodeSlot);
    }

    bool ret = it.IteratorStackMapAndDeopt(visitor);
    if (!ret) {
#ifndef NDEBUG
        LOG_ECMA(DEBUG) << " stackmap don't found returnAddr " << it.GetOptimizedReturnAddr();
#endif
    }
}

void FASTJITFunctionFrame::GetDeoptBundleInfo(const FrameIterator &it, std::vector<kungfu::ARKDeopt>& deopts) const
{
    it.CollectArkDeopt(deopts);
}

void FASTJITFunctionFrame::GetFuncCalleeRegAndOffset(
    const FrameIterator &it, kungfu::CalleeRegAndOffsetVec &ret) const
{
    it.GetCalleeRegAndOffsetVec(ret);
}

ARK_INLINE uintptr_t* SteedFunctionFrame::ComputePrevFrameSp(const FrameIterator &it) const
{
    const JSTaggedType *sp = it.GetSp();
    int delta = it.ComputeDelta();
    ASSERT((delta > 0) && (delta % sizeof(uintptr_t) == 0));
    uintptr_t *preFrameSp = reinterpret_cast<uintptr_t *>(const_cast<JSTaggedType *>(sp)) +
        delta / sizeof(uintptr_t);
    return preFrameSp;
}

ARK_INLINE JSTaggedType* SteedFunctionFrame::GetArgv(const FrameIterator &it) const
{
    uintptr_t *preFrameSp = ComputePrevFrameSp(it);
    return GetArgv(preFrameSp);
}

ARK_INLINE void SteedFunctionFrame::GCIterate(const FrameIterator &it,
    RootVisitor &visitor, [[maybe_unused]] FrameType frameType) const
{
#if ECMASCRIPT_ENABLE_ARK_STEED
    SteedFunctionFrame *frame = GetFrameFromSp(it.GetSp());

    // 1. Visit fixed frame tagged roots: jsFunc, lexicalEnv
    uintptr_t jsFuncSlot = GetFuncAddrFromSp(it.GetSp());
    visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(jsFuncSlot));
    uintptr_t lexEnvSlot = reinterpret_cast<uintptr_t>(&frame->lexicalEnv);
    visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(lexEnvSlot));

    // 2. Visit caller arguments (always tagged)
    uintptr_t *preFrameSp = frame->ComputePrevFrameSp(it);
    auto argc = frame->GetArgc(preFrameSp);
    JSTaggedType *argv = frame->GetArgv(reinterpret_cast<uintptr_t *>(preFrameSp));
    if (argc > 0) {
        uintptr_t start = ToUintPtr(argv);
        uintptr_t end = ToUintPtr(argv + argc);
        visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
    }

    // 3. Visit MachineCode object reference
    auto machineCodeSlot = ObjectSlot(ToUintPtr(it.GetMachineCodeSlot()));
    if (machineCodeSlot.GetTaggedType() != JSTaggedValue::VALUE_UNDEFINED) {
        visitor.VisitRoot(Root::ROOT_FRAME, machineCodeSlot);
    }

    // 4. Visit tagged stack slots using safepoint table
    // The safepoint table header stores num_tagged_slots/num_untagged_slots.
    // Tagged slots are located below the fixed frame header (FP - header_size - slot_offset).
    // For now, use the stackmap/safepoint path from FrameIterator (same as FASTJIT).
    bool ret = it.IteratorStackMapAndDeopt(visitor);
    if (!ret) {
#ifndef NDEBUG
        LOG_ECMA(DEBUG) << " stackmap don't found returnAddr " << it.GetOptimizedReturnAddr();
#endif
    }
    // 5. Visit SafePointTable
    IterateSafePointTable(it, visitor);
#else
    LOG_ECMA(FATAL) << "ark steed disabled, this branch is unreachable!";
#endif
}

#if ECMASCRIPT_ENABLE_ARK_STEED
void SteedFunctionFrame::IterateSafePointTable(const FrameIterator& it, RootVisitor& visitor) const
{
    auto machineCodeSlot = ObjectSlot(ToUintPtr(it.GetMachineCodeSlot()));
    MachineCode *machineCode = MachineCode::Cast(JSTaggedValue(machineCodeSlot.GetTaggedType()).GetTaggedObject());
    uint8_t *safepointTableAddr = machineCode->GetStackMapOrOffsetTableAddress();
    if (safepointTableAddr == nullptr) {
        LOG_ECMA(ERROR) << "safepointTableAddr is nullptr";
        return;
    }
    uint32_t safepointTableSize = machineCode->GetStackMapOrOffsetTableSize();
    if (safepointTableSize == 0) {
        LOG_ECMA(ERROR) << "safepointTableSize is 0";
    }
    arksteed::ArkSteedSafepointTable safepointTable(safepointTableAddr, safepointTableSize);
    if (!safepointTable.IsValid()) {
        LOG_ECMA(ERROR) << "ArkSteedSafepointTable is InValid";
        return;
    }
    // to do: refactor
    uintptr_t fp = reinterpret_cast<uintptr_t>(&GetFrameFromSp(it.GetSp())->prevFp);
    constexpr int32_t kTaggedSlot0OffsetFromFp = -4 * static_cast<int32_t>(sizeof(uintptr_t));
    uint32_t numTaggedSlots = safepointTable.GetNumTaggedSlots();
    for (uint32_t i = 0; i < numTaggedSlots; ++i) {
        intptr_t slotAddr = static_cast<intptr_t>(fp) + kTaggedSlot0OffsetFromFp -
                            static_cast<intptr_t>(i * sizeof(uintptr_t));
        ObjectSlot slot(static_cast<uintptr_t>(slotAddr));
        visitor.VisitRoot(Root::ROOT_FRAME, slot);
    }

    uintptr_t returnAddr = it.GetOptimizedReturnAddr();
    uintptr_t textStart = machineCode->GetText();
    if (returnAddr < textStart) {
        return;
    }
    uint32_t pcOffset = static_cast<uint32_t>(returnAddr - textStart);
    const auto *entry = safepointTable.FindEntry(pcOffset);
    if (entry == nullptr || entry->pcOffset != pcOffset) {
        return;
    }
    uintptr_t start = it.GetCallSiteSp();
    intptr_t end = static_cast<intptr_t>(fp) + kTaggedSlot0OffsetFromFp -
                   static_cast<intptr_t>(safepointTable.GetNumTaggedSlots() + safepointTable.GetNumUntaggedSlots() +
                                         entry->numExtraSpillSlots) *
                   static_cast<intptr_t>(sizeof(uintptr_t));
    if (start >= static_cast<uintptr_t>(end)) {
        return;
    }
    visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(static_cast<uintptr_t>(end)));
}
#endif

void SteedFunctionFrame::GetDeoptBundleInfo(const FrameIterator &it,
    std::vector<kungfu::ARKDeopt>& deopts) const
{
    it.CollectArkDeopt(deopts);
}

void SteedFunctionFrame::GetFuncCalleeRegAndOffset(
    const FrameIterator &it, kungfu::CalleeRegAndOffsetVec &ret) const
{
    it.GetCalleeRegAndOffsetVec(ret);
}

ARK_INLINE void AsmInterpretedFrame::GCIterate(const FrameIterator &it,
    RootVisitor &visitor, bool isBaselineFrame) const
{
    AsmInterpretedFrame *frame = AsmInterpretedFrame::GetFrameFromSp(it.GetSp());
    uintptr_t start = ToUintPtr(it.GetSp());
    uintptr_t end = ToUintPtr(frame->GetCurrentFramePointer());
    visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
    visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->function)));
    visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->thisObj)));
    if (frame->pc != nullptr || isBaselineFrame) {
        visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->acc)));
        visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->env)));
    }

    if (isBaselineFrame) {
        return;
    }
    bool ret = it.IteratorStackMapAndDeopt(visitor);
    if (!ret) {
#ifndef NDEBUG
        LOG_ECMA(DEBUG) << " stackmap don't found returnAddr " << it.GetOptimizedReturnAddr();
#endif
    }
}

ARK_INLINE void InterpretedFrame::GCIterate(const FrameIterator &it, RootVisitor &visitor) const
{
    auto sp = it.GetSp();
    InterpretedFrame *frame = InterpretedFrame::GetFrameFromSp(sp);
    if (frame->function.IsHole()) {
        return;
    }

    JSTaggedType *prevSp = frame->GetPrevFrameFp();
    uintptr_t start = ToUintPtr(sp);
    const JSThread *thread = it.GetThread();
    FrameIterator prevIt(prevSp, thread);
    uintptr_t end = prevIt.GetPrevFrame();

    visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
    visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->function)));
    visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->thisObj)));

    // pc == nullptr, init InterpretedFrame & native InterpretedFrame.
    if (frame->pc != nullptr) {
        visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->acc)));
        visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->constpool)));
        visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->env)));
        visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->profileTypeInfo)));
    }
}

ARK_INLINE void InterpretedBuiltinFrame::GCIterate(const FrameIterator &it, RootVisitor &visitor) const
{
    auto sp = it.GetSp();
    InterpretedBuiltinFrame *frame = InterpretedBuiltinFrame::GetFrameFromSp(sp);
    JSTaggedType *prevSp = frame->GetPrevFrameFp();
    const JSThread *thread = it.GetThread();
    FrameIterator prevIt(prevSp, thread);

    uintptr_t start = ToUintPtr(sp + 2); // 2: numArgs & thread.
    uintptr_t end = prevIt.GetPrevFrame();
    visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
    visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&frame->function)));
}

ARK_INLINE void OptimizedLeaveFrame::GCIterate(const FrameIterator &it, RootVisitor &visitor) const
{
    const JSTaggedType *sp = it.GetSp();
    OptimizedLeaveFrame *frame = OptimizedLeaveFrame::GetFrameFromSp(sp);
    if (frame->argc > 0) {
        JSTaggedType *argv = reinterpret_cast<JSTaggedType *>(&frame->argc + 1);
        uintptr_t start = ToUintPtr(argv); // argv
        uintptr_t end = ToUintPtr(argv + frame->argc);
        visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
    }
}

ARK_INLINE void OptimizedWithArgvLeaveFrame::GCIterate(const FrameIterator &it, RootVisitor &visitor) const
{
    const JSTaggedType *sp = it.GetSp();
    OptimizedWithArgvLeaveFrame *frame = OptimizedWithArgvLeaveFrame::GetFrameFromSp(sp);
    if (frame->argc > 0) {
        uintptr_t* argvPtr = reinterpret_cast<uintptr_t *>(&frame->argc + 1);
        JSTaggedType *argv = reinterpret_cast<JSTaggedType *>(*argvPtr);
        uintptr_t start = ToUintPtr(argv); // argv
        uintptr_t end = ToUintPtr(argv + frame->argc);
        visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
    }
}

ARK_INLINE void OptimizedBuiltinLeaveFrame::GCIterate(const FrameIterator &it, RootVisitor &visitor) const
{
    const JSTaggedType *sp = it.GetSp();
    OptimizedBuiltinLeaveFrame *frame = OptimizedBuiltinLeaveFrame::GetFrameFromSp(sp);
    if (frame->argc > 0) {
        JSTaggedType *argv = reinterpret_cast<JSTaggedType *>(&frame->argc + 1);
        uintptr_t start = ToUintPtr(argv); // argv
        uintptr_t end = ToUintPtr(argv + frame->argc);
        visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
    }
}

ARK_INLINE void BuiltinWithArgvFrame::GCIterate(const FrameIterator &it, RootVisitor &visitor) const
{
    const JSTaggedType *sp = it.GetSp();
    auto frame = BuiltinWithArgvFrame::GetFrameFromSp(sp);
    auto argc = static_cast<uint32_t>(frame->GetNumArgs()) + NUM_MANDATORY_JSFUNC_ARGS;
    JSTaggedType *argv = reinterpret_cast<JSTaggedType *>(frame->GetStackArgsAddress());
    uintptr_t start = ToUintPtr(argv);
    uintptr_t end = ToUintPtr(argv + argc);
    visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
}

ARK_INLINE void BuiltinFrame::GCIterate(const FrameIterator &it, RootVisitor &visitor) const
{
    const JSTaggedType *sp = it.GetSp();
    auto frameType = it.GetFrameType();
    auto frame = BuiltinFrame::GetFrameFromSp(sp);
    // no need to visit stack map for entry frame
    if (frameType == FrameType::BUILTIN_ENTRY_FRAME) {
        // only visit function
        visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(frame->GetStackArgsAddress()));
        return;
    }
    JSTaggedType *argv = reinterpret_cast<JSTaggedType *>(frame->GetStackArgsAddress());
    auto argc = frame->GetNumArgs();
    uintptr_t start = ToUintPtr(argv);
    uintptr_t end = ToUintPtr(argv + argc);
    visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
}

ARK_INLINE void InterpretedEntryFrame::GCIterate(const FrameIterator &it, RootVisitor &visitor) const
{
    const JSTaggedType* sp = it.GetSp();
    InterpretedEntryFrame *frame = InterpretedEntryFrame::GetFrameFromSp(sp);
    JSTaggedType *prevSp = frame->GetPrevFrameFp();
    if (prevSp == nullptr) {
        return;
    }

    const JSThread *thread = it.GetThread();
    FrameIterator prevIt(prevSp, thread);
    uintptr_t start = ToUintPtr(sp + 2); // 2: numArgs & thread.
    uintptr_t end = prevIt.GetPrevFrame();
    visitor.VisitRangeRoot(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end));
}
}  // namespace panda::ecmascript