/* * Copyright (c) 2021-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/interpreter/frame_handler.h" #include "ecmascript/jspandafile/program_object.h" namespace panda::ecmascript { FrameHandler::FrameHandler(const JSThread *thread) : sp_(const_cast(thread->GetCurrentFrame())), thread_(thread) { AdvanceToJSFrame(); } ARK_INLINE void FrameHandler::AdvanceToJSFrame() { if (!thread_->IsAsmInterpreter()) { return; } FrameIterator it(sp_, thread_); for (; !it.Done(); it.Advance()) { FrameType t = it.GetFrameType(); if (IsBaselineBuiltinFrame(t)) { FindAndSetBaselineNativePc(it); } if (IsJSFrame(t) || IsJSEntryFrame(t)) { break; } } sp_ = it.GetSp(); } ARK_INLINE void FrameHandler::PrevJSFrame() { if (!thread_->IsAsmInterpreter()) { FrameIterator it(sp_, thread_); if (IsBaselineBuiltinFrame(it.GetFrameType())) { FindAndSetBaselineNativePc(it); } it.Advance(); sp_ = it.GetSp(); return; } AdvanceToJSFrame(); FrameIterator it(sp_, thread_); FrameType t = it.GetFrameType(); if (t == FrameType::ASM_INTERPRETER_FRAME) { auto frame = it.GetFrame(); if (thread_->IsAsmInterpreter()) { fp_ = frame->GetCurrentFramePointer(); } } it.Advance(); sp_ = it.GetSp(); AdvanceToJSFrame(); } JSTaggedType* FrameHandler::GetPrevJSFrame() { PrevJSFrame(); return GetSp(); } uint32_t FrameHandler::GetNumberArgs() { if (thread_->IsAsmInterpreter()) { auto *frame = AsmInterpretedFrame::GetFrameFromSp(sp_); return static_cast(frame->GetCurrentFramePointer() - sp_); } ASSERT(IsInterpretedFrame()); JSTaggedType *prevSp = nullptr; FrameIterator it(sp_, thread_); if (IsAsmInterpretedFrame()) { auto *frame = it.GetFrame(); prevSp = frame->GetPrevFrameFp(); } else { auto *frame = it.GetFrame(); prevSp = frame->GetPrevFrameFp(); } auto prevSpEnd = reinterpret_cast(GetInterpretedFrameEnd(prevSp)); return static_cast(prevSpEnd - sp_); } JSTaggedValue FrameHandler::GetVRegValue(size_t index) const { ASSERT(IsInterpretedFrame()); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) return JSTaggedValue(sp_[index]); } void FrameHandler::SetVRegValue(size_t index, JSTaggedValue value) { ASSERT(IsInterpretedFrame()); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) sp_[index] = value.GetRawData(); } void FrameHandler::FindAndSetBaselineNativePc(FrameIterator it) { ASSERT(IsBaselineBuiltinFrame(it.GetFrameType())); auto *frame = it.GetFrame(); baselineNativePc_ = frame->GetReturnAddr(); } JSTaggedValue FrameHandler::GetAcc() const { ASSERT(IsInterpretedFrame()); FrameIterator it(sp_, thread_); if (IsAsmInterpretedFrame()) { auto *frame = it.GetFrame(); return frame->acc; } else { auto *frame = it.GetFrame(); return frame->acc; } } uint32_t FrameHandler::GetBytecodeOffset() const { ASSERT(IsInterpretedFrame()); Method *method = GetMethod(); auto offset = GetPc() - method->GetBytecodeArray(); return static_cast(offset); } Method *FrameHandler::GetMethod() const { ASSERT(IsJSFrame()); auto function = GetFunction(); return ECMAObject::Cast(function.GetTaggedObject())->GetCallTarget(); } const JSPandaFile* FrameHandler::GetJSPandaFile() const { auto method = GetMethod(); return method->GetJSPandaFile(); } std::string FrameHandler::GetFileName() const { auto pandaFile = GetJSPandaFile(); ASSERT(pandaFile != nullptr); return pandaFile->GetJSPandaFileDesc().c_str(); } uint32_t FrameHandler::GetAbcId() const { std::string abcName = GetFileName(); pgo::PGOProfilerManager* pm = pgo::PGOProfilerManager::GetInstance(); uint32_t abcId; if (!pm->GetPandaFileId(CString(abcName), abcId) && !abcName.empty()) { LOG_ECMA(ERROR) << "Get method abc id failed. abcName: " << abcName; } return abcId; } uint32_t FrameHandler::GetMethodId() const { return GetMethod()->GetMethodId().GetOffset(); } Method *FrameHandler::CheckAndGetMethod() const { ASSERT(IsJSFrame()); auto function = GetFunction(); if (function.IsJSFunctionBase() || function.IsJSProxy()) { return ECMAObject::Cast(function.GetTaggedObject())->GetCallTarget(); } return nullptr; } JSTaggedValue FrameHandler::GetThis() const { ASSERT(IsInterpretedFrame()); FrameIterator it(sp_, thread_); if (IsAsmInterpretedFrame()) { auto *frame = it.GetFrame(); return frame->thisObj; } else { auto *frame = it.GetFrame(); return frame->thisObj; } } JSTaggedValue FrameHandler::GetFunction() const { ASSERT(IsJSFrame()); if (thread_->IsAsmInterpreter()) { FrameType type = GetFrameType(); switch (type) { case FrameType::ASM_INTERPRETER_FRAME: case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: { auto frame = AsmInterpretedFrame::GetFrameFromSp(sp_); return frame->function; } case FrameType::BUILTIN_FRAME_WITH_ARGV: { auto *frame = BuiltinWithArgvFrame::GetFrameFromSp(sp_); return frame->GetFunction(); } case FrameType::BUILTIN_ENTRY_FRAME: case FrameType::BUILTIN_FRAME: { auto *frame = BuiltinFrame::GetFrameFromSp(sp_); return frame->GetFunction(); } case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME: case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: { auto *frame = OptimizedJSFunctionFrame::GetFrameFromSp(sp_); return frame->GetFunction(); } case FrameType::FASTJIT_FUNCTION_FRAME: case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: { auto *frame = FASTJITFunctionFrame::GetFrameFromSp(sp_); return frame->GetFunction(); } case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME : case FrameType::INTERPRETER_FRAME: case FrameType::INTERPRETER_FAST_NEW_FRAME: case FrameType::INTERPRETER_ENTRY_FRAME: case FrameType::ASM_INTERPRETER_ENTRY_FRAME: case FrameType::ASM_INTERPRETER_BRIDGE_FRAME: case FrameType::INTERPRETER_BUILTIN_FRAME: case FrameType::OPTIMIZED_FRAME: case FrameType::BASELINE_BUILTIN_FRAME: case FrameType::ASM_BRIDGE_FRAME: case FrameType::LEAVE_FRAME: case FrameType::LEAVE_FRAME_WITH_ARGV: case FrameType::BUILTIN_CALL_LEAVE_FRAME: case FrameType::OPTIMIZED_ENTRY_FRAME: default: { LOG_FULL(FATAL) << "frame type error!"; UNREACHABLE(); } } } else { FrameType type = GetFrameType(); if (type == FrameType::INTERPRETER_FRAME || type == FrameType::INTERPRETER_FAST_NEW_FRAME) { auto *frame = InterpretedFrame::GetFrameFromSp(sp_); return frame->function; } else { auto *frame = InterpretedBuiltinFrame::GetFrameFromSp(sp_); return frame->function; } } } const uint8_t *FrameHandler::GetPc() const { ASSERT(IsJSFrame()); FrameIterator it(sp_, thread_); if (IsAsmInterpretedFrame()) { auto *frame = it.GetFrame(); return frame->GetPc(); } else { auto *frame = it.GetFrame(); return frame->GetPc(); } } ConstantPool *FrameHandler::GetConstpool() const { ASSERT(IsInterpretedFrame()); auto method = GetMethod(); JSTaggedValue constpool = method->GetConstantPool(); return ConstantPool::Cast(constpool.GetTaggedObject()); } JSTaggedValue FrameHandler::GetEnv() const { ASSERT(IsInterpretedFrame()); FrameIterator it(sp_, thread_); if (IsAsmInterpretedFrame()) { auto *frame = it.GetFrame(); return frame->GetEnv(); } else { auto *frame = it.GetFrame(); return frame->env; } } void FrameHandler::DumpStack(std::ostream &os) const { size_t i = 0; FrameHandler frameHandler(thread_); for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) { os << "[" << i++ << "]:" << frameHandler.GetMethod()->ParseFunctionName() << "\n"; } } void FrameHandler::DumpPC(std::ostream &os, const uint8_t *pc) const { FrameHandler frameHandler(thread_); ASSERT(frameHandler.HasFrame()); // NOLINTNEXTLINE(cppcoreguidelines-narrowing-conversions, bugprone-narrowing-conversions) int offset = pc - frameHandler.GetMethod()->GetBytecodeArray(); os << "offset: " << offset << "\n"; } ARK_INLINE uintptr_t FrameHandler::GetInterpretedFrameEnd(JSTaggedType *prevSp) const { uintptr_t end = 0U; FrameIterator it(prevSp, thread_); FrameType type = it.GetFrameType(); switch (type) { case FrameType::ASM_INTERPRETER_FRAME: case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: { auto frame = it.GetFrame(); end = ToUintPtr(frame); break; } case FrameType::INTERPRETER_FRAME: case FrameType::INTERPRETER_FAST_NEW_FRAME: { auto frame = it.GetFrame(); end = ToUintPtr(frame); break; } case FrameType::INTERPRETER_ENTRY_FRAME: { auto frame = it.GetFrame(); end = ToUintPtr(frame); break; } case FrameType::INTERPRETER_BUILTIN_FRAME: { auto frame = it.GetFrame(); end = ToUintPtr(frame); break; } case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME : case FrameType::BUILTIN_FRAME_WITH_ARGV: case FrameType::BUILTIN_ENTRY_FRAME: case FrameType::BUILTIN_FRAME: case FrameType::OPTIMIZED_FRAME: case FrameType::ASM_BRIDGE_FRAME: case FrameType::LEAVE_FRAME: case FrameType::BASELINE_BUILTIN_FRAME: case FrameType::LEAVE_FRAME_WITH_ARGV: case FrameType::BUILTIN_CALL_LEAVE_FRAME: case FrameType::OPTIMIZED_ENTRY_FRAME: case FrameType::ASM_INTERPRETER_ENTRY_FRAME: case FrameType::ASM_INTERPRETER_BRIDGE_FRAME: default: { LOG_FULL(FATAL) << "frame type error!"; UNREACHABLE(); } } return end; } void FrameHandler::IterateAssembleStack(const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor, const RootBaseAndDerivedVisitor &derivedVisitor) { JSTaggedType *current = const_cast(thread_->GetLastLeaveFrame()); IterateFrameChain(current, visitor, rangeVisitor, derivedVisitor); } // We seperate InterpretedEntryFrame from assemble stack when asm interpreter is enable. // To protect EcmaRuntimeCallInfo on InterpretedEntryFrame, we iterate InterpretedEntryFrame on thread sp individually. // And only InterpretedEntryFrame is on thread sp when asm interpreter is enable. void FrameHandler::IterateEcmaRuntimeCallInfo(const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor) { ASSERT(thread_->IsAsmInterpreter()); JSTaggedType *current = const_cast(thread_->GetCurrentSPFrame()); for (FrameIterator it(current, thread_); !it.Done(); it.Advance()) { ASSERT(it.GetFrameType() == FrameType::INTERPRETER_ENTRY_FRAME); auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor); } } void FrameHandler::Iterate(const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor, const RootBaseAndDerivedVisitor &derivedVisitor) { if (thread_->IsAsmInterpreter()) { IterateEcmaRuntimeCallInfo(visitor, rangeVisitor); IterateAssembleStack(visitor, rangeVisitor, derivedVisitor); return; } JSTaggedType *current = const_cast(thread_->GetCurrentSPFrame()); FrameType frameType = FrameHandler::GetFrameType(current); if (frameType != FrameType::INTERPRETER_ENTRY_FRAME) { auto leaveFrame = const_cast(thread_->GetLastLeaveFrame()); if (leaveFrame != nullptr) { current = leaveFrame; } } // lazy assignment: only Iterate need arkStackMapParser_ in order to high improve performance if (arkStackMapParser_ == nullptr) { arkStackMapParser_ = const_cast(thread_)->GetEcmaVM()->GetAOTFileManager()->GetStackMapParser(); } IterateFrameChain(current, visitor, rangeVisitor, derivedVisitor); } void FrameHandler::IterateFrameChain(JSTaggedType *start, const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor, const RootBaseAndDerivedVisitor &derivedVisitor) const { JSTaggedType *current = start; // if the current frame type is BASELINE_BUILTIN_FRAME, the upper frame must be BaselineFrame. // isBaselineFrame is used to differentiate the AsmInterpterFrame and BaselineFrame bool isBaselineFrame = false; for (FrameIterator it(current, thread_); !it.Done(); it.Advance()) { FrameType type = it.GetFrameType(); switch (type) { case FrameType::OPTIMIZED_FRAME: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor, derivedVisitor); break; } case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME: case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor, derivedVisitor, type); break; } case FrameType::BASELINE_BUILTIN_FRAME: { isBaselineFrame = true; auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor, derivedVisitor); break; } case FrameType::FASTJIT_FUNCTION_FRAME: case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor, derivedVisitor, type); break; } case FrameType::ASM_INTERPRETER_FRAME: case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor, derivedVisitor, isBaselineFrame); isBaselineFrame = false; break; } case FrameType::INTERPRETER_FRAME: case FrameType::INTERPRETER_FAST_NEW_FRAME: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor); break; } case FrameType::INTERPRETER_BUILTIN_FRAME: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor); break; } case FrameType::LEAVE_FRAME: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor); break; } case FrameType::LEAVE_FRAME_WITH_ARGV: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor); break; } case FrameType::BUILTIN_CALL_LEAVE_FRAME: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor); break; } case FrameType::BUILTIN_FRAME_WITH_ARGV: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor); break; } case FrameType::BUILTIN_ENTRY_FRAME: case FrameType::BUILTIN_FRAME: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor); break; } case FrameType::INTERPRETER_ENTRY_FRAME: { auto frame = it.GetFrame(); frame->GCIterate(it, visitor, rangeVisitor); break; } case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME: case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME: case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME: case FrameType::OPTIMIZED_ENTRY_FRAME: case FrameType::ASM_BRIDGE_FRAME: case FrameType::ASM_INTERPRETER_ENTRY_FRAME: case FrameType::ASM_INTERPRETER_BRIDGE_FRAME: { break; } default: { LOG_FULL(FATAL) << "frame type error!"; UNREACHABLE(); } } } } } // namespace panda::ecmascript