modify debugger events and api test

Signed-off-by: wengchangcheng <wengchangcheng@huawei.com>
Change-Id: I2b63f2d9af7eba0b5319e7ed459bb48e665f13d8
This commit is contained in:
wengchangcheng 2022-01-12 15:43:56 +08:00
parent a19ee9c8c6
commit 46307fb5d8
29 changed files with 576 additions and 399 deletions

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ tags
.byebug_history
.cache
*.swp
*.abc

View File

@ -23,6 +23,7 @@
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tooling/js_pt_lang_ext.h"
#include "include/tooling/pt_lang_extension.h"
namespace panda {
@ -71,7 +72,7 @@ std::unique_ptr<ClassLinkerExtension> EcmaLanguageContext::CreateClassLinkerExte
PandaUniquePtr<tooling::PtLangExt> EcmaLanguageContext::CreatePtLangExt() const
{
return PandaUniquePtr<tooling::PtLangExt>();
return MakePandaUnique<tooling::ecmascript::JSPtLangExt>();
}
void EcmaLanguageContext::ThrowException(ManagedThread *thread, const uint8_t *mutf8_name,

View File

@ -193,7 +193,7 @@ bool EcmaVM::Initialize()
moduleManager_ = new ModuleManager(this);
InitializeFinish();
notificationManager_->VmStartEvent();
notificationManager_->VmInitializationEvent(0);
notificationManager_->VmInitializationEvent(thread_->GetThreadId());
Platform::GetCurrentPlatform()->PostTask(std::make_unique<TrimNewSpaceLimitTask>(heap_));
return true;
}
@ -258,6 +258,7 @@ bool EcmaVM::InitializeFinish()
vmInitialized_ = true;
return true;
}
EcmaVM::~EcmaVM()
{
vmInitialized_ = false;
@ -492,9 +493,6 @@ void EcmaVM::AddPandaFile(const panda_file::File *pf, bool isModule)
{
ASSERT(pf != nullptr);
pandaFileWithProgram_.push_back(std::make_tuple(nullptr, pf, isModule));
// for debugger
notificationManager_->LoadModuleEvent(pf->GetFilename());
}
void EcmaVM::SetProgram(Program *program, const panda_file::File *pf)
@ -503,6 +501,8 @@ void EcmaVM::SetProgram(Program *program, const panda_file::File *pf)
[pf](auto entry) { return std::get<1>(entry) == pf; });
ASSERT(it != pandaFileWithProgram_.end());
std::get<0>(*it) = program;
// for debugger
notificationManager_->LoadModuleEvent(pf->GetFilename());
}
bool EcmaVM::IsFrameworkPandaFile(std::string_view filename) const

View File

@ -93,6 +93,14 @@ namespace panda::ecmascript {
goto *dispatchTable[EcmaOpcode::LAST_OPCODE]; \
} while (false)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define INTERPRETER_HANDLE_RETURN() \
do { \
thread->GetEcmaVM()->GetNotificationManager()->MethodExitEvent(thread, method); \
size_t jumpSize = GetJumpSizeAfterCall(pc); \
DISPATCH_OFFSET(jumpSize); \
} while (false)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define CHECK_SWITCH_TO_DEBUGGER_TABLE() \
if (Runtime::GetCurrent()->IsDebugMode()) { \
@ -228,8 +236,8 @@ JSTaggedValue EcmaInterpreter::ExecuteNative(JSThread *thread, const CallParams&
JSTaggedType *sp = const_cast<JSTaggedType *>(thread->GetCurrentSPFrame());
JSTaggedType *fixedSp = FixSpOnEntry(thread);
JSMethod *methodToCall = params.callTarget->GetCallTarget();
ASSERT(methodToCall->GetNumVregs() == 0);
JSMethod *method = params.callTarget->GetCallTarget();
ASSERT(method->GetNumVregs() == 0);
uint32_t numActualArgs = params.argc + RESERVED_CALL_ARGCOUNT;
// Tags and values of thread, new_tgt and argv_length are put into frames too for native frames
// NOLINTNEXTLINE(hicpp-signed-bitwise)
@ -261,7 +269,7 @@ JSTaggedValue EcmaInterpreter::ExecuteNative(JSThread *thread, const CallParams&
thread->CheckSafepoint();
LOG(DEBUG, INTERPRETER) << "Entry: Runtime Call.";
JSTaggedValue tagged =
reinterpret_cast<EcmaEntrypoint>(const_cast<void *>(methodToCall->GetNativePointer()))(&ecmaRuntimeCallInfo);
reinterpret_cast<EcmaEntrypoint>(const_cast<void *>(method->GetNativePointer()))(&ecmaRuntimeCallInfo);
LOG(DEBUG, INTERPRETER) << "Exit: Runtime Call.";
thread->SetCurrentSPFrame(sp);
#if ECMASCRIPT_ENABLE_ACTIVE_CPUPROFILER
@ -380,7 +388,9 @@ JSTaggedValue EcmaInterpreter::Execute(JSThread *thread, const CallParams& param
thread->CheckSafepoint();
LOG(DEBUG, INTERPRETER) << "break Entry: Runtime Call " << std::hex << reinterpret_cast<uintptr_t>(newSp) << " "
<< std::hex << reinterpret_cast<uintptr_t>(pc);
thread->GetEcmaVM()->GetNotificationManager()->MethodEntryEvent(thread, method);
EcmaInterpreter::RunInternal(thread, ConstantPool::Cast(constpool.GetTaggedObject()), pc, newSp);
thread->GetEcmaVM()->GetNotificationManager()->MethodExitEvent(thread, method);
// NOLINTNEXTLINE(readability-identifier-naming)
const JSTaggedValue resAcc = state->acc;
@ -446,7 +456,9 @@ JSTaggedValue EcmaInterpreter::GeneratorReEnterInterpreter(JSThread *thread, JSH
state->env = env;
// execute interpreter
thread->SetCurrentSPFrame(newSp);
thread->GetEcmaVM()->GetNotificationManager()->MethodEntryEvent(thread, method);
EcmaInterpreter::RunInternal(thread, ConstantPool::Cast(constpool.GetTaggedObject()), resumePc, newSp);
thread->GetEcmaVM()->GetNotificationManager()->MethodExitEvent(thread, method);
JSTaggedValue res = state->acc;
// pop frame
@ -778,9 +790,9 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool
INTERPRETER_GOTO_EXCEPTION_HANDLER();
}
ECMAObject *thisFunc = ECMAObject::Cast(func.GetTaggedObject());
JSMethod *methodToCall = thisFunc->GetCallTarget();
if (methodToCall->IsNative()) {
ASSERT(methodToCall->GetNumVregs() == 0);
JSMethod *method = thisFunc->GetCallTarget();
if (method->IsNative()) {
ASSERT(method->GetNumVregs() == 0);
// Tags and values of thread, new_tgt and argv_length are put into frames too for native frames
// NOLINTNEXTLINE(hicpp-signed-bitwise)
size_t frameSize = FRAME_STATE_SIZE + actualNumArgs + NUM_MANDATORY_JSFUNC_ARGS;
@ -853,16 +865,16 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool
state->function = func;
thread->SetCurrentSPFrame(newSp);
LOG(DEBUG, INTERPRETER) << "Entry: Runtime Call.";
thread->GetEcmaVM()->GetNotificationManager()->MethodEntryEvent(thread, method);
JSTaggedValue retValue = reinterpret_cast<EcmaEntrypoint>(
const_cast<void *>(methodToCall->GetNativePointer()))(&ecmaRuntimeCallInfo);
const_cast<void *>(method->GetNativePointer()))(&ecmaRuntimeCallInfo);
if (UNLIKELY(thread->HasPendingException())) {
INTERPRETER_GOTO_EXCEPTION_HANDLER();
}
LOG(DEBUG, INTERPRETER) << "Exit: Runtime Call.";
thread->SetCurrentSPFrame(sp);
SET_ACC(retValue);
size_t jumpSize = GetJumpSizeAfterCall(pc);
DISPATCH_OFFSET(jumpSize);
INTERPRETER_HANDLE_RETURN();
} else {
if (JSFunction::Cast(thisFunc)->IsClassConstructor()) {
{
@ -875,10 +887,10 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool
}
SAVE_PC();
JSTaggedType *newSp = nullptr;
uint32_t callType = methodToCall->GetCallType();
uint32_t callType = method->GetCallType();
if (callType == NORMAL_CALL_TYPE) { // fast path
uint32_t numVregs = methodToCall->GetNumVregs();
uint32_t numDeclaredArgs = methodToCall->GetNumArgs();
uint32_t numVregs = method->GetNumVregs();
uint32_t numDeclaredArgs = method->GetNumArgs();
uint32_t copyArgs = std::min(numDeclaredArgs, actualNumArgs);
size_t frameSize = FRAME_STATE_SIZE + numVregs + numDeclaredArgs;
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
@ -924,8 +936,8 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool
bool haveNewTarget = callType & HAVE_NEWTARGET_BIT;
bool haveExtra = callType & HAVE_EXTRA_BIT;
bool haveFunc = callType & HAVE_FUNC_BIT;
uint32_t numVregs = methodToCall->GetNumVregs();
uint32_t numDeclaredArgs = methodToCall->GetNumArgs();
uint32_t numVregs = method->GetNumVregs();
uint32_t numDeclaredArgs = method->GetNumArgs();
uint32_t copyArgs = haveFunc + haveNewTarget + haveThis;
size_t frameSize = FRAME_STATE_SIZE + numVregs;
if (haveExtra) {
@ -995,7 +1007,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool
InterpretedFrame *state = GET_FRAME(newSp);
state->base.prev = sp;
state->base.frameType = static_cast<uintptr_t>(FrameType::INTERPRETER_FRAME);
state->pc = pc = JSMethod::Cast(methodToCall)->GetBytecodeArray();
state->pc = pc = JSMethod::Cast(method)->GetBytecodeArray();
state->sp = sp = newSp;
state->function = func;
state->acc = JSTaggedValue::Hole();
@ -1008,6 +1020,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool
thread->SetCurrentSPFrame(newSp);
LOG(DEBUG, INTERPRETER) << "Entry: Runtime Call " << std::hex << reinterpret_cast<uintptr_t>(sp) << " "
<< std::hex << reinterpret_cast<uintptr_t>(pc);
thread->GetEcmaVM()->GetNotificationManager()->MethodEntryEvent(thread, method);
DISPATCH_OFFSET(0);
}
}
@ -1034,8 +1047,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool
constpool = ConstantPool::Cast(prevState->constpool.GetTaggedObject());
size_t jumpSize = GetJumpSizeAfterCall(pc);
DISPATCH_OFFSET(jumpSize);
INTERPRETER_HANDLE_RETURN();
}
HANDLE_OPCODE(HANDLE_RETURNUNDEFINED_PREF) {
LOG_INST() << "return.undefined";
@ -1060,8 +1072,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool
constpool = ConstantPool::Cast(prevState->constpool.GetTaggedObject());
acc = JSTaggedValue::Undefined();
size_t jumpSize = GetJumpSizeAfterCall(pc);
DISPATCH_OFFSET(jumpSize);
INTERPRETER_HANDLE_RETURN();
}
HANDLE_OPCODE(HANDLE_LDNAN_PREF) {
LOG_INST() << "intrinsics::ldnan";
@ -2186,8 +2197,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool
thread->SetCurrentSPFrame(sp);
constpool = ConstantPool::Cast(prevState->constpool.GetTaggedObject());
size_t jumpSize = GetJumpSizeAfterCall(pc);
DISPATCH_OFFSET(jumpSize);
INTERPRETER_HANDLE_RETURN();
}
HANDLE_OPCODE(HANDLE_ASYNCFUNCTIONAWAITUNCAUGHT_PREF_V8_V8) {
uint16_t v0 = READ_INST_8_1();

View File

@ -30,17 +30,17 @@ CString JSMethod::ParseFunctionName() const
void JSMethod::SetCallTypeFromAnnotation()
{
const panda_file::File *panda_file_ = GetPandaFile();
panda_file::File::EntityId field_id_ = GetFileId();
panda_file::MethodDataAccessor mda(*panda_file_, field_id_);
const panda_file::File *pandaFile = GetPandaFile();
panda_file::File::EntityId fieldId = GetFileId();
panda_file::MethodDataAccessor mda(*pandaFile, fieldId);
mda.EnumerateAnnotations([&](panda_file::File::EntityId annotation_id) {
panda_file::AnnotationDataAccessor ada(*panda_file_, annotation_id);
auto *annotation_name = reinterpret_cast<const char *>(panda_file_->GetStringData(ada.GetClassId()).data);
panda_file::AnnotationDataAccessor ada(*pandaFile, annotation_id);
auto *annotation_name = reinterpret_cast<const char *>(pandaFile->GetStringData(ada.GetClassId()).data);
if (::strcmp("L_ESCallTypeAnnotation;", annotation_name) == 0) {
uint32_t elem_count = ada.GetCount();
for (uint32_t i = 0; i < elem_count; i++) {
panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i);
auto *elem_name = reinterpret_cast<const char *>(panda_file_->GetStringData(adae.GetNameId()).data);
auto *elem_name = reinterpret_cast<const char *>(pandaFile->GetStringData(adae.GetNameId()).data);
if (::strcmp("callType", elem_name) == 0) {
callType_ = adae.GetScalarValue().GetValue();
}

View File

@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef LIBPANDABASE_ECMASCRIPT_MEM_C_CONTAINERS_H
#define LIBPANDABASE_ECMASCRIPT_MEM_C_CONTAINERS_H
#ifndef ECMASCRIPT_MEM_C_CONTAINERS_H
#define ECMASCRIPT_MEM_C_CONTAINERS_H
#include <deque>
#include <list>
@ -60,4 +60,4 @@ template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<
using CUnorderedSet = std::unordered_set<Key, Hash, KeyEqual, CAddressAllocator<Key>>;
} // namespace panda::ecmascript
#endif // LIBPANDABASE_ECMASCRIPT_MEM_C_CONTAINERS_H
#endif // ECMASCRIPT_MEM_C_CONTAINERS_H

View File

@ -50,6 +50,7 @@
#include "ecmascript/mem/region.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tagged_array.h"
#include "include/runtime_notification.h"
#include "libpandabase/os/library_loader.h"
#include "utils/pandargs.h"
@ -178,6 +179,7 @@ EcmaVM *JSNApi::CreateJSVM(const RuntimeOption &option)
void JSNApi::DestroyJSVM(EcmaVM *ecmaVm)
{
ecmaVm->GetNotificationManager()->VmDeathEvent();
auto runtime = Runtime::GetCurrent();
if (runtime != nullptr) {
PandaVM *mainVm = runtime->GetPandaVM();

View File

@ -22,6 +22,7 @@
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/napi/include/jsnapi.h"
#include "ecmascript/object_factory.h"
#include "gtest/gtest.h"
#include "include/runtime_options.h"
@ -82,7 +83,8 @@ public:
}
// If you want to call once create, you can refer to BuiltinsMathTest for detail.
static void CreateEcmaVMWithScope(PandaVM *&instance, JSThread *&thread, EcmaHandleScope *&scope)
static void CreateEcmaVMWithScope(PandaVM *&instance, JSThread *&thread, EcmaHandleScope *&scope,
const char *libraryPath = nullptr)
{
JSRuntimeOptions options;
options.SetShouldLoadBootPandaFiles(false);
@ -91,6 +93,10 @@ public:
options.SetRuntimeType("ecmascript");
options.SetPreGcHeapVerifyEnabled(true);
options.SetEnableForceGC(true);
if (libraryPath != nullptr) {
// for class Runtime to StartDebugger
options.SetDebuggerLibraryPath(libraryPath);
}
static EcmaLanguageContext lcEcma;
[[maybe_unused]] bool success = Runtime::Create(options, {&lcEcma});
ASSERT_TRUE(success) << "Cannot create Runtime";
@ -111,8 +117,7 @@ public:
EcmaVM::Cast(instance)->SetEnableForceGC(false);
auto thread = EcmaVM::Cast(instance)->GetJSThread();
thread->ClearException();
[[maybe_unused]] bool success = Runtime::Destroy();
ASSERT_TRUE(success) << "Cannot destroy Runtime";
JSNApi::DestroyJSVM(EcmaVM::Cast(instance));
}
private:

View File

@ -17,6 +17,7 @@
#include "ecmascript/class_linker/program_object-inl.h"
#include "ecmascript/js_thread.h"
#include "runtime/tooling/pt_method_private.h"
namespace panda::tooling::ecmascript {
using panda::ecmascript::Program;
@ -75,40 +76,39 @@ void JSDebugger::BytecodePcChanged(ManagedThread *thread, Method *method, uint32
bool JSDebugger::HandleBreakpoint(const JSThread *thread, const JSMethod *method, uint32_t bcOffset)
{
if (!FindBreakpoint(method, bcOffset)) {
if (hooks_ == nullptr || !FindBreakpoint(method, bcOffset)) {
return false;
}
auto *pf = method->GetPandaFile();
PtLocation location {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
if (hooks_ != nullptr) {
hooks_->Breakpoint(PtThread(thread->GetId()), location);
}
hooks_->Breakpoint(PtThread(thread->GetId()), location);
return true;
}
void JSDebugger::HandleExceptionThrowEvent(const JSThread *thread, const JSMethod *method, uint32_t bcOffset)
{
if (!thread->HasPendingException()) {
if (hooks_ == nullptr || !thread->HasPendingException()) {
return;
}
auto *pf = method->GetPandaFile();
PtLocation throwLocation {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
if (hooks_ != nullptr) {
hooks_->Exception(PtThread(thread->GetId()), throwLocation, PtObject(), throwLocation);
}
hooks_->Exception(PtThread(thread->GetId()), throwLocation, PtObject(), throwLocation);
}
bool JSDebugger::HandleStep(const JSThread *thread, const JSMethod *method, uint32_t bcOffset)
{
if (hooks_ == nullptr) {
return false;
}
auto *pf = method->GetPandaFile();
PtLocation location {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
if (hooks_ != nullptr) {
hooks_->SingleStep(PtThread(thread->GetId()), location);
}
hooks_->SingleStep(PtThread(thread->GetId()), location);
return true;
}
@ -155,4 +155,28 @@ JSMethod *JSDebugger::FindMethod(const PtLocation &location) const
});
return method;
}
void JSDebugger::MethodEntry(ManagedThread *thread, Method *method)
{
if (hooks_ == nullptr) {
return;
}
uint32_t threadId = thread->GetId();
PtThread ptThread(threadId);
hooks_->MethodEntry(ptThread, MethodToPtMethod(method));
}
void JSDebugger::MethodExit(ManagedThread *thread, Method *method)
{
if (hooks_ == nullptr) {
return;
}
bool isExceptionTriggered = thread->HasPendingException();
PtThread ptThread(thread->GetId());
auto sp = const_cast<JSTaggedType *>(JSThread::Cast(thread)->GetCurrentSPFrame());
InterpretedFrameHandler frameHandler(sp);
PtValue retValue(frameHandler.GetAcc().GetRawData());
hooks_->MethodExit(ptThread, MethodToPtMethod(method), isExceptionTriggered, retValue);
}
} // panda::tooling::ecmascript

View File

@ -52,29 +52,35 @@ public:
std::optional<Error> SetBreakpoint(const PtLocation &location) override;
std::optional<Error> RemoveBreakpoint(const PtLocation &location) override;
void BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset) override;
void MethodEntry(ManagedThread *thread, Method *method) override;
void MethodExit(ManagedThread *thread, Method *method) override;
void LoadModule(std::string_view filename) override
{
if (hooks_ != nullptr) {
hooks_->LoadModule(filename);
if (hooks_ == nullptr) {
return;
}
hooks_->LoadModule(filename);
}
void VmStart() override
{
if (hooks_ != nullptr) {
hooks_->VmStart();
if (hooks_ == nullptr) {
return;
}
hooks_->VmStart();
}
void VmInitialization(ManagedThread::ThreadId threadId) override
{
if (hooks_ != nullptr) {
hooks_->VmInitialization(PtThread(threadId));
if (hooks_ == nullptr) {
return;
}
hooks_->VmInitialization(PtThread(threadId));
}
void VmDeath() override
{
if (hooks_ != nullptr) {
hooks_->VmDeath();
if (hooks_ == nullptr) {
return;
}
hooks_->VmDeath();
}
PtLangExt *GetLangExtension() const override
@ -115,8 +121,6 @@ public:
void GarbageCollectorFinish() override {}
void ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size) override {}
void ExceptionCatch(const ManagedThread *thread, const Method *method, uint32_t bcOffset) override {}
void MethodEntry(ManagedThread *thread, Method *method) override {}
void MethodExit(ManagedThread *thread, Method *method) override {}
void ClassLoad(Class *klass) override {}
void ClassPrepare(Class *klass) override {}
void MonitorWait(ObjectHeader *object, int64_t timeout) override {}
@ -221,7 +225,8 @@ public:
private:
static constexpr uint32_t JSDEBUG_EVENT_MASK = RuntimeNotificationManager::Event::LOAD_MODULE |
RuntimeNotificationManager::Event::BYTECODE_PC_CHANGED |
RuntimeNotificationManager::Event::VM_EVENTS;
RuntimeNotificationManager::Event::VM_EVENTS |
RuntimeNotificationManager::Event::METHOD_EVENTS;
JSMethod *FindMethod(const PtLocation &location) const;
bool FindBreakpoint(const JSMethod *method, uint32_t bcOffset) const;

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef ECMASCRIPT_TOOLING_JS_PT_LANG_EXT_H
#define ECMASCRIPT_TOOLING_JS_PT_LANG_EXT_H
namespace panda::tooling::ecmascript {
class JSPtLangExt : public PtLangExt {
public:
JSPtLangExt() = default;
~JSPtLangExt() override = default;
// PtValue API
PtObject ValueToObject(PtValue value) const override
{
return PtObject();
}
// PtClass API
PtClass GetClass(PtObject object) const override
{
return PtClass();
}
PtClass GetClass(PtProperty property) const override
{
return PtClass();
}
void ReleaseClass(PtClass klass) const override {}
const char *GetClassDescriptor(PtClass klass) const override
{
return nullptr;
}
// PtObject API
PandaList<PtProperty> GetProperties(PtObject object) const override
{
return {};
}
PtProperty GetProperty(PtObject object, const char *propertyName) const override
{
return PtProperty();
}
bool AddProperty(PtObject object, const char *propertyName, PtValue value) const override
{
return false;
}
bool RemoveProperty(PtObject object, const char *propertyName) const override
{
return false;
}
// PtProperty API
const char *GetPropertyName(PtProperty property) const override
{
return nullptr;
}
PtValue GetPropertyValue(PtProperty property) const override
{
return PtValue();
}
void SetPropertyPtValue(PtProperty property, PtValue value) const override {}
void ReleasePtValue(const PtValue *value) const override {}
NO_COPY_SEMANTIC(JSPtLangExt);
NO_MOVE_SEMANTIC(JSPtLangExt);
};
} // panda::tooling::ecmascript
#endif // ECMASCRIPT_TOOLING_JS_PT_LANG_EXT_H

View File

@ -84,11 +84,11 @@ void ProtocolHandler::SendResponse(const DispatchRequest &request, const Dispatc
void ProtocolHandler::SendNotification(const EcmaVM *ecmaVm, std::unique_ptr<PtBaseEvents> events)
{
if (!Runtime::GetCurrent()->IsDebugMode() || events == nullptr) {
return;
}
LOG(DEBUG, DEBUGGER) << "ProtocolHandler::SendNotification: " << events->GetName();
SendReply(ecmaVm, events->ToObject(ecmaVm));
if (!Runtime::GetCurrent()->IsDebugMode() || events == nullptr) {
return;
}
LOG(DEBUG, DEBUGGER) << "ProtocolHandler::SendNotification: " << events->GetName();
SendReply(ecmaVm, events->ToObject(ecmaVm));
}
void ProtocolHandler::SendReply(const EcmaVM *ecmaVm, Local<ObjectRef> reply)

View File

@ -43,12 +43,38 @@ ts2abc_gen_abc("ark_debug_abc") {
out_puts = [ test_abc_path ]
}
ohos_shared_library("debugger_entry") {
sources = [ "entry/test_debugger_entry.cpp" ]
configs = [
"//ark/js_runtime:ark_jsruntime_public_config", # should add before
# arkruntime_public_config
"//ark/js_runtime:ark_jsruntime_common_config",
":debug_api_test",
"$ark_root/runtime:arkruntime_public_config",
]
deps = [
":ark_debug_abc",
":jsdebugtest",
"$ark_root/libpandabase:libarkbase",
"$ark_root/libpandafile:libarkfile",
]
if (!is_standard_system) {
deps += [ "$ark_root/runtime:libarkruntime" ]
}
output_extension = "so"
subsystem_name = "test"
}
ohos_shared_library("jsdebugtest") {
sources = [
"init.cpp",
"test_extractor.cpp",
"test_list.cpp",
"test_util.cpp",
"utils/test_entry.cpp",
"utils/test_extractor.cpp",
"utils/test_util.cpp",
"utils/testcases/test_list.cpp",
]
configs = [
@ -81,9 +107,11 @@ ohos_unittest("EcmaDebugApiTest") {
sources = [
# test file
"launcher.cpp",
"debugger_api_test.cpp",
]
defines = [ "DEBUGGER_TEST_LIBRARY=\"libdebugger_entry.so\"" ]
configs = [
"//ark/js_runtime:ark_jsruntime_public_config", # should add before
# arkruntime_public_config
@ -93,7 +121,6 @@ ohos_unittest("EcmaDebugApiTest") {
]
deps = [
":ark_debug_abc",
":jsdebugtest",
"$ark_root/libpandabase:libarkbase",
"$ark_root/libpandafile:libarkfile",
@ -194,6 +221,7 @@ group("unittest") {
":DebuggerEventsTest",
":DebuggerTypesTest",
":EcmaDebugApiTest",
":debugger_entry",
]
}

View File

@ -1,102 +0,0 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef ECMASCRIPT_TOOLING_TEST_JS_SINGLE_STEP_TEST_H
#define ECMASCRIPT_TOOLING_TEST_JS_SINGLE_STEP_TEST_H
#include "ecmascript/tooling/test/test_util.h"
namespace panda::tooling::ecmascript::test {
class JsSingleStepTest : public ApiTest {
public:
JsSingleStepTest()
{
vm_start = [this] {
location_start_ = TestUtil::GetLocation("Sample.js", 4, panda_file_.c_str());
location_end_ = TestUtil::GetLocation("Sample.js", 7, panda_file_.c_str());
return true;
};
vm_death = [this]() {
ASSERT_NE(step_count_, 0);
ASSERT_EQ(breakpoint_count_, 2);
return true;
};
load_module = [this](std::string_view moduleName) {
if (flag) {
if (moduleName.find(panda_file_.c_str()) == std::string_view::npos) {
return true;
}
flag = 0;
ASSERT_SUCCESS(debug_interface->SetBreakpoint(location_end_));
}
return true;
};
breakpoint = [this](PtThread, const PtLocation &location) {
ASSERT_TRUE(location.GetMethodId().IsValid());
ASSERT_LOCATION_EQ(location, location_end_);
// Check what step signalled before breakpoint
ASSERT_LOCATION_EQ(location, location_step_);
ASSERT_TRUE(collect_steps_);
breakpoint_count_++;
// Disable collect steps
collect_steps_ = false;
return true;
};
single_step = [this](PtThread, const PtLocation &location) {
ASSERT_TRUE(location.GetMethodId().IsValid());
if (!collect_steps_) {
if (location_start_ == location) {
collect_steps_ = true;
}
return true;
}
ASSERT_NE(bytecode_offset_, location.GetBytecodeOffset());
location_step_ = location;
step_count_++;
bytecode_offset_ = location.GetBytecodeOffset();
return true;
};
}
std::pair<CString, CString> GetEntryPoint() override
{
return {panda_file_, entry_point_};
}
private:
CString panda_file_ = "/data/app/Sample.abc";
CString entry_point_ = "_GLOBAL::func_main_0";
PtLocation location_start_{nullptr, PtLocation::EntityId(0), 0};
PtLocation location_end_{nullptr, PtLocation::EntityId(0), 0};
PtLocation location_step_{nullptr, PtLocation::EntityId(0), 0};
int32_t step_count_ = 0;
int32_t breakpoint_count_ = 0;
bool collect_steps_ = false;
uint32_t bytecode_offset_ = std::numeric_limits<uint32_t>::max();
bool flag = 1;
};
std::unique_ptr<ApiTest> GetJsSingleStepTest()
{
return std::make_unique<JsSingleStepTest>();
}
} // namespace panda::tooling::ecmascript::test
#endif // ECMASCRIPT_TOOLING_TEST_JS_SINGLE_STEP_TEST_H

View File

@ -16,7 +16,7 @@
#include "ecmascript/ecma_vm.h"
#include "ecmascript/napi/include/jsnapi.h"
#include "ecmascript/tests/test_helper.h"
#include "ecmascript/tooling/test/test_list.h"
#include "ecmascript/tooling/test/utils/testcases/test_list.h"
namespace panda::tooling::ecmascript::test {
using panda::ecmascript::EcmaHandleScope;
@ -24,7 +24,7 @@ using panda::ecmascript::EcmaVM;
using panda::ecmascript::JSThread;
using panda::test::TestHelper;
class EcmaScriptDebugApiTest : public testing::TestWithParam<const char *> {
class DebuggerApiTest : public testing::TestWithParam<const char *> {
public:
static void SetUpTestCase()
{
@ -38,7 +38,8 @@ public:
void SetUp() override
{
TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
SetCurrentTestName(GetParam());
TestHelper::CreateEcmaVMWithScope(instance, thread, scope, DEBUGGER_TEST_LIBRARY);
}
void TearDown() override
@ -49,30 +50,22 @@ public:
PandaVM *instance {nullptr};
EcmaHandleScope *scope {nullptr};
JSThread *thread {nullptr};
protected:
void RunEcmaScriptTest(const char *testName)
{
std::cout << "Running " << testName << std::endl;
SetCurrentTestName(testName);
EcmaVM *vm = EcmaVM::Cast(instance);
ASSERT_NE(vm, nullptr);
auto [pandaFile, entryPoint] = GetTestEntryPoint(testName);
auto fileNameRef = StringRef::NewFromUtf8(vm, pandaFile.c_str(), pandaFile.size());
auto entryRef = StringRef::NewFromUtf8(vm, entryPoint.c_str(), entryPoint.size());
auto res = JSNApi::Execute(vm, fileNameRef, entryRef);
ASSERT_TRUE(res);
}
};
HWTEST_P_L0(EcmaScriptDebugApiTest, EcmaScriptSuite)
HWTEST_P_L0(DebuggerApiTest, EcmaScriptSuite)
{
const char *testName = GetParam();
ASSERT_NE(testName, nullptr);
RunEcmaScriptTest(testName);
const char *testName = GetCurrentTestName();
std::cout << "Running " << testName << std::endl;
EcmaVM *vm = EcmaVM::Cast(instance);
ASSERT_NE(vm, nullptr);
auto [pandaFile, entryPoint] = GetTestEntryPoint(testName);
auto fileNameRef = StringRef::NewFromUtf8(vm, pandaFile.c_str(), pandaFile.size());
auto entryRef = StringRef::NewFromUtf8(vm, entryPoint.c_str(), entryPoint.size());
auto res = JSNApi::Execute(vm, fileNameRef, entryRef);
ASSERT_TRUE(res);
}
INSTANTIATE_TEST_CASE_P(EcmaDebugApiTest, EcmaScriptDebugApiTest,
INSTANTIATE_TEST_CASE_P(EcmaDebugApiTest, DebuggerApiTest,
testing::ValuesIn(GetTestList(panda::panda_file::SourceLang::ECMASCRIPT)));
} // namespace panda::tooling::ecmascript::test

View File

@ -13,11 +13,16 @@
* limitations under the License.
*/
// Js
#ifndef ECMASCRIPT_TOOLING_TEST_JS_VM_EVENT_TEST_H
#define ECMASCRIPT_TOOLING_TEST_JS_VM_EVENT_TEST_H
#include "ecmascript/tooling/test/utils/test_entry.h"
#include "js/js_breakpoint_test.h"
#include "js/js_single_step_test.h"
namespace panda::tooling::ecmascript::test {
extern "C" int StartDebugger()
{
return StartDebuggerImpl();
}
#endif // ECMASCRIPT_TOOLING_TEST_JS_VM_EVENT_TEST_H
extern "C" int StopDebugger()
{
return StopDebuggerImpl();
}
} // namespace panda::tooling::ecmascript::test

View File

@ -13,35 +13,34 @@
* limitations under the License.
*/
#include <memory>
#include "ecmascript/tooling/test/utils/test_entry.h"
#include <thread>
#include "ecmascript/tooling/test/test_runner.h"
#include "ecmascript/tooling/test/test_util.h"
#include "ecmascript/tooling/test/utils/testcases/test_list.h"
#include "ecmascript/tooling/test/utils/test_hooks.h"
namespace panda::tooling::ecmascript::test {
extern const char *GetCurrentTestName();
static std::thread g_debuggerThread;
static std::unique_ptr<TestHooks> g_hooks = nullptr;
static std::unique_ptr<TestRunner> g_runner{nullptr};
extern "C" int32_t StartDebugger(const EcmaVM *vm)
int StartDebuggerImpl()
{
const char *testName = GetCurrentTestName();
g_runner = std::make_unique<TestRunner>(testName, vm);
EcmaVM *vm = EcmaVM::Cast(Runtime::GetCurrent()->GetPandaVM());
g_hooks = std::make_unique<TestHooks>(testName, vm);
g_debuggerThread = std::thread([] {
TestUtil::WaitForInit();
g_runner->Run();
g_hooks->Run();
});
return 0;
}
extern "C" int32_t StopDebugger()
int StopDebuggerImpl()
{
g_debuggerThread.join();
g_runner->TerminateTest();
g_runner.reset();
g_hooks->TerminateTest();
g_hooks.reset();
return 0;
}
} // namespace panda::tooling::ecmascript::test

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TEST_ENTRY_H
#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_ENTRY_H
namespace panda::tooling::ecmascript::test {
int StartDebuggerImpl();
int StopDebuggerImpl();
} // namespace panda::tooling::ecmascript::test
#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_ENTRY_H

View File

@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef ECMASCRIPT_TOOLING_TEST_API_TEST_H
#define ECMASCRIPT_TOOLING_TEST_API_TEST_H
#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EVENTS_H
#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EVENTS_H
#include <utility>
#include "ecmascript/tooling/agent/js_backend.h"
@ -47,25 +47,25 @@ enum class DebugEvent {
std::ostream &operator<<(std::ostream &out, DebugEvent value);
struct ApiTest {
struct TestEvents {
BreakpointCallback breakpoint;
LoadModuleCallback load_module;
LoadModuleCallback loadModule;
PausedCallback paused;
ExceptionCallback exception;
MethodEntryCallback method_entry;
SingleStepCallback single_step;
VmStartCallback vm_start;
VmInitializationCallback vm_init;
VmDeathCallback vm_death;
MethodEntryCallback methodEntry;
SingleStepCallback singleStep;
VmStartCallback vmStart;
VmInitializationCallback vmInit;
VmDeathCallback vmDeath;
Scenario scenario;
JSDebugger *debug_interface {nullptr};
JSBackend *backend {nullptr};
ApiTest();
virtual ~ApiTest() = default;
JSDebugger *debugInterface_ {nullptr};
JSBackend *backend_ {nullptr};
TestEvents();
virtual ~TestEvents() = default;
virtual std::pair<CString, CString> GetEntryPoint() = 0;
};
} // namespace panda::tooling::ecmascript::test
#endif // ECMASCRIPT_TOOLING_TEST_API_TEST_H
#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EVENTS_H

View File

@ -14,10 +14,7 @@
*/
// Taken from panda-tools/panda-debugger/debugger
#include "ecmascript/tooling/test/test_extractor.h"
#include <algorithm>
#include <limits>
#include "ecmascript/tooling/test/utils/test_extractor.h"
#include "libpandabase/utils/leb128.h"
#include "libpandabase/utils/utf.h"

View File

@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef ECMASCRIPT_TOOLING_TEST_DEBUG_INFO_EXTRACTOR_H
#define ECMASCRIPT_TOOLING_TEST_DEBUG_INFO_EXTRACTOR_H
#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EXTRACTOR_H
#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EXTRACTOR_H
#include "ecmascript/mem/c_string.h"
#include "ecmascript/tooling/pt_js_extractor.h"
@ -58,4 +58,4 @@ public:
};
} // namespace panda::tooling::ecmascript::test
#endif // ECMASCRIPT_TOOLING_TEST_DEBUG_INFO_EXTRACTOR_H
#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EXTRACTOR_H

View File

@ -13,26 +13,26 @@
* limitations under the License.
*/
#ifndef ECMASCRIPT_TOOLING_TEST_TEST_RUNNER_H
#define ECMASCRIPT_TOOLING_TEST_TEST_RUNNER_H
#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TEST_HOOKS_H
#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_HOOKS_H
#include "ecmascript/tooling/test/test_util.h"
#include "ecmascript/tooling/agent/js_pt_hooks.h"
#include "ecmascript/tooling/agent/js_backend.h"
#include "ecmascript/tooling/test/utils/test_util.h"
namespace panda::tooling::ecmascript::test {
class TestRunner : public PtHooks {
class TestHooks : public PtHooks {
public:
TestRunner(const char *test_name, const EcmaVM *vm)
TestHooks(const char *testName, const EcmaVM *vm)
{
backend_ = std::make_unique<JSBackend>(vm);
test_name_ = test_name;
test_ = TestUtil::GetTest(test_name);
test_->backend = backend_.get();
test_->debug_interface = backend_->GetDebugger();
debug_interface_ = backend_->GetDebugger();
testName_ = testName;
test_ = TestUtil::GetTest(testName);
test_->backend_ = backend_.get();
test_->debugInterface_ = backend_->GetDebugger();
debugInterface_ = backend_->GetDebugger();
TestUtil::Reset();
debug_interface_->RegisterHooks(this);
debugInterface_->RegisterHooks(this);
}
void Run()
@ -51,8 +51,8 @@ public:
void LoadModule(std::string_view panda_file_name) override
{
if (test_->load_module) {
test_->load_module(panda_file_name);
if (test_->loadModule) {
test_->loadModule(panda_file_name);
}
}
@ -73,48 +73,48 @@ public:
void MethodEntry(PtThread thread, PtMethod method) override
{
if (test_->method_entry) {
test_->method_entry(thread, method);
if (test_->methodEntry) {
test_->methodEntry(thread, method);
}
}
void SingleStep(PtThread thread, const PtLocation &location) override
{
if (test_->single_step) {
test_->single_step(thread, location);
if (test_->singleStep) {
test_->singleStep(thread, location);
}
}
void VmDeath() override
{
if (test_->vm_death) {
test_->vm_death();
if (test_->vmDeath) {
test_->vmDeath();
}
TestUtil::Event(DebugEvent::VM_DEATH);
}
void VmInitialization([[maybe_unused]] PtThread thread) override
{
if (test_->vm_init) {
test_->vm_init();
if (test_->vmInit) {
test_->vmInit();
}
TestUtil::Event(DebugEvent::VM_INITIALIZATION);
}
void VmStart() override
{
if (test_->vm_start) {
test_->vm_start();
if (test_->vmStart) {
test_->vmStart();
}
}
void TerminateTest()
{
debug_interface_->RegisterHooks(nullptr);
debugInterface_->RegisterHooks(nullptr);
if (TestUtil::IsTestFinished()) {
return;
}
LOG(FATAL, DEBUGGER) << "Test " << test_name_ << " failed";
LOG(FATAL, DEBUGGER) << "Test " << testName_ << " failed";
}
void ThreadStart(PtThread) override {}
@ -139,14 +139,14 @@ public:
void ExecutionContextsCleared() override {}
void InspectRequested(PtObject, PtObject) override {}
~TestRunner() = default;
~TestHooks() = default;
private:
std::unique_ptr<JSBackend> backend_ {nullptr};
JSDebugger *debug_interface_;
const char *test_name_;
ApiTest *test_;
JSDebugger *debugInterface_;
const char *testName_;
TestEvents *test_;
};
} // namespace panda::tooling::ecmascript::test
#endif // ECMASCRIPT_TOOLING_TEST_TEST_RUNNER_H
#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_HOOKS_H

View File

@ -13,7 +13,7 @@
* limitations under the License.
*/
#include "ecmascript/tooling/test/test_util.h"
#include "ecmascript/tooling/test/utils/test_util.h"
namespace panda::tooling::ecmascript::test {
TestMap TestUtil::testMap_;
@ -56,7 +56,7 @@ int32_t TestUtil::GetValueRegister(JSMethod *method, const char *varName)
}
}
return -2;
return -1;
}
std::ostream &operator<<(std::ostream &out, DebugEvent value)
@ -66,7 +66,7 @@ std::ostream &operator<<(std::ostream &out, DebugEvent value)
#define ADD_CASE(entry) \
case (entry): \
s = #entry; \
break;
break
switch (value) {
ADD_CASE(DebugEvent::BREAKPOINT);
@ -93,7 +93,7 @@ std::ostream &operator<<(std::ostream &out, std::nullptr_t)
return out << "nullptr";
}
ApiTest::ApiTest()
TestEvents::TestEvents()
{
scenario = []() {
ASSERT_EXITED();

View File

@ -13,37 +13,34 @@
* limitations under the License.
*/
#ifndef ECMASCRIPT_TOOLING_TEST_TEST_UTIL_H
#define ECMASCRIPT_TOOLING_TEST_TEST_UTIL_H
#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H
#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H
#include <climits>
#include <cstdlib>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "api_test.h"
#include "ecmascript/mem/c_containers.h"
#include "ecmascript/tooling/interface/js_debugger.h"
#include "ecmascript/tooling/test/utils/test_events.h"
#include "ecmascript/tooling/test/utils/test_extractor.h"
#include "os/mutex.h"
#include "test_extractor.h"
namespace panda::tooling::ecmascript::test {
using TestMap = std::unordered_map<panda_file::SourceLang, std::unordered_map<const char *, std::unique_ptr<ApiTest>>>;
template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
using CUnorderedMap = panda::ecmascript::CUnorderedMap<Key, T, Hash, KeyEqual>;
using TestMap = CUnorderedMap<panda_file::SourceLang, CUnorderedMap<const char *, std::unique_ptr<TestEvents>>>;
class TestUtil {
public:
static void RegisterTest(panda_file::SourceLang language, const char *testName, std::unique_ptr<ApiTest> test)
static void RegisterTest(panda_file::SourceLang language, const char *testName, std::unique_ptr<TestEvents> test)
{
auto it = testMap_.find(language);
if (it == testMap_.end()) {
std::unordered_map<const char *, std::unique_ptr<ApiTest>> entry;
CUnorderedMap<const char *, std::unique_ptr<TestEvents>> entry;
auto res = testMap_.emplace(language, std::move(entry));
it = res.first;
}
it->second.insert({testName, std::move(test)});
}
static ApiTest *GetTest(const char *name)
static TestEvents *GetTest(const char *name)
{
for (auto it = testMap_.begin(); it != testMap_.end(); ++it) {
auto &internalMap = it->second;
@ -75,13 +72,17 @@ public:
static bool WaitForExit()
{
return WaitForEvent(DebugEvent::VM_DEATH,
[]() REQUIRES(eventMutex_) { return lastEvent_ == DebugEvent::VM_DEATH; }, [] {});
[]() REQUIRES(eventMutex_) {
return lastEvent_ == DebugEvent::VM_DEATH;
}, [] {});
}
static bool WaitForInit()
{
return WaitForEvent(DebugEvent::VM_INITIALIZATION,
[]() REQUIRES(eventMutex_) { return initialized_; }, [] {});
[]() REQUIRES(eventMutex_) {
return initialized_;
}, [] {});
}
static void Event(DebugEvent event, PtThread thread = PtThread::NONE,
@ -126,14 +127,7 @@ public:
TestExtractor extractor(pf);
auto [id, offset] = extractor.GetBreakpointAddress({sourceFile, line});
#if PANDA_LIBCORE_SUPPORT
static std::unordered_set<std::string> absolute_paths;
static char buf[PATH_MAX];
auto res = absolute_paths.insert(::realpath(pandaFile, buf));
return PtLocation(res.first->c_str(), id, offset);
#else // PANDA_LIBCORE_SUPPORT
return PtLocation(pandaFile, id, offset);
#endif // PANDA_LIBCORE_SUPPORT
}
static std::vector<panda_file::LocalVariableInfo> GetVariables(JSMethod *method, uint32_t offset);
@ -205,88 +199,91 @@ private:
std::ostream &operator<<(std::ostream &out, std::nullptr_t);
#define _ASSERT_FAIL(val1, val2, strval1, strval2, msg) \
std::cerr << "Assertion failed at " << __FILE__ << ':' << __LINE__ << std::endl; \
std::cerr << "Expected that " strval1 " is " << msg << " " strval2 << std::endl; \
std::cerr << "\t" strval1 ": " << (val1) << std::endl; \
std::cerr << "\t" strval2 ": " << (val2) << std::endl; \
std::abort();
#define ASSERT_TRUE(cond) \
do { \
auto res = (cond); \
if (!res) { \
_ASSERT_FAIL(res, true, #cond, "true", "equal to") \
} \
#define ASSERT_FAIL_(val1, val2, strval1, strval2, msg) \
do { \
std::cerr << "Assertion failed at " << __FILE__ << ':' << __LINE__ << std::endl; \
std::cerr << "Expected that " strval1 " is " << (msg) << " " strval2 << std::endl; \
std::cerr << "\t" strval1 ": " << (val1) << std::endl; \
std::cerr << "\t" strval2 ": " << (val2) << std::endl; \
std::abort(); \
} while (0)
#define ASSERT_FALSE(cond) \
do { \
auto res = (cond); \
if (res) { \
_ASSERT_FAIL(res, false, #cond, "false", "equal to") \
} \
#define ASSERT_TRUE(cond) \
do { \
auto res = (cond); \
if (!res) { \
ASSERT_FAIL_(res, true, #cond, "true", "equal to"); \
} \
} while (0)
#define ASSERT_EQ(lhs, rhs) \
#define ASSERT_FALSE(cond) \
do { \
auto res = (cond); \
if (res) { \
ASSERT_FAIL_(res, false, #cond, "false", "equal to"); \
} \
} while (0)
#define ASSERT_EQ(lhs, rhs) \
do { \
auto res1 = (lhs); \
decltype(res1) res2 = (rhs); \
if (res1 != res2) { \
ASSERT_FAIL_(res1, res2, #lhs, #rhs, "equal to"); \
} \
} while (0)
#define ASSERT_NE(lhs, rhs) \
do { \
auto res1 = (lhs); \
decltype(res1) res2 = (rhs); \
if (res1 == res2) { \
ASSERT_FAIL_(res1, res2, #lhs, #rhs, "not equal to"); \
} \
} while (0)
#define ASSERT_STREQ(lhs, rhs) \
do { \
auto res1 = (lhs); \
decltype(res1) res2 = (rhs); \
if (::strcmp(res1, res2) != 0) { \
ASSERT_FAIL_(res1, res2, #lhs, #rhs, "equal to"); \
} \
} while (0)
#define ASSERT_SUCCESS(api_call) \
do { \
auto error = api_call; \
if (error) { \
ASSERT_FAIL_(error.value().GetMessage(), "Success", "API call result", "Expected", ""); \
} \
} while (0)
#define ASSERT_EXITED() \
do { \
bool res = TestUtil::WaitForExit(); \
if (!res) { \
ASSERT_FAIL_(TestUtil::IsTestFinished(), true, "TestUtil::IsTestFinished()", "true", ""); \
} \
} while (0)
#define ASSERT_LOCATION_EQ(lhs, rhs) \
do { \
ASSERT_STREQ((lhs).GetPandaFile(), (rhs).GetPandaFile()); \
ASSERT_EQ((lhs).GetMethodId().GetOffset(), (rhs).GetMethodId().GetOffset()); \
ASSERT_EQ((lhs).GetBytecodeOffset(), (rhs).GetBytecodeOffset()); \
} while (0)
#define ASSERT_THREAD_VALID(ecmaVm) \
do { \
auto res1 = (lhs); \
decltype(res1) res2 = (rhs); \
if (res1 != res2) { \
_ASSERT_FAIL(res1, res2, #lhs, #rhs, "equal to") \
} \
ASSERT_NE((ecmaVm).GetId(), PtThread::NONE.GetId()); \
} while (0)
#define ASSERT_NE(lhs, rhs) \
do { \
auto res1 = (lhs); \
decltype(res1) res2 = (rhs); \
if (res1 == res2) { \
_ASSERT_FAIL(res1, res2, #lhs, #rhs, "not equal to") \
} \
} while (0)
#define ASSERT_STREQ(lhs, rhs) \
do { \
auto res1 = (lhs); \
decltype(res1) res2 = (rhs); \
if (::strcmp(res1, res2) != 0) { \
_ASSERT_FAIL(res1, res2, #lhs, #rhs, "equal to") \
} \
} while (0)
#define ASSERT_SUCCESS(api_call) \
do { \
auto error = api_call; \
if (error) { \
_ASSERT_FAIL(error.value().GetMessage(), "Success", "API call result", "Expected", "") \
} \
} while (0)
#define ASSERT_EXITED() \
do { \
bool res = TestUtil::WaitForExit(); \
if (!res) { \
_ASSERT_FAIL(TestUtil::IsTestFinished(), true, "TestUtil::IsTestFinished()", "true", "") \
} \
} while (0)
#define ASSERT_LOCATION_EQ(lhs, rhs) \
do { \
ASSERT_STREQ(lhs.GetPandaFile(), rhs.GetPandaFile()); \
ASSERT_EQ(lhs.GetMethodId().GetOffset(), rhs.GetMethodId().GetOffset()); \
ASSERT_EQ(lhs.GetBytecodeOffset(), rhs.GetBytecodeOffset()); \
} while (0);
#define ASSERT_THREAD_VALID(ecmaVm) \
do { \
ASSERT_NE(ecmaVm.GetId(), PtThread::NONE.GetId()); \
} while (0);
#define ASSERT_BREAKPOINT_SUCCESS(location) \
do { \
PtThread suspended = TestUtil::WaitForBreakpoint(location); \
ASSERT_THREAD_VALID(suspended); \
} while (0);
} while (0)
} // namespace panda::tooling::ecmascript::test
#endif // ECMASCRIPT_TOOLING_TEST_TEST_UTIL_H
#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H

View File

@ -13,20 +13,19 @@
* limitations under the License.
*/
#ifndef ECMASCRIPT_TOOLING_TEST_JS_BREAKPOINT_TEST_H
#define ECMASCRIPT_TOOLING_TEST_JS_BREAKPOINT_TEST_H
#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_TEST_H
#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_TEST_H
#include "ecmascript/tooling/test/test_util.h"
#include "ecmascript/mem/c_string.h"
#include "ecmascript/tooling/test/utils/test_util.h"
namespace panda::tooling::ecmascript::test {
class JsBreakpointTest : public ApiTest {
class JsBreakpointTest : public TestEvents {
public:
JsBreakpointTest()
{
vm_start = [this] {
location_ = TestUtil::GetLocation("Sample.js", 7, panda_file_.c_str());
location_.GetPandaFile();
vmStart = [this] {
location_ = TestUtil::GetLocation("Sample.js", 22, pandaFile_.c_str());
ASSERT_TRUE(location_.GetMethodId().IsValid());
return true;
};
@ -34,57 +33,57 @@ public:
breakpoint = [this](PtThread thread, const PtLocation &location) {
ASSERT_TRUE(location.GetMethodId().IsValid());
ASSERT_LOCATION_EQ(location, location_);
++breakpoint_counter_;
++breakpointCounter_;
TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, thread, location);
return true;
};
load_module = [this](std::string_view moduleName) {
loadModule = [this](std::string_view moduleName) {
if (flag_) {
if (moduleName.find(panda_file_.c_str()) == std::string_view::npos) {
if (moduleName != pandaFile_) {
return true;
}
ASSERT_TRUE(backend->NotifyScriptParsed(0, panda_file_));
flag_ = 0;
location_.GetPandaFile();
ASSERT_SUCCESS(debug_interface->SetBreakpoint(location_));
auto error = debug_interface->SetBreakpoint(location_);
ASSERT_FALSE(!error);
ASSERT_TRUE(backend_->NotifyScriptParsed(0, pandaFile_));
flag_ = false;
auto error = debugInterface_->SetBreakpoint(location_);
ASSERT_FALSE(error.has_value());
}
return true;
};
scenario = [this]() {
ASSERT_BREAKPOINT_SUCCESS(location_);
ASSERT_SUCCESS(debug_interface->RemoveBreakpoint(location_));
TestUtil::Continue();
ASSERT_BREAKPOINT_SUCCESS(location_);
TestUtil::Continue();
ASSERT_SUCCESS(debugInterface_->RemoveBreakpoint(location_));
ASSERT_EXITED();
return true;
};
vm_death = [this]() {
ASSERT_EQ(breakpoint_counter_, 1U);
vmDeath = [this]() {
ASSERT_EQ(breakpointCounter_, 2U);
return true;
};
}
std::pair<CString, CString> GetEntryPoint() override
{
return {panda_file_, entry_point_};
return {pandaFile_, entryPoint_};
}
~JsBreakpointTest() = default;
private:
CString panda_file_ = "/data/app/Sample.abc";
CString entry_point_ = "_GLOBAL::func_main_0";
CString pandaFile_ = "/data/test/Sample.abc";
CString entryPoint_ = "_GLOBAL::func_main_0";
PtLocation location_ {nullptr, PtLocation::EntityId(0), 0};
size_t breakpoint_counter_ = 0;
bool flag_ = 1;
size_t breakpointCounter_ = 0;
bool flag_ = true;
};
std::unique_ptr<ApiTest> GetJsBreakpointTest()
std::unique_ptr<TestEvents> GetJsBreakpointTest()
{
return std::make_unique<JsBreakpointTest>();
}
} // namespace panda::tooling::ecmascript::test
#endif // ECMASCRIPT_TOOLING_TEST_JS_BREAKPOINT_TEST_H
#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_TEST_H

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_SINGLE_STEP_TEST_H
#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_SINGLE_STEP_TEST_H
#include "ecmascript/tooling/test/utils/test_util.h"
namespace panda::tooling::ecmascript::test {
class JsSingleStepTest : public TestEvents {
public:
JsSingleStepTest()
{
vmStart = [this] {
locationStart_ = TestUtil::GetLocation("Sample.js", 19, pandaFile_.c_str());
locationEnd_ = TestUtil::GetLocation("Sample.js", 22, pandaFile_.c_str());
return true;
};
vmDeath = [this]() {
ASSERT_NE(stepCounter_, 0);
ASSERT_EQ(breakpointCounter_, 2);
return true;
};
loadModule = [this](std::string_view moduleName) {
if (flag_) {
if (moduleName != pandaFile_) {
return true;
}
flag_ = false;
ASSERT_SUCCESS(debugInterface_->SetBreakpoint(locationEnd_));
}
return true;
};
breakpoint = [this](PtThread, const PtLocation &location) {
ASSERT_TRUE(location.GetMethodId().IsValid());
ASSERT_LOCATION_EQ(location, locationEnd_);
// Check what step signalled before breakpoint
ASSERT_LOCATION_EQ(location, locationStep_);
ASSERT_TRUE(collectSteps_);
breakpointCounter_++;
// Disable collect steps
collectSteps_ = false;
return true;
};
singleStep = [this](PtThread, const PtLocation &location) {
ASSERT_TRUE(location.GetMethodId().IsValid());
if (!collectSteps_) {
if (locationStart_ == location) {
collectSteps_ = true;
}
return true;
}
ASSERT_NE(bytecodeOffset_, location.GetBytecodeOffset());
locationStep_ = location;
stepCounter_++;
bytecodeOffset_ = location.GetBytecodeOffset();
return true;
};
}
std::pair<CString, CString> GetEntryPoint() override
{
return {pandaFile_, entryPoint_};
}
private:
CString pandaFile_ = "/data/test/Sample.abc";
CString entryPoint_ = "_GLOBAL::func_main_0";
PtLocation locationStart_ {nullptr, PtLocation::EntityId(0), 0};
PtLocation locationEnd_ {nullptr, PtLocation::EntityId(0), 0};
PtLocation locationStep_ {nullptr, PtLocation::EntityId(0), 0};
int32_t stepCounter_ = 0;
int32_t breakpointCounter_ = 0;
bool collectSteps_ = false;
uint32_t bytecodeOffset_ = std::numeric_limits<uint32_t>::max();
bool flag_ = true;
};
std::unique_ptr<TestEvents> GetJsSingleStepTest()
{
return std::make_unique<JsSingleStepTest>();
}
} // namespace panda::tooling::ecmascript::test
#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_SINGLE_STEP_TEST_H

View File

@ -13,15 +13,20 @@
* limitations under the License.
*/
#include "ecmascript/tooling/test/test_list.h"
#include "ecmascript/tooling/test/api_tests/api_tests.h"
#include "ecmascript/tooling/test/test_util.h"
#include "ecmascript/tooling/test/utils/testcases/test_list.h"
#include "ecmascript/tooling/test/utils/test_util.h"
// testcase list
#include "js_breakpoint_test.h"
#include "js_single_step_test.h"
namespace panda::tooling::ecmascript::test {
static const char *g_currentTestName = nullptr;
static void RegisterTests()
{
// Register testcases
TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsBreakpoint", GetJsBreakpointTest());
TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsSingleStepTest", GetJsSingleStepTest());
}

View File

@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef ECMASCRIPT_TOOLING_TEST_TEST_LIST_H
#define ECMASCRIPT_TOOLING_TEST_TEST_LIST_H
#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_TEST_LIST_H
#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_TEST_LIST_H
#include <utility>
#include <vector>
@ -28,8 +28,9 @@ using panda::ecmascript::CString;
std::vector<const char *> GetTestList(panda_file::SourceLang language);
void SetCurrentTestName(const char *testName);
const char *GetCurrentTestName();
std::pair<CString, CString> GetTestEntryPoint(const char *testName);
} // namespace panda::tooling::ecmascript::test
#endif // ECMASCRIPT_TOOLING_TEST_TEST_LIST_H
#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_TEST_LIST_H

View File

@ -15,8 +15,9 @@
<configuration ver="2.0">
<target name="EcmaDebugApiTest">
<preparer>
<option name="push" value="obj/ark/js_runtime/ecmascript/tooling/test/Sample.abc -> /data/app" src="out"/>
<option name="push" value="obj/ark/js_runtime/ecmascript/tooling/test/Sample.abc -> /data/test" src="out"/>
<option name="push" value="test/test/libjsdebugtest.so -> /data/test" src="out"/>
<option name="push" value="test/test/libdebugger_entry.so -> /data/test" src="out"/>
</preparer>
</target>
<target name="DebuggerCommandsTest">