Files
ark_js_runtime/ecmascript/tooling/interface/debugger_api.cpp
T
guobingbing 129ebdbf25 refactor class FrameHandler
Signed-off-by: guobingbing <guobingbing3@huawei.com>
Change-Id: I9f3ff0c04d34a31a34f21da637689eb61d4166f1
2022-05-04 10:36:44 +08:00

328 lines
12 KiB
C++

/*
* Copyright (c) 2021-2022 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/tooling/interface/debugger_api.h"
#include "ecmascript/base/number_helper.h"
#include "ecmascript/interpreter/frame_handler.h"
#include "ecmascript/jspandafile/js_pandafile_executor.h"
#include "ecmascript/jspandafile/program_object.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_method.h"
#include "ecmascript/jspandafile/js_pandafile_manager.h"
#include "ecmascript/mem/c_string.h"
#include "ecmascript/napi/jsnapi_helper.h"
#include "ecmascript/tooling/interface/js_debugger.h"
namespace panda::ecmascript::tooling {
using panda::ecmascript::base::ALLOW_BINARY;
using panda::ecmascript::base::ALLOW_HEX;
using panda::ecmascript::base::ALLOW_OCTAL;
using panda::ecmascript::base::NumberHelper;
// JSPandaFileExecutor
Local<JSValueRef> DebuggerApi::Execute(const EcmaVM *ecmaVm, const void *buffer, size_t size,
std::string_view entryPoint)
{
JSThread *thread = ecmaVm->GetJSThread();
auto result = JSPandaFileExecutor::ExecuteFromBuffer(thread, buffer, size, entryPoint);
if (!result) {
return JSValueRef::Undefined(ecmaVm);
}
return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, result.Value()));
}
// FrameHandler
uint32_t DebuggerApi::GetStackDepth(const EcmaVM *ecmaVm)
{
uint32_t count = 0;
FrameHandler frameHandler(ecmaVm->GetJSThread());
for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) {
if (frameHandler.IsEntryFrame()) {
continue;
}
++count;
}
return count;
}
bool DebuggerApi::StackWalker(const EcmaVM *ecmaVm, std::function<StackState(const FrameHandler *)> func)
{
FrameHandler frameHandler(ecmaVm->GetJSThread());
for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) {
if (frameHandler.IsEntryFrame()) {
continue;
}
StackState state = func(&frameHandler);
if (state == StackState::CONTINUE) {
continue;
}
if (state == StackState::FAILED) {
return false;
}
return true;
}
return true;
}
uint32_t DebuggerApi::GetBytecodeOffset(const EcmaVM *ecmaVm)
{
return FrameHandler(ecmaVm->GetJSThread()).GetBytecodeOffset();
}
JSMethod *DebuggerApi::GetMethod(const EcmaVM *ecmaVm)
{
return FrameHandler(ecmaVm->GetJSThread()).GetMethod();
}
Local<JSValueRef> DebuggerApi::GetVRegValue(const EcmaVM *ecmaVm, size_t index)
{
auto value = FrameHandler(ecmaVm->GetJSThread()).GetVRegValue(index);
JSHandle<JSTaggedValue> handledValue(ecmaVm->GetJSThread(), value);
return JSNApiHelper::ToLocal<JSValueRef>(handledValue);
}
void DebuggerApi::SetVRegValue(const EcmaVM *ecmaVm, size_t index, Local<JSValueRef> value)
{
return FrameHandler(ecmaVm->GetJSThread()).SetVRegValue(index, JSNApiHelper::ToJSTaggedValue(*value));
}
uint32_t DebuggerApi::GetBytecodeOffset(const FrameHandler *frameHandler)
{
return frameHandler->GetBytecodeOffset();
}
JSMethod *DebuggerApi::GetMethod(const FrameHandler *frameHandler)
{
return frameHandler->GetMethod();
}
Local<JSValueRef> DebuggerApi::GetVRegValue(const EcmaVM *ecmaVm,
const FrameHandler *frameHandler, size_t index)
{
auto value = frameHandler->GetVRegValue(index);
JSHandle<JSTaggedValue> handledValue(ecmaVm->GetJSThread(), value);
return JSNApiHelper::ToLocal<JSValueRef>(handledValue);
}
CString DebuggerApi::ToCString(Local<JSValueRef> str)
{
ecmascript::JSHandle<ecmascript::JSTaggedValue> ret = JSNApiHelper::ToJSHandle(str);
ASSERT(ret->IsString());
EcmaString *ecmaStr = EcmaString::Cast(ret.GetTaggedValue().GetTaggedObject());
return ConvertToString(ecmaStr);
}
int32_t DebuggerApi::CStringToInt(const CString &str)
{
return CStringToL(str);
}
int32_t DebuggerApi::StringToInt(Local<JSValueRef> str)
{
return CStringToInt(ToCString(str));
}
// JSThread
Local<JSValueRef> DebuggerApi::GetAndClearException(const EcmaVM *ecmaVm)
{
auto exception = ecmaVm->GetJSThread()->GetException();
JSHandle<JSTaggedValue> handledException(ecmaVm->GetJSThread(), exception);
ecmaVm->GetJSThread()->ClearException();
return JSNApiHelper::ToLocal<JSValueRef>(handledException);
}
void DebuggerApi::SetException(const EcmaVM *ecmaVm, Local<JSValueRef> exception)
{
ecmaVm->GetJSThread()->SetException(JSNApiHelper::ToJSTaggedValue(*exception));
}
void DebuggerApi::ClearException(const EcmaVM *ecmaVm)
{
return ecmaVm->GetJSThread()->ClearException();
}
// NumberHelper
double DebuggerApi::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix)
{
return NumberHelper::StringToDouble(start, end, radix, ALLOW_BINARY | ALLOW_HEX | ALLOW_OCTAL);
}
// JSDebugger
JSDebugger *DebuggerApi::CreateJSDebugger(const EcmaVM *ecmaVm)
{
return new JSDebugger(ecmaVm);
}
void DebuggerApi::DestroyJSDebugger(JSDebugger *debugger)
{
delete debugger;
}
void DebuggerApi::RegisterHooks(JSDebugger *debugger, PtHooks *hooks)
{
debugger->RegisterHooks(hooks);
}
bool DebuggerApi::SetBreakpoint(JSDebugger *debugger, const JSPtLocation &location,
const Local<FunctionRef> &condFuncRef)
{
return debugger->SetBreakpoint(location, condFuncRef);
}
bool DebuggerApi::RemoveBreakpoint(JSDebugger *debugger, const JSPtLocation &location)
{
return debugger->RemoveBreakpoint(location);
}
// JSMethod
CString DebuggerApi::ParseFunctionName(const JSMethod *method)
{
return method->ParseFunctionName();
}
// ScopeInfo
Local<JSValueRef> DebuggerApi::GetProperties(const EcmaVM *ecmaVm, int32_t level, uint32_t slot)
{
JSTaggedValue env = GetCurrentEvaluateEnv(ecmaVm);
for (int i = 0; i < level; i++) {
JSTaggedValue taggedParentEnv = LexicalEnv::Cast(env.GetTaggedObject())->GetParentEnv();
ASSERT(!taggedParentEnv.IsUndefined());
env = taggedParentEnv;
}
JSTaggedValue value = LexicalEnv::Cast(env.GetTaggedObject())->GetProperties(slot);
JSHandle<JSTaggedValue> handledValue(ecmaVm->GetJSThread(), value);
return JSNApiHelper::ToLocal<JSValueRef>(handledValue);
}
void DebuggerApi::SetProperties(const EcmaVM *ecmaVm, int32_t level, uint32_t slot, Local<JSValueRef> value)
{
JSTaggedValue env = GetCurrentEvaluateEnv(ecmaVm);
for (int i = 0; i < level; i++) {
JSTaggedValue taggedParentEnv = LexicalEnv::Cast(env.GetTaggedObject())->GetParentEnv();
ASSERT(!taggedParentEnv.IsUndefined());
env = taggedParentEnv;
}
JSTaggedValue target = JSNApiHelper::ToJSHandle(value).GetTaggedValue();
LexicalEnv::Cast(env.GetTaggedObject())->SetProperties(ecmaVm->GetJSThread(), slot, target);
}
bool DebuggerApi::EvaluateLexicalValue(const EcmaVM *ecmaVm, const CString &name, int32_t &level, uint32_t &slot)
{
JSTaggedValue curEnv = GetCurrentEvaluateEnv(ecmaVm);
for (; curEnv.IsTaggedArray(); curEnv = LexicalEnv::Cast(curEnv.GetTaggedObject())->GetParentEnv(), level++) {
LexicalEnv *lexicalEnv = LexicalEnv::Cast(curEnv.GetTaggedObject());
if (lexicalEnv->GetScopeInfo().IsHole()) {
continue;
}
auto result = JSNativePointer::Cast(lexicalEnv->GetScopeInfo().GetTaggedObject())->GetExternalPointer();
ScopeDebugInfo *scopeDebugInfo = reinterpret_cast<ScopeDebugInfo *>(result);
auto iter = scopeDebugInfo->scopeInfo.find(name);
if (iter == scopeDebugInfo->scopeInfo.end()) {
continue;
}
slot = iter->second;
return true;
}
return false;
}
Local<JSValueRef> DebuggerApi::GetLexicalValueInfo(const EcmaVM *ecmaVm, const CString &name)
{
JSThread *thread = ecmaVm->GetJSThread();
JSTaggedValue curEnv = thread->GetCurrentLexenv();
for (; curEnv.IsTaggedArray(); curEnv = LexicalEnv::Cast(curEnv.GetTaggedObject())->GetParentEnv()) {
LexicalEnv *lexicalEnv = LexicalEnv::Cast(curEnv.GetTaggedObject());
if (lexicalEnv->GetScopeInfo().IsHole()) {
continue;
}
void *pointer = JSNativePointer::Cast(lexicalEnv->GetScopeInfo().GetTaggedObject())->GetExternalPointer();
ScopeDebugInfo *scopeDebugInfo = static_cast<ScopeDebugInfo *>(pointer);
auto iter = scopeDebugInfo->scopeInfo.find(name);
if (iter == scopeDebugInfo->scopeInfo.end()) {
continue;
}
uint32_t slot = iter->second;
JSTaggedValue value = lexicalEnv->GetProperties(slot);
JSHandle<JSTaggedValue> handledValue(thread, value);
return JSNApiHelper::ToLocal<JSValueRef>(handledValue);
}
JSHandle<JSTaggedValue> handledValue(thread, JSTaggedValue::Hole());
return JSNApiHelper::ToLocal<JSValueRef>(handledValue);
}
void DebuggerApi::InitJSDebugger(JSDebugger *debugger)
{
debugger->Init();
}
void DebuggerApi::HandleUncaughtException(const EcmaVM *ecmaVm, CString &message)
{
JSThread *thread = ecmaVm->GetJSThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> exHandle(thread, thread->GetException());
if (exHandle->IsJSError()) {
JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString();
JSHandle<EcmaString> name(JSObject::GetProperty(thread, exHandle, nameKey).GetValue());
JSHandle<JSTaggedValue> msgKey = thread->GlobalConstants()->GetHandledMessageString();
JSHandle<EcmaString> msg(JSObject::GetProperty(thread, exHandle, msgKey).GetValue());
message = ConvertToString(*name) + ": " + ConvertToString(*msg);
} else {
JSHandle<EcmaString> ecmaStr = JSTaggedValue::ToString(thread, exHandle);
message = ConvertToString(*ecmaStr);
}
thread->ClearException();
}
JSTaggedValue DebuggerApi::GetCurrentEvaluateEnv(const EcmaVM *ecmaVm)
{
FrameHandler frameHandler(ecmaVm->GetJSThread());
return frameHandler.GetEnv();
}
Local<FunctionRef> DebuggerApi::GenerateFuncFromBuffer(const EcmaVM *ecmaVm, const void *buffer,
size_t size, std::string_view entryPoint)
{
JSPandaFileManager *mgr = JSPandaFileManager::GetInstance();
const auto *jsPandaFile = mgr->LoadJSPandaFile("", entryPoint, buffer, size);
if (jsPandaFile == nullptr) {
return JSValueRef::Undefined(ecmaVm);
}
JSHandle<Program> program = mgr->GenerateProgram(const_cast<EcmaVM *>(ecmaVm), jsPandaFile);
JSTaggedValue func = program->GetMainFunction();
return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(ecmaVm->GetJSThread(), func));
}
Local<JSValueRef> DebuggerApi::EvaluateViaFuncCall(EcmaVM *ecmaVm, const Local<FunctionRef> &funcRef)
{
JSNApi::EnableUserUncaughtErrorHandler(ecmaVm);
JsDebuggerManager *mgr = ecmaVm->GetJsDebuggerManager();
bool prevDebugMode = mgr->IsDebugMode();
mgr->SetEvaluateCtxFrameSp(const_cast<JSTaggedType *>(ecmaVm->GetJSThread()->GetCurrentSPFrame()));
// in order to catch exception
mgr->SetDebugMode(false);
std::vector<Local<JSValueRef>> args;
auto result = funcRef->Call(ecmaVm, JSValueRef::Undefined(ecmaVm), args.data(), args.size());
mgr->SetDebugMode(prevDebugMode);
mgr->SetEvaluateCtxFrameSp(nullptr);
return result;
}
} // namespace panda::ecmascript::tooling