From 2b24aaf55d0a155a9644fd24fc333971f001c7f1 Mon Sep 17 00:00:00 2001 From: Gymee Date: Wed, 21 Sep 2022 17:07:22 +0800 Subject: [PATCH 1/2] add tooling Issue: #I5RV8U Signed-off-by: Gymee Change-Id: Id2bee91fb49f91dbc683766c19551725b0076299 --- tooling/BUILD.gn | 160 + tooling/agent/debugger_impl.cpp | 1194 +++++++ tooling/agent/debugger_impl.h | 201 ++ tooling/agent/heapprofiler_impl.cpp | 342 ++ tooling/agent/heapprofiler_impl.h | 175 ++ tooling/agent/profiler_impl.cpp | 241 ++ tooling/agent/profiler_impl.h | 95 + tooling/agent/runtime_impl.cpp | 621 ++++ tooling/agent/runtime_impl.h | 140 + tooling/agent/tracing_impl.cpp | 141 + tooling/agent/tracing_impl.h | 80 + tooling/backend/debugger_api.cpp | 374 +++ tooling/backend/debugger_api.h | 101 + tooling/backend/debugger_executor.cpp | 195 ++ tooling/backend/debugger_executor.h | 60 + tooling/backend/js_debugger.cpp | 183 ++ tooling/backend/js_debugger.h | 152 + tooling/backend/js_debugger_interface.h | 133 + tooling/backend/js_pt_extractor.cpp | 118 + tooling/backend/js_pt_extractor.h | 129 + tooling/backend/js_pt_hooks.cpp | 91 + tooling/backend/js_pt_hooks.h | 51 + tooling/backend/js_pt_location.h | 67 + tooling/base/pt_base64.cpp | 125 + tooling/base/pt_base64.h | 31 + tooling/base/pt_events.cpp | 352 +++ tooling/base/pt_events.h | 1266 ++++++++ tooling/base/pt_json.cpp | 432 +++ tooling/base/pt_json.h | 109 + tooling/base/pt_method.h | 74 + tooling/base/pt_params.cpp | 988 ++++++ tooling/base/pt_params.h | 1132 +++++++ tooling/base/pt_returns.cpp | 348 ++ tooling/base/pt_returns.h | 427 +++ tooling/base/pt_script.cpp | 28 + tooling/base/pt_script.h | 118 + tooling/base/pt_types.cpp | 2793 +++++++++++++++++ tooling/base/pt_types.h | 2508 +++++++++++++++ tooling/debugger_service.cpp | 73 + tooling/debugger_service.h | 55 + tooling/dispatcher.cpp | 172 + tooling/dispatcher.h | 147 + tooling/interface/file_stream.cpp | 130 + tooling/interface/file_stream.h | 88 + tooling/interface/js_debugger_manager.h | 149 + tooling/interface/notification_manager.h | 104 + tooling/interface/progress.h | 30 + tooling/interface/stream.h | 48 + tooling/protocol_channel.h | 43 + tooling/protocol_handler.cpp | 131 + tooling/protocol_handler.h | 68 + tooling/test/BUILD.gn | 206 ++ tooling/test/debugger_commands_test.cpp | 67 + tooling/test/debugger_entry_test.cpp | 65 + tooling/test/debugger_events_test.cpp | 436 +++ tooling/test/debugger_params_test.cpp | 476 +++ tooling/test/debugger_returns_test.cpp | 402 +++ tooling/test/debugger_script_test.cpp | 107 + tooling/test/debugger_types_test.cpp | 2642 ++++++++++++++++ tooling/test/entry/test_debugger_entry.cpp | 28 + tooling/test/js_pt_hooks_test.cpp | 118 + tooling/test/pt_base64_test.cpp | 85 + tooling/test/pt_json_test.cpp | 181 ++ tooling/test/testcases/js/arrow_func.js | 23 + tooling/test/testcases/js/async_func.js | 33 + tooling/test/testcases/js/exception.js | 24 + tooling/test/testcases/js/range_error.js | 22 + tooling/test/testcases/js/sample.js | 30 + tooling/test/testcases/js/step.js | 98 + tooling/test/testcases/js/syntax_exception.js | 29 + tooling/test/testcases/js/throw_exception.js | 34 + .../test/testcases/js_breakpoint_arrow_test.h | 86 + .../test/testcases/js_breakpoint_async_test.h | 88 + tooling/test/testcases/js_breakpoint_test.h | 88 + tooling/test/testcases/js_exception_test.h | 113 + tooling/test/testcases/js_range_error_test.h | 113 + tooling/test/testcases/js_single_step_test.h | 105 + tooling/test/testcases/js_step_into_test.h | 127 + tooling/test/testcases/js_step_out_test.h | 127 + tooling/test/testcases/js_step_over_test.h | 127 + .../test/testcases/js_syntax_exception_test.h | 113 + .../test/testcases/js_throw_exception_test.h | 116 + tooling/test/utils/test_entry.cpp | 45 + tooling/test/utils/test_entry.h | 29 + tooling/test/utils/test_events.h | 67 + tooling/test/utils/test_extractor.cpp | 57 + tooling/test/utils/test_extractor.h | 52 + tooling/test/utils/test_hooks.h | 123 + tooling/test/utils/test_list.cpp | 78 + tooling/test/utils/test_list.h | 34 + tooling/test/utils/test_util.cpp | 72 + tooling/test/utils/test_util.h | 289 ++ 92 files changed, 24168 insertions(+) create mode 100644 tooling/BUILD.gn create mode 100644 tooling/agent/debugger_impl.cpp create mode 100644 tooling/agent/debugger_impl.h create mode 100644 tooling/agent/heapprofiler_impl.cpp create mode 100644 tooling/agent/heapprofiler_impl.h create mode 100644 tooling/agent/profiler_impl.cpp create mode 100644 tooling/agent/profiler_impl.h create mode 100644 tooling/agent/runtime_impl.cpp create mode 100644 tooling/agent/runtime_impl.h create mode 100644 tooling/agent/tracing_impl.cpp create mode 100644 tooling/agent/tracing_impl.h create mode 100644 tooling/backend/debugger_api.cpp create mode 100644 tooling/backend/debugger_api.h create mode 100644 tooling/backend/debugger_executor.cpp create mode 100644 tooling/backend/debugger_executor.h create mode 100644 tooling/backend/js_debugger.cpp create mode 100644 tooling/backend/js_debugger.h create mode 100644 tooling/backend/js_debugger_interface.h create mode 100644 tooling/backend/js_pt_extractor.cpp create mode 100644 tooling/backend/js_pt_extractor.h create mode 100644 tooling/backend/js_pt_hooks.cpp create mode 100644 tooling/backend/js_pt_hooks.h create mode 100644 tooling/backend/js_pt_location.h create mode 100644 tooling/base/pt_base64.cpp create mode 100644 tooling/base/pt_base64.h create mode 100644 tooling/base/pt_events.cpp create mode 100644 tooling/base/pt_events.h create mode 100644 tooling/base/pt_json.cpp create mode 100644 tooling/base/pt_json.h create mode 100644 tooling/base/pt_method.h create mode 100644 tooling/base/pt_params.cpp create mode 100644 tooling/base/pt_params.h create mode 100644 tooling/base/pt_returns.cpp create mode 100644 tooling/base/pt_returns.h create mode 100644 tooling/base/pt_script.cpp create mode 100644 tooling/base/pt_script.h create mode 100644 tooling/base/pt_types.cpp create mode 100644 tooling/base/pt_types.h create mode 100644 tooling/debugger_service.cpp create mode 100644 tooling/debugger_service.h create mode 100644 tooling/dispatcher.cpp create mode 100644 tooling/dispatcher.h create mode 100644 tooling/interface/file_stream.cpp create mode 100644 tooling/interface/file_stream.h create mode 100644 tooling/interface/js_debugger_manager.h create mode 100644 tooling/interface/notification_manager.h create mode 100644 tooling/interface/progress.h create mode 100644 tooling/interface/stream.h create mode 100644 tooling/protocol_channel.h create mode 100644 tooling/protocol_handler.cpp create mode 100644 tooling/protocol_handler.h create mode 100644 tooling/test/BUILD.gn create mode 100644 tooling/test/debugger_commands_test.cpp create mode 100644 tooling/test/debugger_entry_test.cpp create mode 100644 tooling/test/debugger_events_test.cpp create mode 100644 tooling/test/debugger_params_test.cpp create mode 100644 tooling/test/debugger_returns_test.cpp create mode 100644 tooling/test/debugger_script_test.cpp create mode 100644 tooling/test/debugger_types_test.cpp create mode 100644 tooling/test/entry/test_debugger_entry.cpp create mode 100644 tooling/test/js_pt_hooks_test.cpp create mode 100644 tooling/test/pt_base64_test.cpp create mode 100644 tooling/test/pt_json_test.cpp create mode 100644 tooling/test/testcases/js/arrow_func.js create mode 100644 tooling/test/testcases/js/async_func.js create mode 100644 tooling/test/testcases/js/exception.js create mode 100644 tooling/test/testcases/js/range_error.js create mode 100644 tooling/test/testcases/js/sample.js create mode 100644 tooling/test/testcases/js/step.js create mode 100644 tooling/test/testcases/js/syntax_exception.js create mode 100644 tooling/test/testcases/js/throw_exception.js create mode 100644 tooling/test/testcases/js_breakpoint_arrow_test.h create mode 100644 tooling/test/testcases/js_breakpoint_async_test.h create mode 100644 tooling/test/testcases/js_breakpoint_test.h create mode 100644 tooling/test/testcases/js_exception_test.h create mode 100644 tooling/test/testcases/js_range_error_test.h create mode 100644 tooling/test/testcases/js_single_step_test.h create mode 100644 tooling/test/testcases/js_step_into_test.h create mode 100644 tooling/test/testcases/js_step_out_test.h create mode 100644 tooling/test/testcases/js_step_over_test.h create mode 100644 tooling/test/testcases/js_syntax_exception_test.h create mode 100644 tooling/test/testcases/js_throw_exception_test.h create mode 100644 tooling/test/utils/test_entry.cpp create mode 100644 tooling/test/utils/test_entry.h create mode 100644 tooling/test/utils/test_events.h create mode 100644 tooling/test/utils/test_extractor.cpp create mode 100644 tooling/test/utils/test_extractor.h create mode 100644 tooling/test/utils/test_hooks.h create mode 100644 tooling/test/utils/test_list.cpp create mode 100644 tooling/test/utils/test_list.h create mode 100644 tooling/test/utils/test_util.cpp create mode 100644 tooling/test/utils/test_util.h diff --git a/tooling/BUILD.gn b/tooling/BUILD.gn new file mode 100644 index 00000000..5bb81c79 --- /dev/null +++ b/tooling/BUILD.gn @@ -0,0 +1,160 @@ +# 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. + +import("//arkcompiler/ets_runtime/js_runtime_config.gni") +import("//build/ohos.gni") + +config("ark_ecma_debugger_config") { + configs = [ + "//arkcompiler/ets_runtime:ark_jsruntime_common_config", + "//arkcompiler/ets_runtime:ark_jsruntime_public_config", + ] + + include_dirs = [ + ".", + "//third_party/boost", + "//third_party/cJSON", + ] +} + +debugger_sources = [ + "agent/debugger_impl.cpp", + "agent/runtime_impl.cpp", + "agent/tracing_impl.cpp", + "backend/debugger_executor.cpp", + "backend/js_pt_extractor.cpp", + "backend/js_pt_hooks.cpp", + "base/pt_base64.cpp", + "base/pt_events.cpp", + "base/pt_json.cpp", + "base/pt_params.cpp", + "base/pt_returns.cpp", + "base/pt_script.cpp", + "base/pt_types.cpp", + "debugger_service.cpp", + "dispatcher.cpp", + "protocol_handler.cpp", +] +if (!is_mingw && !is_mac) { + debugger_sources += [ + "agent/heapprofiler_impl.cpp", + "agent/profiler_impl.cpp", + ] +} + +source_set("libark_ecma_debugger_set") { + sources = debugger_sources + + public_configs = [ ":ark_ecma_debugger_config" ] + + deps = [ + "$ark_root/libpandafile:arkfile_header_deps", + "//third_party/cJSON:cjson_static", + ] + + if (is_mingw || is_mac) { + if (is_mingw) { + platform = "windows" + } else { + platform = "mac" + } + deps += [ + "$ark_root/libpandafile:libarkfile_static", + "//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog_$platform", + ] + defines = [ "ENABLE_HILOG" ] + } + + if (is_ohos && is_standard_system) { + if (enable_hilog) { + defines = [ "ENABLE_HILOG" ] + include_dirs = + [ "//base/hiviewdfx/hilog/interfaces/native/innerkits/include" ] + } + if (enable_leak_check) { + sources += [ "$js_root/ecmascript/dfx/native_dfx/backtrace.cpp" ] + defines += [ + "ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK", + "ECMASCRIPT_ENABLE_GLOBAL_LEAK_CHECK", + ] + } + } + + cflags_cc = [ "-fvisibility=hidden" ] +} + +ohos_shared_library("libark_ecma_debugger") { + deps = [ + ":libark_ecma_debugger_set", + "//arkcompiler/ets_runtime:libark_jsruntime", + ] + + install_enable = true + + if (is_ohos && is_standard_system) { + if (enable_hilog) { + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + } + } + + if (!is_mingw && !is_mac) { + output_extension = "so" + } + + if (!is_standard_system) { + relative_install_dir = "ark" + } + part_name = "ark_js_runtime" + subsystem_name = "ark" +} + +source_set("libark_ecma_debugger_test_set") { + sources = debugger_sources + + public_configs = [ ":ark_ecma_debugger_config" ] + + defines = [ "DEBUGGER_TEST" ] + + if (is_ohos && is_standard_system) { + if (enable_hilog) { + defines += [ "ENABLE_HILOG" ] + include_dirs = + [ "//base/hiviewdfx/hilog/interfaces/native/innerkits/include" ] + } + } + + deps = [ + "$ark_root/libpandafile:arkfile_header_deps", + "//third_party/cJSON:cjson_static", + ] +} + +ohos_shared_library("libark_ecma_debugger_test") { + deps = [ + ":libark_ecma_debugger_test_set", + "//arkcompiler/ets_runtime:libark_jsruntime_test", + ] + + if (is_ohos && is_standard_system) { + if (enable_hilog) { + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + } + } + + public_configs = [ ":ark_ecma_debugger_config" ] + + install_enable = false + + output_extension = "so" + subsystem_name = "test" +} diff --git a/tooling/agent/debugger_impl.cpp b/tooling/agent/debugger_impl.cpp new file mode 100644 index 00000000..5eb6b325 --- /dev/null +++ b/tooling/agent/debugger_impl.cpp @@ -0,0 +1,1194 @@ +/* + * 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/agent/debugger_impl.h" +#include "ecmascript/jspandafile/js_pandafile_manager.h" +#include "ecmascript/napi/jsnapi_helper.h" +#include "ecmascript/tooling/base/pt_base64.h" +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/backend/debugger_executor.h" +#include "ecmascript/tooling/dispatcher.h" +#include "ecmascript/tooling/protocol_handler.h" + +namespace panda::ecmascript::tooling { +using namespace std::placeholders; + +using ObjectType = RemoteObject::TypeName; +using ObjectSubType = RemoteObject::SubTypeName; +using ObjectClassName = RemoteObject::ClassName; +using StepperType = JSPtExtractor::SingleStepper::Type; + +#ifdef DEBUGGER_TEST +const std::string DATA_APP_PATH = "/"; +#else +const std::string DATA_APP_PATH = "/data/"; +#endif + +DebuggerImpl::DebuggerImpl(const EcmaVM *vm, ProtocolChannel *channel, RuntimeImpl *runtime) + : vm_(vm), frontend_(channel), runtime_(runtime) +{ + hooks_ = std::make_unique(this); + + jsDebugger_ = DebuggerApi::CreateJSDebugger(vm_); + DebuggerApi::RegisterHooks(jsDebugger_, hooks_.get()); + + DebuggerExecutor::Initialize(vm_); + updaterFunc_ = std::bind(&DebuggerImpl::UpdateScopeObject, this, _1, _2, _3); + stepperFunc_ = std::bind(&DebuggerImpl::ClearSingleStepper, this); + vm_->GetJsDebuggerManager()->SetLocalScopeUpdater(&updaterFunc_); + vm_->GetJsDebuggerManager()->SetStepperFunc(&stepperFunc_); +} + +DebuggerImpl::~DebuggerImpl() +{ + DebuggerApi::DestroyJSDebugger(jsDebugger_); +} + +bool DebuggerImpl::NotifyScriptParsed(ScriptId scriptId, const std::string &fileName, std::string_view entryPoint) +{ +#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_ANDROID) + if (fileName.substr(0, DATA_APP_PATH.length()) != DATA_APP_PATH) { + LOG_DEBUGGER(DEBUG) << "NotifyScriptParsed: unsupport file: " << fileName; + return false; + } +#endif + + const JSPandaFile *jsPandaFile = nullptr; + JSPandaFileManager::GetInstance()->EnumerateJSPandaFiles([&jsPandaFile, &fileName]( + const panda::ecmascript::JSPandaFile *pf) { + if (fileName == pf->GetJSPandaFileDesc().c_str()) { + jsPandaFile = pf; + return false; + } + return true; + }); + if (jsPandaFile == nullptr) { + LOG_DEBUGGER(ERROR) << "NotifyScriptParsed: unknown file: " << fileName; + return false; + } + + JSPtExtractor *extractor = GetExtractor(jsPandaFile); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "NotifyScriptParsed: Unsupported file: " << fileName; + return false; + } + + auto mainMethodIndex = panda_file::File::EntityId(jsPandaFile->GetMainMethodIndex(entryPoint.data())); + const std::string &source = extractor->GetSourceCode(mainMethodIndex); + const std::string &url = extractor->GetSourceFile(mainMethodIndex); + const uint32_t MIN_SOURCE_CODE_LENGTH = 5; // maybe return 'ANDA' when source code is empty + if (source.size() < MIN_SOURCE_CODE_LENGTH) { + LOG_DEBUGGER(ERROR) << "NotifyScriptParsed: invalid file: " << fileName; + return false; + } + // store here for performance of get extractor from url + extractors_[url] = extractor; + + auto scriptFunc = [this](PtScript *script) -> bool { + frontend_.ScriptParsed(vm_, *script); + return true; + }; + if (MatchScripts(scriptFunc, fileName, ScriptMatchType::URL)) { + LOG_DEBUGGER(WARN) << "NotifyScriptParsed: already loaded: " << fileName; + return false; + } + + // Notify script parsed event + std::unique_ptr script = std::make_unique(scriptId, fileName, url, source); + + frontend_.ScriptParsed(vm_, *script); + + // Store parsed script in map + scripts_[script->GetScriptId()] = std::move(script); + return true; +} + +bool DebuggerImpl::NotifySingleStep(const JSPtLocation &location) +{ + if (UNLIKELY(pauseOnNextByteCode_)) { + if (IsSkipLine(location)) { + return false; + } + pauseOnNextByteCode_ = false; + LOG_DEBUGGER(INFO) << "StepComplete: pause on next bytecode"; + return true; + } + + if (LIKELY(singleStepper_ == nullptr)) { + return false; + } + + // step not complete + if (!singleStepper_->StepComplete(location.GetBytecodeOffset())) { + return false; + } + + // skip unknown file or special line -1 + if (IsSkipLine(location)) { + return false; + } + + singleStepper_.reset(); + LOG_DEBUGGER(INFO) << "StepComplete: pause on current byte_code"; + return true; +} + +bool DebuggerImpl::IsSkipLine(const JSPtLocation &location) +{ + JSPtExtractor *extractor = nullptr; + auto scriptFunc = [this, &extractor](PtScript *script) -> bool { + extractor = GetExtractor(script->GetUrl()); + return true; + }; + if (!MatchScripts(scriptFunc, location.GetPandaFile(), ScriptMatchType::FILE_NAME) || extractor == nullptr) { + LOG_DEBUGGER(INFO) << "StepComplete: skip unknown file"; + return true; + } + + auto callbackFunc = [](int32_t line) -> bool { + return line == JSPtExtractor::SPECIAL_LINE_MARK; + }; + File::EntityId methodId = location.GetMethodId(); + uint32_t offset = location.GetBytecodeOffset(); + if (extractor->MatchLineWithOffset(callbackFunc, methodId, offset)) { + LOG_DEBUGGER(INFO) << "StepComplete: skip -1"; + return true; + } + + return false; +} + +bool DebuggerImpl::CheckPauseOnException() +{ + if (pauseOnException_ == PauseOnExceptionsState::NONE) { + return false; + } + if (pauseOnException_ == PauseOnExceptionsState::UNCAUGHT) { + if (DebuggerApi::IsExceptionCaught(vm_)) { + return false; + } + } + return true; +} + +void DebuggerImpl::NotifyPaused(std::optional location, PauseReason reason) +{ + if (reason == EXCEPTION && !CheckPauseOnException()) { + return; + } + + Local exception = DebuggerApi::GetAndClearException(vm_); + + std::vector hitBreakpoints; + if (location.has_value()) { + BreakpointDetails detail; + JSPtExtractor *extractor = nullptr; + auto scriptFunc = [this, &extractor, &detail](PtScript *script) -> bool { + detail.url_ = script->GetUrl(); + extractor = GetExtractor(detail.url_); + return true; + }; + auto callbackLineFunc = [&detail](int32_t line) -> bool { + detail.line_ = line; + return true; + }; + auto callbackColumnFunc = [&detail](int32_t column) -> bool { + detail.column_ = column; + return true; + }; + File::EntityId methodId = location->GetMethodId(); + uint32_t offset = location->GetBytecodeOffset(); + if (!MatchScripts(scriptFunc, location->GetPandaFile(), ScriptMatchType::FILE_NAME) || + extractor == nullptr || !extractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) || + !extractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) { + LOG_DEBUGGER(ERROR) << "NotifyPaused: unknown " << location->GetPandaFile(); + return; + } + hitBreakpoints.emplace_back(BreakpointDetails::ToString(detail)); + } + + // Do something cleaning on paused + CleanUpOnPaused(); + + // Notify paused event + std::vector> callFrames; + if (!GenerateCallFrames(&callFrames)) { + LOG_DEBUGGER(ERROR) << "NotifyPaused: GenerateCallFrames failed"; + return; + } + tooling::Paused paused; + paused.SetCallFrames(std::move(callFrames)).SetReason(reason).SetHitBreakpoints(std::move(hitBreakpoints)); + if (reason == EXCEPTION && exception->IsError()) { + std::unique_ptr tmpException = RemoteObject::FromTagged(vm_, exception); + paused.SetData(std::move(tmpException)); + } + frontend_.Paused(vm_, paused); + + frontend_.WaitForDebugger(vm_); + DebuggerApi::SetException(vm_, exception); +} + +void DebuggerImpl::NotifyNativeCalling(const void *nativeAddress) +{ + // native calling only after step into should be reported + if (singleStepper_ != nullptr && + singleStepper_->GetStepperType() == StepperType::STEP_INTO) { + tooling::NativeCalling nativeCalling; + nativeCalling.SetNativeAddress(nativeAddress); + frontend_.NativeCalling(vm_, nativeCalling); + frontend_.WaitForDebugger(vm_); + } +} + +void DebuggerImpl::NotifyPendingJobEntry() +{ + if (singleStepper_ != nullptr) { + singleStepper_.reset(); + pauseOnNextByteCode_ = true; + } +} + +void DebuggerImpl::NotifyHandleProtocolCommand() +{ + auto *handler = vm_->GetJsDebuggerManager()->GetDebuggerHandler(); + handler->ProcessCommand(); +} + +void DebuggerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) +{ + static std::unordered_map dispatcherTable { + { "enable", &DebuggerImpl::DispatcherImpl::Enable }, + { "disable", &DebuggerImpl::DispatcherImpl::Disable }, + { "evaluateOnCallFrame", &DebuggerImpl::DispatcherImpl::EvaluateOnCallFrame }, + { "getPossibleBreakpoints", &DebuggerImpl::DispatcherImpl::GetPossibleBreakpoints }, + { "getScriptSource", &DebuggerImpl::DispatcherImpl::GetScriptSource }, + { "pause", &DebuggerImpl::DispatcherImpl::Pause }, + { "removeBreakpoint", &DebuggerImpl::DispatcherImpl::RemoveBreakpoint }, + { "resume", &DebuggerImpl::DispatcherImpl::Resume }, + { "setAsyncCallStackDepth", &DebuggerImpl::DispatcherImpl::SetAsyncCallStackDepth }, + { "setBreakpointByUrl", &DebuggerImpl::DispatcherImpl::SetBreakpointByUrl }, + { "setPauseOnExceptions", &DebuggerImpl::DispatcherImpl::SetPauseOnExceptions }, + { "stepInto", &DebuggerImpl::DispatcherImpl::StepInto }, + { "stepOut", &DebuggerImpl::DispatcherImpl::StepOut }, + { "stepOver", &DebuggerImpl::DispatcherImpl::StepOver }, + { "setMixedDebugEnabled", &DebuggerImpl::DispatcherImpl::SetMixedDebugEnabled }, + { "replyNativeCalling", &DebuggerImpl::DispatcherImpl::ReplyNativeCalling } + }; + + const std::string &method = request.GetMethod(); + LOG_DEBUGGER(DEBUG) << "dispatch [" << method << "] to DebuggerImpl"; + auto entry = dispatcherTable.find(method); + if (entry != dispatcherTable.end() && entry->second != nullptr) { + (this->*(entry->second))(request); + } else { + SendResponse(request, DispatchResponse::Fail("Unknown method: " + method)); + } +} + +void DebuggerImpl::DispatcherImpl::Enable(const DispatchRequest &request) +{ + std::unique_ptr params = EnableParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + + UniqueDebuggerId id; + DispatchResponse response = debugger_->Enable(*params, &id); + + EnableReturns result(id); + SendResponse(request, response, result); +} + +void DebuggerImpl::DispatcherImpl::Disable(const DispatchRequest &request) +{ + DispatchResponse response = debugger_->Disable(); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::EvaluateOnCallFrame(const DispatchRequest &request) +{ + std::unique_ptr params = EvaluateOnCallFrameParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + std::unique_ptr result1; + DispatchResponse response = debugger_->EvaluateOnCallFrame(*params, &result1); + if (result1 == nullptr) { + SendResponse(request, response); + return; + } + + EvaluateOnCallFrameReturns result(std::move(result1)); + SendResponse(request, response, result); +} + +void DebuggerImpl::DispatcherImpl::GetPossibleBreakpoints(const DispatchRequest &request) +{ + std::unique_ptr params = GetPossibleBreakpointsParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + std::vector> locations; + DispatchResponse response = debugger_->GetPossibleBreakpoints(*params, &locations); + GetPossibleBreakpointsReturns result(std::move(locations)); + SendResponse(request, response, result); +} + +void DebuggerImpl::DispatcherImpl::GetScriptSource(const DispatchRequest &request) +{ + std::unique_ptr params = GetScriptSourceParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + std::string source; + DispatchResponse response = debugger_->GetScriptSource(*params, &source); + GetScriptSourceReturns result(source); + SendResponse(request, response, result); +} + +void DebuggerImpl::DispatcherImpl::Pause(const DispatchRequest &request) +{ + DispatchResponse response = debugger_->Pause(); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::RemoveBreakpoint(const DispatchRequest &request) +{ + std::unique_ptr params = RemoveBreakpointParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = debugger_->RemoveBreakpoint(*params); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::Resume(const DispatchRequest &request) +{ + std::unique_ptr params = ResumeParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = debugger_->Resume(*params); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::SetAsyncCallStackDepth(const DispatchRequest &request) +{ + DispatchResponse response = debugger_->SetAsyncCallStackDepth(); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::SetBreakpointByUrl(const DispatchRequest &request) +{ + std::unique_ptr params = SetBreakpointByUrlParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + + std::string out_id; + std::vector> outLocations; + DispatchResponse response = debugger_->SetBreakpointByUrl(*params, &out_id, &outLocations); + SetBreakpointByUrlReturns result(out_id, std::move(outLocations)); + SendResponse(request, response, result); +} + +void DebuggerImpl::DispatcherImpl::SetPauseOnExceptions(const DispatchRequest &request) +{ + std::unique_ptr params = SetPauseOnExceptionsParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + + DispatchResponse response = debugger_->SetPauseOnExceptions(*params); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::StepInto(const DispatchRequest &request) +{ + std::unique_ptr params = StepIntoParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = debugger_->StepInto(*params); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::StepOut(const DispatchRequest &request) +{ + DispatchResponse response = debugger_->StepOut(); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::StepOver(const DispatchRequest &request) +{ + std::unique_ptr params = StepOverParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = debugger_->StepOver(*params); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::SetMixedDebugEnabled(const DispatchRequest &request) +{ + std::unique_ptr params = SetMixedDebugParams::Create(request.GetParams()); + DispatchResponse response = debugger_->SetMixedDebugEnabled(*params); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::ReplyNativeCalling(const DispatchRequest &request) +{ + std::unique_ptr params = ReplyNativeCallingParams::Create(request.GetParams()); + DispatchResponse response = debugger_->ReplyNativeCalling(*params); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::SetBlackboxPatterns(const DispatchRequest &request) +{ + DispatchResponse response = debugger_->SetBlackboxPatterns(); + SendResponse(request, response); +} + +bool DebuggerImpl::Frontend::AllowNotify(const EcmaVM *vm) const +{ + return vm->GetJsDebuggerManager()->IsDebugMode() && channel_ != nullptr; +} + +void DebuggerImpl::Frontend::BreakpointResolved(const EcmaVM *vm) +{ + if (!AllowNotify(vm)) { + return; + } + + tooling::BreakpointResolved breakpointResolved; + channel_->SendNotification(breakpointResolved); +} + +void DebuggerImpl::Frontend::Paused(const EcmaVM *vm, const tooling::Paused &paused) +{ + if (!AllowNotify(vm)) { + return; + } + + channel_->SendNotification(paused); +} + +void DebuggerImpl::Frontend::NativeCalling(const EcmaVM *vm, const tooling::NativeCalling &nativeCalling) +{ + if (!AllowNotify(vm)) { + return; + } + + channel_->SendNotification(nativeCalling); +} + +void DebuggerImpl::Frontend::Resumed(const EcmaVM *vm) +{ + if (!AllowNotify(vm)) { + return; + } + + channel_->RunIfWaitingForDebugger(); + tooling::Resumed resumed; + channel_->SendNotification(resumed); +} + +void DebuggerImpl::Frontend::ScriptFailedToParse(const EcmaVM *vm) +{ + if (!AllowNotify(vm)) { + return; + } + + tooling::ScriptFailedToParse scriptFailedToParse; + channel_->SendNotification(scriptFailedToParse); +} + +void DebuggerImpl::Frontend::ScriptParsed(const EcmaVM *vm, const PtScript &script) +{ + if (!AllowNotify(vm)) { + return; + } + + tooling::ScriptParsed scriptParsed; + scriptParsed.SetScriptId(script.GetScriptId()) + .SetUrl(script.GetUrl()) + .SetStartLine(0) + .SetStartColumn(0) + .SetEndLine(script.GetEndLine()) + .SetEndColumn(0) + .SetExecutionContextId(0) + .SetHash(script.GetHash()); + + channel_->SendNotification(scriptParsed); +} + +void DebuggerImpl::Frontend::WaitForDebugger(const EcmaVM *vm) +{ + if (!AllowNotify(vm)) { + return; + } + + channel_->WaitForDebugger(); +} + +DispatchResponse DebuggerImpl::Enable([[maybe_unused]] const EnableParams ¶ms, UniqueDebuggerId *id) +{ + ASSERT(id != nullptr); + *id = 0; + vm_->GetJsDebuggerManager()->SetDebugMode(true); + for (auto &script : scripts_) { + frontend_.ScriptParsed(vm_, *script.second); + } + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::Disable() +{ + vm_->GetJsDebuggerManager()->SetDebugMode(false); + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::EvaluateOnCallFrame(const EvaluateOnCallFrameParams ¶ms, + std::unique_ptr *result) +{ + CallFrameId callFrameId = params.GetCallFrameId(); + const std::string &expression = params.GetExpression(); + if (callFrameId < 0 || callFrameId >= static_cast(callFrameHandlers_.size())) { + return DispatchResponse::Fail("Invalid callFrameId."); + } + + std::string dest; + if (!DecodeAndCheckBase64(expression, dest)) { + LOG_DEBUGGER(ERROR) << "EvaluateValue: base64 decode failed"; + auto ret = CmptEvaluateValue(callFrameId, expression, result); + if (ret.has_value()) { + LOG_DEBUGGER(ERROR) << "Evaluate fail, expression: " << expression; + } + return DispatchResponse::Create(ret); + } + + auto funcRef = DebuggerApi::GenerateFuncFromBuffer(vm_, dest.data(), dest.size(), + JSPandaFile::ENTRY_FUNCTION_NAME); + auto res = DebuggerApi::EvaluateViaFuncCall(const_cast(vm_), funcRef, + callFrameHandlers_[callFrameId]); + if (vm_->GetJSThread()->HasPendingException()) { + LOG_DEBUGGER(ERROR) << "EvaluateValue: has pending exception"; + std::string msg; + DebuggerApi::HandleUncaughtException(vm_, msg); + *result = RemoteObject::FromTagged(vm_, + Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, msg.data()))); + return DispatchResponse::Fail(msg); + } + + *result = RemoteObject::FromTagged(vm_, res); + runtime_->CacheObjectIfNeeded(res, (*result).get()); + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::GetPossibleBreakpoints(const GetPossibleBreakpointsParams ¶ms, + std::vector> *locations) +{ + Location *start = params.GetStart(); + auto iter = scripts_.find(start->GetScriptId()); + if (iter == scripts_.end()) { + return DispatchResponse::Fail("Unknown file name."); + } + JSPtExtractor *extractor = GetExtractor(iter->second->GetUrl()); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "GetPossibleBreakpoints: extractor is null"; + return DispatchResponse::Fail("Unknown file name."); + } + + int32_t line = start->GetLine(); + int32_t column = start->GetColumn(); + auto callbackFunc = []([[maybe_unused]] File::EntityId id, [[maybe_unused]] uint32_t offset) -> bool { + return true; + }; + if (extractor->MatchWithLocation(callbackFunc, line, column)) { + std::unique_ptr location = std::make_unique(); + location->SetScriptId(start->GetScriptId()).SetLine(line).SetColumn(column); + locations->emplace_back(std::move(location)); + } + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::GetScriptSource(const GetScriptSourceParams ¶ms, std::string *source) +{ + ScriptId scriptId = params.GetScriptId(); + auto iter = scripts_.find(scriptId); + if (iter == scripts_.end()) { + *source = ""; + return DispatchResponse::Fail("unknown script id: " + std::to_string(scriptId)); + } + *source = iter->second->GetScriptSource(); + + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::Pause() +{ + pauseOnNextByteCode_ = true; + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::RemoveBreakpoint(const RemoveBreakpointParams ¶ms) +{ + std::string id = params.GetBreakpointId(); + LOG_DEBUGGER(INFO) << "RemoveBreakpoint: " << id; + BreakpointDetails metaData{}; + if (!BreakpointDetails::ParseBreakpointId(id, &metaData)) { + return DispatchResponse::Fail("Parse breakpoint id failed"); + } + JSPtExtractor *extractor = GetExtractor(metaData.url_); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: extractor is null"; + return DispatchResponse::Fail("Unknown file name."); + } + + std::string fileName; + auto scriptFunc = [&fileName](PtScript *script) -> bool { + fileName = script->GetFileName(); + return true; + }; + if (!MatchScripts(scriptFunc, metaData.url_, ScriptMatchType::URL)) { + LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: Unknown url: " << metaData.url_; + return DispatchResponse::Fail("Unknown file name."); + } + + auto callbackFunc = [this, fileName](File::EntityId id, uint32_t offset) -> bool { + JSPtLocation location {fileName.c_str(), id, offset}; + return DebuggerApi::RemoveBreakpoint(jsDebugger_, location); + }; + if (!extractor->MatchWithLocation(callbackFunc, metaData.line_, metaData.column_)) { + LOG_DEBUGGER(ERROR) << "failed to set breakpoint location number: " + << metaData.line_ << ":" << metaData.column_; + return DispatchResponse::Fail("Breakpoint not found."); + } + + LOG_DEBUGGER(INFO) << "remove breakpoint line number:" << metaData.line_; + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::Resume([[maybe_unused]] const ResumeParams ¶ms) +{ + frontend_.Resumed(vm_); + singleStepper_.reset(); + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::SetAsyncCallStackDepth() +{ + return DispatchResponse::Fail("SetAsyncCallStackDepth not support now"); +} + +DispatchResponse DebuggerImpl::SetBreakpointByUrl(const SetBreakpointByUrlParams ¶ms, + std::string *outId, + std::vector> *outLocations) +{ + const std::string &url = params.GetUrl(); + int32_t lineNumber = params.GetLine(); + int32_t columnNumber = params.GetColumn(); + auto condition = params.HasCondition() ? params.GetCondition() : std::optional {}; + + JSPtExtractor *extractor = GetExtractor(url); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: extractor is null"; + return DispatchResponse::Fail("Unknown file name."); + } + + ScriptId scriptId; + std::string fileName; + auto scriptFunc = [&scriptId, &fileName](PtScript *script) -> bool { + scriptId = script->GetScriptId(); + fileName = script->GetFileName(); + return true; + }; + if (!MatchScripts(scriptFunc, url, ScriptMatchType::URL)) { + LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: Unknown url: " << url; + return DispatchResponse::Fail("Unknown file name."); + } + + auto callbackFunc = [this, fileName, &condition](File::EntityId id, uint32_t offset) -> bool { + JSPtLocation location {fileName.c_str(), id, offset}; + Local condFuncRef = FunctionRef::Undefined(vm_); + if (condition.has_value() && !condition.value().empty()) { + std::string dest; + if (!DecodeAndCheckBase64(condition.value(), dest)) { + LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: base64 decode failed"; + return false; + } + condFuncRef = DebuggerApi::GenerateFuncFromBuffer(vm_, dest.data(), dest.size(), + JSPandaFile::ENTRY_FUNCTION_NAME); + if (condFuncRef->IsUndefined()) { + LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: generate function failed"; + return false; + } + } + return DebuggerApi::SetBreakpoint(jsDebugger_, location, condFuncRef); + }; + if (!extractor->MatchWithLocation(callbackFunc, lineNumber, columnNumber)) { + LOG_DEBUGGER(ERROR) << "failed to set breakpoint location number: " << lineNumber << ":" << columnNumber; + return DispatchResponse::Fail("Breakpoint not found."); + } + + BreakpointDetails metaData{lineNumber, 0, url}; + *outId = BreakpointDetails::ToString(metaData); + *outLocations = std::vector>(); + std::unique_ptr location = std::make_unique(); + location->SetScriptId(scriptId).SetLine(lineNumber).SetColumn(0); + outLocations->emplace_back(std::move(location)); + + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::SetPauseOnExceptions(const SetPauseOnExceptionsParams ¶ms) +{ + pauseOnException_ = params.GetState(); + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::StepInto([[maybe_unused]] const StepIntoParams ¶ms) +{ + JSPtExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_)); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "StepOver: extractor is null"; + return DispatchResponse::Fail("Unknown file name."); + } + singleStepper_ = extractor->GetStepIntoStepper(vm_); + + frontend_.Resumed(vm_); + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::StepOut() +{ + JSPtExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_)); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "StepOut: extractor is null"; + return DispatchResponse::Fail("Unknown file name."); + } + singleStepper_ = extractor->GetStepOutStepper(vm_); + + frontend_.Resumed(vm_); + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::StepOver([[maybe_unused]] const StepOverParams ¶ms) +{ + JSPtExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_)); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "StepOver: extractor is null"; + return DispatchResponse::Fail("Unknown file name."); + } + singleStepper_ = extractor->GetStepOverStepper(vm_); + + frontend_.Resumed(vm_); + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::SetBlackboxPatterns() +{ + return DispatchResponse::Fail("SetBlackboxPatterns not support now"); +} + +DispatchResponse DebuggerImpl::SetMixedDebugEnabled([[maybe_unused]] const SetMixedDebugParams ¶ms) +{ + vm_->GetJsDebuggerManager()->SetMixedDebugEnabled(params.GetEnabled()); + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::ReplyNativeCalling([[maybe_unused]] const ReplyNativeCallingParams ¶ms) +{ + frontend_.Resumed(vm_); + if (params.GetUserCode()) { + singleStepper_.reset(); + } + return DispatchResponse::Ok(); +} + +void DebuggerImpl::CleanUpOnPaused() +{ + runtime_->curObjectId_ = 0; + runtime_->properties_.clear(); + + callFrameHandlers_.clear(); + scopeObjects_.clear(); +} + +std::string DebuggerImpl::Trim(const std::string &str) +{ + std::string ret = str; + // If ret has only ' ', remove all charactors. + ret.erase(ret.find_last_not_of(' ') + 1); + // If ret has only ' ', remove all charactors. + ret.erase(0, ret.find_first_not_of(' ')); + return ret; +} + +JSPtExtractor *DebuggerImpl::GetExtractor(const JSPandaFile *jsPandaFile) +{ + return JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); +} + +JSPtExtractor *DebuggerImpl::GetExtractor(const std::string &url) +{ + auto iter = extractors_.find(url); + if (iter == extractors_.end()) { + return nullptr; + } + + return iter->second; +} + +bool DebuggerImpl::GenerateCallFrames(std::vector> *callFrames) +{ + CallFrameId callFrameId = 0; + auto walkerFunc = [this, &callFrameId, &callFrames](const FrameHandler *frameHandler) -> StackState { + if (DebuggerApi::IsNativeMethod(frameHandler)) { + LOG_DEBUGGER(INFO) << "GenerateCallFrames: Skip CFrame and Native method"; + return StackState::CONTINUE; + } + std::unique_ptr callFrame = std::make_unique(); + if (!GenerateCallFrame(callFrame.get(), frameHandler, callFrameId)) { + if (callFrameId == 0) { + return StackState::FAILED; + } + } else { + SaveCallFrameHandler(frameHandler); + callFrames->emplace_back(std::move(callFrame)); + callFrameId++; + } + return StackState::CONTINUE; + }; + return DebuggerApi::StackWalker(vm_, walkerFunc); +} + +void DebuggerImpl::SaveCallFrameHandler(const FrameHandler *frameHandler) +{ + auto handlerPtr = DebuggerApi::NewFrameHandler(vm_); + *handlerPtr = *frameHandler; + callFrameHandlers_.emplace_back(handlerPtr); +} + +bool DebuggerImpl::GenerateCallFrame(CallFrame *callFrame, + const FrameHandler *frameHandler, CallFrameId callFrameId) +{ + Method *method = DebuggerApi::GetMethod(frameHandler); + auto methodId = method->GetMethodId(); + JSPtExtractor *extractor = GetExtractor(method->GetJSPandaFile()); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "GenerateCallFrame: extractor is null"; + return false; + } + + // functionName + std::string functionName = method->ParseFunctionName(); + + // location + std::unique_ptr location = std::make_unique(); + std::string url = extractor->GetSourceFile(methodId); + auto scriptFunc = [&location](PtScript *script) -> bool { + location->SetScriptId(script->GetScriptId()); + return true; + }; + if (!MatchScripts(scriptFunc, url, ScriptMatchType::URL)) { + LOG_DEBUGGER(ERROR) << "GenerateCallFrame: Unknown url: " << url; + return false; + } + auto callbackLineFunc = [&location](int32_t line) -> bool { + location->SetLine(line); + return true; + }; + auto callbackColumnFunc = [&location](int32_t column) -> bool { + location->SetColumn(column); + return true; + }; + if (!extractor->MatchLineWithOffset(callbackLineFunc, methodId, DebuggerApi::GetBytecodeOffset(frameHandler)) || + !extractor->MatchColumnWithOffset(callbackColumnFunc, methodId, DebuggerApi::GetBytecodeOffset(frameHandler))) { + LOG_DEBUGGER(ERROR) << "GenerateCallFrame: unknown offset: " << DebuggerApi::GetBytecodeOffset(frameHandler); + return false; + } + + // scopeChain & this + std::unique_ptr thisObj = std::make_unique(); + thisObj->SetType(ObjectType::Undefined); + + std::vector> scopeChain; + scopeChain.emplace_back(GetLocalScopeChain(frameHandler, &thisObj)); + scopeChain.emplace_back(GetGlobalScopeChain()); + + callFrame->SetCallFrameId(callFrameId) + .SetFunctionName(functionName) + .SetLocation(std::move(location)) + .SetUrl(url) + .SetScopeChain(std::move(scopeChain)) + .SetThis(std::move(thisObj)); + return true; +} + +std::unique_ptr DebuggerImpl::GetLocalScopeChain(const FrameHandler *frameHandler, + std::unique_ptr *thisObj) +{ + auto localScope = std::make_unique(); + + Method *method = DebuggerApi::GetMethod(frameHandler); + auto methodId = method->GetMethodId(); + const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); + JSPtExtractor *extractor = GetExtractor(jsPandaFile); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "GetScopeChain: extractor is null"; + return localScope; + } + + std::unique_ptr local = std::make_unique(); + Local localObj = ObjectRef::New(vm_); + local->SetType(ObjectType::Object) + .SetObjectId(runtime_->curObjectId_) + .SetClassName(ObjectClassName::Object) + .SetDescription(RemoteObject::ObjectDescription); + auto *sp = DebuggerApi::GetSp(frameHandler); + scopeObjects_[sp] = runtime_->curObjectId_; + runtime_->properties_[runtime_->curObjectId_++] = Global(vm_, localObj); + + Local thisVal = JSValueRef::Undefined(vm_); + GetLocalVariables(frameHandler, methodId, jsPandaFile, thisVal, localObj); + *thisObj = RemoteObject::FromTagged(vm_, thisVal); + runtime_->CacheObjectIfNeeded(thisVal, (*thisObj).get()); + + const LineNumberTable &lines = extractor->GetLineNumberTable(methodId); + std::unique_ptr startLoc = std::make_unique(); + std::unique_ptr endLoc = std::make_unique(); + auto scriptFunc = [&startLoc, &endLoc, lines](PtScript *script) -> bool { + startLoc->SetScriptId(script->GetScriptId()) + .SetLine(lines.front().line) + .SetColumn(0); + endLoc->SetScriptId(script->GetScriptId()) + .SetLine(lines.back().line + 1) + .SetColumn(0); + return true; + }; + if (MatchScripts(scriptFunc, extractor->GetSourceFile(methodId), ScriptMatchType::URL)) { + localScope->SetType(Scope::Type::Local()) + .SetObject(std::move(local)) + .SetStartLocation(std::move(startLoc)) + .SetEndLocation(std::move(endLoc)); + } + + return localScope; +} + +void DebuggerImpl::GetLocalVariables(const FrameHandler *frameHandler, panda_file::File::EntityId methodId, + const JSPandaFile *jsPandaFile, Local &thisVal, Local &localObj) +{ + auto *extractor = GetExtractor(jsPandaFile); + Local value = JSValueRef::Undefined(vm_); + // in case of arrow function, which doesn't have this in local variable table + bool hasThis = false; + for (const auto &[varName, regIndex] : extractor->GetLocalVariableTable(methodId)) { + value = DebuggerApi::GetVRegValue(vm_, frameHandler, regIndex); + if (varName == "4newTarget") { + continue; + } + + if (varName == "this") { + thisVal = value; + hasThis = true; + continue; + } + Local name = JSValueRef::Undefined(vm_); + if (varName == "4funcObj") { + if (value->IsFunction()) { + auto funcName = Local(value)->GetName(vm_)->ToString(); + name = StringRef::NewFromUtf8(vm_, funcName.c_str()); + } else { + continue; + } + } else { + name = StringRef::NewFromUtf8(vm_, varName.c_str()); + } + PropertyAttribute descriptor(value, true, true, true); + localObj->DefineProperty(vm_, name, descriptor); + } + + // closure variables are stored in env + JSTaggedValue env = DebuggerApi::GetEnv(frameHandler); + if (env.IsTaggedArray() && DebuggerApi::GetBytecodeOffset(frameHandler) != 0) { + LexicalEnv *lexEnv = LexicalEnv::Cast(env.GetTaggedObject()); + if (lexEnv->GetScopeInfo().IsHole()) { + return; + } + auto ptr = JSNativePointer::Cast(lexEnv->GetScopeInfo().GetTaggedObject())->GetExternalPointer(); + auto *scopeDebugInfo = reinterpret_cast(ptr); + JSThread *thread = vm_->GetJSThread(); + for (const auto &[varName, slot] : scopeDebugInfo->scopeInfo) { + // skip possible duplicate variables both in local variable table and env + if (varName == "4newTarget") { + continue; + } + value = JSNApiHelper::ToLocal( + JSHandle(thread, lexEnv->GetProperties(slot))); + if (varName == "this") { + if (!hasThis) { + thisVal = value; + } + continue; + } + Local name = StringRef::NewFromUtf8(vm_, varName.c_str()); + PropertyAttribute descriptor(value, true, true, true); + localObj->DefineProperty(vm_, name, descriptor); + } + } +} + +std::unique_ptr DebuggerImpl::GetGlobalScopeChain() +{ + auto globalScope = std::make_unique(); + + std::unique_ptr global = std::make_unique(); + global->SetType(ObjectType::Object) + .SetObjectId(runtime_->curObjectId_) + .SetClassName(ObjectClassName::Global) + .SetDescription(RemoteObject::GlobalDescription); + globalScope->SetType(Scope::Type::Global()).SetObject(std::move(global)); + runtime_->properties_[runtime_->curObjectId_++] = Global(vm_, JSNApi::GetGlobalObject(vm_)); + return globalScope; +} + +void DebuggerImpl::UpdateScopeObject(const FrameHandler *frameHandler, + std::string_view varName, Local newVal) +{ + auto *sp = DebuggerApi::GetSp(frameHandler); + auto iter = scopeObjects_.find(sp); + if (iter == scopeObjects_.end()) { + LOG_DEBUGGER(ERROR) << "UpdateScopeObject: object not found"; + return; + } + + auto objectId = iter->second; + Local localObj = runtime_->properties_[objectId].ToLocal(vm_); + Local name = StringRef::NewFromUtf8(vm_, varName.data()); + if (localObj->Has(vm_, name)) { + LOG_DEBUGGER(DEBUG) << "UpdateScopeObject: set new value"; + PropertyAttribute descriptor(newVal, true, true, true); + localObj->DefineProperty(vm_, name, descriptor); + } else { + LOG_DEBUGGER(ERROR) << "UpdateScopeObject: not found " << varName; + } +} + +void DebuggerImpl::ClearSingleStepper() +{ + if (singleStepper_ != nullptr) { + singleStepper_.reset(); + } +} + +std::optional DebuggerImpl::CmptEvaluateValue(CallFrameId callFrameId, const std::string &expression, + std::unique_ptr *result) +{ + if (DebuggerApi::IsNativeMethod(vm_)) { + *result = RemoteObject::FromTagged(vm_, + Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Native Frame not support."))); + return "Native Frame not support."; + } + JSPtExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_)); + if (extractor == nullptr) { + *result = RemoteObject::FromTagged(vm_, + Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Internal error."))); + return "Internal error."; + } + std::string varName = expression; + std::string varValue; + std::string::size_type indexEqual = expression.find_first_of('=', 0); + if (indexEqual != std::string::npos) { + varName = Trim(expression.substr(0, indexEqual)); + varValue = Trim(expression.substr(indexEqual + 1, expression.length())); + } + + Local name = StringRef::NewFromUtf8(vm_, varName.c_str()); + FrameHandler *frameHandler = callFrameHandlers_[callFrameId].get(); + if (varValue.empty()) { + Local ret = DebuggerExecutor::GetValue(vm_, frameHandler, name); + if (!ret.IsEmpty()) { + *result = RemoteObject::FromTagged(vm_, ret); + runtime_->CacheObjectIfNeeded(ret, (*result).get()); + return {}; + } + } else { + Local value = ConvertToLocal(varValue); + if (value.IsEmpty()) { + return "Unsupported expression."; + } + JsDebuggerManager *mgr = vm_->GetJsDebuggerManager(); + mgr->SetEvalFrameHandler(callFrameHandlers_[callFrameId]); + bool ret = DebuggerExecutor::SetValue(vm_, frameHandler, name, value); + mgr->SetEvalFrameHandler(nullptr); + if (ret) { + *result = RemoteObject::FromTagged(vm_, value); + return {}; + } + } + + *result = RemoteObject::FromTagged(vm_, + Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Unsupported expression."))); + return "Unsupported expression."; +} + +Local DebuggerImpl::ConvertToLocal(const std::string &varValue) +{ + Local taggedValue; + if (varValue == "false") { + taggedValue = JSValueRef::False(vm_); + } else if (varValue == "true") { + taggedValue = JSValueRef::True(vm_); + } else if (varValue == "undefined") { + taggedValue = JSValueRef::Undefined(vm_); + } else if (varValue[0] == '\"' && varValue[varValue.length() - 1] == '\"') { + // 2 : 2 means length + taggedValue = StringRef::NewFromUtf8(vm_, varValue.substr(1, varValue.length() - 2).c_str()); + } else { + auto begin = reinterpret_cast((varValue.c_str())); + auto end = begin + varValue.length(); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + double d = DebuggerApi::StringToDouble(begin, end, 0); + if (!std::isnan(d)) { + taggedValue = NumberRef::New(vm_, d); + } + } + return taggedValue; +} + +bool DebuggerImpl::DecodeAndCheckBase64(const std::string &src, std::string &dest) +{ + uint32_t numOctets = PtBase64::Decode(src, dest); + if (numOctets > File::MAGIC_SIZE && + memcmp(dest.data(), File::MAGIC.data(), File::MAGIC_SIZE) == 0) { + return true; + } + return false; +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/agent/debugger_impl.h b/tooling/agent/debugger_impl.h new file mode 100644 index 00000000..341a65d0 --- /dev/null +++ b/tooling/agent/debugger_impl.h @@ -0,0 +1,201 @@ +/* + * 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_AGENT_DEBUGGER_IMPL_H +#define ECMASCRIPT_TOOLING_AGENT_DEBUGGER_IMPL_H + +#include "ecmascript/tooling/agent/runtime_impl.h" +#include "ecmascript/tooling/backend/js_pt_hooks.h" +#include "ecmascript/tooling/base/pt_method.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/backend/js_pt_extractor.h" +#include "ecmascript/tooling/dispatcher.h" +#include "ecmascript/tooling/interface/js_debugger_manager.h" + +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +namespace test { +class TestHooks; +} // namespace test +class DebuggerImpl final { +public: + DebuggerImpl(const EcmaVM *vm, ProtocolChannel *channel, RuntimeImpl *runtime); + ~DebuggerImpl(); + + // event + bool NotifyScriptParsed(ScriptId scriptId, const std::string &fileName, + std::string_view entryPoint = "func_main_0"); + bool NotifySingleStep(const JSPtLocation &location); + void NotifyPaused(std::optional location, PauseReason reason); + void NotifyPendingJobEntry(); + void NotifyHandleProtocolCommand(); + void NotifyNativeCalling(const void *nativeAddress); + + DispatchResponse Enable(const EnableParams ¶ms, UniqueDebuggerId *id); + DispatchResponse Disable(); + DispatchResponse EvaluateOnCallFrame(const EvaluateOnCallFrameParams ¶ms, + std::unique_ptr *result); + DispatchResponse GetPossibleBreakpoints(const GetPossibleBreakpointsParams ¶ms, + std::vector> *outLocations); + DispatchResponse GetScriptSource(const GetScriptSourceParams ¶ms, std::string *source); + DispatchResponse Pause(); + DispatchResponse RemoveBreakpoint(const RemoveBreakpointParams ¶ms); + DispatchResponse Resume(const ResumeParams ¶ms); + DispatchResponse SetAsyncCallStackDepth(); + DispatchResponse SetBreakpointByUrl(const SetBreakpointByUrlParams ¶ms, std::string *outId, + std::vector> *outLocations); + DispatchResponse SetPauseOnExceptions(const SetPauseOnExceptionsParams ¶ms); + DispatchResponse StepInto(const StepIntoParams ¶ms); + DispatchResponse StepOut(); + DispatchResponse StepOver(const StepOverParams ¶ms); + DispatchResponse SetBlackboxPatterns(); + DispatchResponse SetMixedDebugEnabled(const SetMixedDebugParams ¶ms); + DispatchResponse ReplyNativeCalling(const ReplyNativeCallingParams ¶ms); + + /** + * @brief: match first script and callback + * + * @return: true means matched and callback execute success + */ + template + bool MatchScripts(const Callback &cb, const std::string &matchStr, ScriptMatchType type) const + { + for (const auto &script : scripts_) { + std::string value; + switch (type) { + case ScriptMatchType::URL: { + value = script.second->GetUrl(); + break; + } + case ScriptMatchType::FILE_NAME: { + value = script.second->GetFileName(); + break; + } + case ScriptMatchType::HASH: { + value = script.second->GetHash(); + break; + } + default: { + return false; + } + } + if (matchStr == value) { + return cb(script.second.get()); + } + } + return false; + } + bool GenerateCallFrames(std::vector> *callFrames); + + class DispatcherImpl final : public DispatcherBase { + public: + DispatcherImpl(ProtocolChannel *channel, std::unique_ptr debugger) + : DispatcherBase(channel), debugger_(std::move(debugger)) {} + ~DispatcherImpl() override = default; + + void Dispatch(const DispatchRequest &request) override; + void Enable(const DispatchRequest &request); + void Disable(const DispatchRequest &request); + void EvaluateOnCallFrame(const DispatchRequest &request); + void GetPossibleBreakpoints(const DispatchRequest &request); + void GetScriptSource(const DispatchRequest &request); + void Pause(const DispatchRequest &request); + void RemoveBreakpoint(const DispatchRequest &request); + void Resume(const DispatchRequest &request); + void SetAsyncCallStackDepth(const DispatchRequest &request); + void SetBreakpointByUrl(const DispatchRequest &request); + void SetPauseOnExceptions(const DispatchRequest &request); + void StepInto(const DispatchRequest &request); + void StepOut(const DispatchRequest &request); + void StepOver(const DispatchRequest &request); + void SetMixedDebugEnabled(const DispatchRequest &request); + void SetBlackboxPatterns(const DispatchRequest &request); + void ReplyNativeCalling(const DispatchRequest &request); + + private: + NO_COPY_SEMANTIC(DispatcherImpl); + NO_MOVE_SEMANTIC(DispatcherImpl); + + using AgentHandler = void (DebuggerImpl::DispatcherImpl::*)(const DispatchRequest &request); + std::unique_ptr debugger_ {}; + }; + +private: + NO_COPY_SEMANTIC(DebuggerImpl); + NO_MOVE_SEMANTIC(DebuggerImpl); + + std::string Trim(const std::string &str); + JSPtExtractor *GetExtractor(const JSPandaFile *jsPandaFile); + JSPtExtractor *GetExtractor(const std::string &url); + std::optional CmptEvaluateValue(CallFrameId callFrameId, const std::string &expression, + std::unique_ptr *result); + bool GenerateCallFrame(CallFrame *callFrame, const FrameHandler *frameHandler, CallFrameId frameId); + void SaveCallFrameHandler(const FrameHandler *frameHandler); + std::unique_ptr GetLocalScopeChain(const FrameHandler *frameHandler, + std::unique_ptr *thisObj); + std::unique_ptr GetGlobalScopeChain(); + void GetLocalVariables(const FrameHandler *frameHandler, panda_file::File::EntityId methodId, + const JSPandaFile *jsPandaFile, Local &thisVal, Local &localObj); + void CleanUpOnPaused(); + void UpdateScopeObject(const FrameHandler *frameHandler, std::string_view varName, Local newVal); + void ClearSingleStepper(); + Local ConvertToLocal(const std::string &varValue); + bool DecodeAndCheckBase64(const std::string &src, std::string &dest); + bool IsSkipLine(const JSPtLocation &location); + bool CheckPauseOnException(); + + class Frontend { + public: + explicit Frontend(ProtocolChannel *channel) : channel_(channel) {} + ~Frontend() = default; + + void BreakpointResolved(const EcmaVM *vm); + void Paused(const EcmaVM *vm, const tooling::Paused &paused); + void Resumed(const EcmaVM *vm); + void NativeCalling(const EcmaVM *vm, const tooling::NativeCalling &nativeCalling); + void ScriptFailedToParse(const EcmaVM *vm); + void ScriptParsed(const EcmaVM *vm, const PtScript &script); + void WaitForDebugger(const EcmaVM *vm); + + private: + bool AllowNotify(const EcmaVM *vm) const; + + ProtocolChannel *channel_ {nullptr}; + }; + + const EcmaVM *vm_ {nullptr}; + Frontend frontend_; + + RuntimeImpl *runtime_ {nullptr}; + std::unique_ptr hooks_ {nullptr}; + JSDebugger *jsDebugger_ {nullptr}; + + std::unordered_map extractors_ {}; + std::unordered_map> scripts_ {}; + PauseOnExceptionsState pauseOnException_ {PauseOnExceptionsState::NONE}; + bool pauseOnNextByteCode_ {false}; + std::unique_ptr singleStepper_ {nullptr}; + + std::unordered_map scopeObjects_ {}; + std::vector> callFrameHandlers_; + JsDebuggerManager::ObjectUpdaterFunc updaterFunc_ {nullptr}; + JsDebuggerManager::SingleStepperFunc stepperFunc_ {nullptr}; + + friend class JSPtHooks; + friend class test::TestHooks; +}; +} // namespace panda::ecmascript::tooling +#endif \ No newline at end of file diff --git a/tooling/agent/heapprofiler_impl.cpp b/tooling/agent/heapprofiler_impl.cpp new file mode 100644 index 00000000..8111a6cd --- /dev/null +++ b/tooling/agent/heapprofiler_impl.cpp @@ -0,0 +1,342 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/agent/heapprofiler_impl.h" + +namespace panda::ecmascript::tooling { +void HeapProfilerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) +{ + static std::unordered_map dispatcherTable { + { "addInspectedHeapObject", &HeapProfilerImpl::DispatcherImpl::AddInspectedHeapObject }, + { "collectGarbage", &HeapProfilerImpl::DispatcherImpl::CollectGarbage }, + { "enable", &HeapProfilerImpl::DispatcherImpl::Enable }, + { "disable", &HeapProfilerImpl::DispatcherImpl::Disable }, + { "getHeapObjectId", &HeapProfilerImpl::DispatcherImpl::GetHeapObjectId }, + { "getObjectByHeapObjectId", &HeapProfilerImpl::DispatcherImpl::GetObjectByHeapObjectId }, + { "getSamplingProfile", &HeapProfilerImpl::DispatcherImpl::GetSamplingProfile }, + { "startSampling", &HeapProfilerImpl::DispatcherImpl::StartSampling }, + { "startTrackingHeapObjects", &HeapProfilerImpl::DispatcherImpl::StartTrackingHeapObjects }, + { "stopSampling", &HeapProfilerImpl::DispatcherImpl::StopSampling }, + { "stopTrackingHeapObjects", &HeapProfilerImpl::DispatcherImpl::StopTrackingHeapObjects }, + { "takeHeapSnapshot", &HeapProfilerImpl::DispatcherImpl::TakeHeapSnapshot } + }; + + const std::string &method = request.GetMethod(); + LOG_DEBUGGER(DEBUG) << "dispatch [" << method << "] to HeapProfilerImpl"; + auto entry = dispatcherTable.find(method); + if (entry != dispatcherTable.end() && entry->second != nullptr) { + (this->*(entry->second))(request); + } else { + SendResponse(request, DispatchResponse::Fail("Unknown method: " + method)); + } +} + +void HeapProfilerImpl::DispatcherImpl::AddInspectedHeapObject(const DispatchRequest &request) +{ + std::unique_ptr params = AddInspectedHeapObjectParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = heapprofiler_->AddInspectedHeapObject(*params); + SendResponse(request, response); +} + +void HeapProfilerImpl::DispatcherImpl::CollectGarbage(const DispatchRequest &request) +{ + DispatchResponse response = heapprofiler_->CollectGarbage(); + SendResponse(request, response); +} + +void HeapProfilerImpl::DispatcherImpl::Enable(const DispatchRequest &request) +{ + DispatchResponse response = heapprofiler_->Enable(); + SendResponse(request, response); +} + +void HeapProfilerImpl::DispatcherImpl::Disable(const DispatchRequest &request) +{ + DispatchResponse response = heapprofiler_->Disable(); + SendResponse(request, response); +} + +void HeapProfilerImpl::DispatcherImpl::GetHeapObjectId(const DispatchRequest &request) +{ + std::unique_ptr params = GetHeapObjectIdParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + + HeapSnapshotObjectId objectId; + DispatchResponse response = heapprofiler_->GetHeapObjectId(*params, &objectId); + GetHeapObjectIdReturns result(std::move(objectId)); + SendResponse(request, response, result); +} + +void HeapProfilerImpl::DispatcherImpl::GetObjectByHeapObjectId(const DispatchRequest &request) +{ + std::unique_ptr params = GetObjectByHeapObjectIdParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + + std::unique_ptr remoteObjectResult; + DispatchResponse response = heapprofiler_->GetObjectByHeapObjectId(*params, &remoteObjectResult); + if (remoteObjectResult == nullptr) { + SendResponse(request, response); + return; + } + + GetObjectByHeapObjectIdReturns result(std::move(remoteObjectResult)); + SendResponse(request, response, result); +} + +void HeapProfilerImpl::DispatcherImpl::GetSamplingProfile(const DispatchRequest &request) +{ + std::unique_ptr profile; + DispatchResponse response = heapprofiler_->GetSamplingProfile(&profile); + if (profile == nullptr) { + SendResponse(request, response); + return; + } + + // The return value type of GetSamplingProfile is the same as of StopSampling. + StopSamplingReturns result(std::move(profile)); + SendResponse(request, response, result); +} + +void HeapProfilerImpl::DispatcherImpl::StartSampling(const DispatchRequest &request) +{ + std::unique_ptr params = StartSamplingParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = heapprofiler_->StartSampling(*params); + SendResponse(request, response); +} + +void HeapProfilerImpl::DispatcherImpl::StartTrackingHeapObjects(const DispatchRequest &request) +{ + std::unique_ptr params = + StartTrackingHeapObjectsParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = heapprofiler_->StartTrackingHeapObjects(*params); + SendResponse(request, response); +} + + +void HeapProfilerImpl::DispatcherImpl::StopSampling(const DispatchRequest &request) +{ + std::unique_ptr profile; + DispatchResponse response = heapprofiler_->StopSampling(&profile); + if (profile == nullptr) { + SendResponse(request, response); + return; + } + + StopSamplingReturns result(std::move(profile)); + SendResponse(request, response, result); +} + +void HeapProfilerImpl::DispatcherImpl::StopTrackingHeapObjects(const DispatchRequest &request) +{ + std::unique_ptr params = StopTrackingHeapObjectsParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = heapprofiler_->StopTrackingHeapObjects(*params); + SendResponse(request, response); +} + +void HeapProfilerImpl::DispatcherImpl::TakeHeapSnapshot(const DispatchRequest &request) +{ + std::unique_ptr params = StopTrackingHeapObjectsParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = heapprofiler_->TakeHeapSnapshot(*params); + SendResponse(request, response); +} + +bool HeapProfilerImpl::Frontend::AllowNotify() const +{ + return channel_ != nullptr; +} + +void HeapProfilerImpl::Frontend::AddHeapSnapshotChunk(char *data, int32_t size) +{ + if (!AllowNotify()) { + return; + } + + tooling::AddHeapSnapshotChunk addHeapSnapshotChunk; + addHeapSnapshotChunk.GetChunk().resize(size); + for (int32_t i = 0; i < size; ++i) { + addHeapSnapshotChunk.GetChunk()[i] = data[i]; + } + + channel_->SendNotification(addHeapSnapshotChunk); +} + +void HeapProfilerImpl::Frontend::ReportHeapSnapshotProgress(int32_t done, int32_t total) +{ + if (!AllowNotify()) { + return; + } + + tooling::ReportHeapSnapshotProgress reportHeapSnapshotProgress; + reportHeapSnapshotProgress.SetDone(done).SetTotal(total); + if (done >= total) { + reportHeapSnapshotProgress.SetFinished(true); + } + channel_->SendNotification(reportHeapSnapshotProgress); +} + +void HeapProfilerImpl::Frontend::HeapStatsUpdate(HeapStat* updateData, int32_t count) +{ + if (!AllowNotify()) { + return; + } + std::vector statsDiff; + for (int32_t i = 0; i < count; ++i) { + statsDiff.emplace_back(updateData[i].index_); + statsDiff.emplace_back(updateData[i].count_); + statsDiff.emplace_back(updateData[i].size_); + } + tooling::HeapStatsUpdate heapStatsUpdate; + heapStatsUpdate.SetStatsUpdate(std::move(statsDiff)); + channel_->SendNotification(heapStatsUpdate); +} + +void HeapProfilerImpl::Frontend::LastSeenObjectId(int32_t lastSeenObjectId) +{ + if (!AllowNotify()) { + return; + } + + tooling::LastSeenObjectId lastSeenObjectIdEvent; + lastSeenObjectIdEvent.SetLastSeenObjectId(lastSeenObjectId); + int64_t timestamp = 0; + struct timeval tv = {0, 0}; + gettimeofday(&tv, nullptr); + const int THOUSAND = 1000; + timestamp = static_cast(tv.tv_usec + tv.tv_sec * THOUSAND * THOUSAND); + double timestampMS = static_cast(timestamp) / THOUSAND; + lastSeenObjectIdEvent.SetTimestamp(timestampMS); + channel_->SendNotification(lastSeenObjectIdEvent); +} + +void HeapProfilerImpl::Frontend::ResetProfiles() +{ + if (!AllowNotify()) { + return; + } +} + +DispatchResponse HeapProfilerImpl::AddInspectedHeapObject([[maybe_unused]] const AddInspectedHeapObjectParams ¶ms) +{ + return DispatchResponse::Fail("AddInspectedHeapObject not support now"); +} + +DispatchResponse HeapProfilerImpl::CollectGarbage() +{ + return DispatchResponse::Fail("CollectGarbage not support now"); +} + +DispatchResponse HeapProfilerImpl::Enable() +{ + return DispatchResponse::Ok(); +} + +DispatchResponse HeapProfilerImpl::Disable() +{ + return DispatchResponse::Ok(); +} + +DispatchResponse HeapProfilerImpl::GetHeapObjectId([[maybe_unused]] const GetHeapObjectIdParams ¶ms, + HeapSnapshotObjectId *objectId) +{ + ASSERT(objectId != nullptr); + *objectId = 0; + return DispatchResponse::Fail("GetHeapObjectId not support now"); +} + +DispatchResponse HeapProfilerImpl::GetObjectByHeapObjectId([[maybe_unused]] const GetObjectByHeapObjectIdParams ¶ms, + [[maybe_unused]] std::unique_ptr *remoteObjectResult) +{ + return DispatchResponse::Fail("GetObjectByHeapObjectId not support now"); +} + +DispatchResponse HeapProfilerImpl::GetSamplingProfile([[maybe_unused]]std::unique_ptr *profile) +{ + return DispatchResponse::Fail("GetSamplingProfile not support now"); +} + +DispatchResponse HeapProfilerImpl::StartSampling([[maybe_unused]]const StartSamplingParams ¶ms) +{ + return DispatchResponse::Fail("StartSampling not support now"); +} + +DispatchResponse HeapProfilerImpl::StartTrackingHeapObjects(const StartTrackingHeapObjectsParams ¶ms) +{ + bool traceAllocation = params.GetTrackAllocations(); + bool result = panda::DFXJSNApi::StartHeapTracking(vm_, INTERVAL, true, &stream_, traceAllocation); + if (result) { + return DispatchResponse::Ok(); + } else { + return DispatchResponse::Fail("StartHeapTracking fail"); + } +} + +DispatchResponse HeapProfilerImpl::StopSampling([[maybe_unused]]std::unique_ptr *profile) +{ + return DispatchResponse::Fail("StopSampling not support now."); +} + +DispatchResponse HeapProfilerImpl::StopTrackingHeapObjects(const StopTrackingHeapObjectsParams ¶ms) +{ + bool result = false; + if (params.GetReportProgress()) { + HeapProfilerProgress progress(&frontend_); + result = panda::DFXJSNApi::StopHeapTracking(vm_, &stream_, &progress); + } else { + result = panda::DFXJSNApi::StopHeapTracking(vm_, &stream_, nullptr); + } + if (result) { + return DispatchResponse::Ok(); + } else { + return DispatchResponse::Fail("StopHeapTracking fail"); + } +} + +DispatchResponse HeapProfilerImpl::TakeHeapSnapshot(const StopTrackingHeapObjectsParams ¶ms) +{ + if (params.GetReportProgress()) { + HeapProfilerProgress progress(&frontend_); + panda::DFXJSNApi::DumpHeapSnapshot(vm_, 0, &stream_, &progress, true); + } else { + panda::DFXJSNApi::DumpHeapSnapshot(vm_, 0, &stream_, nullptr, true); + } + return DispatchResponse::Ok(); +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/agent/heapprofiler_impl.h b/tooling/agent/heapprofiler_impl.h new file mode 100644 index 00000000..b88fce3e --- /dev/null +++ b/tooling/agent/heapprofiler_impl.h @@ -0,0 +1,175 @@ +/* + * 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_AGENT_HEAPPROFILER_IMPL_H +#define ECMASCRIPT_TOOLING_AGENT_HEAPPROFILER_IMPL_H + +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/dispatcher.h" +#include "ecmascript/tooling/interface/stream.h" +#include "ecmascript/tooling/interface/progress.h" +#include "ecmascript/tooling/protocol_handler.h" +#include "ecmascript/tooling/protocol_channel.h" +#include "ecmascript/napi/include/dfx_jsnapi.h" + +#include "libpandabase/macros.h" + +#include + +static const double INTERVAL = 0.05; + +namespace panda::ecmascript::tooling { +class HeapProfilerImpl final { +public: + explicit HeapProfilerImpl(const EcmaVM *vm, ProtocolChannel *channel) + : vm_(vm), frontend_(channel), stream_(&frontend_) {} + ~HeapProfilerImpl() = default; + + DispatchResponse AddInspectedHeapObject(const AddInspectedHeapObjectParams ¶ms); + DispatchResponse CollectGarbage(); + DispatchResponse Enable(); + DispatchResponse Disable(); + DispatchResponse GetHeapObjectId(const GetHeapObjectIdParams ¶ms, HeapSnapshotObjectId *objectId); + DispatchResponse GetObjectByHeapObjectId(const GetObjectByHeapObjectIdParams ¶ms, + std::unique_ptr *remoteObjectResult); + DispatchResponse GetSamplingProfile(std::unique_ptr *profile); + DispatchResponse StartSampling(const StartSamplingParams ¶ms); + DispatchResponse StartTrackingHeapObjects(const StartTrackingHeapObjectsParams ¶ms); + DispatchResponse StopSampling(std::unique_ptr *profile); + DispatchResponse StopTrackingHeapObjects(const StopTrackingHeapObjectsParams ¶ms); + // The params type of TakeHeapSnapshot is the same as of StopTrackingHeapObjects. + DispatchResponse TakeHeapSnapshot(const StopTrackingHeapObjectsParams ¶ms); + + class DispatcherImpl final : public DispatcherBase { + public: + DispatcherImpl(ProtocolChannel *channel, std::unique_ptr heapprofiler) + : DispatcherBase(channel), heapprofiler_(std::move(heapprofiler)) {} + ~DispatcherImpl() override = default; + + void Dispatch(const DispatchRequest &request) override; + void AddInspectedHeapObject(const DispatchRequest &request); + void CollectGarbage(const DispatchRequest &request); + void Enable(const DispatchRequest &request); + void Disable(const DispatchRequest &request); + void GetHeapObjectId(const DispatchRequest &request); + void GetObjectByHeapObjectId(const DispatchRequest &request); + void GetSamplingProfile(const DispatchRequest &request); + void StartSampling(const DispatchRequest &request); + void StartTrackingHeapObjects(const DispatchRequest &request); + void StopSampling(const DispatchRequest &request); + void StopTrackingHeapObjects(const DispatchRequest &request); + void TakeHeapSnapshot(const DispatchRequest &request); + + private: + NO_COPY_SEMANTIC(DispatcherImpl); + NO_MOVE_SEMANTIC(DispatcherImpl); + + using AgentHandler = void (HeapProfilerImpl::DispatcherImpl::*)(const DispatchRequest &request); + std::unique_ptr heapprofiler_ {}; + }; + +private: + NO_COPY_SEMANTIC(HeapProfilerImpl); + NO_MOVE_SEMANTIC(HeapProfilerImpl); + + class Frontend { + public: + explicit Frontend(ProtocolChannel *channel) : channel_(channel) {} + ~Frontend() = default; + + void AddHeapSnapshotChunk(char *data, int32_t size); + void ReportHeapSnapshotProgress(int32_t done, int32_t total); + void HeapStatsUpdate(HeapStat* updateData, int32_t count); + void LastSeenObjectId(int32_t lastSeenObjectId); + void ResetProfiles(); + + private: + bool AllowNotify() const; + + ProtocolChannel *channel_ {nullptr}; + }; + + class HeapProfilerStream final : public Stream { + public: + explicit HeapProfilerStream(Frontend *frontend) + : frontend_(frontend) {} + + void EndOfStream() override {} + int GetSize() override + { + static const int heapProfilerChunkSise = 100_KB; + return heapProfilerChunkSise; + } + bool WriteChunk(char *data, int32_t size) override + { + if (!Good()) { + return false; + } + frontend_->AddHeapSnapshotChunk(data, size); + return true; + } + bool Good() override + { + return frontend_ != nullptr; + } + + void UpdateHeapStats(HeapStat* updateData, int32_t count) override + { + if (!Good()) { + return; + } + frontend_->HeapStatsUpdate(updateData, count); + } + + void UpdateLastSeenObjectId(int32_t lastSeenObjectId) override + { + if (!Good()) { + return; + } + frontend_->LastSeenObjectId(lastSeenObjectId); + } + + private: + NO_COPY_SEMANTIC(HeapProfilerStream); + NO_MOVE_SEMANTIC(HeapProfilerStream); + + Frontend *frontend_ {nullptr}; + }; + + class HeapProfilerProgress final : public Progress { + public: + explicit HeapProfilerProgress(Frontend *frontend) + : frontend_(frontend) {} + + void ReportProgress(int32_t done, int32_t total) override + { + frontend_->ReportHeapSnapshotProgress(done, total); + } + + private: + NO_COPY_SEMANTIC(HeapProfilerProgress); + NO_MOVE_SEMANTIC(HeapProfilerProgress); + + Frontend *frontend_ {nullptr}; + }; + + const EcmaVM *vm_ {nullptr}; + Frontend frontend_; + HeapProfilerStream stream_; +}; +} // namespace panda::ecmascript::tooling +#endif \ No newline at end of file diff --git a/tooling/agent/profiler_impl.cpp b/tooling/agent/profiler_impl.cpp new file mode 100644 index 00000000..b0541493 --- /dev/null +++ b/tooling/agent/profiler_impl.cpp @@ -0,0 +1,241 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/agent/profiler_impl.h" + +#include "ecmascript/napi/include/dfx_jsnapi.h" +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/protocol_channel.h" + +namespace panda::ecmascript::tooling { +void ProfilerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) +{ + static std::unordered_map dispatcherTable { + { "disable", &ProfilerImpl::DispatcherImpl::Disable }, + { "enable", &ProfilerImpl::DispatcherImpl::Enable }, + { "start", &ProfilerImpl::DispatcherImpl::Start }, + { "stop", &ProfilerImpl::DispatcherImpl::Stop }, + { "SetSamplingInterval", &ProfilerImpl::DispatcherImpl::SetSamplingInterval }, + { "getBestEffortCoverage", &ProfilerImpl::DispatcherImpl::GetBestEffortCoverage }, + { "stopPreciseCoverage", &ProfilerImpl::DispatcherImpl::StopPreciseCoverage }, + { "takePreciseCoverage", &ProfilerImpl::DispatcherImpl::TakePreciseCoverage }, + { "startPreciseCoverage", &ProfilerImpl::DispatcherImpl::StartPreciseCoverage }, + { "startTypeProfile", &ProfilerImpl::DispatcherImpl::StartTypeProfile }, + { "stopTypeProfile", &ProfilerImpl::DispatcherImpl::StopTypeProfile }, + { "takeTypeProfile", &ProfilerImpl::DispatcherImpl::TakeTypeProfile } + }; + + const std::string &method = request.GetMethod(); + LOG_DEBUGGER(DEBUG) << "dispatch [" << method << "] to ProfilerImpl"; + auto entry = dispatcherTable.find(method); + if (entry != dispatcherTable.end() && entry->second != nullptr) { + (this->*(entry->second))(request); + } else { + SendResponse(request, DispatchResponse::Fail("Unknown method: " + method)); + } +} + +void ProfilerImpl::DispatcherImpl::Disable(const DispatchRequest &request) +{ + DispatchResponse response = profiler_->Disable(); + SendResponse(request, response); +} + +void ProfilerImpl::DispatcherImpl::Enable(const DispatchRequest &request) +{ + DispatchResponse response = profiler_->Enable(); + SendResponse(request, response); +} + +void ProfilerImpl::DispatcherImpl::Start(const DispatchRequest &request) +{ + DispatchResponse response = profiler_->Start(); + SendResponse(request, response); +} + +void ProfilerImpl::DispatcherImpl::Stop(const DispatchRequest &request) +{ + std::unique_ptr profile; + DispatchResponse response = profiler_->Stop(&profile); + if (profile == nullptr) { + SendResponse(request, response); + return; + } + + StopReturns result(std::move(profile)); + SendResponse(request, response, result); +} + +void ProfilerImpl::DispatcherImpl::SetSamplingInterval(const DispatchRequest &request) +{ + std::unique_ptr params = SetSamplingIntervalParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = profiler_->SetSamplingInterval(*params); + SendResponse(request, response); +} + +void ProfilerImpl::DispatcherImpl::GetBestEffortCoverage(const DispatchRequest &request) +{ + DispatchResponse response = profiler_->GetBestEffortCoverage(); + SendResponse(request, response); +} + +void ProfilerImpl::DispatcherImpl::StopPreciseCoverage(const DispatchRequest &request) +{ + DispatchResponse response = profiler_->StopPreciseCoverage(); + SendResponse(request, response); +} + +void ProfilerImpl::DispatcherImpl::TakePreciseCoverage(const DispatchRequest &request) +{ + DispatchResponse response = profiler_->TakePreciseCoverage(); + SendResponse(request, response); +} + +void ProfilerImpl::DispatcherImpl::StartPreciseCoverage(const DispatchRequest &request) +{ + std::unique_ptr params = StartPreciseCoverageParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = profiler_->StartPreciseCoverage(*params); + SendResponse(request, response); +} + +void ProfilerImpl::DispatcherImpl::StartTypeProfile(const DispatchRequest &request) +{ + DispatchResponse response = profiler_->StartTypeProfile(); + SendResponse(request, response); +} + +void ProfilerImpl::DispatcherImpl::StopTypeProfile(const DispatchRequest &request) +{ + DispatchResponse response = profiler_->StopTypeProfile(); + SendResponse(request, response); +} + +void ProfilerImpl::DispatcherImpl::TakeTypeProfile(const DispatchRequest &request) +{ + DispatchResponse response = profiler_->TakeTypeProfile(); + SendResponse(request, response); +} + +bool ProfilerImpl::Frontend::AllowNotify() const +{ + return channel_ != nullptr; +} + +void ProfilerImpl::Frontend::ConsoleProfileFinished() +{ + if (!AllowNotify()) { + return; + } + + tooling::ConsoleProfileFinished consoleProfileFinished; + channel_->SendNotification(consoleProfileFinished); +} + +void ProfilerImpl::Frontend::ConsoleProfileStarted() +{ + if (!AllowNotify()) { + return; + } + + tooling::ConsoleProfileStarted consoleProfileStarted; + channel_->SendNotification(consoleProfileStarted); +} + +void ProfilerImpl::Frontend::PreciseCoverageDeltaUpdate() +{ + if (!AllowNotify()) { + return; + } + + tooling::PreciseCoverageDeltaUpdate preciseCoverageDeltaUpdate; + channel_->SendNotification(preciseCoverageDeltaUpdate); +} + +DispatchResponse ProfilerImpl::Disable() +{ + return DispatchResponse::Ok(); +} + +DispatchResponse ProfilerImpl::Enable() +{ + return DispatchResponse::Ok(); +} + +DispatchResponse ProfilerImpl::Start() +{ + panda::DFXJSNApi::StartCpuProfilerForInfo(vm_); + return DispatchResponse::Ok(); +} + +DispatchResponse ProfilerImpl::Stop(std::unique_ptr *profile) +{ + auto profileInfo = panda::DFXJSNApi::StopCpuProfilerForInfo(vm_); + if (profileInfo == nullptr) { + LOG_DEBUGGER(ERROR) << "Transfer DFXJSNApi::StopCpuProfilerImpl is failure"; + return DispatchResponse::Fail("Stop is failure"); + } + *profile = Profile::FromProfileInfo(*profileInfo); + return DispatchResponse::Ok(); +} + +DispatchResponse ProfilerImpl::SetSamplingInterval(const SetSamplingIntervalParams ¶ms) +{ + panda::DFXJSNApi::SetCpuSamplingInterval(vm_, params.GetInterval()); + return DispatchResponse::Ok(); +} + +DispatchResponse ProfilerImpl::GetBestEffortCoverage() +{ + return DispatchResponse::Fail("GetBestEffortCoverage not support now"); +} + +DispatchResponse ProfilerImpl::StopPreciseCoverage() +{ + return DispatchResponse::Fail("StopPreciseCoverage not support now"); +} + +DispatchResponse ProfilerImpl::TakePreciseCoverage() +{ + return DispatchResponse::Fail("TakePreciseCoverage not support now"); +} + +DispatchResponse ProfilerImpl::StartPreciseCoverage([[maybe_unused]] const StartPreciseCoverageParams ¶ms) +{ + return DispatchResponse::Fail("StartPreciseCoverage not support now"); +} + +DispatchResponse ProfilerImpl::StartTypeProfile() +{ + return DispatchResponse::Fail("StartTypeProfile not support now"); +} + +DispatchResponse ProfilerImpl::StopTypeProfile() +{ + return DispatchResponse::Fail("StopTypeProfile not support now"); +} + +DispatchResponse ProfilerImpl::TakeTypeProfile() +{ + return DispatchResponse::Fail("TakeTypeProfile not support now"); +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/agent/profiler_impl.h b/tooling/agent/profiler_impl.h new file mode 100644 index 00000000..bd76841b --- /dev/null +++ b/tooling/agent/profiler_impl.h @@ -0,0 +1,95 @@ +/* + * 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_AGENT_PROFILER_IMPL_H +#define ECMASCRIPT_TOOLING_AGENT_PROFILER_IMPL_H + +#include "ecmascript/dfx/cpu_profiler/samples_record.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/dispatcher.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +class ProfilerImpl final { +public: + ProfilerImpl(const EcmaVM *vm, ProtocolChannel *channel) : vm_(vm), frontend_(channel) {} + ~ProfilerImpl() = default; + + DispatchResponse Disable(); + DispatchResponse Enable(); + DispatchResponse Start(); + DispatchResponse Stop(std::unique_ptr *profile); + DispatchResponse SetSamplingInterval(const SetSamplingIntervalParams ¶ms); + DispatchResponse GetBestEffortCoverage(); + DispatchResponse StopPreciseCoverage(); + DispatchResponse TakePreciseCoverage(); + DispatchResponse StartPreciseCoverage (const StartPreciseCoverageParams ¶ms); + DispatchResponse StartTypeProfile(); + DispatchResponse StopTypeProfile(); + DispatchResponse TakeTypeProfile(); + + class DispatcherImpl final : public DispatcherBase { + public: + DispatcherImpl(ProtocolChannel *channel, std::unique_ptr profiler) + : DispatcherBase(channel), profiler_(std::move(profiler)) {} + ~DispatcherImpl() override = default; + + void Dispatch(const DispatchRequest &request) override; + void Enable(const DispatchRequest &request); + void Disable(const DispatchRequest &request); + void Start(const DispatchRequest &request); + void Stop(const DispatchRequest &request); + void SetSamplingInterval(const DispatchRequest &request); + void GetBestEffortCoverage(const DispatchRequest &request); + void StopPreciseCoverage(const DispatchRequest &request); + void TakePreciseCoverage(const DispatchRequest &request); + void StartPreciseCoverage(const DispatchRequest &request); + void StartTypeProfile(const DispatchRequest &request); + void StopTypeProfile(const DispatchRequest &request); + void TakeTypeProfile(const DispatchRequest &request); + + private: + NO_COPY_SEMANTIC(DispatcherImpl); + NO_MOVE_SEMANTIC(DispatcherImpl); + + using AgentHandler = void (ProfilerImpl::DispatcherImpl::*)(const DispatchRequest &request); + std::unique_ptr profiler_ {}; + }; + + class Frontend { + public: + explicit Frontend(ProtocolChannel *channel) : channel_(channel) {} + ~Frontend() = default; + + void ConsoleProfileFinished(); + void ConsoleProfileStarted(); + void PreciseCoverageDeltaUpdate(); + + private: + bool AllowNotify() const; + + ProtocolChannel *channel_ {nullptr}; + }; + +private: + NO_COPY_SEMANTIC(ProfilerImpl); + NO_MOVE_SEMANTIC(ProfilerImpl); + + const EcmaVM *vm_ {nullptr}; + [[maybe_unused]] Frontend frontend_; +}; +} // namespace panda::ecmascript::tooling +#endif diff --git a/tooling/agent/runtime_impl.cpp b/tooling/agent/runtime_impl.cpp new file mode 100644 index 00000000..87334692 --- /dev/null +++ b/tooling/agent/runtime_impl.cpp @@ -0,0 +1,621 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/agent/runtime_impl.h" + +#include + +#include "ecmascript/napi/include/dfx_jsnapi.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/protocol_channel.h" + +namespace panda::ecmascript::tooling { +void RuntimeImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) +{ + static std::unordered_map dispatcherTable { + { "enable", &RuntimeImpl::DispatcherImpl::Enable }, + { "getProperties", &RuntimeImpl::DispatcherImpl::GetProperties }, + { "runIfWaitingForDebugger", &RuntimeImpl::DispatcherImpl::RunIfWaitingForDebugger }, + { "callFunctionOn", &RuntimeImpl::DispatcherImpl::CallFunctionOn }, + { "getHeapUsage", &RuntimeImpl::DispatcherImpl::GetHeapUsage } + }; + + const std::string &method = request.GetMethod(); + LOG_DEBUGGER(DEBUG) << "dispatch [" << method << "] to RuntimeImpl"; + + auto entry = dispatcherTable.find(method); + if (entry != dispatcherTable.end()) { + (this->*(entry->second))(request); + } else { + LOG_DEBUGGER(ERROR) << "unknown method: " << method; + SendResponse(request, DispatchResponse::Fail("unknown method: " + method)); + } +} + +void RuntimeImpl::DispatcherImpl::Enable(const DispatchRequest &request) +{ + DispatchResponse response = runtime_->Enable(); + SendResponse(request, response); +} + +void RuntimeImpl::DispatcherImpl::Disable(const DispatchRequest &request) +{ + DispatchResponse response = runtime_->Disable(); + SendResponse(request, response); +} + +void RuntimeImpl::DispatcherImpl::RunIfWaitingForDebugger(const DispatchRequest &request) +{ + DispatchResponse response = runtime_->RunIfWaitingForDebugger(); + SendResponse(request, response); +} + +void RuntimeImpl::DispatcherImpl::GetProperties(const DispatchRequest &request) +{ + std::unique_ptr params = GetPropertiesParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + + std::vector> outPropertyDesc; + std::optional>> outInternalDescs; + std::optional>> outPrivateProperties; + std::optional> outExceptionDetails; + DispatchResponse response = runtime_->GetProperties(*params, &outPropertyDesc, &outInternalDescs, + &outPrivateProperties, &outExceptionDetails); + if (outExceptionDetails) { + ASSERT(outExceptionDetails.value() != nullptr); + LOG_DEBUGGER(WARN) << "GetProperties thrown an exception"; + } + GetPropertiesReturns result(std::move(outPropertyDesc), + std::move(outInternalDescs), + std::move(outPrivateProperties), + std::move(outExceptionDetails)); + SendResponse(request, response, result); +} + +void RuntimeImpl::DispatcherImpl::CallFunctionOn(const DispatchRequest &request) +{ + std::unique_ptr params = CallFunctionOnParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + + std::unique_ptr outRemoteObject; + std::optional> outExceptionDetails; + DispatchResponse response = runtime_->CallFunctionOn(*params, &outRemoteObject, &outExceptionDetails); + if (outExceptionDetails) { + ASSERT(outExceptionDetails.value() != nullptr); + LOG_DEBUGGER(WARN) << "CallFunctionOn thrown an exception"; + } + if (outRemoteObject == nullptr) { + SendResponse(request, response); + return; + } + + CallFunctionOnReturns result(std::move(outRemoteObject), std::move(outExceptionDetails)); + SendResponse(request, response, result); +} + +void RuntimeImpl::DispatcherImpl::GetHeapUsage(const DispatchRequest &request) +{ + double usedSize = 0; + double totalSize = 0; + DispatchResponse response = runtime_->GetHeapUsage(&usedSize, &totalSize); + GetHeapUsageReturns result(usedSize, totalSize); + SendResponse(request, response, result); +} + +bool RuntimeImpl::Frontend::AllowNotify() const +{ + return channel_ != nullptr; +} + +void RuntimeImpl::Frontend::RunIfWaitingForDebugger() +{ + if (!AllowNotify()) { + return; + } + + channel_->RunIfWaitingForDebugger(); +} + +DispatchResponse RuntimeImpl::Enable() +{ + return DispatchResponse::Ok(); +} + +DispatchResponse RuntimeImpl::Disable() +{ + return DispatchResponse::Ok(); +} + +DispatchResponse RuntimeImpl::RunIfWaitingForDebugger() +{ + frontend_.RunIfWaitingForDebugger(); + return DispatchResponse::Ok(); +} + +DispatchResponse RuntimeImpl::CallFunctionOn([[maybe_unused]] const CallFunctionOnParams ¶ms, + std::unique_ptr *outRemoteObject, + [[maybe_unused]] std::optional> *outExceptionDetails) +{ + // Return EvalError temporarily. + auto error = Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Unsupport eval now")); + *outRemoteObject = RemoteObject::FromTagged(vm_, error); + return DispatchResponse::Ok(); +} + +DispatchResponse RuntimeImpl::GetHeapUsage(double *usedSize, double *totalSize) +{ +#ifdef ECMASCRIPT_SUPPORT_HEAPPROFILER + *totalSize = static_cast(DFXJSNApi::GetHeapTotalSize(vm_)); + *usedSize = static_cast(DFXJSNApi::GetHeapUsedSize(vm_)); +#else + *totalSize = 0; + *usedSize = 0; +#endif + return DispatchResponse::Ok(); +} + +DispatchResponse RuntimeImpl::GetProperties(const GetPropertiesParams ¶ms, + std::vector> *outPropertyDesc, + [[maybe_unused]] std::optional>> *outInternalDescs, + [[maybe_unused]] std::optional>> *outPrivateProps, + [[maybe_unused]] std::optional> *outExceptionDetails) +{ + RemoteObjectId objectId = params.GetObjectId(); + bool isOwn = params.GetOwnProperties(); + bool isAccessorOnly = params.GetAccessPropertiesOnly(); + auto iter = properties_.find(objectId); + if (iter == properties_.end()) { + LOG_DEBUGGER(ERROR) << "RuntimeImpl::GetProperties Unknown object id: " << objectId; + return DispatchResponse::Fail("Unknown object id"); + } + Local value = Local(vm_, iter->second); + if (value.IsEmpty() || !value->IsObject()) { + LOG_DEBUGGER(ERROR) << "RuntimeImpl::GetProperties should a js object"; + return DispatchResponse::Fail("Not a object"); + } + bool skipProto = false; + if (!internalObjects_.IsEmpty() && internalObjects_->Get(vm_, value)->IsNumber()) { + if (static_cast(internalObjects_->Get(vm_, value)->ToNumber(vm_)->Value()) == + ArkInternalValueType::Entry) { + skipProto = true; + } + } + if (value->IsArrayBuffer()) { + Local arrayBufferRef(value); + AddTypedArrayRefs(arrayBufferRef, outPropertyDesc); + } else if (value->IsSharedArrayBuffer()) { + Local arrayBufferRef(value); + AddSharedArrayBufferRefs(arrayBufferRef, outPropertyDesc); + } else if (value->IsMapIterator()) { + GetMapIteratorValue(value, outPropertyDesc); + } else if (value->IsSetIterator()) { + GetSetIteratorValue(value, outPropertyDesc); + } else if (value->IsJSPrimitiveRef() && value->IsJSPrimitiveNumber()) { + GetPrimitiveNumberValue(value, outPropertyDesc); + } else if (value->IsJSPrimitiveRef() && value->IsJSPrimitiveString()) { + GetPrimitiveStringValue(value, outPropertyDesc); + } else if (value->IsJSPrimitiveRef() && value->IsJSPrimitiveBoolean()) { + GetPrimitiveBooleanValue(value, outPropertyDesc); + } else if (value->IsGeneratorFunction()) { + GetGeneratorFunctionValue(value, outPropertyDesc); + } else if (value->IsGeneratorObject()) { + GetGeneratorObjectValue(value, outPropertyDesc); + } else if (value->IsJSNumberFormat()) { + GetNumberFormatValue(value, outPropertyDesc); + } else if (value->IsJSCollator()) { + GetCollatorValue(value, outPropertyDesc); + } else if (value->IsJSDateTimeFormat()) { + GetDateTimeFormatValue(value, outPropertyDesc); + } else if (value->IsMap()) { + GetMapValue(value, outPropertyDesc); + } else if (value->IsRegExp()) { + GetRegExpValue(value, outPropertyDesc); + } else if (value->IsSet()) { + GetSetValue(value, outPropertyDesc); + } + Local keys = Local(value)->GetOwnPropertyNames(vm_); + int32_t length = keys->Length(vm_); + Local name = JSValueRef::Undefined(vm_); + for (int32_t i = 0; i < length; ++i) { + name = keys->Get(vm_, i); + PropertyAttribute jsProperty = PropertyAttribute::Default(); + if (!Local(value)->GetOwnProperty(vm_, name, jsProperty)) { + continue; + } + std::unique_ptr debuggerProperty = + PropertyDescriptor::FromProperty(vm_, name, jsProperty); + if (isAccessorOnly && !jsProperty.HasGetter() && !jsProperty.HasSetter()) { + continue; + } + if (debuggerProperty->HasGet()) { + debuggerProperty->GetGet()->SetObjectId(curObjectId_); + properties_[curObjectId_++] = Global(vm_, jsProperty.GetGetter(vm_)); + } + if (debuggerProperty->HasSet()) { + debuggerProperty->GetSet()->SetObjectId(curObjectId_); + properties_[curObjectId_++] = Global(vm_, jsProperty.GetSetter(vm_)); + } + if (debuggerProperty->HasValue()) { + Local vValue = jsProperty.GetValue(vm_); + if (vValue->IsObject() && !vValue->IsProxy()) { + debuggerProperty->GetValue()->SetObjectId(curObjectId_); + properties_[curObjectId_++] = Global(vm_, vValue); + } + } + if (debuggerProperty->HasSymbol()) { + debuggerProperty->GetSymbol()->SetObjectId(curObjectId_); + properties_[curObjectId_++] = Global(vm_, name); + } + outPropertyDesc->emplace_back(std::move(debuggerProperty)); + } + if (!skipProto) { + GetProtoOrProtoType(value, isOwn, isAccessorOnly, outPropertyDesc); + } + GetAdditionalProperties(value, outPropertyDesc); + + return DispatchResponse::Ok(); +} + +void RuntimeImpl::AddTypedArrayRefs(Local arrayBufferRef, + std::vector> *outPropertyDesc) +{ + int32_t arrayBufferByteLength = arrayBufferRef->ByteLength(vm_); + int32_t typedArrayLength = arrayBufferByteLength; + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Int8Array]]", outPropertyDesc); + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Uint8Array]]", outPropertyDesc); + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Uint8ClampedArray]]", outPropertyDesc); + + if ((arrayBufferByteLength % NumberSize::BYTES_OF_16BITS) == 0) { + typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_16BITS; + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Int16Array]]", outPropertyDesc); + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Uint16Array]]", outPropertyDesc); + } + + if ((arrayBufferByteLength % NumberSize::BYTES_OF_32BITS) == 0) { + typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_32BITS; + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Int32Array]]", outPropertyDesc); + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Uint32Array]]", outPropertyDesc); + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Float32Array]]", outPropertyDesc); + } + + if ((arrayBufferByteLength % NumberSize::BYTES_OF_64BITS) == 0) { + typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_64BITS; + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Float64Array]]", outPropertyDesc); + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[BigInt64Array]]", outPropertyDesc); + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[BigUint64Array]]", outPropertyDesc); + } +} + +void RuntimeImpl::AddSharedArrayBufferRefs(Local arrayBufferRef, + std::vector> *outPropertyDesc) +{ + int32_t arrayBufferByteLength = arrayBufferRef->ByteLength(vm_); + int32_t typedArrayLength = arrayBufferByteLength; + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Int8Array]]", outPropertyDesc); + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Uint8Array]]", outPropertyDesc); + + if ((arrayBufferByteLength % NumberSize::BYTES_OF_16BITS) == 0) { + typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_16BITS; + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Int16Array]]", outPropertyDesc); + } + + if ((arrayBufferByteLength % NumberSize::BYTES_OF_32BITS) == 0) { + typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_32BITS; + AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Int32Array]]", outPropertyDesc); + } + Local jsValueRef; + jsValueRef = NumberRef::New(vm_, arrayBufferByteLength); + SetKeyValue(jsValueRef, outPropertyDesc, "[[ArrayBufferByteLength]]"); + SetKeyValue(jsValueRef, outPropertyDesc, "byteLength"); +} + +template +void RuntimeImpl::AddTypedArrayRef(Local arrayBufferRef, int32_t length, const char* name, + std::vector> *outPropertyDesc) +{ + Local jsValueRefTypedArray(TypedArrayRef::New(vm_, arrayBufferRef, 0, length)); + std::unique_ptr remoteObjectTypedArray = RemoteObject::FromTagged(vm_, jsValueRefTypedArray); + remoteObjectTypedArray->SetObjectId(curObjectId_); + properties_[curObjectId_++] = Global(vm_, jsValueRefTypedArray); + std::unique_ptr debuggerProperty = std::make_unique(); + debuggerProperty->SetName(name) + .SetWritable(true) + .SetConfigurable(true) + .SetEnumerable(false) + .SetIsOwn(true) + .SetValue(std::move(remoteObjectTypedArray)); + outPropertyDesc->emplace_back(std::move(debuggerProperty)); +} + +void RuntimeImpl::CacheObjectIfNeeded(Local valRef, RemoteObject *remoteObj) +{ + if (valRef->IsObject() && !valRef->IsProxy()) { + remoteObj->SetObjectId(curObjectId_); + properties_[curObjectId_++] = Global(vm_, valRef); + } +} + +void RuntimeImpl::GetProtoOrProtoType(Local value, bool isOwn, bool isAccessorOnly, + std::vector> *outPropertyDesc) +{ + if (!isAccessorOnly && isOwn && !value->IsProxy()) { + return; + } + // Get Function ProtoOrHClass + if (value->IsConstructor()) { + Local prototype = Local(value)->GetFunctionPrototype(vm_); + std::unique_ptr protoObj = RemoteObject::FromTagged(vm_, prototype); + CacheObjectIfNeeded(prototype, protoObj.get()); + std::unique_ptr debuggerProperty = std::make_unique(); + debuggerProperty->SetName("prototype") + .SetWritable(false) + .SetConfigurable(false) + .SetEnumerable(false) + .SetIsOwn(true) + .SetValue(std::move(protoObj)); + outPropertyDesc->emplace_back(std::move(debuggerProperty)); + } + // Get __proto__ + Local proto = Local(value)->GetPrototype(vm_); + std::unique_ptr protoObj = RemoteObject::FromTagged(vm_, proto); + CacheObjectIfNeeded(proto, protoObj.get()); + std::unique_ptr debuggerProperty = std::make_unique(); + debuggerProperty->SetName("__proto__") + .SetWritable(true) + .SetConfigurable(true) + .SetEnumerable(false) + .SetIsOwn(true) + .SetValue(std::move(protoObj)); + outPropertyDesc->emplace_back(std::move(debuggerProperty)); +} + +void RuntimeImpl::GetAdditionalProperties(Local value, + std::vector> *outPropertyDesc) +{ + // The length of the TypedArray have to be limited(less than or equal to lengthTypedArrayLimit) until we construct + // the PropertyPreview class. Let lengthTypedArrayLimit be 10000 temporarily. + static const uint32_t lengthTypedArrayLimit = 10000; + + // The width of the string-expression for JSTypedArray::MAX_TYPED_ARRAY_INDEX which is euqal to + // JSObject::MAX_ELEMENT_INDEX which is equal to std::numeric_limits::max(). (42,9496,7295) + static const int32_t widthStrExprMaxElementIndex = 10; + + if (value->IsTypedArray()) { + Local localTypedArrayRef(value); + uint32_t lengthTypedArray = localTypedArrayRef->ArrayLength(vm_); + if (lengthTypedArray > lengthTypedArrayLimit) { + LOG_DEBUGGER(ERROR) << "The length of the TypedArray is non-compliant or unsupported."; + return; + } + for (uint32_t i = 0; i < lengthTypedArray; i++) { + Local localValRefElement = localTypedArrayRef->Get(vm_, i); + std::unique_ptr remoteObjElement = RemoteObject::FromTagged(vm_, localValRefElement); + remoteObjElement->SetObjectId(curObjectId_); + properties_[curObjectId_++] = Global(vm_, localValRefElement); + std::unique_ptr debuggerProperty = std::make_unique(); + + std::ostringstream osNameElement; + osNameElement << std::right << std::setw(widthStrExprMaxElementIndex) << i; + std::string cStrNameElement = osNameElement.str(); + debuggerProperty->SetName(cStrNameElement) + .SetWritable(true) + .SetConfigurable(true) + .SetEnumerable(false) + .SetIsOwn(true) + .SetValue(std::move(remoteObjElement)); + outPropertyDesc->emplace_back(std::move(debuggerProperty)); + } + } +} + +void RuntimeImpl::SetKeyValue(Local &jsValueRef, + std::vector> *outPropertyDesc, const std::string &strProName) +{ + std::unique_ptr remoteObj = RemoteObject::FromTagged(vm_, jsValueRef); + remoteObj->SetObjectId(curObjectId_); + properties_[curObjectId_++] = Global(vm_, jsValueRef); + std::unique_ptr debuggerProperty = std::make_unique(); + debuggerProperty->SetName(strProName) + .SetWritable(false) + .SetConfigurable(false) + .SetEnumerable(false) + .SetIsOwn(false) + .SetValue(std::move(remoteObj)); + outPropertyDesc->emplace_back(std::move(debuggerProperty)); +} + +void RuntimeImpl::GetPrimitiveNumberValue(Local value, + std::vector> *outPropertyDesc) +{ + Local jsValueRef; + jsValueRef = value->ToNumber(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "[[PrimitiveValue]]"); +} + +void RuntimeImpl::GetPrimitiveStringValue(Local value, + std::vector> *outPropertyDesc) +{ + Local jsValueRef; + jsValueRef = value->ToString(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "[[PrimitiveValue]]"); +} + +void RuntimeImpl::GetPrimitiveBooleanValue(Local value, + std::vector> *outPropertyDesc) +{ + Local jsValueRef; + jsValueRef = value->ToBoolean(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "[[PrimitiveValue]]"); +} + +void RuntimeImpl::GetMapIteratorValue(Local value, + std::vector> *outPropertyDesc) +{ + Local jsValueRef; + Local iterRef = value->ToObject(vm_); + if (!iterRef.IsEmpty()) { + jsValueRef = NumberRef::New(vm_, iterRef->GetIndex()); + SetKeyValue(jsValueRef, outPropertyDesc, "[[IteratorIndex]]"); + jsValueRef = iterRef->GetKind(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "[[IteratorKind]]"); + } +} + +void RuntimeImpl::GetSetIteratorValue(Local value, + std::vector> *outPropertyDesc) +{ + Local jsValueRef; + Local iterRef = value->ToObject(vm_); + if (!iterRef.IsEmpty()) { + jsValueRef = NumberRef::New(vm_, iterRef->GetIndex()); + SetKeyValue(jsValueRef, outPropertyDesc, "[[IteratorIndex]]"); + jsValueRef = iterRef->GetKind(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "[[IteratorKind]]"); + } +} + +void RuntimeImpl::GetGeneratorFunctionValue(Local value, + std::vector> *outPropertyDesc) +{ + Local jsValueRef; + Local genFuncRef = value->ToObject(vm_); + if (!genFuncRef.IsEmpty()) { + jsValueRef = BooleanRef::New(vm_, genFuncRef->IsGenerator()); + SetKeyValue(jsValueRef, outPropertyDesc, "[[IsGenerator]]"); + } +} + +void RuntimeImpl::GetGeneratorObjectValue(Local value, + std::vector> *outPropertyDesc) +{ + Local jsValueRef; + Local genObjRef = value->ToObject(vm_); + if (!genObjRef.IsEmpty()) { + jsValueRef = genObjRef->GetGeneratorState(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "[[GeneratorState]]"); + jsValueRef = genObjRef->GetGeneratorFunction(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "[[GeneratorFunction]]"); + jsValueRef = JSNApi::GetGlobalObject(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "[[GeneratorReceiver]]"); + } +} + +void RuntimeImpl::GetNumberFormatValue(Local value, + std::vector> *outPropertyDesc) +{ + Local numberFormatRef = value->ToObject(vm_); + Local jsValueRef = numberFormatRef->GetFormatFunction(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "format"); +} + +void RuntimeImpl::GetCollatorValue(Local value, + std::vector> *outPropertyDesc) +{ + Local collatorRef = value->ToObject(vm_); + Local jsValueRef = collatorRef->GetCompareFunction(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "compare"); +} + +void RuntimeImpl::GetDateTimeFormatValue(Local value, + std::vector> *outPropertyDesc) +{ + Local dtFormatRef = value->ToObject(vm_); + Local jsValueRef = dtFormatRef->GetFormatFunction(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "format"); +} + +void RuntimeImpl::AddInternalProperties(Local objRef, ArkInternalValueType type) +{ + if (internalObjects_.IsEmpty()) { + internalObjects_ = Global(vm_, MapRef::New(vm_)); + } + internalObjects_->Set(vm_, objRef, NumberRef::New(vm_, static_cast(type))); +} + +void RuntimeImpl::GetMapValue(Local value, + std::vector> *outPropertyDesc) +{ + Local mapRef = value->ToObject(vm_); + int32_t len = mapRef->GetSize(); + Local jsValueRef = NumberRef::New(vm_, len); + SetKeyValue(jsValueRef, outPropertyDesc, "size"); + jsValueRef = ArrayRef::New(vm_, len); + for (int32_t i = 0; i < len; i++) { + Local jsKey = mapRef->GetKey(vm_, i); + Local jsValue = mapRef->GetValue(vm_, i); + Local objRef = ObjectRef::New(vm_); + objRef->Set(vm_, StringRef::NewFromUtf8(vm_, "key"), jsKey); + objRef->Set(vm_, StringRef::NewFromUtf8(vm_, "value"), jsValue); + AddInternalProperties(objRef, ArkInternalValueType::Entry); + ArrayRef::SetValueAt(vm_, jsValueRef, i, objRef); + } + AddInternalProperties(jsValueRef, ArkInternalValueType::Entry); + SetKeyValue(jsValueRef, outPropertyDesc, "[[Entries]]"); +} + +void RuntimeImpl::GetSetValue(Local value, + std::vector> *outPropertyDesc) +{ + Local setRef = value->ToObject(vm_); + int32_t len = setRef->GetSize(); + Local jsValueRef = NumberRef::New(vm_, len); + SetKeyValue(jsValueRef, outPropertyDesc, "size"); + jsValueRef = ArrayRef::New(vm_, len); + for (int32_t i = 0; i < len; i++) { + Local elementRef = setRef->GetValue(vm_, i); + if (elementRef->IsObject()) { + Local objRef = ObjectRef::New(vm_); + objRef->Set(vm_, StringRef::NewFromUtf8(vm_, "value"), elementRef); + AddInternalProperties(objRef, ArkInternalValueType::Entry); + ArrayRef::SetValueAt(vm_, jsValueRef, i, objRef); + } else { + ArrayRef::SetValueAt(vm_, jsValueRef, i, elementRef); + } + } + AddInternalProperties(jsValueRef, ArkInternalValueType::Entry); + SetKeyValue(jsValueRef, outPropertyDesc, "[[Entries]]"); +} + +void RuntimeImpl::GetRegExpValue(Local value, + std::vector> *outPropertyDesc) +{ + Local regExpRef = value->ToObject(vm_); + Local jsValueRef = regExpRef->IsGlobal(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "global"); + jsValueRef = regExpRef->IsIgnoreCase(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "ignoreCase"); + jsValueRef = regExpRef->IsMultiline(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "multiline"); + jsValueRef = regExpRef->IsDotAll(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "dotAll"); + SetKeyValue(jsValueRef, outPropertyDesc, "hasIndices"); + jsValueRef = regExpRef->IsUtf16(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "unicode"); + jsValueRef = regExpRef->IsStick(vm_); + SetKeyValue(jsValueRef, outPropertyDesc, "sticky"); + std::string strFlags = regExpRef->GetOriginalFlags(); + jsValueRef = StringRef::NewFromUtf8(vm_, strFlags.c_str()); + SetKeyValue(jsValueRef, outPropertyDesc, "flags"); + std::string strSource = regExpRef->GetOriginalSource(vm_)->ToString(); + jsValueRef = StringRef::NewFromUtf8(vm_, strSource.c_str()); + SetKeyValue(jsValueRef, outPropertyDesc, "source"); +} +} // namespace panda::ecmascript::tooling \ No newline at end of file diff --git a/tooling/agent/runtime_impl.h b/tooling/agent/runtime_impl.h new file mode 100644 index 00000000..e0a5044f --- /dev/null +++ b/tooling/agent/runtime_impl.h @@ -0,0 +1,140 @@ +/* + * 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_AGENT_RUNTIME_IMPL_H +#define ECMASCRIPT_TOOLING_AGENT_RUNTIME_IMPL_H + + +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/dispatcher.h" + +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +enum class ArkInternalValueType {None, Entry, Scope, ScopeList}; +class RuntimeImpl final { +public: + RuntimeImpl(const EcmaVM *vm, ProtocolChannel *channel) + : vm_(vm), frontend_(channel) {} + ~RuntimeImpl() = default; + + DispatchResponse Enable(); + DispatchResponse Disable(); + DispatchResponse RunIfWaitingForDebugger(); + DispatchResponse CallFunctionOn( + const CallFunctionOnParams ¶ms, + std::unique_ptr *outRemoteObject, + std::optional> *outExceptionDetails); + DispatchResponse GetHeapUsage(double *usedSize, double *totalSize); + DispatchResponse GetProperties( + const GetPropertiesParams ¶ms, + std::vector> *outPropertyDesc, + std::optional>> *outInternalDescs, + std::optional>> *outPrivateProps, + std::optional> *outExceptionDetails); + + class DispatcherImpl final : public DispatcherBase { + public: + DispatcherImpl(ProtocolChannel *channel, std::unique_ptr runtime) + : DispatcherBase(channel), runtime_(std::move(runtime)) {} + ~DispatcherImpl() override = default; + + void Dispatch(const DispatchRequest &request) override; + void Disable(const DispatchRequest &request); + void Enable(const DispatchRequest &request); + void RunIfWaitingForDebugger(const DispatchRequest &request); + void GetProperties(const DispatchRequest &request); + void CallFunctionOn(const DispatchRequest &request); + void GetHeapUsage(const DispatchRequest &request); + + private: + using AgentHandler = void (RuntimeImpl::DispatcherImpl::*)(const DispatchRequest &request); + std::unique_ptr runtime_ {}; + + NO_COPY_SEMANTIC(DispatcherImpl); + NO_MOVE_SEMANTIC(DispatcherImpl); + }; + +private: + NO_COPY_SEMANTIC(RuntimeImpl); + NO_MOVE_SEMANTIC(RuntimeImpl); + enum NumberSize : uint8_t { BYTES_OF_16BITS = 2, BYTES_OF_32BITS = 4, BYTES_OF_64BITS = 8 }; + + void CacheObjectIfNeeded(Local valRef, RemoteObject *remoteObj); + + template + void AddTypedArrayRef(Local arrayBufferRef, int32_t length, + const char* name, std::vector> *outPropertyDesc); + void AddTypedArrayRefs(Local arrayBufferRef, + std::vector> *outPropertyDesc); + void AddSharedArrayBufferRefs(Local arrayBufferRef, + std::vector> *outPropertyDesc); + void GetProtoOrProtoType(Local value, bool isOwn, bool isAccessorOnly, + std::vector> *outPropertyDesc); + void GetAdditionalProperties(Local value, + std::vector> *outPropertyDesc); + void SetKeyValue(Local &jsValueRef, + std::vector> *outPropertyDesc, const std::string &cstrProName); + void GetPrimitiveNumberValue(Local value, + std::vector> *outPropertyDesc); + void GetPrimitiveStringValue(Local value, + std::vector> *outPropertyDesc); + void GetPrimitiveBooleanValue(Local value, + std::vector> *outPropertyDesc); + void GetMapIteratorValue(Local value, + std::vector> *outPropertyDesc); + void GetSetIteratorValue(Local value, + std::vector> *outPropertyDesc); + void GetGeneratorFunctionValue(Local value, + std::vector> *outPropertyDesc); + void GetGeneratorObjectValue(Local value, + std::vector> *outPropertyDesc); + void GetNumberFormatValue(Local value, + std::vector> *outPropertyDesc); + void GetCollatorValue(Local value, + std::vector> *outPropertyDesc); + void GetDateTimeFormatValue(Local value, + std::vector> *outPropertyDesc); + void AddInternalProperties(Local object, ArkInternalValueType type); + void GetMapValue(Local value, + std::vector> *outPropertyDesc); + void GetSetValue(Local value, + std::vector> *outPropertyDesc); + void GetRegExpValue(Local value, + std::vector> *outPropertyDesc); + class Frontend { + public: + explicit Frontend(ProtocolChannel *channel) : channel_(channel) {} + ~Frontend() = default; + + void RunIfWaitingForDebugger(); + + private: + bool AllowNotify() const; + + ProtocolChannel *channel_ {nullptr}; + }; + + const EcmaVM *vm_ {nullptr}; + Frontend frontend_; + + RemoteObjectId curObjectId_ {0}; + std::unordered_map> properties_ {}; + Global internalObjects_; + + friend class DebuggerImpl; +}; +} // namespace panda::ecmascript::tooling +#endif \ No newline at end of file diff --git a/tooling/agent/tracing_impl.cpp b/tooling/agent/tracing_impl.cpp new file mode 100644 index 00000000..8de90cc4 --- /dev/null +++ b/tooling/agent/tracing_impl.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 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/agent/tracing_impl.h" + +#include "ecmascript/napi/include/dfx_jsnapi.h" +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/protocol_channel.h" + +namespace panda::ecmascript::tooling { +void TracingImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) +{ + static std::unordered_map dispatcherTable { + { "end", &TracingImpl::DispatcherImpl::End }, + { "getCategories", &TracingImpl::DispatcherImpl::GetCategories }, + { "recordClockSyncMarker", &TracingImpl::DispatcherImpl::RecordClockSyncMarker }, + { "requestMemoryDump", &TracingImpl::DispatcherImpl::RequestMemoryDump }, + { "start", &TracingImpl::DispatcherImpl::Start } + }; + + const std::string &method = request.GetMethod(); + LOG_DEBUGGER(DEBUG) << "dispatch [" << method << "] to TracingImpl"; + auto entry = dispatcherTable.find(method); + if (entry != dispatcherTable.end() && entry->second != nullptr) { + (this->*(entry->second))(request); + } else { + SendResponse(request, DispatchResponse::Fail("Unknown method: " + method)); + } +} + +void TracingImpl::DispatcherImpl::End(const DispatchRequest &request) +{ + DispatchResponse response = tracing_->End(); + SendResponse(request, response); +} + +void TracingImpl::DispatcherImpl::GetCategories(const DispatchRequest &request) +{ + std::vector categories; + DispatchResponse response = tracing_->GetCategories(categories); + SendResponse(request, response); +} + +void TracingImpl::DispatcherImpl::RecordClockSyncMarker(const DispatchRequest &request) +{ + std::string syncId; + DispatchResponse response = tracing_->RecordClockSyncMarker(syncId); + SendResponse(request, response); +} + +void TracingImpl::DispatcherImpl::RequestMemoryDump(const DispatchRequest &request) +{ + std::unique_ptr params = + RequestMemoryDumpParams::Create(request.GetParams()); + std::string dumpGuid; + bool success = false; + DispatchResponse response = tracing_->RequestMemoryDump(std::move(params), dumpGuid, success); + SendResponse(request, response); +} + +void TracingImpl::DispatcherImpl::Start(const DispatchRequest &request) +{ + std::unique_ptr params = + StartParams::Create(request.GetParams()); + DispatchResponse response = tracing_->Start(std::move(params)); + SendResponse(request, response); +} + +bool TracingImpl::Frontend::AllowNotify() const +{ + return channel_ != nullptr; +} + +void TracingImpl::Frontend::BufferUsage() +{ + if (!AllowNotify()) { + return; + } + + tooling::BufferUsage bufferUsage; + channel_->SendNotification(bufferUsage); +} + +void TracingImpl::Frontend::DataCollected() +{ + if (!AllowNotify()) { + return; + } + + tooling::DataCollected dataCollected; + channel_->SendNotification(dataCollected); +} + +void TracingImpl::Frontend::TracingComplete() +{ + if (!AllowNotify()) { + return; + } + + tooling::TracingComplete tracingComplete; + channel_->SendNotification(tracingComplete); +} + +DispatchResponse TracingImpl::End() +{ + return DispatchResponse::Fail("End not support now."); +} + +DispatchResponse TracingImpl::GetCategories([[maybe_unused]] std::vector categories) +{ + return DispatchResponse::Fail("GetCategories not support now."); +} + +DispatchResponse TracingImpl::RecordClockSyncMarker([[maybe_unused]] std::string syncId) +{ + return DispatchResponse::Fail("RecordClockSyncMarker not support now."); +} + +DispatchResponse TracingImpl::RequestMemoryDump([[maybe_unused]] std::unique_ptr params, + [[maybe_unused]] std::string dumpGuid, [[maybe_unused]] bool success) +{ + return DispatchResponse::Fail("RequestMemoryDump not support now."); +} + +DispatchResponse TracingImpl::Start([[maybe_unused]] std::unique_ptr params) +{ + return DispatchResponse::Fail("Start not support now."); +} +} // namespace panda::ecmascript::tooling \ No newline at end of file diff --git a/tooling/agent/tracing_impl.h b/tooling/agent/tracing_impl.h new file mode 100644 index 00000000..41e5f864 --- /dev/null +++ b/tooling/agent/tracing_impl.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_AGENT_TRACING_IMPL_H +#define ECMASCRIPT_TOOLING_AGENT_TRACING_IMPL_H + +#include "ecmascript/dfx/cpu_profiler/samples_record.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/dispatcher.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +class TracingImpl final { +public: + explicit TracingImpl(const EcmaVM *vm, ProtocolChannel *channel) : vm_(vm), frontend_(channel) {} + ~TracingImpl() = default; + + DispatchResponse End(); + DispatchResponse GetCategories(std::vector categories); + DispatchResponse RecordClockSyncMarker(std::string syncId); + DispatchResponse RequestMemoryDump(std::unique_ptr params, + std::string dumpGuid, bool success); + DispatchResponse Start(std::unique_ptr params); + + class DispatcherImpl final : public DispatcherBase { + public: + DispatcherImpl(ProtocolChannel *channel, std::unique_ptr tracing) + : DispatcherBase(channel), tracing_(std::move(tracing)) {} + ~DispatcherImpl() override = default; + void Dispatch(const DispatchRequest &request) override; + void End(const DispatchRequest &request); + void GetCategories(const DispatchRequest &request); + void RecordClockSyncMarker(const DispatchRequest &request); + void RequestMemoryDump(const DispatchRequest &request); + void Start(const DispatchRequest &request); + + private: + NO_COPY_SEMANTIC(DispatcherImpl); + NO_MOVE_SEMANTIC(DispatcherImpl); + + using AgentHandler = void (TracingImpl::DispatcherImpl::*)(const DispatchRequest &request); + std::unique_ptr tracing_ {}; + }; + + class Frontend { + public: + explicit Frontend(ProtocolChannel *channel) : channel_(channel) {} + ~Frontend() = default; + + void BufferUsage(); + void DataCollected(); + void TracingComplete(); + + private: + bool AllowNotify() const; + ProtocolChannel *channel_ {nullptr}; + }; + +private: + NO_COPY_SEMANTIC(TracingImpl); + NO_MOVE_SEMANTIC(TracingImpl); + + [[maybe_unused]] const EcmaVM *vm_ {nullptr}; + [[maybe_unused]] Frontend frontend_; +}; +} // namespace panda::ecmascript::tooling +#endif \ No newline at end of file diff --git a/tooling/backend/debugger_api.cpp b/tooling/backend/debugger_api.cpp new file mode 100644 index 00000000..8c881e88 --- /dev/null +++ b/tooling/backend/debugger_api.cpp @@ -0,0 +1,374 @@ +/* + * 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/backend/debugger_api.h" + +#include "ecmascript/base/number_helper.h" +#include "ecmascript/interpreter/frame_handler.h" +#include "ecmascript/interpreter/slow_runtime_stub.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/jspandafile/js_pandafile_executor.h" +#include "ecmascript/jspandafile/program_object.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/jspandafile/js_pandafile_manager.h" +#include "ecmascript/method.h" +#include "ecmascript/napi/jsnapi_helper.h" +#include "ecmascript/tooling/backend/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; + +// FrameHandler +uint32_t DebuggerApi::GetStackDepth(const EcmaVM *ecmaVm) +{ + uint32_t count = 0; + FrameHandler frameHandler(ecmaVm->GetJSThread()); + for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) { + if (frameHandler.IsEntryFrame() || frameHandler.IsBuiltinFrame()) { + continue; + } + ++count; + } + return count; +} + +std::shared_ptr DebuggerApi::NewFrameHandler(const EcmaVM *ecmaVm) +{ + return std::make_shared(ecmaVm->GetJSThread()); +} + +bool DebuggerApi::StackWalker(const EcmaVM *ecmaVm, std::function func) +{ + FrameHandler frameHandler(ecmaVm->GetJSThread()); + for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) { + if (frameHandler.IsEntryFrame() || frameHandler.IsBuiltinFrame()) { + 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(); +} + +std::unique_ptr DebuggerApi::GetMethod(const EcmaVM *ecmaVm) +{ + FrameHandler frameHandler(ecmaVm->GetJSThread()); + Method* method = frameHandler.GetMethod(); + std::unique_ptr ptMethod = std::make_unique( + method->GetJSPandaFile(), method->GetMethodId(), method->IsNativeWithCallField()); + return ptMethod; +} + +void DebuggerApi::SetVRegValue(FrameHandler *frameHandler, size_t index, Local value) +{ + return frameHandler->SetVRegValue(index, JSNApiHelper::ToJSTaggedValue(*value)); +} + +uint32_t DebuggerApi::GetBytecodeOffset(const FrameHandler *frameHandler) +{ + return frameHandler->GetBytecodeOffset(); +} + +Method *DebuggerApi::GetMethod(const FrameHandler *frameHandler) +{ + return frameHandler->GetMethod(); +} + +bool DebuggerApi::IsNativeMethod(const EcmaVM *ecmaVm) +{ + FrameHandler frameHandler(ecmaVm->GetJSThread()); + return DebuggerApi::IsNativeMethod(&frameHandler); +} + +bool DebuggerApi::IsNativeMethod(const FrameHandler *frameHandler) +{ + Method* method = frameHandler->GetMethod(); + return method->IsNativeWithCallField(); +} + +JSPandaFile *DebuggerApi::GetJSPandaFile(const EcmaVM *ecmaVm) +{ + Method *method = FrameHandler(ecmaVm->GetJSThread()).GetMethod(); + return const_cast(method->GetJSPandaFile()); +} + +JSTaggedValue DebuggerApi::GetEnv(const FrameHandler *frameHandler) +{ + return frameHandler->GetEnv(); +} + +JSTaggedType *DebuggerApi::GetSp(const FrameHandler *frameHandler) +{ + return frameHandler->GetSp(); +} + +int32_t DebuggerApi::GetVregIndex(const FrameHandler *frameHandler, std::string_view name) +{ + Method *method = DebuggerApi::GetMethod(frameHandler); + if (method->IsNativeWithCallField()) { + LOG_DEBUGGER(ERROR) << "GetVregIndex: native frame not support"; + return -1; + } + JSPtExtractor *extractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile()); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "GetVregIndex: extractor is null"; + return -1; + } + auto table = extractor->GetLocalVariableTable(method->GetMethodId()); + auto iter = table.find(name.data()); + if (iter == table.end()) { + return -1; + } + return iter->second; +} + +Local DebuggerApi::GetVRegValue(const EcmaVM *ecmaVm, + const FrameHandler *frameHandler, size_t index) +{ + auto value = frameHandler->GetVRegValue(index); + JSHandle handledValue(ecmaVm->GetJSThread(), value); + return JSNApiHelper::ToLocal(handledValue); +} + +// JSThread +Local DebuggerApi::GetAndClearException(const EcmaVM *ecmaVm) +{ + auto exception = ecmaVm->GetJSThread()->GetException(); + JSHandle handledException(ecmaVm->GetJSThread(), exception); + ecmaVm->GetJSThread()->ClearException(); + return JSNApiHelper::ToLocal(handledException); +} + +void DebuggerApi::SetException(const EcmaVM *ecmaVm, Local 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, + Local condFuncRef) +{ + return debugger->SetBreakpoint(location, condFuncRef); +} + +bool DebuggerApi::RemoveBreakpoint(JSDebugger *debugger, const JSPtLocation &location) +{ + return debugger->RemoveBreakpoint(location); +} + +// ScopeInfo +Local DebuggerApi::GetProperties(const EcmaVM *vm, const FrameHandler *frameHandler, + int32_t level, uint32_t slot) +{ + JSTaggedValue env = frameHandler->GetEnv(); + 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 handledValue(vm->GetJSThread(), value); + return JSNApiHelper::ToLocal(handledValue); +} + +void DebuggerApi::SetProperties(const EcmaVM *vm, const FrameHandler *frameHandler, + int32_t level, uint32_t slot, Local value) +{ + JSTaggedValue env = frameHandler->GetEnv(); + 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(vm->GetJSThread(), slot, target); +} + +std::pair DebuggerApi::GetLevelSlot(const FrameHandler *frameHandler, std::string_view name) +{ + int32_t level = 0; + uint32_t slot = 0; + JSTaggedValue curEnv = frameHandler->GetEnv(); + 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(result); + auto iter = scopeDebugInfo->scopeInfo.find(name.data()); + if (iter == scopeDebugInfo->scopeInfo.end()) { + continue; + } + slot = iter->second; + return std::make_pair(level, slot); + } + return std::make_pair(-1, 0); +} + +Local DebuggerApi::GetGlobalValue(const EcmaVM *vm, Local name) +{ + JSTaggedValue result; + JSTaggedValue globalObj = vm->GetGlobalEnv()->GetGlobalObject(); + JSThread *thread = vm->GetJSThread(); + + JSTaggedValue key = JSNApiHelper::ToJSTaggedValue(*name); + JSTaggedValue globalRec = SlowRuntimeStub::LdGlobalRecord(thread, key); + if (!globalRec.IsUndefined()) { + ASSERT(globalRec.IsPropertyBox()); + result = PropertyBox::Cast(globalRec.GetTaggedObject())->GetValue(); + return JSNApiHelper::ToLocal(JSHandle(thread, result)); + } + + JSTaggedValue globalVar = FastRuntimeStub::GetGlobalOwnProperty(thread, globalObj, key); + if (!globalVar.IsHole()) { + return JSNApiHelper::ToLocal(JSHandle(thread, globalVar)); + } else { + result = SlowRuntimeStub::TryLdGlobalByNameFromGlobalProto(thread, globalObj, key); + return JSNApiHelper::ToLocal(JSHandle(thread, result)); + } + + return Local(); +} + +bool DebuggerApi::SetGlobalValue(const EcmaVM *vm, Local name, Local value) +{ + JSTaggedValue result; + JSTaggedValue globalObj = vm->GetGlobalEnv()->GetGlobalObject(); + JSThread *thread = vm->GetJSThread(); + + JSTaggedValue key = JSNApiHelper::ToJSTaggedValue(*name); + JSTaggedValue newVal = JSNApiHelper::ToJSTaggedValue(*value); + JSTaggedValue globalRec = SlowRuntimeStub::LdGlobalRecord(thread, key); + if (!globalRec.IsUndefined()) { + result = SlowRuntimeStub::TryUpdateGlobalRecord(thread, key, newVal); + return !result.IsException(); + } + + JSTaggedValue globalVar = FastRuntimeStub::GetGlobalOwnProperty(thread, globalObj, key); + if (!globalVar.IsHole()) { + result = SlowRuntimeStub::StGlobalVar(thread, key, newVal); + return !result.IsException(); + } + + return false; +} + +void DebuggerApi::HandleUncaughtException(const EcmaVM *ecmaVm, std::string &message) +{ + JSThread *thread = ecmaVm->GetJSThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + JSHandle exHandle(thread, thread->GetException()); + if (exHandle->IsJSError()) { + JSHandle nameKey = globalConst->GetHandledNameString(); + JSHandle name(JSObject::GetProperty(thread, exHandle, nameKey).GetValue()); + JSHandle msgKey = globalConst->GetHandledMessageString(); + JSHandle msg(JSObject::GetProperty(thread, exHandle, msgKey).GetValue()); + message = ConvertToString(*name) + ": " + ConvertToString(*msg); + } else { + JSHandle ecmaStr = JSTaggedValue::ToString(thread, exHandle); + message = ConvertToString(*ecmaStr); + } + thread->ClearException(); +} + +Local DebuggerApi::GenerateFuncFromBuffer(const EcmaVM *ecmaVm, const void *buffer, + size_t size, std::string_view entryPoint) +{ + JSPandaFileManager *mgr = JSPandaFileManager::GetInstance(); + const auto *jsPandaFile = mgr->LoadJSPandaFile(ecmaVm->GetJSThread(), "", entryPoint, buffer, size); + if (jsPandaFile == nullptr) { + return JSValueRef::Undefined(ecmaVm); + } + + JSHandle program = mgr->GenerateProgram(const_cast(ecmaVm), jsPandaFile, entryPoint); + JSTaggedValue func = program->GetMainFunction(); + return JSNApiHelper::ToLocal(JSHandle(ecmaVm->GetJSThread(), func)); +} + +Local DebuggerApi::EvaluateViaFuncCall(EcmaVM *ecmaVm, Local funcRef, + std::shared_ptr &frameHandler) +{ + JSNApi::EnableUserUncaughtErrorHandler(ecmaVm); + + JsDebuggerManager *mgr = ecmaVm->GetJsDebuggerManager(); + bool prevDebugMode = mgr->IsDebugMode(); + mgr->SetEvalFrameHandler(frameHandler); + mgr->SetDebugMode(false); // in order to catch exception + std::vector> args; + auto result = funcRef->Call(ecmaVm, JSValueRef::Undefined(ecmaVm), args.data(), args.size()); + mgr->SetDebugMode(prevDebugMode); + mgr->SetEvalFrameHandler(nullptr); + + return result; +} + +bool DebuggerApi::IsExceptionCaught(const EcmaVM *ecmaVm) +{ + FrameHandler frameHandler(ecmaVm->GetJSThread()); + for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) { + if (frameHandler.IsEntryFrame()) { + return false; + } + auto method = frameHandler.GetMethod(); + if (ecmaVm->FindCatchBlock(method, frameHandler.GetBytecodeOffset())) { + return true; + } + } + return false; +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/backend/debugger_api.h b/tooling/backend/debugger_api.h new file mode 100644 index 00000000..5c1f1a26 --- /dev/null +++ b/tooling/backend/debugger_api.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_API_H +#define ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_API_H + +#include + +#include "ecmascript/common.h" +#include "ecmascript/jspandafile/scope_info_extractor.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/lexical_env.h" +#include "ecmascript/tooling/backend/js_debugger_interface.h" +#include "ecmascript/tooling/base/pt_method.h" + +namespace panda { +namespace ecmascript { +class FrameHandler; +class EcmaVM; +class Method; +class JSThread; +namespace tooling { +class JSDebugger; +} +} // ecmascript +} // panda + +namespace panda::ecmascript::tooling { +enum StackState { + CONTINUE, + FAILED, + SUCCESS, +}; + +class PUBLIC_API DebuggerApi { +public: + // FrameHandler + static uint32_t GetStackDepth(const EcmaVM *ecmaVm); + static std::shared_ptr NewFrameHandler(const EcmaVM *ecmaVm); + static bool StackWalker(const EcmaVM *ecmaVm, std::function func); + + static uint32_t GetBytecodeOffset(const EcmaVM *ecmaVm); + static uint32_t GetBytecodeOffset(const FrameHandler *frameHandler); + static std::unique_ptr GetMethod(const EcmaVM *ecmaVm); + static Method *GetMethod(const FrameHandler *frameHandler); + static bool IsNativeMethod(const EcmaVM *ecmaVm); + static bool IsNativeMethod(const FrameHandler *frameHandler); + static JSPandaFile *GetJSPandaFile(const EcmaVM *ecmaVm); + + static JSTaggedValue GetEnv(const FrameHandler *frameHandler); + static JSTaggedType *GetSp(const FrameHandler *frameHandler); + static int32_t GetVregIndex(const FrameHandler *frameHandler, std::string_view name); + static Local GetVRegValue(const EcmaVM *ecmaVm, + const FrameHandler *frameHandler, size_t index); + static void SetVRegValue(FrameHandler *frameHandler, size_t index, Local value); + + static Local GetProperties(const EcmaVM *ecmaVm, const FrameHandler *frameHandler, + int32_t level, uint32_t slot); + static void SetProperties(const EcmaVM *vm, const FrameHandler *frameHandler, int32_t level, + uint32_t slot, Local value); + static std::pair GetLevelSlot(const FrameHandler *frameHandler, std::string_view name); + static Local GetGlobalValue(const EcmaVM *vm, Local name); + static bool SetGlobalValue(const EcmaVM *vm, Local name, Local value); + + // JSThread + static Local GetAndClearException(const EcmaVM *ecmaVm); + static void SetException(const EcmaVM *ecmaVm, Local exception); + static void ClearException(const EcmaVM *ecmaVm); + static bool IsExceptionCaught(const EcmaVM *ecmaVm); + + // NumberHelper + static double StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix); + + // JSDebugger + static JSDebugger *CreateJSDebugger(const EcmaVM *ecmaVm); + static void DestroyJSDebugger(JSDebugger *debugger); + static void RegisterHooks(JSDebugger *debugger, PtHooks *hooks); + static bool SetBreakpoint(JSDebugger *debugger, const JSPtLocation &location, + Local condFuncRef); + static bool RemoveBreakpoint(JSDebugger *debugger, const JSPtLocation &location); + static void HandleUncaughtException(const EcmaVM *ecmaVm, std::string &message); + static Local EvaluateViaFuncCall(EcmaVM *ecmaVm, Local funcRef, + std::shared_ptr &frameHandler); + static Local GenerateFuncFromBuffer(const EcmaVM *ecmaVm, const void *buffer, size_t size, + std::string_view entryPoint); +}; +} // namespace panda::ecmascript::tooling + +#endif // ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_API_H diff --git a/tooling/backend/debugger_executor.cpp b/tooling/backend/debugger_executor.cpp new file mode 100644 index 00000000..cbf67754 --- /dev/null +++ b/tooling/backend/debugger_executor.cpp @@ -0,0 +1,195 @@ +/* + * 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/backend/debugger_executor.h" + +#include "ecmascript/tooling/backend/debugger_api.h" +#include "ecmascript/tooling/interface/js_debugger_manager.h" + +namespace panda::ecmascript::tooling { +void DebuggerExecutor::Initialize(const EcmaVM *vm) +{ + [[maybe_unused]] EcmaHandleScope handleScope(vm->GetJSThread()); + Local globalObj = JSNApi::GetGlobalObject(vm); + globalObj->Set(vm, StringRef::NewFromUtf8(vm, "debuggerSetValue"), FunctionRef::New( + const_cast(vm), DebuggerExecutor::DebuggerSetValue)); + globalObj->Set(vm, StringRef::NewFromUtf8(vm, "debuggerGetValue"), FunctionRef::New( + const_cast(vm), DebuggerExecutor::DebuggerGetValue)); +} + +Local DebuggerExecutor::DebuggerGetValue(JsiRuntimeCallInfo *runtimeCallInfo) +{ + EcmaVM *vm = runtimeCallInfo->GetVM(); + uint32_t argc = runtimeCallInfo->GetArgsNumber(); + if (argc != NUM_ARGS) { + return JSValueRef::Undefined(vm); + } + Local name = runtimeCallInfo->GetCallArgRef(0); + if (!name->IsString()) { + return JSValueRef::Undefined(vm); + } + Local isThrow = runtimeCallInfo->GetCallArgRef(1); + + auto &frameHandler = vm->GetJsDebuggerManager()->GetEvalFrameHandler(); + ASSERT(frameHandler); + + Local value = GetValue(vm, frameHandler.get(), Local(name)); + if (!value.IsEmpty()) { + return value; + } + + if (!isThrow->ToBoolean(vm)->Value()) { + DebuggerApi::ClearException(vm); + return JSValueRef::Undefined(vm); + } + + std::string varName = Local(name)->ToString(); + ThrowException(vm, varName + " is not defined"); + return Local(); +} + +Local DebuggerExecutor::DebuggerSetValue(JsiRuntimeCallInfo *runtimeCallInfo) +{ + EcmaVM *vm = runtimeCallInfo->GetVM(); + uint32_t argc = runtimeCallInfo->GetArgsNumber(); + if (argc != NUM_ARGS) { + return JSValueRef::Undefined(vm); + } + Local name = runtimeCallInfo->GetCallArgRef(0); + if (!name->IsString()) { + return JSValueRef::Undefined(vm); + } + Local value = runtimeCallInfo->GetCallArgRef(1); + + auto &frameHandler = vm->GetJsDebuggerManager()->GetEvalFrameHandler(); + ASSERT(frameHandler); + + if (SetValue(vm, frameHandler.get(), Local(name), value)) { + return value; + } + + std::string varName = StringRef::Cast(*name)->ToString(); + ThrowException(vm, varName + " is not defined"); + return Local(); +} + +Local DebuggerExecutor::GetValue(const EcmaVM *vm, const FrameHandler *frameHandler, Local name) +{ + Local value; + value = GetLocalValue(vm, frameHandler, name); + if (!value.IsEmpty()) { + return value; + } + value = GetLexicalValue(vm, frameHandler, name); + if (!value.IsEmpty()) { + return value; + } + value = GetGlobalValue(vm, name); + if (!value.IsEmpty()) { + return value; + } + + return Local(); +} + +bool DebuggerExecutor::SetValue(const EcmaVM *vm, FrameHandler *frameHandler, + Local name, Local value) +{ + if (SetLocalValue(vm, frameHandler, name, value)) { + return true; + } + if (SetLexicalValue(vm, frameHandler, name, value)) { + return true; + } + if (SetGlobalValue(vm, name, value)) { + return true; + } + + return false; +} + +void DebuggerExecutor::ThrowException(const EcmaVM *vm, const std::string &error) +{ + Local msg = StringRef::NewFromUtf8(vm, error.c_str()); + Local exception = Exception::ReferenceError(vm, msg); + JSNApi::ThrowException(vm, exception); +} + +Local DebuggerExecutor::GetLocalValue(const EcmaVM *vm, const FrameHandler *frameHandler, + Local name) +{ + Local result; + + int32_t index = DebuggerApi::GetVregIndex(frameHandler, name->ToString()); + if (index == -1) { + return result; + } + + result = DebuggerApi::GetVRegValue(vm, frameHandler, index); + return result; +} + +bool DebuggerExecutor::SetLocalValue(const EcmaVM *vm, FrameHandler *frameHandler, + Local name, Local value) +{ + std::string varName = name->ToString(); + int32_t index = DebuggerApi::GetVregIndex(frameHandler, varName); + if (index == -1) { + return false; + } + + DebuggerApi::SetVRegValue(frameHandler, index, value); + vm->GetJsDebuggerManager()->NotifyLocalScopeUpdated(varName, value); + return true; +} + +Local DebuggerExecutor::GetLexicalValue(const EcmaVM *vm, const FrameHandler *frameHandler, + Local name) +{ + Local result; + + auto [level, slot] = DebuggerApi::GetLevelSlot(frameHandler, name->ToString()); + if (level == -1) { + return result; + } + + result = DebuggerApi::GetProperties(vm, frameHandler, level, slot); + return result; +} + +bool DebuggerExecutor::SetLexicalValue(const EcmaVM *vm, const FrameHandler *frameHandler, + Local name, Local value) +{ + std::string varName = name->ToString(); + auto [level, slot] = DebuggerApi::GetLevelSlot(frameHandler, varName); + if (level == -1) { + return false; + } + + DebuggerApi::SetProperties(vm, frameHandler, level, slot, value); + vm->GetJsDebuggerManager()->NotifyLocalScopeUpdated(varName, value); + return true; +} + +Local DebuggerExecutor::GetGlobalValue(const EcmaVM *vm, Local name) +{ + return DebuggerApi::GetGlobalValue(vm, name); +} + +bool DebuggerExecutor::SetGlobalValue(const EcmaVM *vm, Local name, Local value) +{ + return DebuggerApi::SetGlobalValue(vm, name, value); +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/backend/debugger_executor.h b/tooling/backend/debugger_executor.h new file mode 100644 index 00000000..491cc3de --- /dev/null +++ b/tooling/backend/debugger_executor.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_EXECUTOR_H +#define ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_EXECUTOR_H + +#include "ecmascript/napi/include/jsnapi.h" + +namespace panda::ecmascript { +class FrameHandler; +namespace tooling { +class DebuggerExecutor { +public: + DebuggerExecutor() = default; + ~DebuggerExecutor() = default; + + static void Initialize(const EcmaVM *vm); + + static Local GetValue(const EcmaVM *vm, const FrameHandler *frameHandler, Local name); + static bool SetValue(const EcmaVM *vm, FrameHandler *frameHandler, + Local name, Local value); + +private: + NO_COPY_SEMANTIC(DebuggerExecutor); + NO_MOVE_SEMANTIC(DebuggerExecutor); + + static Local DebuggerGetValue(JsiRuntimeCallInfo *runtimeCallInfo); + static Local DebuggerSetValue(JsiRuntimeCallInfo *runtimeCallInfo); + + static void ThrowException(const EcmaVM *vm, const std::string &error); + + static Local GetLocalValue(const EcmaVM *vm, const FrameHandler *frameHandler, Local name); + static Local GetLexicalValue(const EcmaVM *vm, const FrameHandler *frameHandler, + Local name); + static Local GetGlobalValue(const EcmaVM *vm, Local name); + + static bool SetLocalValue(const EcmaVM *vm, FrameHandler *frameHandler, + Local name, Local value); + static bool SetLexicalValue(const EcmaVM *vm, const FrameHandler *frameHandler, + Local name, Local value); + static bool SetGlobalValue(const EcmaVM *vm, Local name, Local value); + + constexpr static uint32_t NUM_ARGS = 2; +}; +} // namespace tooling +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_EXECUTOR_H diff --git a/tooling/backend/js_debugger.cpp b/tooling/backend/js_debugger.cpp new file mode 100644 index 00000000..069dd9bb --- /dev/null +++ b/tooling/backend/js_debugger.cpp @@ -0,0 +1,183 @@ +/* + * 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/backend/js_debugger.h" + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/interpreter/frame_handler.h" +#include "ecmascript/jspandafile/js_pandafile_manager.h" + +namespace panda::ecmascript::tooling { +using panda::ecmascript::base::BuiltinsBase; + +bool JSDebugger::SetBreakpoint(const JSPtLocation &location, Local condFuncRef) +{ + std::unique_ptr ptMethod = FindMethod(location); + if (ptMethod == nullptr) { + LOG_DEBUGGER(ERROR) << "SetBreakpoint: Cannot find MethodLiteral"; + return false; + } + + if (location.GetBytecodeOffset() >= ptMethod->GetCodeSize()) { + LOG_DEBUGGER(ERROR) << "SetBreakpoint: Invalid breakpoint location"; + return false; + } + + auto [_, success] = breakpoints_.emplace(ptMethod.release(), location.GetBytecodeOffset(), + Global(ecmaVm_, condFuncRef)); + if (!success) { + // also return true + LOG_DEBUGGER(WARN) << "SetBreakpoint: Breakpoint already exists"; + } + + return true; +} + +bool JSDebugger::RemoveBreakpoint(const JSPtLocation &location) +{ + std::unique_ptr ptMethod = FindMethod(location); + if (ptMethod == nullptr) { + LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: Cannot find MethodLiteral"; + return false; + } + + if (!RemoveBreakpoint(ptMethod, location.GetBytecodeOffset())) { + LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: Breakpoint not found"; + return false; + } + + return true; +} + +void JSDebugger::BytecodePcChanged(JSThread *thread, JSHandle method, uint32_t bcOffset) +{ + ASSERT(bcOffset < method->GetCodeSize() && "code size of current Method less then bcOffset"); + HandleExceptionThrowEvent(thread, method, bcOffset); + + // Step event is reported before breakpoint, according to the spec. + if (!HandleStep(method, bcOffset)) { + HandleBreakpoint(method, bcOffset); + } +} + +bool JSDebugger::HandleBreakpoint(JSHandle method, uint32_t bcOffset) +{ + auto breakpoint = FindBreakpoint(method, bcOffset); + if (hooks_ == nullptr || !breakpoint.has_value()) { + return false; + } + + JSThread *thread = ecmaVm_->GetJSThread(); + auto condFuncRef = breakpoint.value().GetConditionFunction(); + if (condFuncRef->IsFunction()) { + LOG_DEBUGGER(INFO) << "HandleBreakpoint: begin evaluate condition"; + auto handlerPtr = std::make_shared(ecmaVm_->GetJSThread()); + auto res = DebuggerApi::EvaluateViaFuncCall(const_cast(ecmaVm_), + condFuncRef.ToLocal(ecmaVm_), handlerPtr); + if (thread->HasPendingException()) { + LOG_DEBUGGER(ERROR) << "HandleBreakpoint: has pending exception"; + thread->ClearException(); + return false; + } + bool isMeet = res->ToBoolean(ecmaVm_)->Value(); + if (!isMeet) { + LOG_DEBUGGER(ERROR) << "HandleBreakpoint: condition not meet"; + return false; + } + } + + auto *pf = method->GetJSPandaFile(); + JSPtLocation location {pf->GetJSPandaFileDesc().c_str(), method->GetMethodId(), bcOffset}; + + hooks_->Breakpoint(location); + return true; +} + +void JSDebugger::HandleExceptionThrowEvent(const JSThread *thread, JSHandle method, uint32_t bcOffset) +{ + if (hooks_ == nullptr || !thread->HasPendingException()) { + return; + } + + auto *pf = method->GetJSPandaFile(); + JSPtLocation throwLocation {pf->GetJSPandaFileDesc().c_str(), method->GetMethodId(), bcOffset}; + + hooks_->Exception(throwLocation); +} + +bool JSDebugger::HandleStep(JSHandle method, uint32_t bcOffset) +{ + if (hooks_ == nullptr) { + return false; + } + + auto *pf = method->GetJSPandaFile(); + JSPtLocation location {pf->GetJSPandaFileDesc().c_str(), method->GetMethodId(), bcOffset}; + + return hooks_->SingleStep(location); +} + +std::optional JSDebugger::FindBreakpoint(JSHandle method, uint32_t bcOffset) const +{ + for (const auto &bp : breakpoints_) { + if ((bp.GetBytecodeOffset() == bcOffset) && + (bp.GetPtMethod()->GetJSPandaFile() == method->GetJSPandaFile()) && + (bp.GetPtMethod()->GetMethodId() == method->GetMethodId())) { + return bp; + } + } + return {}; +} + +bool JSDebugger::RemoveBreakpoint(const std::unique_ptr &ptMethod, uint32_t bcOffset) +{ + for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) { + const auto &bp = *it; + if ((bp.GetBytecodeOffset() == bcOffset) && + (bp.GetPtMethod()->GetJSPandaFile() == ptMethod->GetJSPandaFile()) && + (bp.GetPtMethod()->GetMethodId() == ptMethod->GetMethodId())) { + it = breakpoints_.erase(it); + return true; + } + } + + return false; +} + +std::unique_ptr JSDebugger::FindMethod(const JSPtLocation &location) const +{ + std::unique_ptr ptMethod {nullptr}; + ::panda::ecmascript::JSPandaFileManager::GetInstance()->EnumerateJSPandaFiles([&ptMethod, location]( + const panda::ecmascript::JSPandaFile *jsPandaFile) { + if (jsPandaFile->GetJSPandaFileDesc() == location.GetPandaFile()) { + MethodLiteral *methodsData = jsPandaFile->GetMethodLiterals(); + uint32_t numberMethods = jsPandaFile->GetNumMethods(); + for (uint32_t i = 0; i < numberMethods; ++i) { + if (methodsData[i].GetMethodId() == location.GetMethodId()) { + MethodLiteral *methodLiteral = methodsData + i; + ptMethod = std::make_unique(jsPandaFile, + methodLiteral->GetMethodId(), methodLiteral->IsNativeWithCallField()); + return false; + } + } + } + return true; + }); + return ptMethod; +} +} // namespace panda::tooling::ecmascript diff --git a/tooling/backend/js_debugger.h b/tooling/backend/js_debugger.h new file mode 100644 index 00000000..145670d1 --- /dev/null +++ b/tooling/backend/js_debugger.h @@ -0,0 +1,152 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_BACKEND_JS_DEBUGGER_H +#define ECMASCRIPT_TOOLING_BACKEND_JS_DEBUGGER_H + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/jspandafile/method_literal.h" +#include "ecmascript/tooling/backend/debugger_api.h" +#include "ecmascript/tooling/base/pt_method.h" +#include "ecmascript/tooling/interface/notification_manager.h" +#include "ecmascript/tooling/interface/js_debugger_manager.h" + +namespace panda::ecmascript::tooling { +class JSBreakpoint { +public: + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) + JSBreakpoint(PtMethod *ptMethod, uint32_t bcOffset, const Global &condFuncRef) + : ptMethod_(ptMethod), bcOffset_(bcOffset), condFuncRef_(condFuncRef) {} + ~JSBreakpoint() = default; + + PtMethod *GetPtMethod() const + { + return ptMethod_; + } + + uint32_t GetBytecodeOffset() const + { + return bcOffset_; + } + + bool operator==(const JSBreakpoint &bpoint) const + { + return ptMethod_->GetMethodId() == bpoint.GetPtMethod()->GetMethodId() && + ptMethod_->GetJSPandaFile() == bpoint.GetPtMethod()->GetJSPandaFile() && + GetBytecodeOffset() == bpoint.GetBytecodeOffset(); + } + + const Global &GetConditionFunction() + { + return condFuncRef_; + } + + DEFAULT_COPY_SEMANTIC(JSBreakpoint); + DEFAULT_MOVE_SEMANTIC(JSBreakpoint); + +private: + PtMethod *ptMethod_ {nullptr}; + uint32_t bcOffset_; + Global condFuncRef_; +}; + +class HashJSBreakpoint { +public: + size_t operator()(const JSBreakpoint &bpoint) const + { + return (std::hash()(bpoint.GetPtMethod())) ^ (std::hash()(bpoint.GetBytecodeOffset())); + } +}; + +class JSDebugger : public JSDebugInterface, RuntimeListener { +public: + explicit JSDebugger(const EcmaVM *vm) : ecmaVm_(vm) + { + notificationMgr_ = ecmaVm_->GetJsDebuggerManager()->GetNotificationManager(); + notificationMgr_->AddListener(this); + } + ~JSDebugger() override + { + notificationMgr_->RemoveListener(); + } + + void RegisterHooks(PtHooks *hooks) override + { + hooks_ = hooks; + // send vm start event after add hooks + notificationMgr_->VmStartEvent(); + } + void UnregisterHooks() override + { + // send vm death event before delete hooks + notificationMgr_->VmDeathEvent(); + hooks_ = nullptr; + } + + bool SetBreakpoint(const JSPtLocation &location, Local condFuncRef) override; + bool RemoveBreakpoint(const JSPtLocation &location) override; + void BytecodePcChanged(JSThread *thread, JSHandle method, uint32_t bcOffset) override; + void LoadModule(std::string_view filename, std::string_view entryPoint) override + { + if (hooks_ == nullptr) { + return; + } + hooks_->LoadModule(filename, entryPoint); + } + void VmStart() override + { + if (hooks_ == nullptr) { + return; + } + hooks_->VmStart(); + } + void VmDeath() override + { + if (hooks_ == nullptr) { + return; + } + hooks_->VmDeath(); + } + void PendingJobEntry() override + { + if (hooks_ == nullptr) { + return; + } + hooks_->PendingJobEntry(); + } + void NativeCalling(const void *nativeAddress) override + { + if (hooks_ == nullptr) { + return; + } + hooks_->NativeCalling(nativeAddress); + } +private: + std::unique_ptr FindMethod(const JSPtLocation &location) const; + std::optional FindBreakpoint(JSHandle method, uint32_t bcOffset) const; + bool RemoveBreakpoint(const std::unique_ptr &ptMethod, uint32_t bcOffset); + void HandleExceptionThrowEvent(const JSThread *thread, JSHandle method, uint32_t bcOffset); + bool HandleStep(JSHandle method, uint32_t bcOffset); + bool HandleBreakpoint(JSHandle method, uint32_t bcOffset); + + const EcmaVM *ecmaVm_; + PtHooks *hooks_ {nullptr}; + NotificationManager *notificationMgr_ {nullptr}; + + CUnorderedSet breakpoints_ {}; +}; +} // namespace panda::ecmascript::tooling + +#endif // ECMASCRIPT_TOOLING_BACKEND_JS_DEBUGGER_H diff --git a/tooling/backend/js_debugger_interface.h b/tooling/backend/js_debugger_interface.h new file mode 100644 index 00000000..a8b02858 --- /dev/null +++ b/tooling/backend/js_debugger_interface.h @@ -0,0 +1,133 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUG_INTERFACE_H +#define ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUG_INTERFACE_H + +#include + +#include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/tooling/backend/js_pt_location.h" +#include "libpandafile/file.h" +#include "libpandabase/macros.h" +#include "libpandabase/utils/expected.h" + +namespace panda::ecmascript::tooling { +struct JSPtStepRange { + uint32_t startBcOffset {0}; + uint32_t endBcOffset {0}; +}; + +enum PauseReason { + AMBIGUOUS, + ASSERT, + DEBUGCOMMAND, + DOM, + EVENTLISTENER, + EXCEPTION, + INSTRUMENTATION, + OOM, + OTHER, + PROMISEREJECTION, + XHR, + BREAK_ON_START +}; + +class PtHooks { +public: + PtHooks() = default; + + /** + * \brief called by the ecmavm when breakpoint hits. Thread where breakpoint hits is stopped until + * continue or step event will be received + * @param thread Identifier of the thread where breakpoint hits. Now the callback is called in the same + * thread + * @param location Breakpoint location + */ + virtual void Breakpoint(const JSPtLocation &location) = 0; + + /** + * \brief called by the ecmavm when panda file is loaded + * @param pandaFileName Path to panda file that is loaded + */ + virtual void LoadModule(std::string_view pandaFileName, std::string_view entryPoint) = 0; + + /** + * \brief called before executing pending job + */ + virtual void PendingJobEntry() = 0; + + /** + * \brief called by the ecmavm when virtual machine start initialization + */ + virtual void VmStart() = 0; + + /** + * \brief called by the ecmavm when virtual machine death + */ + virtual void VmDeath() = 0; + + virtual void Exception(const JSPtLocation &location) = 0; + + virtual bool SingleStep(const JSPtLocation &location) = 0; + + virtual void NativeCalling(const void *nativeAddress) = 0; + + virtual ~PtHooks() = default; + + NO_COPY_SEMANTIC(PtHooks); + NO_MOVE_SEMANTIC(PtHooks); +}; + +class JSDebugInterface { +public: + JSDebugInterface() = default; + + /** + * \brief Register debug hooks in the ecmavm + * @param hooks Pointer to object that implements PtHooks interface + * @return Error if any errors occur + */ + virtual void RegisterHooks(PtHooks *hooks) = 0; + + /** + * \brief Unregister debug hooks in the ecmavm + * @return Error if any errors occur + */ + virtual void UnregisterHooks() = 0; + + /** + * \brief Set breakpoint to \param location with an optional \param condition + * @param location Breakpoint location + * @param condition Optional condition + * @return Error if any errors occur + */ + virtual bool SetBreakpoint(const JSPtLocation &location, Local condFuncRef) = 0; + + /** + * \brief Remove breakpoint from \param location + * @param location Breakpoint location + * @return Error if any errors occur + */ + virtual bool RemoveBreakpoint(const JSPtLocation &location) = 0; + + virtual ~JSDebugInterface() = default; + + NO_COPY_SEMANTIC(JSDebugInterface); + NO_MOVE_SEMANTIC(JSDebugInterface); +}; +} // namespace panda::ecmascript::tooling + +#endif // ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUG_INTERFACE_H diff --git a/tooling/backend/js_pt_extractor.cpp b/tooling/backend/js_pt_extractor.cpp new file mode 100644 index 00000000..aacf3334 --- /dev/null +++ b/tooling/backend/js_pt_extractor.cpp @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/backend/js_pt_extractor.h" + +#include "ecmascript/tooling/backend/debugger_api.h" + +namespace panda::ecmascript::tooling { +uint32_t JSPtExtractor::SingleStepper::GetStackDepth() const +{ + return DebuggerApi::GetStackDepth(ecmaVm_); +} + +bool JSPtExtractor::SingleStepper::InStepRange(uint32_t pc) const +{ + for (const auto &range : stepRanges_) { + if (pc >= range.startBcOffset && pc < range.endBcOffset) { + return true; + } + } + return false; +} + +bool JSPtExtractor::SingleStepper::StepComplete(uint32_t bcOffset) const +{ + std::unique_ptr ptMethod = DebuggerApi::GetMethod(ecmaVm_); + uint32_t stackDepth = GetStackDepth(); + + switch (type_) { + case Type::STEP_INTO: { + if ((method_->GetMethodId() == ptMethod->GetMethodId()) && + (method_->GetJSPandaFile() == ptMethod->GetJSPandaFile()) && + (InStepRange(bcOffset))) { + return false; + } + break; + } + case Type::STEP_OVER: { + if (stackDepth_ < stackDepth) { + return false; + } + if (stackDepth_ == stackDepth && InStepRange(bcOffset)) { + return false; + } + break; + } + case Type::STEP_OUT: { + if (stackDepth_ <= stackDepth) { + return false; + } + break; + } + default: { + return false; + } + } + + return true; +} + +std::unique_ptr JSPtExtractor::GetStepIntoStepper(const EcmaVM *ecmaVm) +{ + return GetStepper(ecmaVm, SingleStepper::Type::STEP_INTO); +} + +std::unique_ptr JSPtExtractor::GetStepOverStepper(const EcmaVM *ecmaVm) +{ + return GetStepper(ecmaVm, SingleStepper::Type::STEP_OVER); +} + +std::unique_ptr JSPtExtractor::GetStepOutStepper(const EcmaVM *ecmaVm) +{ + return GetStepper(ecmaVm, SingleStepper::Type::STEP_OUT); +} + +std::list JSPtExtractor::GetStepRanges(File::EntityId methodId, uint32_t offset) +{ + std::list ranges {}; + const LineNumberTable &table = GetLineNumberTable(methodId); + auto callbackFunc = [table, &ranges](int32_t line) -> bool { + for (auto it = table.begin(); it != table.end(); ++it) { + auto next = it + 1; + if (it->line == line) { + JSPtStepRange range {it->offset, next != table.end() ? next->offset : UINT32_MAX}; + ranges.push_back(range); + } + } + return true; + }; + MatchLineWithOffset(callbackFunc, methodId, offset); + return ranges; +} + +std::unique_ptr JSPtExtractor::GetStepper(const EcmaVM *ecmaVm, SingleStepper::Type type) +{ + std::unique_ptr ptMethod = DebuggerApi::GetMethod(ecmaVm); + ASSERT(ptMethod != nullptr); + + if (type == SingleStepper::Type::STEP_OUT) { + return std::make_unique(ecmaVm, std::move(ptMethod), std::list {}, type); + } + + std::list ranges = GetStepRanges(ptMethod->GetMethodId(), DebuggerApi::GetBytecodeOffset(ecmaVm)); + return std::make_unique(ecmaVm, std::move(ptMethod), std::move(ranges), type); +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/backend/js_pt_extractor.h b/tooling/backend/js_pt_extractor.h new file mode 100644 index 00000000..41cf4a29 --- /dev/null +++ b/tooling/backend/js_pt_extractor.h @@ -0,0 +1,129 @@ +/* + * 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_BACKEND_JS_PT_EXTRACTOR_H +#define ECMASCRIPT_TOOLING_BACKEND_JS_PT_EXTRACTOR_H + +#include "ecmascript/js_thread.h" +#include "ecmascript/jspandafile/debug_info_extractor.h" +#include "ecmascript/tooling/backend/js_debugger_interface.h" +#include "ecmascript/tooling/base/pt_method.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +using panda::panda_file::File; + +class JSPtExtractor : public DebugInfoExtractor { +public: + class SingleStepper { + public: + enum class Type { STEP_INTO, STEP_OVER, STEP_OUT }; + SingleStepper(const EcmaVM *ecmaVm, std::unique_ptr ptMethod, + std::list stepRanges, Type type) + : ecmaVm_(ecmaVm), + method_(std::move(ptMethod)), + stepRanges_(std::move(stepRanges)), + stackDepth_(GetStackDepth()), + type_(type) + { + } + virtual ~SingleStepper() = default; + NO_COPY_SEMANTIC(SingleStepper); + NO_MOVE_SEMANTIC(SingleStepper); + + bool StepComplete(uint32_t bcOffset) const; + Type GetStepperType() const + { + return type_; + } + private: + uint32_t GetStackDepth() const; + bool InStepRange(uint32_t pc) const; + + const EcmaVM *ecmaVm_; + std::unique_ptr method_; + std::list stepRanges_; + uint32_t stackDepth_; + Type type_; + }; + + explicit JSPtExtractor(const JSPandaFile *jsPandaFile) : DebugInfoExtractor(jsPandaFile) {} + virtual ~JSPtExtractor() = default; + + template + bool MatchWithLocation(const Callback &cb, int32_t line, int32_t column) + { + if (line == SPECIAL_LINE_MARK) { + return false; + } + + auto methods = GetMethodIdList(); + for (const auto &method : methods) { + const LineNumberTable &lineTable = GetLineNumberTable(method); + const ColumnNumberTable &columnTable = GetColumnNumberTable(method); + for (uint32_t i = 0; i < lineTable.size(); i++) { + if (lineTable[i].line != line) { + continue; + } + uint32_t currentOffset = lineTable[i].offset; + uint32_t nextOffset = ((i == lineTable.size() - 1) ? UINT32_MAX : lineTable[i + 1].offset); + for (const auto &pair : columnTable) { + if (pair.column == column && pair.offset >= currentOffset && pair.offset < nextOffset) { + return cb(method, pair.offset); + } + } + return cb(method, currentOffset); + } + } + return false; + } + + template + bool MatchLineWithOffset(const Callback &cb, File::EntityId methodId, uint32_t offset) + { + int32_t line = 0; + const LineNumberTable &lineTable = GetLineNumberTable(methodId); + auto iter = std::upper_bound(lineTable.begin(), lineTable.end(), LineTableEntry {offset, 0}); + if (iter != lineTable.begin()) { + line = (iter - 1)->line; + } + return cb(line); + } + + template + bool MatchColumnWithOffset(const Callback &cb, File::EntityId methodId, uint32_t offset) + { + int32_t column = 0; + const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId); + auto iter = std::upper_bound(columnTable.begin(), columnTable.end(), ColumnTableEntry {offset, 0}); + if (iter != columnTable.begin()) { + column = (iter - 1)->column; + } + return cb(column); + } + std::unique_ptr GetStepIntoStepper(const EcmaVM *ecmaVm); + std::unique_ptr GetStepOverStepper(const EcmaVM *ecmaVm); + std::unique_ptr GetStepOutStepper(const EcmaVM *ecmaVm); + + constexpr static int32_t SPECIAL_LINE_MARK = -1; + +private: + NO_COPY_SEMANTIC(JSPtExtractor); + NO_MOVE_SEMANTIC(JSPtExtractor); + std::list GetStepRanges(File::EntityId methodId, uint32_t offset); + std::unique_ptr GetStepper(const EcmaVM *ecmaVm, SingleStepper::Type type); +}; +} // namespace panda::ecmascript::tooling +#endif // ECMASCRIPT_TOOLING_BACKEND_JS_PT_EXTRACTOR_H diff --git a/tooling/backend/js_pt_hooks.cpp b/tooling/backend/js_pt_hooks.cpp new file mode 100644 index 00000000..f47bc2a4 --- /dev/null +++ b/tooling/backend/js_pt_hooks.cpp @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/backend/js_pt_hooks.h" + +#include "ecmascript/tooling/agent/debugger_impl.h" + +namespace panda::ecmascript::tooling { +void JSPtHooks::Breakpoint(const JSPtLocation &location) +{ + LOG_DEBUGGER(VERBOSE) << "JSPtHooks: Breakpoint => " << location.GetMethodId() << ": " + << location.GetBytecodeOffset(); + + [[maybe_unused]] LocalScope scope(debugger_->vm_); + debugger_->NotifyPaused(location, INSTRUMENTATION); +} + +void JSPtHooks::Exception([[maybe_unused]] const JSPtLocation &location) +{ + LOG_DEBUGGER(VERBOSE) << "JSPtHooks: Exception"; + [[maybe_unused]] LocalScope scope(debugger_->vm_); + + debugger_->NotifyPaused({}, EXCEPTION); +} + +bool JSPtHooks::SingleStep(const JSPtLocation &location) +{ + LOG_DEBUGGER(VERBOSE) << "JSPtHooks: SingleStep => " << location.GetBytecodeOffset(); + + [[maybe_unused]] LocalScope scope(debugger_->vm_); + if (UNLIKELY(firstTime_)) { + firstTime_ = false; + + debugger_->NotifyPaused({}, BREAK_ON_START); + return false; + } + + // pause or step complete + if (debugger_->NotifySingleStep(location)) { + debugger_->NotifyPaused({}, OTHER); + return true; + } + + // temporary "safepoint" to handle possible protocol command + debugger_->NotifyHandleProtocolCommand(); + + return false; +} + +void JSPtHooks::LoadModule(std::string_view pandaFileName, std::string_view entryPoint) +{ + LOG_DEBUGGER(VERBOSE) << "JSPtHooks: LoadModule: " << pandaFileName; + + [[maybe_unused]] LocalScope scope(debugger_->vm_); + + static uint32_t scriptId = 0; + if (debugger_->NotifyScriptParsed(scriptId++, pandaFileName.data(), entryPoint)) { + firstTime_ = true; + } +} + +void JSPtHooks::PendingJobEntry() +{ + LOG_DEBUGGER(VERBOSE) << "JSPtHooks: PendingJobEntry"; + + [[maybe_unused]] LocalScope scope(debugger_->vm_); + + debugger_->NotifyPendingJobEntry(); +} + +void JSPtHooks::NativeCalling(const void *nativeAddress) +{ + LOG_DEBUGGER(INFO) << "JSPtHooks: NativeCalling, addr = " << nativeAddress; + + [[maybe_unused]] LocalScope scope(debugger_->vm_); + + debugger_->NotifyNativeCalling(nativeAddress); +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/backend/js_pt_hooks.h b/tooling/backend/js_pt_hooks.h new file mode 100644 index 00000000..3b5ef631 --- /dev/null +++ b/tooling/backend/js_pt_hooks.h @@ -0,0 +1,51 @@ +/* + * 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_BACKEND_JS_PT_HOOKS_H +#define ECMASCRIPT_TOOLING_BACKEND_JS_PT_HOOKS_H + +#include "ecmascript/tooling/backend/js_pt_extractor.h" +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/base/pt_script.h" +#include "ecmascript/tooling/backend/js_debugger_interface.h" + +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +class DebuggerImpl; + +class JSPtHooks : public PtHooks { +public: + explicit JSPtHooks(DebuggerImpl *debugger) : debugger_(debugger) {} + ~JSPtHooks() override = default; + + void Breakpoint(const JSPtLocation &location) override; + void LoadModule(std::string_view pandaFileName, std::string_view entryPoint) override; + void Exception(const JSPtLocation &location) override; + bool SingleStep(const JSPtLocation &location) override; + void NativeCalling(const void *nativeAddress) override; + void PendingJobEntry() override; + void VmStart() override {} + void VmDeath() override {} + +private: + NO_COPY_SEMANTIC(JSPtHooks); + NO_MOVE_SEMANTIC(JSPtHooks); + + DebuggerImpl *debugger_ {nullptr}; + bool firstTime_ {true}; +}; +} // namespace panda::ecmascript::tooling +#endif // ECMASCRIPT_TOOLING_BACKEND_JS_PT_HOOKS_H \ No newline at end of file diff --git a/tooling/backend/js_pt_location.h b/tooling/backend/js_pt_location.h new file mode 100644 index 00000000..434372fa --- /dev/null +++ b/tooling/backend/js_pt_location.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_BACKEND_JS_PT_LOCATION_H +#define ECMASCRIPT_TOOLING_BACKEND_JS_PT_LOCATION_H + +#include + +#include "libpandafile/file.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +class JSPtLocation { +public: + using EntityId = panda_file::File::EntityId; + + explicit JSPtLocation(const char *pandaFile, EntityId methodId, uint32_t bytecodeOffset) + : pandaFile_(pandaFile), methodId_(methodId), bytecodeOffset_(bytecodeOffset) + { + } + + const char *GetPandaFile() const + { + return pandaFile_; + } + + EntityId GetMethodId() const + { + return methodId_; + } + + uint32_t GetBytecodeOffset() const + { + return bytecodeOffset_; + } + + bool operator==(const JSPtLocation &location) const + { + return methodId_ == location.methodId_ && bytecodeOffset_ == location.bytecodeOffset_ && + ::strcmp(pandaFile_, location.pandaFile_) == 0; + } + + ~JSPtLocation() = default; + + DEFAULT_COPY_SEMANTIC(JSPtLocation); + DEFAULT_MOVE_SEMANTIC(JSPtLocation); + +private: + const char *pandaFile_; + EntityId methodId_; + uint32_t bytecodeOffset_ {0}; +}; +} // namespace panda::ecmascript::tooling + +#endif // ECMASCRIPT_TOOLING_BACKEND_JS_PT_LOCATION_H diff --git a/tooling/base/pt_base64.cpp b/tooling/base/pt_base64.cpp new file mode 100644 index 00000000..805717d2 --- /dev/null +++ b/tooling/base/pt_base64.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 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/base/pt_base64.h" + +namespace panda::ecmascript::tooling { +static const uint8_t DECODE_STR_LEN = 3; +static const uint8_t ENCODE_STR_LEN = 4; +static const uint8_t INVAILD_STR = 255; +static uint8_t decodeMap[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255, + 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255 +}; +static char encodeMap[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"}; +uint32_t PtBase64::Decode(const std::string &input, std::string &output) +{ + size_t srcLen = input.size(); + if (srcLen < ENCODE_STR_LEN || srcLen % ENCODE_STR_LEN != 0) { + return 0; + } + + std::string strDecode; + uint32_t decodeLen = srcLen / ENCODE_STR_LEN * DECODE_STR_LEN; + strDecode.resize(decodeLen); + + uint32_t i = 0; + uint32_t j = 0; + uint32_t equalsCnt = 0; + const char* src = input.data(); + char base64Char[ENCODE_STR_LEN]; + while (srcLen--) { + if (*src != '=') { + auto const v = decodeMap[static_cast(*src)]; + if (v == INVAILD_STR) { + return 0; + } + base64Char[j++] = v; + } else { + base64Char[j++] = '\0'; + equalsCnt++; + } + if (j == ENCODE_STR_LEN) { + // 2: shift 2bits, 4: shift 4bits + strDecode[i] = (base64Char[0] << 2) | ((base64Char[1] & 0x30) >> 4); + // 2: shift 2bits, 4: shift 4bits + strDecode[i + 1] = (base64Char[1] << 4) | ((base64Char[2] & 0x3c) >> 2); + // 2: shift 2bits, 3: the last encode str, 6: shift 6bits + strDecode[i + 2] = (base64Char[2] << 6) | base64Char[3]; + j = 0; + i += DECODE_STR_LEN; + } + src++; + } + decodeLen -= equalsCnt; + strDecode.resize(decodeLen); + output = std::move(strDecode); + return decodeLen; +} + +uint32_t PtBase64::Encode(const std::string &input, std::string &output) +{ + uint32_t srcLen = input.size(); + if (srcLen == 0) { + return 0; + } + + uint32_t lastLen = srcLen % DECODE_STR_LEN; + uint32_t encodeLen; + uint32_t equalsCnt; + if (lastLen == 0) { + encodeLen = srcLen / DECODE_STR_LEN * ENCODE_STR_LEN; + equalsCnt = 0; + } else { + encodeLen = (srcLen / DECODE_STR_LEN + 1) * ENCODE_STR_LEN; + equalsCnt = DECODE_STR_LEN - lastLen; + } + + uint32_t i = 0; + uint32_t j = 0; + uint32_t index = 0; + std::string strEncode; + strEncode.resize(encodeLen); + const char* src = input.data(); + while (i + 3 < encodeLen) { // 3: the last encode str + index = src[j] >> 2; // 2: shift 2bits + strEncode[i] = encodeMap[index]; + index = ((src[j] & 0x03) << 4) | (src[j + 1] >> 4); // 4: shift 4bits + strEncode[i + 1] = encodeMap[index]; + index = ((src[j + 1] & 0x0F) << 2) | (src[j + 2] >> 6); // 2: shift 2bits, 6: shift 6bits + strEncode[i + 2] = encodeMap[index]; // 2: the second char + index = src[j + 2] & 0x3F; // 2: the second char + strEncode[i + 3] = encodeMap[index]; // 3: the third char + + i += ENCODE_STR_LEN; + j += DECODE_STR_LEN; + } + + if (equalsCnt == 1) { + strEncode[encodeLen - 1] = '='; + } else if (equalsCnt == 2) { // 2: Equal's count + strEncode[encodeLen - 1] = '='; + strEncode[encodeLen - 2] = '='; // 2: the last two chars + } + output = std::move(strEncode); + return encodeLen; +} +} // namespace panda::ecmascript::tooling \ No newline at end of file diff --git a/tooling/base/pt_base64.h b/tooling/base/pt_base64.h new file mode 100644 index 00000000..43092841 --- /dev/null +++ b/tooling/base/pt_base64.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_BASE_PT_BASE64_H +#define ECMASCRIPT_TOOLING_BASE_PT_BASE64_H + +#include + +namespace panda::ecmascript::tooling { +class PtBase64 { +public: + PtBase64() = default; + ~PtBase64() = default; + + static uint32_t Decode(const std::string &input, std::string &output); + static uint32_t Encode(const std::string &input, std::string &output); +}; +} // namespace panda::ecmascript::tooling +#endif diff --git a/tooling/base/pt_events.cpp b/tooling/base/pt_events.cpp new file mode 100644 index 00000000..b0fabe16 --- /dev/null +++ b/tooling/base/pt_events.cpp @@ -0,0 +1,352 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/base/pt_events.h" + +namespace panda::ecmascript::tooling { +std::unique_ptr BreakpointResolved::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + result->Add("breakpointId", breakpointId_.c_str()); + ASSERT(location_ != nullptr); + result->Add("location", location_->ToJson()); + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr Paused::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr array = PtJson::CreateArray(); + size_t len = callFrames_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(callFrames_[i] != nullptr); + array->Push(callFrames_[i]->ToJson()); + } + result->Add("callFrames", array); + result->Add("reason", reason_.c_str()); + if (data_) { + ASSERT(data_.value() != nullptr); + result->Add("data", data_.value()->ToJson()); + } + if (hitBreakpoints_) { + std::unique_ptr breakpoints = PtJson::CreateArray(); + len = hitBreakpoints_->size(); + for (size_t i = 0; i < len; i++) { + breakpoints->Push(hitBreakpoints_.value()[i].c_str()); + } + result->Add("hitBreakpoints", breakpoints); + } + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr Resumed::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr NativeCalling::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr object = PtJson::CreateObject(); + result->Add("nativeAddress", reinterpret_cast(GetNativeAddress())); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr ScriptFailedToParse::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("scriptId", std::to_string(scriptId_).c_str()); + result->Add("url", url_.c_str()); + result->Add("startLine", startLine_); + result->Add("startColumn", startColumn_); + result->Add("endLine", endLine_); + result->Add("endColumn", endColumn_); + result->Add("executionContextId", executionContextId_); + result->Add("hash", hash_.c_str()); + if (sourceMapUrl_) { + result->Add("sourceMapURL", sourceMapUrl_->c_str()); + } + if (hasSourceUrl_) { + result->Add("hasSourceURL", hasSourceUrl_.value()); + } + if (isModule_) { + result->Add("isModule", isModule_.value()); + } + if (length_) { + result->Add("length", length_.value()); + } + if (codeOffset_) { + result->Add("codeOffset", codeOffset_.value()); + } + if (scriptLanguage_) { + result->Add("scriptLanguage", scriptLanguage_->c_str()); + } + if (embedderName_) { + result->Add("embedderName", embedderName_->c_str()); + } + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr ScriptParsed::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("scriptId", std::to_string(scriptId_).c_str()); + result->Add("url", url_.c_str()); + result->Add("startLine", startLine_); + result->Add("startColumn", startColumn_); + result->Add("endLine", endLine_); + result->Add("endColumn", endColumn_); + result->Add("executionContextId", executionContextId_); + result->Add("hash", hash_.c_str()); + if (isLiveEdit_) { + result->Add("isLiveEdit", isLiveEdit_.value()); + } + if (sourceMapUrl_) { + result->Add("sourceMapURL", sourceMapUrl_->c_str()); + } + if (hasSourceUrl_) { + result->Add("hasSourceURL", hasSourceUrl_.value()); + } + if (isModule_) { + result->Add("isModule", isModule_.value()); + } + if (length_) { + result->Add("length", length_.value()); + } + if (codeOffset_) { + result->Add("codeOffset", codeOffset_.value()); + } + if (scriptLanguage_) { + result->Add("scriptLanguage", scriptLanguage_->c_str()); + } + if (embedderName_) { + result->Add("embedderName", embedderName_->c_str()); + } + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr AddHeapSnapshotChunk::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("chunk", chunk_.c_str()); + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr ConsoleProfileFinished::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("id", id_.c_str()); + ASSERT(location_ != nullptr); + result->Add("location", location_->ToJson()); + ASSERT(profile_ != nullptr); + result->Add("profile", profile_->ToJson()); + if (title_) { + result->Add("title", title_->c_str()); + } + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr ConsoleProfileStarted::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("id", id_.c_str()); + ASSERT(location_ != nullptr); + result->Add("location", location_->ToJson()); + if (title_) { + result->Add("title", title_->c_str()); + } + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr PreciseCoverageDeltaUpdate::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("timestamp", timestamp_); + result->Add("occasion", occasion_.c_str()); + std::unique_ptr array = PtJson::CreateArray(); + size_t len = result_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(result_[i] != nullptr); + std::unique_ptr res = result_[i]->ToJson(); + array->Push(res); + } + result->Add("result", array); + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr HeapStatsUpdate::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr array = PtJson::CreateArray(); + size_t len = statsUpdate_.size(); + for (size_t i = 0; i < len; i++) { + array->Push(statsUpdate_[i]); + } + result->Add("statsUpdate", array); + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr LastSeenObjectId::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("lastSeenObjectId", lastSeenObjectId_); + result->Add("timestamp", timestamp_); + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr ReportHeapSnapshotProgress::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("done", done_); + result->Add("total", total_); + if (finished_) { + result->Add("finished", finished_.value()); + } + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr BufferUsage::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + if (percentFull_) { + result->Add("percentFull", percentFull_.value()); + } + if (eventCount_) { + result->Add("eventCount", eventCount_.value()); + } + if (value_) { + result->Add("value", value_.value()); + } + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr DataCollected::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr array = PtJson::CreateArray(); + size_t len = value_.size(); + for (size_t i = 0; i < len; i++) { + array->Push(value_[i]); + } + result->Add("value", array); + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} + +std::unique_ptr TracingComplete::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("dataLossOccurred", dataLossOccurred_); + if (traceFormat_) { + result->Add("traceFormat", traceFormat_.value()->c_str()); + } + if (streamCompression_) { + result->Add("streamCompression", streamCompression_.value()->c_str()); + } + + std::unique_ptr object = PtJson::CreateObject(); + object->Add("method", GetName().c_str()); + object->Add("params", result); + + return object; +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/base/pt_events.h b/tooling/base/pt_events.h new file mode 100644 index 00000000..3f7b86c5 --- /dev/null +++ b/tooling/base/pt_events.h @@ -0,0 +1,1266 @@ +/* + * 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_BASE_PT_EVENTS_H +#define ECMASCRIPT_TOOLING_BASE_PT_EVENTS_H + +#include +#include + +#include "ecmascript/tooling/base/pt_script.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/dispatcher.h" + +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +class PtBaseEvents : public PtBaseTypes { +public: + PtBaseEvents() = default; + ~PtBaseEvents() override = default; + virtual std::string GetName() const = 0; + +private: + NO_COPY_SEMANTIC(PtBaseEvents); + NO_MOVE_SEMANTIC(PtBaseEvents); +}; + +class BreakpointResolved final : public PtBaseEvents { +public: + BreakpointResolved() = default; + ~BreakpointResolved() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "Debugger.breakpointResolved"; + } + + BreakpointId GetBreakpointId() const + { + return breakpointId_; + } + + BreakpointResolved &SetBreakpointId(const BreakpointId &breakpointId) + { + breakpointId_ = breakpointId; + return *this; + } + + Location *GetLocation() const + { + return location_.get(); + } + + BreakpointResolved &SetLocation(std::unique_ptr location) + { + location_ = std::move(location); + return *this; + } + +private: + NO_COPY_SEMANTIC(BreakpointResolved); + NO_MOVE_SEMANTIC(BreakpointResolved); + + BreakpointId breakpointId_ {}; + std::unique_ptr location_ {nullptr}; +}; + +class Paused final : public PtBaseEvents { +public: + Paused() = default; + ~Paused() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "Debugger.paused"; + } + + const std::vector> *GetCallFrames() const + { + return &callFrames_; + } + + Paused &SetCallFrames(std::vector> callFrames) + { + callFrames_ = std::move(callFrames); + return *this; + } + + const std::string &GetReason() const + { + return reason_; + } + + Paused &SetReason(PauseReason reason) + { + reason_ = GetReasonString(reason); + return *this; + } + + static std::string GetReasonString(PauseReason reason) + { + switch (reason) { + case AMBIGUOUS: { + return "ambiguous"; + } + case ASSERT: { + return "assert"; + } + case DEBUGCOMMAND: { + return "debugCommand"; + } + case DOM: { + return "DOM"; + } + case EVENTLISTENER: { + return "EventListener"; + } + case EXCEPTION: { + return "exception"; + } + case INSTRUMENTATION: { + return "instrumentation"; + } + case OOM: { + return "OOM"; + } + case OTHER: { + return "other"; + } + case PROMISEREJECTION: { + return "promiseRejection"; + } + case XHR: { + return "XHR"; + } + case BREAK_ON_START: { + return "Break on start"; + } + default: { + LOG_DEBUGGER(ERROR) << "Unknown paused reason: " << reason; + } + } + return ""; + } + + RemoteObject *GetData() const + { + if (data_) { + return data_->get(); + } + return nullptr; + } + + Paused &SetData(std::unique_ptr data) + { + data_ = std::move(data); + return *this; + } + + bool HasData() const + { + return data_.has_value(); + } + + std::vector GetHitBreakpoints() const + { + return hitBreakpoints_.value_or(std::vector()); + } + + Paused &SetHitBreakpoints(std::vector hitBreakpoints) + { + hitBreakpoints_ = std::move(hitBreakpoints); + return *this; + } + + bool HasHitBreakpoints() const + { + return hitBreakpoints_.has_value(); + } + +private: + NO_COPY_SEMANTIC(Paused); + NO_MOVE_SEMANTIC(Paused); + + std::vector> callFrames_ {}; + std::string reason_ {}; + std::optional> data_ {}; + std::optional> hitBreakpoints_ {}; +}; + +class Resumed final : public PtBaseEvents { +public: + Resumed() = default; + ~Resumed() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "Debugger.resumed"; + } + +private: + NO_COPY_SEMANTIC(Resumed); + NO_MOVE_SEMANTIC(Resumed); +}; + +class NativeCalling final : public PtBaseEvents { +public: + NativeCalling() = default; + ~NativeCalling() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "Debugger.nativeCalling"; + } + + const void *GetNativeAddress() const + { + return nativeAddress_; + } + + NativeCalling &SetNativeAddress(const void *nativeAddress) + { + nativeAddress_ = nativeAddress; + return *this; + } + +private: + NO_COPY_SEMANTIC(NativeCalling); + NO_MOVE_SEMANTIC(NativeCalling); + + const void *nativeAddress_ { nullptr }; +}; + +class ScriptFailedToParse final : public PtBaseEvents { +public: + ScriptFailedToParse() = default; + ~ScriptFailedToParse() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "Debugger.scriptFailedToParse"; + } + + ScriptId GetScriptId() const + { + return scriptId_; + } + + ScriptFailedToParse &SetScriptId(ScriptId scriptId) + { + scriptId_ = scriptId; + return *this; + } + + const std::string &GetUrl() const + { + return url_; + } + + ScriptFailedToParse &SetUrl(const std::string &url) + { + url_ = url; + return *this; + } + + int32_t GetStartLine() const + { + return startLine_; + } + + ScriptFailedToParse &SetStartLine(int32_t startLine) + { + startLine_ = startLine; + return *this; + } + + int32_t GetStartColumn() const + { + return startColumn_; + } + + ScriptFailedToParse &SetStartColumn(int32_t startColumn) + { + startColumn_ = startColumn; + return *this; + } + + int32_t GetEndLine() const + { + return endLine_; + } + + ScriptFailedToParse &SetEndLine(int32_t endLine) + { + endLine_ = endLine; + return *this; + } + + int32_t GetEndColumn() const + { + return endColumn_; + } + + ScriptFailedToParse &SetEndColumn(int32_t endColumn) + { + endColumn_ = endColumn; + return *this; + } + + int32_t GetExecutionContextId() const + { + return executionContextId_; + } + + ScriptFailedToParse &SetExecutionContextId(int32_t executionContextId) + { + executionContextId_ = executionContextId; + return *this; + } + + const std::string &GetHash() const + { + return hash_; + } + + ScriptFailedToParse &SetHash(const std::string &hash) + { + hash_ = hash; + return *this; + } + + Local GetExecutionContextAuxData() const + { + return execContextAuxData_.value_or(Local()); + } + + ScriptFailedToParse &SetExecutionContextAuxData(Local execContextAuxData) + { + execContextAuxData_ = execContextAuxData; + return *this; + } + + bool HasExecutionContextAuxData() const + { + return execContextAuxData_.has_value(); + } + + const std::string &GetSourceMapURL() const + { + ASSERT(HasSourceMapUrl()); + return sourceMapUrl_.value(); + } + + ScriptFailedToParse &SetSourceMapURL(const std::string &sourceMapUrl) + { + sourceMapUrl_ = sourceMapUrl; + return *this; + } + + bool HasSourceMapUrl() const + { + return sourceMapUrl_.has_value(); + } + + bool GetHasSourceURL() const + { + return hasSourceUrl_.value_or(false); + } + + ScriptFailedToParse &SetHasSourceURL(bool hasSourceUrl) + { + hasSourceUrl_ = hasSourceUrl; + return *this; + } + + bool HasHasSourceURL() const + { + return hasSourceUrl_.has_value(); + } + + bool GetIsModule() const + { + return isModule_.value_or(false); + } + + ScriptFailedToParse &SetIsModule(bool isModule) + { + isModule_ = isModule; + return *this; + } + + bool HasIsModule() const + { + return isModule_.has_value(); + } + + int32_t GetLength() const + { + return length_.value_or(0); + } + + ScriptFailedToParse &SetLength(int32_t length) + { + length_ = length; + return *this; + } + + bool HasLength() const + { + return length_.has_value(); + } + + int32_t GetCodeOffset() const + { + return codeOffset_.value_or(0); + } + + ScriptFailedToParse &SetCodeOffset(int32_t codeOffset) + { + codeOffset_ = codeOffset; + return *this; + } + + bool HasCodeOffset() const + { + return codeOffset_.has_value(); + } + + const std::string &GetScriptLanguage() const + { + ASSERT(HasScriptLanguage()); + return scriptLanguage_.value(); + } + + ScriptFailedToParse &SetScriptLanguage(const std::string &scriptLanguage) + { + scriptLanguage_ = scriptLanguage; + return *this; + } + + bool HasScriptLanguage() const + { + return scriptLanguage_.has_value(); + } + + const std::string &GetEmbedderName() const + { + ASSERT(HasEmbedderName()); + return embedderName_.value(); + } + + ScriptFailedToParse &SetEmbedderName(const std::string &embedderName) + { + embedderName_ = embedderName; + return *this; + } + + bool HasEmbedderName() const + { + return embedderName_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ScriptFailedToParse); + NO_MOVE_SEMANTIC(ScriptFailedToParse); + + ScriptId scriptId_ {0}; + std::string url_ {}; + int32_t startLine_ {0}; + int32_t startColumn_ {0}; + int32_t endLine_ {0}; + int32_t endColumn_ {0}; + ExecutionContextId executionContextId_ {0}; + std::string hash_ {}; + std::optional> execContextAuxData_ {}; + std::optional sourceMapUrl_ {}; + std::optional hasSourceUrl_ {}; + std::optional isModule_ {}; + std::optional length_ {}; + std::optional codeOffset_ {}; + std::optional scriptLanguage_ {}; + std::optional embedderName_ {}; +}; + +class ScriptParsed final : public PtBaseEvents { +public: + ScriptParsed() = default; + ~ScriptParsed() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "Debugger.scriptParsed"; + } + + ScriptId GetScriptId() const + { + return scriptId_; + } + + ScriptParsed &SetScriptId(ScriptId scriptId) + { + scriptId_ = scriptId; + return *this; + } + + const std::string &GetUrl() const + { + return url_; + } + + ScriptParsed &SetUrl(const std::string &url) + { + url_ = url; + return *this; + } + + int32_t GetStartLine() const + { + return startLine_; + } + + ScriptParsed &SetStartLine(int32_t startLine) + { + startLine_ = startLine; + return *this; + } + + int32_t GetStartColumn() const + { + return startColumn_; + } + + ScriptParsed &SetStartColumn(int32_t startColumn) + { + startColumn_ = startColumn; + return *this; + } + + int32_t GetEndLine() const + { + return endLine_; + } + + ScriptParsed &SetEndLine(int32_t endLine) + { + endLine_ = endLine; + return *this; + } + + int32_t GetEndColumn() const + { + return endColumn_; + } + + ScriptParsed &SetEndColumn(int32_t endColumn) + { + endColumn_ = endColumn; + return *this; + } + + int32_t GetExecutionContextId() const + { + return executionContextId_; + } + + ScriptParsed &SetExecutionContextId(int32_t executionContextId) + { + executionContextId_ = executionContextId; + return *this; + } + + const std::string &GetHash() const + { + return hash_; + } + + ScriptParsed &SetHash(const std::string &hash) + { + hash_ = hash; + return *this; + } + + bool GetIsLiveEdit() const + { + return isLiveEdit_.value_or(false); + } + + ScriptParsed &SetIsLiveEdit(bool isLiveEdit) + { + isLiveEdit_ = isLiveEdit; + return *this; + } + + bool HasIsLiveEdit() const + { + return isLiveEdit_.has_value(); + } + + Local GetExecutionContextAuxData() const + { + return execContextAuxData_.value_or(Local()); + } + + ScriptParsed &SetExecutionContextAuxData(Local execContextAuxData) + { + execContextAuxData_ = execContextAuxData; + return *this; + } + + bool HasExecutionContextAuxData() const + { + return execContextAuxData_.has_value(); + } + + const std::string &GetSourceMapURL() const + { + ASSERT(HasSourceMapUrl()); + return sourceMapUrl_.value(); + } + + ScriptParsed &SetSourceMapURL(const std::string &sourceMapUrl) + { + sourceMapUrl_ = sourceMapUrl; + return *this; + } + + bool HasSourceMapUrl() const + { + return sourceMapUrl_.has_value(); + } + + bool GetHasSourceURL() const + { + return hasSourceUrl_.value_or(false); + } + + ScriptParsed &SetHasSourceURL(bool hasSourceUrl) + { + hasSourceUrl_ = hasSourceUrl; + return *this; + } + + bool HasHasSourceURL() const + { + return hasSourceUrl_.has_value(); + } + + bool GetIsModule() const + { + return isModule_.value_or(false); + } + + ScriptParsed &SetIsModule(bool isModule) + { + isModule_ = isModule; + return *this; + } + + bool HasIsModule() const + { + return isModule_.has_value(); + } + + int32_t GetLength() const + { + return length_.value_or(0); + } + + ScriptParsed &SetLength(int32_t length) + { + length_ = length; + return *this; + } + + bool HasLength() const + { + return length_.has_value(); + } + + int32_t GetCodeOffset() const + { + return codeOffset_.value_or(0); + } + + ScriptParsed &SetCodeOffset(int32_t codeOffset) + { + codeOffset_ = codeOffset; + return *this; + } + + bool HasCodeOffset() const + { + return codeOffset_.has_value(); + } + + const std::string &GetScriptLanguage() const + { + ASSERT(HasScriptLanguage()); + return scriptLanguage_.value(); + } + + ScriptParsed &SetScriptLanguage(const std::string &scriptLanguage) + { + scriptLanguage_ = scriptLanguage; + return *this; + } + + bool HasScriptLanguage() const + { + return scriptLanguage_.has_value(); + } + + const std::string &GetEmbedderName() const + { + ASSERT(HasEmbedderName()); + return embedderName_.value(); + } + + ScriptParsed &SetEmbedderName(const std::string &embedderName) + { + embedderName_ = embedderName; + return *this; + } + + bool HasEmbedderName() const + { + return embedderName_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ScriptParsed); + NO_MOVE_SEMANTIC(ScriptParsed); + + ScriptId scriptId_ {0}; + std::string url_ {}; + int32_t startLine_ {0}; + int32_t startColumn_ {0}; + int32_t endLine_ {0}; + int32_t endColumn_ {0}; + ExecutionContextId executionContextId_ {0}; + std::string hash_ {}; + std::optional> execContextAuxData_ {}; + std::optional isLiveEdit_ {}; + std::optional sourceMapUrl_ {}; + std::optional hasSourceUrl_ {}; + std::optional isModule_ {}; + std::optional length_ {}; + std::optional codeOffset_ {}; + std::optional scriptLanguage_ {}; + std::optional embedderName_ {}; +}; + +class AddHeapSnapshotChunk final : public PtBaseEvents { +public: + AddHeapSnapshotChunk() = default; + ~AddHeapSnapshotChunk() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "HeapProfiler.addHeapSnapshotChunk"; + } + + std::string &GetChunk() + { + return chunk_; + } + +private: + NO_COPY_SEMANTIC(AddHeapSnapshotChunk); + NO_MOVE_SEMANTIC(AddHeapSnapshotChunk); + + std::string chunk_ {}; +}; + +class ConsoleProfileFinished final : public PtBaseEvents { +public: + ConsoleProfileFinished() = default; + ~ConsoleProfileFinished() override = default; + std::unique_ptr ToJson() const override; + std::string GetName() const override + { + return "Profile.ConsoleProfileFinished"; + } + + const std::string &GetId() const + { + return id_; + } + + ConsoleProfileFinished &SetId(const std::string &id) + { + id_ = id; + return *this; + } + + Location *GetLocation() const + { + return location_.get(); + } + + ConsoleProfileFinished &SetLocation(std::unique_ptr location) + { + location_ = std::move(location); + return *this; + } + + Profile *GetProfile() const + { + return profile_.get(); + } + + ConsoleProfileFinished &SetProfile(std::unique_ptr profile) + { + profile_ = std::move(profile); + return *this; + } + + const std::string &GetTitle() const + { + ASSERT(HasTitle()); + return title_.value(); + } + + ConsoleProfileFinished &SetTitle(const std::string &title) + { + title_ = title; + return *this; + } + + bool HasTitle() const + { + return title_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ConsoleProfileFinished); + NO_MOVE_SEMANTIC(ConsoleProfileFinished); + + std::string id_ {}; + std::unique_ptr location_ {nullptr}; + std::unique_ptr profile_ {nullptr}; + std::optional title_ {}; +}; + +class ConsoleProfileStarted final : public PtBaseEvents { +public: + ConsoleProfileStarted() = default; + ~ConsoleProfileStarted() override = default; + std::unique_ptr ToJson() const override; + std::string GetName() const override + { + return "Profile.ConsoleProfileStarted"; + } + + const std::string &GetId() const + { + return id_; + } + + ConsoleProfileStarted &SetId(const std::string &id) + { + id_ = id; + return *this; + } + + Location *GetLocation() const + { + return location_.get(); + } + + ConsoleProfileStarted &SetLocation(std::unique_ptr location) + { + location_ = std::move(location); + return *this; + } + + const std::string &GetTitle() const + { + ASSERT(HasTitle()); + return title_.value(); + } + + ConsoleProfileStarted &SetTitle(const std::string &title) + { + title_ = title; + return *this; + } + + bool HasTitle() const + { + return title_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ConsoleProfileStarted); + NO_MOVE_SEMANTIC(ConsoleProfileStarted); + + std::string id_ {}; + std::unique_ptr location_ {nullptr}; + std::optional title_ {}; +}; + +class PreciseCoverageDeltaUpdate final : public PtBaseEvents { +public: + PreciseCoverageDeltaUpdate() = default; + ~PreciseCoverageDeltaUpdate() override = default; + std::unique_ptr ToJson() const override; + std::string GetName() const override + { + return "Profile.PreciseCoverageDeltaUpdate"; + } + + int64_t GetTimestamp() const + { + return timestamp_; + } + + PreciseCoverageDeltaUpdate &SetTimestamp(int64_t timestamp) + { + timestamp_ = timestamp; + return *this; + } + + const std::string &GetOccasion() const + { + return occasion_; + } + + PreciseCoverageDeltaUpdate &SetOccasion(const std::string &occasion) + { + occasion_ = occasion; + return *this; + } + + const std::vector> *GetResult() const + { + return &result_; + } + + PreciseCoverageDeltaUpdate &SetResult(std::vector> result) + { + result_ = std::move(result); + return *this; + } + +private: + NO_COPY_SEMANTIC(PreciseCoverageDeltaUpdate); + NO_MOVE_SEMANTIC(PreciseCoverageDeltaUpdate); + + int64_t timestamp_ {0}; + std::string occasion_ {}; + std::vector> result_ {}; +}; + +class HeapStatsUpdate final : public PtBaseEvents { +public: + HeapStatsUpdate() = default; + ~HeapStatsUpdate() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "HeapProfiler.heapStatsUpdate"; + } + + const std::vector *GetStatsUpdate() const + { + return &statsUpdate_; + } + + HeapStatsUpdate &SetStatsUpdate(std::vector statsUpdate) + { + statsUpdate_ = std::move(statsUpdate); + return *this; + } + +private: + NO_COPY_SEMANTIC(HeapStatsUpdate); + NO_MOVE_SEMANTIC(HeapStatsUpdate); + + std::vector statsUpdate_ {}; +}; + +class LastSeenObjectId final : public PtBaseEvents { +public: + LastSeenObjectId() = default; + ~LastSeenObjectId() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "HeapProfiler.lastSeenObjectId"; + } + + int32_t GetLastSeenObjectId() const + { + return lastSeenObjectId_; + } + + LastSeenObjectId &SetLastSeenObjectId(int32_t lastSeenObjectId) + { + lastSeenObjectId_ = lastSeenObjectId; + return *this; + } + + double GetTimestamp() const + { + return timestamp_; + } + + LastSeenObjectId &SetTimestamp(double timestamp) + { + timestamp_ = timestamp; + return *this; + } + +private: + NO_COPY_SEMANTIC(LastSeenObjectId); + NO_MOVE_SEMANTIC(LastSeenObjectId); + + int32_t lastSeenObjectId_ {}; + double timestamp_ {}; +}; + +class ReportHeapSnapshotProgress final : public PtBaseEvents { +public: + ReportHeapSnapshotProgress() = default; + ~ReportHeapSnapshotProgress() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "HeapProfiler.reportHeapSnapshotProgress"; + } + + int32_t GetDone() const + { + return done_; + } + + ReportHeapSnapshotProgress &SetDone(int32_t done) + { + done_ = done; + return *this; + } + + int32_t GetTotal() const + { + return total_; + } + + ReportHeapSnapshotProgress &SetTotal(int32_t total) + { + total_ = total; + return *this; + } + + bool GetFinished() const + { + return finished_.value_or(false); + } + + ReportHeapSnapshotProgress &SetFinished(bool finished) + { + finished_ = finished; + return *this; + } + +private: + NO_COPY_SEMANTIC(ReportHeapSnapshotProgress); + NO_MOVE_SEMANTIC(ReportHeapSnapshotProgress); + + int32_t done_ {}; + int32_t total_ {}; + std::optional finished_ {}; +}; + +class BufferUsage final : public PtBaseEvents { +public: + BufferUsage() = default; + ~BufferUsage() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "Tracing.BufferUsage"; + } + + int32_t GetPercentFull() const + { + return percentFull_.value(); + } + + BufferUsage &SetPercentFull(int32_t percentFull) + { + percentFull_ = percentFull; + return *this; + } + + bool HasPercentFull() const + { + return percentFull_.has_value(); + } + + int32_t GetEventCount() const + { + return eventCount_.value(); + } + + BufferUsage &SetEventCount(int32_t eventCount) + { + eventCount_ = eventCount; + return *this; + } + + bool HasEventCount() const + { + return eventCount_.has_value(); + } + + int32_t GetValue() const + { + return value_.value(); + } + + BufferUsage &SetValue(int32_t value) + { + value_ = value; + return *this; + } + + bool HasValue() const + { + return value_.has_value(); + } + +private: + NO_COPY_SEMANTIC(BufferUsage); + NO_MOVE_SEMANTIC(BufferUsage); + + std::optional percentFull_ {0}; + std::optional eventCount_ {0}; + std::optional value_ {0}; +}; + +class DataCollected final : public PtBaseEvents { +public: + DataCollected() = default; + ~DataCollected() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "Tracing.DataCollected"; + } + + const std::vector> *GetValue() const + { + return &value_; + } + + DataCollected &SetValue(std::vector> value) + { + value_ = std::move(value); + return *this; + } + +private: + NO_COPY_SEMANTIC(DataCollected); + NO_MOVE_SEMANTIC(DataCollected); + + std::vector> value_ {}; +}; + +class TracingComplete final : public PtBaseEvents { +public: + TracingComplete() = default; + ~TracingComplete() override = default; + std::unique_ptr ToJson() const override; + + std::string GetName() const override + { + return "Tracing.TracingComplete"; + } + + bool GetDataLossOccurred() const + { + return dataLossOccurred_; + } + + TracingComplete &SetDataLossOccurred(bool dataLossOccurred) + { + dataLossOccurred_ = dataLossOccurred; + return *this; + } + + StreamFormat *GetTraceFormat() const + { + if (traceFormat_) { + return traceFormat_->get(); + } + return nullptr; + } + + TracingComplete &SetTraceFormat(std::unique_ptr traceFormat) + { + traceFormat_ = std::move(traceFormat); + return *this; + } + + bool HasTraceFormat() const + { + return traceFormat_.has_value(); + } + + StreamCompression *GetStreamCompression() const + { + if (streamCompression_) { + return streamCompression_->get(); + } + return nullptr; + } + + TracingComplete &SetStreamCompression(std::unique_ptr streamCompression) + { + streamCompression_ = std::move(streamCompression); + return *this; + } + + bool HasStreamCompression() const + { + return streamCompression_.has_value(); + } + +private: + NO_COPY_SEMANTIC(TracingComplete); + NO_MOVE_SEMANTIC(TracingComplete); + + bool dataLossOccurred_ {}; + /* + * { TracingComplete.stream } IO is currently not supported; + */ + std::optional> traceFormat_ {}; + std::optional> streamCompression_ {}; +}; +} // namespace panda::ecmascript::tooling +#endif diff --git a/tooling/base/pt_json.cpp b/tooling/base/pt_json.cpp new file mode 100644 index 00000000..2943e98b --- /dev/null +++ b/tooling/base/pt_json.cpp @@ -0,0 +1,432 @@ +/* + * Copyright (c) 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/base/pt_json.h" + +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +std::unique_ptr PtJson::CreateObject() +{ + return std::make_unique(cJSON_CreateObject()); +} + +std::unique_ptr PtJson::CreateArray() +{ + return std::make_unique(cJSON_CreateArray()); +} + +void PtJson::ReleaseRoot() +{ + if (object_ != nullptr) { + cJSON_Delete(object_); + object_ = nullptr; + } +} + +std::unique_ptr PtJson::Parse(const std::string &data) +{ + cJSON *value = cJSON_ParseWithOpts(data.c_str(), nullptr, true); + return std::make_unique(value); +} + +std::string PtJson::Stringify() const +{ + if (object_ == nullptr) { + return ""; + } + + char *str = cJSON_PrintUnformatted(object_); + if (str == nullptr) { + return ""; + } + + std::string result(str); + cJSON_free(str); + return result; +} + +bool PtJson::Add(const char *key, bool value) const +{ + ASSERT(key != nullptr && !Contains(key)); + + cJSON *node = cJSON_CreateBool(value); + if (node == nullptr) { + return false; + } + + cJSON_bool ret = cJSON_AddItemToObject(object_, key, node); + if (ret == 0) { + cJSON_Delete(node); + return false; + } + + return true; +} + +bool PtJson::Add(const char *key, int32_t value) const +{ + return Add(key, static_cast(value)); +} + +bool PtJson::Add(const char *key, int64_t value) const +{ + return Add(key, static_cast(value)); +} + +bool PtJson::Add(const char *key, double value) const +{ + ASSERT(key != nullptr && !Contains(key)); + + cJSON *node = cJSON_CreateNumber(value); + if (node == nullptr) { + return false; + } + + cJSON_bool ret = cJSON_AddItemToObject(object_, key, node); + if (ret == 0) { + cJSON_Delete(node); + return false; + } + + return true; +} + +bool PtJson::Add(const char *key, const char *value) const +{ + ASSERT(key != nullptr && !Contains(key)); + + cJSON *node = cJSON_CreateString(value); + if (node == nullptr) { + return false; + } + + cJSON_bool ret = cJSON_AddItemToObject(object_, key, node); + if (ret == 0) { + cJSON_Delete(node); + return false; + } + + return true; +} + +bool PtJson::Add(const char *key, const std::unique_ptr &value) const +{ + ASSERT(key != nullptr && !Contains(key)); + + cJSON *node = value->GetJson(); + if (node == nullptr) { + return false; + } + + cJSON_bool ret = cJSON_AddItemToObject(object_, key, node); + if (ret == 0) { + return false; + } + + return true; +} + +bool PtJson::Push(bool value) const +{ + cJSON *node = cJSON_CreateBool(value); + if (node == nullptr) { + return false; + } + + cJSON_bool ret = cJSON_AddItemToArray(object_, node); + if (ret == 0) { + cJSON_Delete(node); + return false; + } + + return true; +} + +bool PtJson::Push(int32_t value) const +{ + return Push(static_cast(value)); +} + +bool PtJson::Push(int64_t value) const +{ + return Push(static_cast(value)); +} + +bool PtJson::Push(double value) const +{ + cJSON *node = cJSON_CreateNumber(value); + if (node == nullptr) { + return false; + } + + cJSON_bool ret = cJSON_AddItemToArray(object_, node); + if (ret == 0) { + cJSON_Delete(node); + return false; + } + + return true; +} + +bool PtJson::Push(const char *value) const +{ + cJSON *node = cJSON_CreateString(value); + if (node == nullptr) { + return false; + } + + cJSON_bool ret = cJSON_AddItemToArray(object_, node); + if (ret == 0) { + cJSON_Delete(node); + return false; + } + + return true; +} + +bool PtJson::Push(const std::unique_ptr &value) const +{ + if (value == nullptr) { + return false; + } + + cJSON *node = value->GetJson(); + if (node == nullptr) { + return false; + } + + cJSON_bool ret = cJSON_AddItemToArray(object_, node); + if (ret == 0) { + return false; + } + + return true; +} + +bool PtJson::Remove(const char *key) const +{ + ASSERT(key != nullptr && Contains(key)); + + cJSON_DeleteItemFromObject(object_, key); + return true; +} + +bool PtJson::Contains(const char *key) const +{ + cJSON *node = cJSON_GetObjectItemCaseSensitive(object_, key); + return node != nullptr; +} + +std::string PtJson::GetKey() const +{ + if (object_ == nullptr || object_->string == nullptr) { + return ""; + } + + return std::string(object_->string); +} + +cJSON *PtJson::GetJson() const +{ + return object_; +} + +bool PtJson::IsBool() const +{ + return cJSON_IsBool(object_) != 0; +} + +bool PtJson::IsNumber() const +{ + return cJSON_IsNumber(object_) != 0; +} + +bool PtJson::IsString() const +{ + return cJSON_IsString(object_) != 0; +} + +bool PtJson::IsObject() const +{ + return cJSON_IsObject(object_) != 0; +} + +bool PtJson::IsArray() const +{ + return cJSON_IsArray(object_) != 0; +} + +bool PtJson::IsNull() const +{ + return cJSON_IsNull(object_) != 0; +} + +bool PtJson::GetBool(bool defaultValue) const +{ + if (!IsBool()) { + return defaultValue; + } + + return cJSON_IsTrue(object_) != 0; +} + +int32_t PtJson::GetInt(int32_t defaultValue) const +{ + if (!IsNumber()) { + return defaultValue; + } + + return static_cast(object_->valuedouble); +} + +int64_t PtJson::GetInt64(int64_t defaultValue) const +{ + if (!IsNumber()) { + return defaultValue; + } + + return static_cast(object_->valuedouble); +} + +double PtJson::GetDouble(double defaultValue) const +{ + if (!IsNumber()) { + return defaultValue; + } + + return object_->valuedouble; +} + +std::string PtJson::GetString() const +{ + if (!IsString()) { + return ""; + } + + return std::string(object_->valuestring); +} + +int32_t PtJson::GetSize() const +{ + return cJSON_GetArraySize(object_); +} + +std::unique_ptr PtJson::Get(int32_t index) const +{ + return std::make_unique(cJSON_GetArrayItem(object_, index)); +} + +Result PtJson::GetBool(const char *key, bool *value) const +{ + cJSON *item = cJSON_GetObjectItem(object_, key); + if (item == nullptr) { + return Result::NOT_EXIST; + } + if (cJSON_IsBool(item) == 0) { + return Result::TYPE_ERROR; + } + + *value = cJSON_IsTrue(item) != 0; + return Result::SUCCESS; +} + +Result PtJson::GetInt(const char *key, int32_t *value) const +{ + double result; + Result ret = GetDouble(key, &result); + if (ret == Result::SUCCESS) { + *value = static_cast(result); + } + return ret; +} + +Result PtJson::GetInt64(const char *key, int64_t *value) const +{ + double result; + Result ret = GetDouble(key, &result); + if (ret == Result::SUCCESS) { + *value = static_cast(result); + } + return ret; +} + +Result PtJson::GetDouble(const char *key, double *value) const +{ + cJSON *item = cJSON_GetObjectItem(object_, key); + if (item == nullptr) { + return Result::NOT_EXIST; + } + if (cJSON_IsNumber(item) == 0) { + return Result::TYPE_ERROR; + } + + *value = item->valuedouble; + return Result::SUCCESS; +} + +Result PtJson::GetString(const char *key, std::string *value) const +{ + cJSON *item = cJSON_GetObjectItem(object_, key); + if (item == nullptr) { + return Result::NOT_EXIST; + } + if (cJSON_IsString(item) == 0) { + return Result::TYPE_ERROR; + } + + *value = item->valuestring; + return Result::SUCCESS; +} + +Result PtJson::GetObject(const char *key, std::unique_ptr *value) const +{ + cJSON *item = cJSON_GetObjectItem(object_, key); + if (item == nullptr) { + return Result::NOT_EXIST; + } + if (cJSON_IsObject(item) == 0) { + return Result::TYPE_ERROR; + } + + *value = std::make_unique(item); + return Result::SUCCESS; +} + +Result PtJson::GetArray(const char *key, std::unique_ptr *value) const +{ + cJSON *item = cJSON_GetObjectItem(object_, key); + if (item == nullptr) { + return Result::NOT_EXIST; + } + if (cJSON_IsArray(item) == 0) { + return Result::TYPE_ERROR; + } + + *value = std::make_unique(item); + return Result::SUCCESS; +} + +Result PtJson::GetAny(const char *key, std::unique_ptr *value) const +{ + cJSON *item = cJSON_GetObjectItem(object_, key); + if (item == nullptr) { + return Result::NOT_EXIST; + } + + *value = std::make_unique(item); + return Result::SUCCESS; +} +} // namespace panda::ecmascript diff --git a/tooling/base/pt_json.h b/tooling/base/pt_json.h new file mode 100644 index 00000000..88f6cc15 --- /dev/null +++ b/tooling/base/pt_json.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_BASE_PT_JSON_H +#define ECMASCRIPT_TOOLING_BASE_PT_JSON_H + +#include +#include + +#include "cJSON.h" + +namespace panda::ecmascript::tooling { +enum class Result : uint8_t { + SUCCESS, + NOT_EXIST, + TYPE_ERROR, +}; + +class PtJson { +public: + PtJson() = default; + explicit PtJson(cJSON *object) : object_(object) {} + ~PtJson() = default; + + // Create empty json object + static std::unique_ptr CreateObject(); + static std::unique_ptr CreateArray(); + + // Release cJSON object memory + void ReleaseRoot(); + + // String parse to json + static std::unique_ptr Parse(const std::string &data); + + // To string + std::string Stringify() const; + + // Add Json child + bool Add(const char *key, bool value) const; + bool Add(const char *key, int32_t value) const; + bool Add(const char *key, int64_t value) const; + bool Add(const char *key, double value) const; + bool Add(const char *key, const char *value) const; + bool Add(const char *key, const std::unique_ptr &value) const; + + // Push back to array + bool Push(bool value) const; + bool Push(int32_t value) const; + bool Push(int64_t value) const; + bool Push(double value) const; + bool Push(const char *value) const; + bool Push(const std::unique_ptr &value) const; + + // Remove Json child + bool Remove(const char *key) const; + + bool Contains(const char *key) const; + + std::string GetKey() const; + + cJSON *GetJson() const; + + // Type check + bool IsBool() const; + bool IsNumber() const; + bool IsString() const; + bool IsObject() const; + bool IsArray() const; + bool IsNull() const; + + // Object accessor + bool GetBool(bool defaultValue = false) const; + int32_t GetInt(int32_t defaultValue = 0) const; + int64_t GetInt64(int64_t defaultValue = 0) const; + double GetDouble(double defaultValue = 0.0) const; + std::string GetString() const; + + // Array accessor + int32_t GetSize() const; + std::unique_ptr Get(int32_t index) const; + + // Child item accessor + Result GetBool(const char *key, bool *value) const; + Result GetInt(const char *key, int32_t *value) const; + Result GetInt64(const char *key, int64_t *value) const; + Result GetDouble(const char *key, double *value) const; + Result GetString(const char *key, std::string *value) const; + Result GetObject(const char *key, std::unique_ptr *value) const; + Result GetArray(const char *key, std::unique_ptr *value) const; + Result GetAny(const char *key, std::unique_ptr *value) const; + +private: + cJSON *object_ = nullptr; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_TOOLING_BASE_PT_JSON_H diff --git a/tooling/base/pt_method.h b/tooling/base/pt_method.h new file mode 100644 index 00000000..945f8e08 --- /dev/null +++ b/tooling/base/pt_method.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_BASE_PT_METHOD_H +#define ECMASCRIPT_TOOLING_BASE_PT_METHOD_H + +#include "ecmascript/jspandafile/js_pandafile.h" + +#include "libpandafile/code_data_accessor-inl.h" +#include "libpandafile/method_data_accessor-inl.h" + +namespace panda::ecmascript::tooling { +class PtMethod { +public: + explicit PtMethod(const JSPandaFile *jsPandaFile, EntityId methodId, bool isNative) + : jsPandaFile_(jsPandaFile), methodId_(methodId), isNative_(isNative) + { + } + ~PtMethod() = default; + + const JSPandaFile *GetJSPandaFile() const + { + return jsPandaFile_; + } + + EntityId GetMethodId() const + { + return methodId_; + } + + bool IsNativeMethod() const + { + return isNative_; + } + + bool operator==(const PtMethod &method) const + { + return methodId_ == method.methodId_ && + jsPandaFile_ == method.jsPandaFile_; + } + + uint32_t GetCodeSize() const + { + if (jsPandaFile_ == nullptr) { + return 0; + } + panda_file::MethodDataAccessor mda(*(jsPandaFile_->GetPandaFile()), methodId_); + auto codeId = mda.GetCodeId().value(); + if (!codeId.IsValid()) { + return 0; + } + panda_file::CodeDataAccessor cda(*(jsPandaFile_->GetPandaFile()), codeId); + return cda.GetCodeSize(); + } + +private: + const JSPandaFile *jsPandaFile_ {nullptr}; + panda_file::File::EntityId methodId_ {0}; + bool isNative_ {false}; +}; +} // namespace panda::ecmascript::tooling +#endif // ECMASCRIPT_TOOLING_BASE_PT_METHOD_H diff --git a/tooling/base/pt_params.cpp b/tooling/base/pt_params.cpp new file mode 100644 index 00000000..fc135d53 --- /dev/null +++ b/tooling/base/pt_params.cpp @@ -0,0 +1,988 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/base/pt_params.h" + +namespace panda::ecmascript::tooling { +std::unique_ptr EnableParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + double maxScriptsCacheSize; + ret = params.GetDouble("maxScriptsCacheSize", &maxScriptsCacheSize); + if (ret == Result::SUCCESS) { + paramsObject->maxScriptsCacheSize_ = maxScriptsCacheSize; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'maxScriptsCacheSize';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "EnableParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr EvaluateOnCallFrameParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::string callFrameId; + ret = params.GetString("callFrameId", &callFrameId); + if (ret == Result::SUCCESS) { + paramsObject->callFrameId_ = std::stoi(callFrameId); + } else { + error += "Unknown 'callFrameId';"; + } + std::string expression; + ret = params.GetString("expression", &expression); + if (ret == Result::SUCCESS) { + paramsObject->expression_ = std::move(expression); + } else { + error += "Unknown 'expression';"; + } + std::string objectGroup; + ret = params.GetString("objectGroup", &objectGroup); + if (ret == Result::SUCCESS) { + paramsObject->objectGroup_ = std::move(objectGroup); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'objectGroup';"; + } + bool includeCommandLineAPI = false; + ret = params.GetBool("includeCommandLineAPI", &includeCommandLineAPI); + if (ret == Result::SUCCESS) { + paramsObject->includeCommandLineAPI_ = includeCommandLineAPI; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'includeCommandLineAPI';"; + } + bool silent = false; + ret = params.GetBool("silent", &silent); + if (ret == Result::SUCCESS) { + paramsObject->silent_ = silent; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'silent';"; + } + bool returnByValue = false; + ret = params.GetBool("returnByValue", &returnByValue); + if (ret == Result::SUCCESS) { + paramsObject->returnByValue_ = returnByValue; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'returnByValue';"; + } + bool generatePreview = false; + ret = params.GetBool("generatePreview", &generatePreview); + if (ret == Result::SUCCESS) { + paramsObject->generatePreview_ = generatePreview; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'generatePreview';"; + } + bool throwOnSideEffect = false; + ret = params.GetBool("throwOnSideEffect", &throwOnSideEffect); + if (ret == Result::SUCCESS) { + paramsObject->throwOnSideEffect_ = throwOnSideEffect; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'throwOnSideEffect';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "EvaluateOnCallFrameParams::Create " << error; + return nullptr; + } + return paramsObject; +} + +std::unique_ptr GetPossibleBreakpointsParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::unique_ptr start; + ret = params.GetObject("start", &start); + if (ret == Result::SUCCESS) { + std::unique_ptr location = Location::Create(*start); + if (location == nullptr) { + error += "Unknown 'start';"; + } else { + paramsObject->start_ = std::move(location); + } + } else { + error += "Unknown 'start';"; + } + std::unique_ptr end; + ret = params.GetObject("start", &end); + if (ret == Result::SUCCESS) { + std::unique_ptr location = Location::Create(*end); + if (location == nullptr) { + error += "Unknown 'end';"; + } else { + paramsObject->end_ = std::move(location); + } + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'end';"; + } + bool restrictToFunction = false; + ret = params.GetBool("restrictToFunction", &restrictToFunction); + if (ret == Result::SUCCESS) { + paramsObject->restrictToFunction_ = restrictToFunction; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'restrictToFunction';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "GetPossibleBreakpointsParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr GetScriptSourceParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::string scriptId; + ret = params.GetString("scriptId", &scriptId); + if (ret == Result::SUCCESS) { + paramsObject->scriptId_ = std::stoi(scriptId); + } else { + error += "Unknown 'scriptId';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "GetScriptSourceParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr RemoveBreakpointParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::string breakpointId; + ret = params.GetString("breakpointId", &breakpointId); + if (ret == Result::SUCCESS) { + paramsObject->breakpointId_ = std::move(breakpointId); + } else { + error += "Unknown 'breakpointId';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "RemoveBreakpointParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr ResumeParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + bool terminateOnResume = false; + ret = params.GetBool("terminateOnResume", &terminateOnResume); + if (ret == Result::SUCCESS) { + paramsObject->terminateOnResume_ = terminateOnResume; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'terminateOnResume';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "ResumeParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr SetAsyncCallStackDepthParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + int32_t maxDepth; + ret = params.GetInt("maxDepth", &maxDepth); + if (ret == Result::SUCCESS) { + paramsObject->maxDepth_ = maxDepth; + } else { + error += "Unknown 'maxDepth';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SetAsyncCallStackDepthParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr SetBlackboxPatternsParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::unique_ptr patterns; + ret = params.GetArray("patterns", &patterns); + if (ret == Result::SUCCESS) { + int32_t len = patterns->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr item = patterns->Get(i); + if (item->IsString()) { + paramsObject->patterns_.emplace_back(item->GetString()); + } else { + error += "'patterns' items should be a String;"; + } + } + } else { + error += "Unknown 'patterns';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SetBlackboxPatternsParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr SetBreakpointByUrlParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + int32_t lineNumber; + ret = params.GetInt("lineNumber", &lineNumber); + if (ret == Result::SUCCESS) { + paramsObject->lineNumber_ = lineNumber; + } else { + error += "Unknown 'lineNumber';"; + } + std::string url; + ret = params.GetString("url", &url); + if (ret == Result::SUCCESS) { + paramsObject->url_ = std::move(url); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'url';"; + } + std::string urlRegex; + ret = params.GetString("urlRegex", &urlRegex); + if (ret == Result::SUCCESS) { + paramsObject->urlRegex_ = std::move(urlRegex); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'urlRegex';"; + } + std::string scriptHash; + ret = params.GetString("scriptHash", &scriptHash); + if (ret == Result::SUCCESS) { + paramsObject->scriptHash_ = std::move(scriptHash); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'scriptHash';"; + } + int32_t columnNumber; + ret = params.GetInt("columnNumber", &columnNumber); + if (ret == Result::SUCCESS) { + paramsObject->columnNumber_ = columnNumber; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'columnNumber';"; + } + std::string condition; + ret = params.GetString("condition", &condition); + if (ret == Result::SUCCESS) { + paramsObject->condition_ = std::move(condition); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'condition';"; + } + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SetBreakpointByUrlParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr SetPauseOnExceptionsParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::string state; + ret = params.GetString("state", &state); + if (ret == Result::SUCCESS) { + paramsObject->StoreState(state); + } else { + error += "Unknown 'state';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SetPauseOnExceptionsParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr StepIntoParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + bool breakOnAsyncCall = false; + ret = params.GetBool("breakOnAsyncCall", &breakOnAsyncCall); + if (ret == Result::SUCCESS) { + paramsObject->breakOnAsyncCall_ = breakOnAsyncCall; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'breakOnAsyncCall';"; + } + std::unique_ptr skipList; + ret = params.GetArray("skipList", &skipList); + if (ret == Result::SUCCESS) { + int32_t len = skipList->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr obj = LocationRange::Create(*skipList->Get(i)); + if (obj == nullptr) { + error += "'skipList' items LocationRange is invalid;"; + } else { + paramsObject->skipList_->emplace_back(std::move(obj)); + } + } + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'skipList';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "StepIntoParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr StepOverParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::unique_ptr skipList; + ret = params.GetArray("skipList", &skipList); + if (ret == Result::SUCCESS) { + int32_t len = skipList->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr obj = LocationRange::Create(*skipList->Get(i)); + if (obj == nullptr) { + error += "'skipList' items LocationRange is invalid;"; + } else { + paramsObject->skipList_->emplace_back(std::move(obj)); + } + } + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'skipList';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "StepOverParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr SetMixedDebugParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + bool enabled = false; + ret = params.GetBool("enabled", &enabled); + if (ret == Result::SUCCESS) { + paramsObject->enabled_ = enabled; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'enabled';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SetMixedDebugParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr ReplyNativeCallingParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + bool userCode= false; + ret = params.GetBool("userCode", &userCode); + if (ret == Result::SUCCESS) { + paramsObject->userCode_ = userCode; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'userCode';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "ReplyNativeCallingParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr GetPropertiesParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::string objectId; + ret = params.GetString("objectId", &objectId); + if (ret == Result::SUCCESS) { + paramsObject->objectId_ = std::stoi(objectId); + } else { + error += "Unknown 'objectId';"; + } + bool ownProperties = false; + ret = params.GetBool("ownProperties", &ownProperties); + if (ret == Result::SUCCESS) { + paramsObject->ownProperties_ = ownProperties; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'ownProperties';"; + } + bool accessorPropertiesOnly = false; + ret = params.GetBool("accessorPropertiesOnly", &accessorPropertiesOnly); + if (ret == Result::SUCCESS) { + paramsObject->accessorPropertiesOnly_ = accessorPropertiesOnly; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'accessorPropertiesOnly';"; + } + bool generatePreview = false; + ret = params.GetBool("generatePreview", &generatePreview); + if (ret == Result::SUCCESS) { + paramsObject->generatePreview_ = generatePreview; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'generatePreview';"; + } + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "GetPropertiesParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr CallFunctionOnParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + // paramsObject->functionDeclaration_ + std::string functionDeclaration; + ret = params.GetString("functionDeclaration", &functionDeclaration); + if (ret == Result::SUCCESS) { + paramsObject->functionDeclaration_ = std::move(functionDeclaration); + } else { + error += "Unknown 'functionDeclaration';"; + } + // paramsObject->objectId_ + std::string objectId; + ret = params.GetString("objectId", &objectId); + if (ret == Result::SUCCESS) { + paramsObject->objectId_ = std::stoi(objectId); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'objectId';"; + } + // paramsObject->arguments_ + std::unique_ptr arguments; + ret = params.GetArray("arguments", &arguments); + if (ret == Result::SUCCESS) { + int32_t len = arguments->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr obj = CallArgument::Create(*arguments->Get(i)); + if (obj == nullptr) { + error += "'arguments' items CallArgument is invaild;"; + } else { + paramsObject->arguments_->emplace_back(std::move(obj)); + } + } + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'arguments';"; + } + // paramsObject->silent_ + bool silent = false; + ret = params.GetBool("silent", &silent); + if (ret == Result::SUCCESS) { + paramsObject->silent_ = silent; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'silent';"; + } + // paramsObject->returnByValue_ + bool returnByValue = false; + ret = params.GetBool("returnByValue", &returnByValue); + if (ret == Result::SUCCESS) { + paramsObject->returnByValue_ = returnByValue; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'returnByValue';"; + } + // paramsObject->generatePreview_ + bool generatePreview = false; + ret = params.GetBool("generatePreview", &generatePreview); + if (ret == Result::SUCCESS) { + paramsObject->generatePreview_ = generatePreview; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'generatePreview';"; + } + // paramsObject->userGesture_ + bool userGesture = false; + ret = params.GetBool("userGesture", &userGesture); + if (ret == Result::SUCCESS) { + paramsObject->userGesture_ = userGesture; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'userGesture';"; + } + // paramsObject->awaitPromise_ + bool awaitPromise = false; + ret = params.GetBool("awaitPromise", &awaitPromise); + if (ret == Result::SUCCESS) { + paramsObject->awaitPromise_ = awaitPromise; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'awaitPromise';"; + } + // paramsObject->executionContextId_ + int32_t executionContextId; + ret = params.GetInt("executionContextId", &executionContextId); + if (ret == Result::SUCCESS) { + paramsObject->executionContextId_ = executionContextId; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'executionContextId';"; + } + // paramsObject->objectGroup_ + std::string objectGroup; + ret = params.GetString("objectGroup", &objectGroup); + if (ret == Result::SUCCESS) { + paramsObject->objectGroup_ = std::move(objectGroup); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'objectGroup';"; + } + // paramsObject->throwOnSideEffect_ + bool throwOnSideEffect = false; + ret = params.GetBool("throwOnSideEffect", &throwOnSideEffect); + if (ret == Result::SUCCESS) { + paramsObject->throwOnSideEffect_ = throwOnSideEffect; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'throwOnSideEffect';"; + } + + // Check whether the error is empty. + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "CallFunctionOnParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr StartSamplingParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + int32_t samplingInterval; + ret = params.GetInt("samplingInterval", &samplingInterval); + if (ret == Result::SUCCESS) { + paramsObject->samplingInterval_ = samplingInterval; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'samplingInterval';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "StartSamplingParams::Create " << error; + return nullptr; + } + return paramsObject; +} + +std::unique_ptr StartTrackingHeapObjectsParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + bool trackAllocations = false; + ret = params.GetBool("trackAllocations", &trackAllocations); + if (ret == Result::SUCCESS) { + paramsObject->trackAllocations_ = trackAllocations; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'trackAllocations';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "StartTrackingHeapObjectsParams::Create " << error; + return nullptr; + } + return paramsObject; +} + +std::unique_ptr StopTrackingHeapObjectsParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + bool reportProgress = false; + ret = params.GetBool("reportProgress", &reportProgress); + if (ret == Result::SUCCESS) { + paramsObject->reportProgress_ = reportProgress; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'reportProgress';"; + } + + bool treatGlobalObjectsAsRoots = false; + ret = params.GetBool("treatGlobalObjectsAsRoots", &treatGlobalObjectsAsRoots); + if (ret == Result::SUCCESS) { + paramsObject->treatGlobalObjectsAsRoots_ = treatGlobalObjectsAsRoots; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'treatGlobalObjectsAsRoots';"; + } + + bool captureNumericValue = false; + ret = params.GetBool("captureNumericValue", &captureNumericValue); + if (ret == Result::SUCCESS) { + paramsObject->captureNumericValue_ = captureNumericValue; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'captureNumericValue';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "StopTrackingHeapObjectsParams::Create " << error; + return nullptr; + } + return paramsObject; +} + +std::unique_ptr AddInspectedHeapObjectParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::string heapObjectId; + ret = params.GetString("heapObjectId", &heapObjectId); + if (ret == Result::SUCCESS) { + paramsObject->heapObjectId_ = std::stoi(heapObjectId); + } else { + error += "Unknown 'heapObjectId';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "AddInspectedHeapObjectParams::Create " << error; + return nullptr; + } + return paramsObject; +} + +std::unique_ptr GetHeapObjectIdParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::string objectId; + ret = params.GetString("objectId", &objectId); + if (ret == Result::SUCCESS) { + paramsObject->objectId_ = std::stoi(objectId); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'objectId';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "GetHeapObjectIdParams::Create " << error; + return nullptr; + } + return paramsObject; +} + +std::unique_ptr GetObjectByHeapObjectIdParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::string objectId; + ret = params.GetString("objectId", &objectId); + if (ret == Result::SUCCESS) { + paramsObject->objectId_ = std::stoi(objectId); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'objectId';"; + } + + std::string objectGroup; + ret = params.GetString("objectGroup", &objectGroup); + if (ret == Result::SUCCESS) { + paramsObject->objectGroup_ = std::move(objectGroup); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'objectGroup';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "GetObjectByHeapObjectIdParams::Create " << error; + return nullptr; + } + return paramsObject; +} + +std::unique_ptr StartPreciseCoverageParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + bool callCount = false; + ret = params.GetBool("callCount", &callCount); + if (ret == Result::SUCCESS) { + paramsObject->callCount_ = callCount; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'callCount';"; + } + + bool detailed = false; + ret = params.GetBool("detailed", &detailed); + if (ret == Result::SUCCESS) { + paramsObject->detailed_ = detailed; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'detailed';"; + } + + bool allowTriggeredUpdates = false; + ret = params.GetBool("allowTriggeredUpdates", &allowTriggeredUpdates); + if (ret == Result::SUCCESS) { + paramsObject->allowTriggeredUpdates_ = allowTriggeredUpdates; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'allowTriggeredUpdates';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "StartPreciseCoverageParams::Create " << error; + return nullptr; + } + return paramsObject; +} + +std::unique_ptr SetSamplingIntervalParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + int32_t interval = 0; + ret = params.GetInt("interval", &interval); + if (ret == Result::SUCCESS) { + paramsObject->interval_ = interval; + } else { + error += "Unknown 'interval';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SetSamplingIntervalParams::Create " << error; + return nullptr; + } + return paramsObject; +} + +std::unique_ptr RecordClockSyncMarkerParams::Create(const PtJson ¶ms) +{ + std::string error; + auto recordClockSyncMarkerParams = std::make_unique(); + Result ret; + + std::string syncId; + ret = params.GetString("syncId", &syncId); + if (ret == Result::SUCCESS) { + recordClockSyncMarkerParams->syncId_ = syncId; + } else { + error += "Unknown 'syncId';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "RecordClockSyncMarkerParams::Create " << error; + return nullptr; + } + + return recordClockSyncMarkerParams; +} + +std::unique_ptr RequestMemoryDumpParams::Create(const PtJson ¶ms) +{ + std::string error; + auto requestMemoryDumpParams = std::make_unique(); + Result ret; + + bool deterministic = false; + ret = params.GetBool("deterministic", &deterministic); + if (ret == Result::SUCCESS) { + requestMemoryDumpParams->deterministic_ = deterministic; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'deterministic';"; + } + + std::string levelOfDetail; + ret = params.GetString("levelOfDetail", &levelOfDetail); + if (ret == Result::SUCCESS) { + if (MemoryDumpLevelOfDetailValues::Valid(levelOfDetail)) { + requestMemoryDumpParams->levelOfDetail_ = std::move(levelOfDetail); + } else { + error += "'levelOfDetail' is invalid;"; + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'levelOfDetail';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "RequestMemoryDumpParams::Create " << error; + return nullptr; + } + + return requestMemoryDumpParams; +} + +std::unique_ptr StartParams::Create(const PtJson ¶ms) +{ + std::string error; + auto startParams = std::make_unique(); + Result ret; + + std::string categories; + ret = params.GetString("categories", &categories); + if (ret == Result::SUCCESS) { + startParams->categories_ = std::move(categories); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'categories';"; + } + + std::string options; + ret = params.GetString("options", &options); + if (ret == Result::SUCCESS) { + startParams->options_ = std::move(options); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'options';"; + } + + int32_t bufferUsageReportingInterval = 0; + ret = params.GetInt("bufferUsageReportingInterval", &bufferUsageReportingInterval); + if (ret == Result::SUCCESS) { + startParams->bufferUsageReportingInterval_ = bufferUsageReportingInterval; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'bufferUsageReportingInterval';"; + } + + std::string transferMode; + ret = params.GetString("transferMode", &transferMode); + if (ret == Result::SUCCESS) { + if (StartParams::TransferModeValues::Valid(transferMode)) { + startParams->transferMode_ = std::move(transferMode); + } else { + error += "'transferMode' is invalid;"; + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'transferMode';"; + } + + std::string streamFormat; + ret = params.GetString("streamFormat", &streamFormat); + if (ret == Result::SUCCESS) { + if (StreamFormatValues::Valid(streamFormat)) { + startParams->streamFormat_ = std::move(streamFormat); + } else { + error += "'streamFormat' is invalid;"; + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'streamFormat';"; + } + + std::string streamCompression; + ret = params.GetString("streamCompression", &streamCompression); + if (ret == Result::SUCCESS) { + if (StreamCompressionValues::Valid(streamCompression)) { + startParams->streamCompression_ = std::move(streamCompression); + } else { + error += "'streamCompression' is invalid;"; + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'streamCompression';"; + } + + std::unique_ptr traceConfig; + ret = params.GetObject("traceConfig", &traceConfig); + if (ret == Result::SUCCESS) { + std::unique_ptr pTraceConfig = TraceConfig::Create(*traceConfig); + if (pTraceConfig == nullptr) { + error += "'traceConfig' format invalid;"; + } else { + startParams->traceConfig_ = std::move(pTraceConfig); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'traceConfig';"; + } + + std::string perfettoConfig; + ret = params.GetString("perfettoConfig", &perfettoConfig); + if (ret == Result::SUCCESS) { + startParams->perfettoConfig_ = std::move(perfettoConfig); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'perfettoConfig';"; + } + + std::string tracingBackend; + ret = params.GetString("tracingBackend", &tracingBackend); + if (ret == Result::SUCCESS) { + if (TracingBackendValues::Valid(tracingBackend)) { + startParams->tracingBackend_ = std::move(tracingBackend); + } else { + error += "'tracingBackend' is invalid;"; + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'tracingBackend';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "StartParams::Create " << error; + return nullptr; + } + + return startParams; +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/base/pt_params.h b/tooling/base/pt_params.h new file mode 100644 index 00000000..a2d813c7 --- /dev/null +++ b/tooling/base/pt_params.h @@ -0,0 +1,1132 @@ +/* + * 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_BASE_PT_PARAMS_H +#define ECMASCRIPT_TOOLING_BASE_PT_PARAMS_H + +#include "ecmascript/tooling/base/pt_types.h" + +namespace panda::ecmascript::tooling { +class PtBaseParams : public PtBaseTypes { +public: + PtBaseParams() = default; + ~PtBaseParams() override = default; + std::unique_ptr ToJson() const override + { + UNREACHABLE(); + } + +private: + NO_COPY_SEMANTIC(PtBaseParams); + NO_MOVE_SEMANTIC(PtBaseParams); +}; + +class EnableParams : public PtBaseParams { +public: + EnableParams() = default; + ~EnableParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + double GetMaxScriptsCacheSize() const + { + return maxScriptsCacheSize_.value_or(0); + } + + bool HasMaxScriptsCacheSize() const + { + return maxScriptsCacheSize_.has_value(); + } + +private: + NO_COPY_SEMANTIC(EnableParams); + NO_MOVE_SEMANTIC(EnableParams); + + std::optional maxScriptsCacheSize_ {}; +}; + +class EvaluateOnCallFrameParams : public PtBaseParams { +public: + EvaluateOnCallFrameParams() = default; + ~EvaluateOnCallFrameParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + CallFrameId GetCallFrameId() const + { + return callFrameId_; + } + + const std::string &GetExpression() const + { + return expression_; + } + +private: + NO_COPY_SEMANTIC(EvaluateOnCallFrameParams); + NO_MOVE_SEMANTIC(EvaluateOnCallFrameParams); + + CallFrameId callFrameId_ {}; + std::string expression_ {}; + std::optional objectGroup_ {}; + std::optional includeCommandLineAPI_ {}; + std::optional silent_ {}; + std::optional returnByValue_ {}; + std::optional generatePreview_ {}; + std::optional throwOnSideEffect_ {}; +}; + +class GetPossibleBreakpointsParams : public PtBaseParams { +public: + GetPossibleBreakpointsParams() = default; + ~GetPossibleBreakpointsParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + Location *GetStart() const + { + return start_.get(); + } + + Location *GetEnd() const + { + if (end_) { + return end_->get(); + } + return nullptr; + } + + bool HasEnd() const + { + return end_.has_value(); + } + + bool GetRestrictToFunction() const + { + return restrictToFunction_.value_or(false); + } + + bool HasRestrictToFunction() const + { + return restrictToFunction_.has_value(); + } + +private: + NO_COPY_SEMANTIC(GetPossibleBreakpointsParams); + NO_MOVE_SEMANTIC(GetPossibleBreakpointsParams); + + std::unique_ptr start_ {nullptr}; + std::optional> end_ {}; + std::optional restrictToFunction_ {}; +}; + +class GetScriptSourceParams : public PtBaseParams { +public: + GetScriptSourceParams() = default; + ~GetScriptSourceParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + ScriptId GetScriptId() const + { + return scriptId_; + } + +private: + NO_COPY_SEMANTIC(GetScriptSourceParams); + NO_MOVE_SEMANTIC(GetScriptSourceParams); + + ScriptId scriptId_ {0}; +}; + +class RemoveBreakpointParams : public PtBaseParams { +public: + RemoveBreakpointParams() = default; + ~RemoveBreakpointParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + BreakpointId GetBreakpointId() const + { + return breakpointId_; + } + +private: + NO_COPY_SEMANTIC(RemoveBreakpointParams); + NO_MOVE_SEMANTIC(RemoveBreakpointParams); + + BreakpointId breakpointId_ {}; +}; + +class ResumeParams : public PtBaseParams { +public: + ResumeParams() = default; + ~ResumeParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + bool GetTerminateOnResume() const + { + return terminateOnResume_.value_or(false); + } + + bool HasTerminateOnResume() const + { + return terminateOnResume_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ResumeParams); + NO_MOVE_SEMANTIC(ResumeParams); + + std::optional terminateOnResume_ {}; +}; + +class SetAsyncCallStackDepthParams : public PtBaseParams { +public: + SetAsyncCallStackDepthParams() = default; + ~SetAsyncCallStackDepthParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + int32_t GetMaxDepth() const + { + return maxDepth_; + } + +private: + NO_COPY_SEMANTIC(SetAsyncCallStackDepthParams); + NO_MOVE_SEMANTIC(SetAsyncCallStackDepthParams); + + int32_t maxDepth_ {0}; +}; + +class SetBlackboxPatternsParams : public PtBaseParams { +public: + SetBlackboxPatternsParams() = default; + ~SetBlackboxPatternsParams() override = default; + static std::unique_ptr Create(const PtJson ¶ms); + + std::list GetPatterns() const + { + return patterns_; + } + +private: + NO_COPY_SEMANTIC(SetBlackboxPatternsParams); + NO_MOVE_SEMANTIC(SetBlackboxPatternsParams); + + std::list patterns_ {}; +}; + +class SetBreakpointByUrlParams : public PtBaseParams { +public: + SetBreakpointByUrlParams() = default; + ~SetBreakpointByUrlParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + int32_t GetLine() const + { + return lineNumber_; + } + + const std::string &GetUrl() const + { + ASSERT(HasUrl()); + return url_.value(); + } + + bool HasUrl() const + { + return url_.has_value(); + } + + const std::string &GetUrlRegex() const + { + ASSERT(HasUrlRegex()); + return urlRegex_.value(); + } + + bool HasUrlRegex() const + { + return urlRegex_.has_value(); + } + + const std::string &GetScriptHash() const + { + ASSERT(HasScriptHash()); + return scriptHash_.value(); + } + + bool HasScriptHash() const + { + return scriptHash_.has_value(); + } + + int32_t GetColumn() const + { + return columnNumber_.value_or(0); + } + + bool HasColumn() const + { + return columnNumber_.has_value(); + } + + const std::string &GetCondition() const + { + ASSERT(HasCondition()); + return condition_.value(); + } + + bool HasCondition() const + { + return condition_.has_value(); + } + +private: + NO_COPY_SEMANTIC(SetBreakpointByUrlParams); + NO_MOVE_SEMANTIC(SetBreakpointByUrlParams); + + int32_t lineNumber_ {0}; + std::optional url_ {}; + std::optional urlRegex_ {}; + std::optional scriptHash_ {}; + std::optional columnNumber_ {0}; + std::optional condition_ {}; +}; + +enum class PauseOnExceptionsState : uint8_t { NONE, UNCAUGHT, ALL }; + +class SetPauseOnExceptionsParams : public PtBaseParams { +public: + SetPauseOnExceptionsParams() = default; + ~SetPauseOnExceptionsParams() override = default; + static std::unique_ptr Create(const PtJson ¶ms); + + PauseOnExceptionsState GetState() const + { + return state_; + } + + bool StoreState(const std::string &state) + { + if (state == "none") { + state_ = PauseOnExceptionsState::NONE; + return true; + } + if (state == "uncaught") { + state_ = PauseOnExceptionsState::UNCAUGHT; + return true; + } + if (state == "all") { + state_ = PauseOnExceptionsState::ALL; + return true; + } + return false; + } + +private: + NO_COPY_SEMANTIC(SetPauseOnExceptionsParams); + NO_MOVE_SEMANTIC(SetPauseOnExceptionsParams); + + PauseOnExceptionsState state_ {PauseOnExceptionsState::ALL}; +}; + +class StepIntoParams : public PtBaseParams { +public: + StepIntoParams() = default; + ~StepIntoParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + bool GetBreakOnAsyncCall() const + { + return breakOnAsyncCall_.value_or(false); + } + + bool HasBreakOnAsyncCall() const + { + return breakOnAsyncCall_.has_value(); + } + + const std::list> *GetSkipList() const + { + if (!skipList_) { + return nullptr; + } + return &(skipList_.value()); + } + + bool HasSkipList() const + { + return skipList_.has_value(); + } + +private: + NO_COPY_SEMANTIC(StepIntoParams); + NO_MOVE_SEMANTIC(StepIntoParams); + + std::optional breakOnAsyncCall_ {}; + std::optional>> skipList_ {}; +}; + +class StepOverParams : public PtBaseParams { +public: + StepOverParams() = default; + ~StepOverParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + const std::list> *GetSkipList() const + { + if (!skipList_) { + return nullptr; + } + return &(skipList_.value()); + } + + bool HasSkipList() const + { + return skipList_.has_value(); + } + +private: + NO_COPY_SEMANTIC(StepOverParams); + NO_MOVE_SEMANTIC(StepOverParams); + + std::optional>> skipList_ {}; +}; + +class SetMixedDebugParams : public PtBaseParams { +public: + SetMixedDebugParams() = default; + ~SetMixedDebugParams() override = default; + static std::unique_ptr Create(const PtJson ¶ms); + + bool GetEnabled() const + { + return enabled_; + } + +private: + NO_COPY_SEMANTIC(SetMixedDebugParams); + NO_MOVE_SEMANTIC(SetMixedDebugParams); + + bool enabled_ { false }; +}; + +class ReplyNativeCallingParams : public PtBaseParams { +public: + ReplyNativeCallingParams() = default; + ~ReplyNativeCallingParams() override = default; + static std::unique_ptr Create(const PtJson ¶ms); + + bool GetUserCode() const + { + return userCode_; + } + +private: + NO_COPY_SEMANTIC(ReplyNativeCallingParams); + NO_MOVE_SEMANTIC(ReplyNativeCallingParams); + + bool userCode_ { false }; +}; + +class GetPropertiesParams : public PtBaseParams { +public: + GetPropertiesParams() = default; + ~GetPropertiesParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + RemoteObjectId GetObjectId() const + { + return objectId_; + } + + bool GetOwnProperties() const + { + return ownProperties_.value_or(false); + } + + bool HasOwnProperties() const + { + return ownProperties_.has_value(); + } + + bool GetAccessPropertiesOnly() const + { + return accessorPropertiesOnly_.value_or(false); + } + + bool HasAccessPropertiesOnly() const + { + return accessorPropertiesOnly_.has_value(); + } + + bool GetGeneratePreview() const + { + return generatePreview_.value_or(false); + } + + bool HasGeneratePreview() const + { + return generatePreview_.has_value(); + } + +private: + NO_COPY_SEMANTIC(GetPropertiesParams); + NO_MOVE_SEMANTIC(GetPropertiesParams); + + RemoteObjectId objectId_ {}; + std::optional ownProperties_ {}; + std::optional accessorPropertiesOnly_ {}; + std::optional generatePreview_ {}; +}; + +class CallFunctionOnParams : public PtBaseParams { +public: + CallFunctionOnParams() = default; + ~CallFunctionOnParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + const std::string &GetFunctionDeclaration() + { + return functionDeclaration_; + } + + RemoteObjectId GetObjectId() const + { + return objectId_.value_or(-1); + } + + CallFunctionOnParams &SetObjectId(RemoteObjectId objectId) + { + objectId_ = objectId; + return *this; + } + + bool HasObjectId() const + { + return objectId_.has_value(); + } + + const std::vector> *GetArguments() const + { + if (!arguments_) { + return nullptr; + } + return &(arguments_.value()); + } + + bool HasArguments() const + { + return arguments_.has_value(); + } + + bool GetSilent() const + { + return silent_.value_or(false); + } + + bool HasSilent() const + { + return silent_.has_value(); + } + + bool GetReturnByValue() const + { + return returnByValue_.value_or(false); + } + + bool HasReturnByValue() const + { + return returnByValue_.has_value(); + } + + bool GetGeneratePreview() const + { + return generatePreview_.value_or(false); + } + + bool HasGeneratePreview() const + { + return generatePreview_.has_value(); + } + + bool GetUserGesture() const + { + return userGesture_.value_or(false); + } + + bool HasUserGesture() const + { + return userGesture_.has_value(); + } + + bool GetAwaitPromise() const + { + return awaitPromise_.value_or(false); + } + + bool HasAwaitPromise() const + { + return awaitPromise_.has_value(); + } + + ExecutionContextId GetExecutionContextId() const + { + return executionContextId_.value_or(-1); + } + + CallFunctionOnParams &SetExecutionContextId(ExecutionContextId executionContextId) + { + executionContextId_ = executionContextId; + return *this; + } + + bool HasExecutionContextId() const + { + return executionContextId_.has_value(); + } + + const std::string &GetObjectGroup() const + { + ASSERT(HasObjectGroup()); + return objectGroup_.value(); + } + + bool HasObjectGroup() const + { + return objectGroup_.has_value(); + } + + bool GetThrowOnSideEffect() const + { + return throwOnSideEffect_.value_or(false); + } + + bool HasThrowOnSideEffect() const + { + return throwOnSideEffect_.has_value(); + } + +private: + NO_COPY_SEMANTIC(CallFunctionOnParams); + NO_MOVE_SEMANTIC(CallFunctionOnParams); + + std::string functionDeclaration_ {}; + std::optional objectId_ {}; + std::optional>> arguments_ {}; + std::optional silent_ {}; + std::optional returnByValue_ {}; + std::optional generatePreview_ {}; + std::optional userGesture_ {}; + std::optional awaitPromise_ {}; + std::optional executionContextId_ {}; + std::optional objectGroup_ {}; + std::optional throwOnSideEffect_ {}; +}; + +class StartSamplingParams : public PtBaseParams { +public: + StartSamplingParams() = default; + ~StartSamplingParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + int32_t GetSamplingInterval() const + { + return samplingInterval_.value_or(32768); + } + +private: + NO_COPY_SEMANTIC(StartSamplingParams); + NO_MOVE_SEMANTIC(StartSamplingParams); + + std::optional samplingInterval_ {32768}; +}; + +class StartTrackingHeapObjectsParams : public PtBaseParams { +public: + StartTrackingHeapObjectsParams() = default; + ~StartTrackingHeapObjectsParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + bool GetTrackAllocations() const + { + return trackAllocations_.value_or(false); + } + + bool HasTrackAllocations() const + { + return trackAllocations_.has_value(); + } + +private: + NO_COPY_SEMANTIC(StartTrackingHeapObjectsParams); + NO_MOVE_SEMANTIC(StartTrackingHeapObjectsParams); + + std::optional trackAllocations_; +}; + +class StopTrackingHeapObjectsParams : public PtBaseParams { +public: + StopTrackingHeapObjectsParams() = default; + ~StopTrackingHeapObjectsParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + bool GetReportProgress() const + { + return reportProgress_.value_or(false); + } + + bool HasReportProgress() const + { + return reportProgress_.has_value(); + } + + bool GetTreatGlobalObjectsAsRoots() const + { + return treatGlobalObjectsAsRoots_.value_or(false); + } + + bool HasTreatGlobalObjectsAsRoots() const + { + return treatGlobalObjectsAsRoots_.has_value(); + } + + bool GetCaptureNumericValue() const + { + return captureNumericValue_.value_or(false); + } + + bool HasCaptureNumericValue() const + { + return captureNumericValue_.has_value(); + } + +private: + NO_COPY_SEMANTIC(StopTrackingHeapObjectsParams); + NO_MOVE_SEMANTIC(StopTrackingHeapObjectsParams); + + std::optional reportProgress_ {}; + std::optional treatGlobalObjectsAsRoots_ {}; + std::optional captureNumericValue_ {}; +}; + +class AddInspectedHeapObjectParams : public PtBaseParams { +public: + AddInspectedHeapObjectParams() = default; + ~AddInspectedHeapObjectParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + HeapSnapshotObjectId GetHeapObjectId() const + { + return heapObjectId_; + } + +private: + NO_COPY_SEMANTIC(AddInspectedHeapObjectParams); + NO_MOVE_SEMANTIC(AddInspectedHeapObjectParams); + + HeapSnapshotObjectId heapObjectId_ {}; +}; + +class GetHeapObjectIdParams : public PtBaseParams { +public: + GetHeapObjectIdParams() = default; + ~GetHeapObjectIdParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + RemoteObjectId GetObjectId() const + { + return objectId_; + } + +private: + NO_COPY_SEMANTIC(GetHeapObjectIdParams); + NO_MOVE_SEMANTIC(GetHeapObjectIdParams); + + RemoteObjectId objectId_ {}; +}; + +class GetObjectByHeapObjectIdParams : public PtBaseParams { +public: + GetObjectByHeapObjectIdParams() = default; + ~GetObjectByHeapObjectIdParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + HeapSnapshotObjectId GetObjectId() const + { + return objectId_; + } + + const std::string &GetObjectGroup() const + { + ASSERT(HasObjectGroup()); + return objectGroup_.value(); + } + + bool HasObjectGroup() const + { + return objectGroup_.has_value(); + } + +private: + NO_COPY_SEMANTIC(GetObjectByHeapObjectIdParams); + NO_MOVE_SEMANTIC(GetObjectByHeapObjectIdParams); + + HeapSnapshotObjectId objectId_ {}; + std::optional objectGroup_ {}; +}; + +class StartPreciseCoverageParams : public PtBaseParams { +public: + StartPreciseCoverageParams() = default; + ~StartPreciseCoverageParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + bool GetCallCount() const + { + return callCount_.value_or(false); + } + + bool HasCallCount() const + { + return callCount_.has_value(); + } + + bool GetDetailed() const + { + return detailed_.value_or(false); + } + + bool HasDetailed() const + { + return detailed_.has_value(); + } + + bool GetAllowTriggeredUpdates() const + { + return allowTriggeredUpdates_.value_or(false); + } + + bool HasAllowTriggeredUpdates() const + { + return allowTriggeredUpdates_.has_value(); + } + +private: + NO_COPY_SEMANTIC(StartPreciseCoverageParams); + NO_MOVE_SEMANTIC(StartPreciseCoverageParams); + + std::optional callCount_ {}; + std::optional detailed_ {}; + std::optional allowTriggeredUpdates_ {}; +}; + +class SetSamplingIntervalParams : public PtBaseParams { +public: + SetSamplingIntervalParams() = default; + ~SetSamplingIntervalParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + int32_t GetInterval() const + { + return interval_; + } + + SetSamplingIntervalParams &SetInterval(int32_t interval) + { + interval_ = interval; + return *this; + } + +private: + NO_COPY_SEMANTIC(SetSamplingIntervalParams); + NO_MOVE_SEMANTIC(SetSamplingIntervalParams); + + int32_t interval_ {0}; +}; + +class RecordClockSyncMarkerParams : public PtBaseParams { +public: + RecordClockSyncMarkerParams() = default; + ~RecordClockSyncMarkerParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + std::string GetSyncId() const + { + return syncId_; + } + + RecordClockSyncMarkerParams &SetSyncId(std::string syncId) + { + syncId_ = syncId; + return *this; + } + +private: + NO_COPY_SEMANTIC(RecordClockSyncMarkerParams); + NO_MOVE_SEMANTIC(RecordClockSyncMarkerParams); + + std::string syncId_ {}; +}; + +class RequestMemoryDumpParams : public PtBaseParams { +public: + RequestMemoryDumpParams() = default; + ~RequestMemoryDumpParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + bool GetDeterministic() const + { + return deterministic_.value(); + } + + RequestMemoryDumpParams &SetDeterministic(bool deterministic) + { + deterministic_ = deterministic; + return *this; + } + + bool HasDeterministic() const + { + return deterministic_.has_value(); + } + + MemoryDumpLevelOfDetail GetLevelOfDetail() const + { + return levelOfDetail_.value(); + } + + RequestMemoryDumpParams &SetLevelOfDetail(const MemoryDumpLevelOfDetail &levelOfDetail) + { + levelOfDetail_ = levelOfDetail; + return *this; + } + + bool HasLevelOfDetail() const + { + return levelOfDetail_.has_value(); + } + +private: + NO_COPY_SEMANTIC(RequestMemoryDumpParams); + NO_MOVE_SEMANTIC(RequestMemoryDumpParams); + + std::optional deterministic_ {}; + std::optional levelOfDetail_ {}; +}; + +class StartParams : public PtBaseParams { +public: + StartParams() = default; + ~StartParams() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + std::string GetCategories() const + { + return categories_.value(); + } + + StartParams &SetCategories(std::string categories) + { + categories_ = categories; + return *this; + } + + bool HasCategories() const + { + return categories_.has_value(); + } + + std::string GetOptions() const + { + return options_.value(); + } + + StartParams &SetOptions(std::string options) + { + options_ = options; + return *this; + } + + bool HasOptions() const + { + return options_.has_value(); + } + + int32_t GetBufferUsageReportingInterval() const + { + return bufferUsageReportingInterval_.value(); + } + + StartParams &SetBufferUsageReportingInterval(int32_t bufferUsageReportingInterval) + { + bufferUsageReportingInterval_ = bufferUsageReportingInterval; + return *this; + } + + bool HasBufferUsageReportingInterval() const + { + return bufferUsageReportingInterval_.has_value(); + } + + std::string GetTransferMode() const + { + return transferMode_.value(); + } + + StartParams &SetTransferMode(std::string transferMode) + { + transferMode_ = transferMode; + return *this; + } + + bool HasTransferMode() const + { + return transferMode_.has_value(); + } + + struct TransferModeValues { + static bool Valid(const std::string &values) + { + return values == ReportEvents() || values == ReturnAsStream(); + } + static std::string ReportEvents() + { + return "ReportEvents"; + } + static std::string ReturnAsStream() + { + return "ReturnAsStream"; + } + }; + + StreamFormat GetStreamFormat() const + { + return streamFormat_.value(); + } + + StartParams &SetStreamFormat(const StreamFormat &streamFormat) + { + streamFormat_ = streamFormat; + return *this; + } + + bool HasStreamFormat() const + { + return streamFormat_.has_value(); + } + + StreamCompression GetStreamCompression() const + { + return streamCompression_.value(); + } + + StartParams &SetStreamCompression(const StreamCompression &streamCompression) + { + streamCompression_ = streamCompression; + return *this; + } + + bool HasStreamCompression() const + { + return streamCompression_.has_value(); + } + + TraceConfig *GetTraceConfig() const + { + if (traceConfig_) { + return traceConfig_->get(); + } + return nullptr; + } + + StartParams &SetTraceConfig(std::unique_ptr &traceConfig) + { + traceConfig_ = std::move(traceConfig); + return *this; + } + + bool HasTraceConfig() const + { + return traceConfig_.has_value(); + } + + std::string GetPerfettoConfig() const + { + return perfettoConfig_.value(); + } + + StartParams &SetPerfettoConfig(std::string perfettoConfig) + { + perfettoConfig_ = perfettoConfig; + return *this; + } + + bool HasPerfettoConfig() const + { + return perfettoConfig_.has_value(); + } + + TracingBackend GetTracingBackend() const + { + return tracingBackend_.value(); + } + + StartParams &SetTracingBackend(const TracingBackend &tracingBackend) + { + tracingBackend_ = tracingBackend; + return *this; + } + + bool HasTracingBackend() const + { + return tracingBackend_.has_value(); + } + +private: + NO_COPY_SEMANTIC(StartParams); + NO_MOVE_SEMANTIC(StartParams); + + std::optional categories_ {}; + std::optional options_ {}; + std::optional bufferUsageReportingInterval_ {0}; + std::optional transferMode_ {}; + std::optional streamFormat_ {}; + std::optional streamCompression_ {}; + std::optional> traceConfig_ {}; + std::optional perfettoConfig_ {}; + std::optional tracingBackend_ {}; +}; +} // namespace panda::ecmascript::tooling +#endif \ No newline at end of file diff --git a/tooling/base/pt_returns.cpp b/tooling/base/pt_returns.cpp new file mode 100644 index 00000000..1d4e576c --- /dev/null +++ b/tooling/base/pt_returns.cpp @@ -0,0 +1,348 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/base/pt_returns.h" + +namespace panda::ecmascript::tooling { +std::unique_ptr EnableReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("debuggerId", std::to_string(debuggerId_).c_str()); + + return result; +} + +std::unique_ptr SetBreakpointByUrlReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("breakpointId", id_.c_str()); + std::unique_ptr array = PtJson::CreateArray(); + size_t len = locations_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(locations_[i] != nullptr); + std::unique_ptr location = locations_[i]->ToJson(); + array->Push(location); + } + result->Add("locations", array); + + return result; +} + +std::unique_ptr EvaluateOnCallFrameReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + ASSERT(result_ != nullptr); + result->Add("result", result_->ToJson()); + if (exceptionDetails_) { + ASSERT(exceptionDetails_.value() != nullptr); + result->Add("exceptionDetails", exceptionDetails_.value()->ToJson()); + } + + return result; +} + +std::unique_ptr GetPossibleBreakpointsReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr array = PtJson::CreateArray(); + size_t len = locations_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(locations_[i] != nullptr); + std::unique_ptr location = locations_[i]->ToJson(); + array->Push(location); + } + result->Add("locations", array); + + return result; +} + +std::unique_ptr GetScriptSourceReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("scriptSource", scriptSource_.c_str()); + if (bytecode_) { + result->Add("bytecode", bytecode_->c_str()); + } + + return result; +} + +std::unique_ptr RestartFrameReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr array = PtJson::CreateArray(); + size_t len = callFrames_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(callFrames_[i] != nullptr); + std::unique_ptr location = callFrames_[i]->ToJson(); + array->Push(location); + } + result->Add("callFrames", array); + + return result; +} + +std::unique_ptr SearchInContentReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr array = PtJson::CreateArray(); + size_t len = result_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(result_[i] != nullptr); + std::unique_ptr res = result_[i]->ToJson(); + array->Push(res); + } + result->Add("result", array); + + return result; +} + +std::unique_ptr SetBreakpointReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("breakpointId", breakpointId_.c_str()); + ASSERT(location_ != nullptr); + result->Add("actualLocation", location_->ToJson()); + + return result; +} + +std::unique_ptr SetInstrumentationBreakpointReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("breakpointId", breakpointId_.c_str()); + + return result; +} + +std::unique_ptr SetScriptSourceReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + if (callFrames_) { + std::unique_ptr array = PtJson::CreateArray(); + size_t len = callFrames_->size(); + for (size_t i = 0; i < len; i++) { + ASSERT(callFrames_.value()[i] != nullptr); + std::unique_ptr location = callFrames_.value()[i]->ToJson(); + array->Push(location); + } + result->Add("callFrames", array); + } + if (stackChanged_) { + result->Add("stackChanged", stackChanged_.value()); + } + if (exceptionDetails_) { + ASSERT(exceptionDetails_.value() != nullptr); + result->Add("exceptionDetails", exceptionDetails_.value()->ToJson()); + } + + return result; +} + +std::unique_ptr GetPropertiesReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr array = PtJson::CreateArray(); + size_t len = result_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(result_[i] != nullptr); + std::unique_ptr location = result_[i]->ToJson(); + array->Push(location); + } + result->Add("result", array); + if (internalPropertyDescripties_) { + array = PtJson::CreateArray(); + len = internalPropertyDescripties_->size(); + for (size_t i = 0; i < len; i++) { + ASSERT(internalPropertyDescripties_.value()[i] != nullptr); + std::unique_ptr location = internalPropertyDescripties_.value()[i]->ToJson(); + array->Push(location); + } + result->Add("internalProperties", array); + } + if (privateProperties_) { + array = PtJson::CreateArray(); + len = privateProperties_->size(); + for (size_t i = 0; i < len; i++) { + ASSERT(privateProperties_.value()[i] != nullptr); + std::unique_ptr location = privateProperties_.value()[i]->ToJson(); + array->Push(location); + } + result->Add("privateProperties", array); + } + if (exceptionDetails_) { + ASSERT(exceptionDetails_.value() != nullptr); + result->Add("exceptionDetails", exceptionDetails_.value()->ToJson()); + } + + return result; +} + +std::unique_ptr CallFunctionOnReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + ASSERT(result_ != nullptr); + result->Add("result", result_->ToJson()); + if (exceptionDetails_) { + ASSERT(exceptionDetails_.value() != nullptr); + result->Add("exceptionDetails", exceptionDetails_.value()->ToJson()); + } + + return result; +} + +std::unique_ptr StopSamplingReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + ASSERT(profile_ != nullptr); + result->Add("profile", profile_->ToJson()); + + return result; +} + +std::unique_ptr GetHeapObjectIdReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("heapSnapshotObjectId", std::to_string(heapSnapshotObjectId_).c_str()); + + return result; +} + +std::unique_ptr GetObjectByHeapObjectIdReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + ASSERT(remoteObjectResult_ != nullptr); + result->Add("result", remoteObjectResult_->ToJson()); + + return result; +} + +std::unique_ptr StopReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + ASSERT(profile_ != nullptr); + result->Add("profile", profile_->ToJson()); + + return result; +} + +std::unique_ptr GetHeapUsageReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("usedSize", usedSize_); + result->Add("totalSize", totalSize_); + + return result; +} + +std::unique_ptr GetBestEffortCoverageReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr array = PtJson::CreateArray(); + size_t len = result_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(result_[i] != nullptr); + std::unique_ptr scriptCoverage = result_[i]->ToJson(); + array->Push(scriptCoverage); + } + result->Add("result", array); + + return result; +} + +std::unique_ptr StartPreciseCoverageReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("timestamp", timestamp_); + + return result; +} + +std::unique_ptr TakePreciseCoverageReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr array = PtJson::CreateArray(); + size_t len = result_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(result_[i] != nullptr); + std::unique_ptr scriptTypeProfile = result_[i]->ToJson(); + array->Push(scriptTypeProfile); + } + result->Add("result", array); + result->Add("timestamp", timestamp_); + + return result; +} + +std::unique_ptr TakeTypeProfileReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr array = PtJson::CreateArray(); + size_t len = result_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(result_[i] != nullptr); + std::unique_ptr scriptTypeProfile = result_[i]->ToJson(); + array->Push(scriptTypeProfile); + } + result->Add("result", array); + + return result; +} + +std::unique_ptr GetCategoriesReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + std::unique_ptr categories = PtJson::CreateArray(); + size_t len = categories_.size(); + for (size_t i = 0; i < len; i++) { + categories->Push(categories_[i].c_str()); + } + result->Add("categories", categories); + + return result; +} + +std::unique_ptr RequestMemoryDumpReturns::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("dumpGuid", dumpGuid_.c_str()); + result->Add("success", success_); + + return result; +} +} // namespace panda::ecmascript::tooling \ No newline at end of file diff --git a/tooling/base/pt_returns.h b/tooling/base/pt_returns.h new file mode 100644 index 00000000..3a10671d --- /dev/null +++ b/tooling/base/pt_returns.h @@ -0,0 +1,427 @@ +/* + * 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_BASE_PT_RETURNS_H +#define ECMASCRIPT_TOOLING_BASE_PT_RETURNS_H + +#include "ecmascript/tooling/base/pt_types.h" + +namespace panda::ecmascript::tooling { +class PtBaseReturns : public PtBaseTypes { +public: + PtBaseReturns() = default; + ~PtBaseReturns() override = default; + std::unique_ptr ToJson() const override + { + return PtJson::CreateObject(); + } + +private: + NO_COPY_SEMANTIC(PtBaseReturns); + NO_MOVE_SEMANTIC(PtBaseReturns); +}; + +class EnableReturns : public PtBaseReturns { +public: + explicit EnableReturns(UniqueDebuggerId id) : debuggerId_(id) {} + ~EnableReturns() override = default; + + std::unique_ptr ToJson() const override; + +private: + EnableReturns() = default; + NO_COPY_SEMANTIC(EnableReturns); + NO_MOVE_SEMANTIC(EnableReturns); + + UniqueDebuggerId debuggerId_ {}; +}; + +class SetBreakpointByUrlReturns : public PtBaseReturns { +public: + explicit SetBreakpointByUrlReturns(const std::string &id, std::vector> locations) + : id_(id), locations_(std::move(locations)) + {} + ~SetBreakpointByUrlReturns() override = default; + + std::unique_ptr ToJson() const override; + +private: + SetBreakpointByUrlReturns() = default; + NO_COPY_SEMANTIC(SetBreakpointByUrlReturns); + NO_MOVE_SEMANTIC(SetBreakpointByUrlReturns); + + std::string id_ {}; + std::vector> locations_ {}; +}; + +class EvaluateOnCallFrameReturns : public PtBaseReturns { +public: + explicit EvaluateOnCallFrameReturns(std::unique_ptr result, + std::optional> exceptionDetails = std::nullopt) + : result_(std::move(result)), exceptionDetails_(std::move(exceptionDetails)) + {} + ~EvaluateOnCallFrameReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + EvaluateOnCallFrameReturns() = default; + NO_COPY_SEMANTIC(EvaluateOnCallFrameReturns); + NO_MOVE_SEMANTIC(EvaluateOnCallFrameReturns); + + std::unique_ptr result_ {}; + std::optional> exceptionDetails_ {}; +}; + +class GetPossibleBreakpointsReturns : public PtBaseReturns { +public: + explicit GetPossibleBreakpointsReturns(std::vector> locations) + : locations_(std::move(locations)) + {} + ~GetPossibleBreakpointsReturns() override = default; + + std::unique_ptr ToJson() const override; + +private: + GetPossibleBreakpointsReturns() = default; + NO_COPY_SEMANTIC(GetPossibleBreakpointsReturns); + NO_MOVE_SEMANTIC(GetPossibleBreakpointsReturns); + + std::vector> locations_ {}; +}; + +class GetScriptSourceReturns : public PtBaseReturns { +public: + explicit GetScriptSourceReturns(const std::string &scriptSource, std::optional bytecode = std::nullopt) + : scriptSource_(scriptSource), bytecode_(std::move(bytecode)) + {} + ~GetScriptSourceReturns() override = default; + + std::unique_ptr ToJson() const override; + +private: + GetScriptSourceReturns() = default; + NO_COPY_SEMANTIC(GetScriptSourceReturns); + NO_MOVE_SEMANTIC(GetScriptSourceReturns); + + std::string scriptSource_ {}; + std::optional bytecode_ {}; +}; + +class RestartFrameReturns : public PtBaseReturns { +public: + explicit RestartFrameReturns(std::vector> callFrames) + : callFrames_(std::move(callFrames)) + {} + ~RestartFrameReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + RestartFrameReturns() = default; + NO_COPY_SEMANTIC(RestartFrameReturns); + NO_MOVE_SEMANTIC(RestartFrameReturns); + + std::vector> callFrames_ {}; +}; + +class SearchInContentReturns : public PtBaseReturns { +public: + explicit SearchInContentReturns(std::vector> result) : result_(std::move(result)) + {} + ~SearchInContentReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + SearchInContentReturns() = default; + NO_COPY_SEMANTIC(SearchInContentReturns); + NO_MOVE_SEMANTIC(SearchInContentReturns); + + std::vector> result_ {}; +}; + +class SetBreakpointReturns : public PtBaseReturns { +public: + explicit SetBreakpointReturns(const std::string &id, std::unique_ptr location) + : breakpointId_(id), location_(std::move(location)) + {} + ~SetBreakpointReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + SetBreakpointReturns() = default; + NO_COPY_SEMANTIC(SetBreakpointReturns); + NO_MOVE_SEMANTIC(SetBreakpointReturns); + std::string breakpointId_ {}; + std::unique_ptr location_ {}; +}; + +class SetInstrumentationBreakpointReturns : public PtBaseReturns { +public: + explicit SetInstrumentationBreakpointReturns(const std::string &id) : breakpointId_(id) + {} + ~SetInstrumentationBreakpointReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + SetInstrumentationBreakpointReturns() = default; + NO_COPY_SEMANTIC(SetInstrumentationBreakpointReturns); + NO_MOVE_SEMANTIC(SetInstrumentationBreakpointReturns); + + std::string breakpointId_ {}; +}; + +class SetScriptSourceReturns : public PtBaseReturns { +public: + explicit SetScriptSourceReturns(std::optional>> callFrames = std::nullopt, + std::optional stackChanged = std::nullopt, + std::optional> exceptionDetails = std::nullopt) + : callFrames_(std::move(callFrames)), + stackChanged_(stackChanged), + exceptionDetails_(std::move(exceptionDetails)) + {} + ~SetScriptSourceReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + SetScriptSourceReturns() = default; + NO_COPY_SEMANTIC(SetScriptSourceReturns); + NO_MOVE_SEMANTIC(SetScriptSourceReturns); + + std::optional>> callFrames_ {}; + std::optional stackChanged_ {}; + std::optional> exceptionDetails_ {}; +}; + +class GetPropertiesReturns : public PtBaseReturns { +public: + explicit GetPropertiesReturns(std::vector> descriptor, + std::optional>> internalDescripties = std::nullopt, + std::optional>> privateProperties = std::nullopt, + std::optional> exceptionDetails = std::nullopt) + : result_(std::move(descriptor)), + internalPropertyDescripties_(std::move(internalDescripties)), + privateProperties_(std::move(privateProperties)), + exceptionDetails_(std::move(exceptionDetails)) + {} + ~GetPropertiesReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + GetPropertiesReturns() = default; + NO_COPY_SEMANTIC(GetPropertiesReturns); + NO_MOVE_SEMANTIC(GetPropertiesReturns); + + std::vector> result_ {}; + std::optional>> internalPropertyDescripties_ {}; + std::optional>> privateProperties_ {}; + std::optional> exceptionDetails_ {}; +}; + +class CallFunctionOnReturns : public PtBaseReturns { +public: + explicit CallFunctionOnReturns(std::unique_ptr result, + std::optional> exceptionDetails = std::nullopt) + : result_(std::move(result)), + exceptionDetails_(std::move(exceptionDetails)) + {} + ~CallFunctionOnReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + CallFunctionOnReturns() = default; + NO_COPY_SEMANTIC(CallFunctionOnReturns); + NO_MOVE_SEMANTIC(CallFunctionOnReturns); + + std::unique_ptr result_ {}; + std::optional> exceptionDetails_ {}; +}; + +class StopSamplingReturns : public PtBaseReturns { +public: + explicit StopSamplingReturns(std::unique_ptr profile) + : profile_(std::move(profile)) + {} + ~StopSamplingReturns() override = default; + + std::unique_ptr ToJson() const override; + +private: + StopSamplingReturns() = default; + NO_COPY_SEMANTIC(StopSamplingReturns); + NO_MOVE_SEMANTIC(StopSamplingReturns); + + std::unique_ptr profile_ {}; +}; + +class GetHeapObjectIdReturns : public PtBaseReturns { +public: + explicit GetHeapObjectIdReturns(HeapSnapshotObjectId heapSnapshotObjectId) + : heapSnapshotObjectId_(std::move(heapSnapshotObjectId)) + {} + ~GetHeapObjectIdReturns() override = default; + + std::unique_ptr ToJson() const override; + +private: + GetHeapObjectIdReturns() = default; + NO_COPY_SEMANTIC(GetHeapObjectIdReturns); + NO_MOVE_SEMANTIC(GetHeapObjectIdReturns); + + HeapSnapshotObjectId heapSnapshotObjectId_ {}; +}; + +class GetObjectByHeapObjectIdReturns : public PtBaseReturns { +public: + explicit GetObjectByHeapObjectIdReturns(std::unique_ptr remoteObjectResult) + : remoteObjectResult_(std::move(remoteObjectResult)) + {} + ~GetObjectByHeapObjectIdReturns() override = default; + + std::unique_ptr ToJson() const override; + +private: + GetObjectByHeapObjectIdReturns() = default; + NO_COPY_SEMANTIC(GetObjectByHeapObjectIdReturns); + NO_MOVE_SEMANTIC(GetObjectByHeapObjectIdReturns); + + std::unique_ptr remoteObjectResult_ {}; +}; + +class StopReturns : public PtBaseReturns { +public: + explicit StopReturns(std::unique_ptr profile) : profile_(std::move(profile)) {} + ~StopReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + StopReturns() = default; + NO_COPY_SEMANTIC(StopReturns); + NO_MOVE_SEMANTIC(StopReturns); + + std::unique_ptr profile_ {}; +}; + +class GetHeapUsageReturns : public PtBaseReturns { +public: + explicit GetHeapUsageReturns(double usedSize, double totalSize) + : usedSize_(usedSize), totalSize_(totalSize) {} + ~GetHeapUsageReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + GetHeapUsageReturns() = default; + NO_COPY_SEMANTIC(GetHeapUsageReturns); + NO_MOVE_SEMANTIC(GetHeapUsageReturns); + + double usedSize_ {0.0}; + double totalSize_ {0.0}; +}; + +class GetBestEffortCoverageReturns : public PtBaseReturns { +public: + explicit GetBestEffortCoverageReturns(std::vector> result) + : result_(std::move(result)) + {} + ~GetBestEffortCoverageReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + GetBestEffortCoverageReturns() = default; + NO_COPY_SEMANTIC(GetBestEffortCoverageReturns); + NO_MOVE_SEMANTIC(GetBestEffortCoverageReturns); + + std::vector> result_ {}; +}; + +class StartPreciseCoverageReturns : public PtBaseReturns { +public: + explicit StartPreciseCoverageReturns(int64_t tamp) : timestamp_(tamp) {} + ~StartPreciseCoverageReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + StartPreciseCoverageReturns() = default; + NO_COPY_SEMANTIC(StartPreciseCoverageReturns); + NO_MOVE_SEMANTIC(StartPreciseCoverageReturns); + + int64_t timestamp_ {0}; +}; + +class TakePreciseCoverageReturns : public PtBaseReturns { +public: + explicit TakePreciseCoverageReturns(std::vector> result, int64_t tamp) + : result_(std::move(result)), + timestamp_(tamp) + {} + ~TakePreciseCoverageReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + TakePreciseCoverageReturns() = default; + NO_COPY_SEMANTIC(TakePreciseCoverageReturns); + NO_MOVE_SEMANTIC(TakePreciseCoverageReturns); + + std::vector> result_ {}; + int64_t timestamp_ {0}; +}; + +class TakeTypeProfileReturns : public PtBaseReturns { +public: + explicit TakeTypeProfileReturns(std::vector> result) + : result_(std::move(result)) + {} + ~TakeTypeProfileReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + TakeTypeProfileReturns() = default; + NO_COPY_SEMANTIC(TakeTypeProfileReturns); + NO_MOVE_SEMANTIC(TakeTypeProfileReturns); + + std::vector> result_ {}; +}; + +class GetCategoriesReturns : public PtBaseReturns { +public: + explicit GetCategoriesReturns(std::vector categories) + : categories_(std::move(categories)) + {} + ~GetCategoriesReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + GetCategoriesReturns() = default; + NO_COPY_SEMANTIC(GetCategoriesReturns); + NO_MOVE_SEMANTIC(GetCategoriesReturns); + + std::vector categories_ {}; +}; + +class RequestMemoryDumpReturns : public PtBaseReturns { +public: + explicit RequestMemoryDumpReturns(std::string dumpGuid, bool success) + : dumpGuid_(dumpGuid), success_(success) {} + ~RequestMemoryDumpReturns() override = default; + std::unique_ptr ToJson() const override; + +private: + RequestMemoryDumpReturns() = default; + NO_COPY_SEMANTIC(RequestMemoryDumpReturns); + NO_MOVE_SEMANTIC(RequestMemoryDumpReturns); + + std::string dumpGuid_ {}; + bool success_ {}; +}; +} // namespace panda::ecmascript::tooling +#endif \ No newline at end of file diff --git a/tooling/base/pt_script.cpp b/tooling/base/pt_script.cpp new file mode 100644 index 00000000..120442d9 --- /dev/null +++ b/tooling/base/pt_script.cpp @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/base/pt_script.h" +#include "ecmascript/tooling/backend/debugger_api.h" + +namespace panda::ecmascript::tooling { +PtScript::PtScript(ScriptId scriptId, const std::string &fileName, const std::string &url, const std::string &source) + : scriptId_(scriptId), + fileName_(fileName), + url_(url), + scriptSource_(source) +{ + endLine_ = std::count(scriptSource_.begin(), scriptSource_.end(), '\n'); +} +} // namespace panda::ecmascript::tooling \ No newline at end of file diff --git a/tooling/base/pt_script.h b/tooling/base/pt_script.h new file mode 100644 index 00000000..ab88b06a --- /dev/null +++ b/tooling/base/pt_script.h @@ -0,0 +1,118 @@ +/* + * 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_BASE_PT_SCRIPT_H +#define ECMASCRIPT_TOOLING_BASE_PT_SCRIPT_H + +#include "ecmascript/tooling/base/pt_types.h" + +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +enum class ScriptMatchType : uint8_t { + URL, + FILE_NAME, + HASH, +}; + +class PtScript { +public: + PtScript(ScriptId scriptId, const std::string &fileName, const std::string &url, const std::string &source); + ~PtScript() = default; + + ScriptId GetScriptId() const + { + return scriptId_; + } + + void SetScriptId(ScriptId scriptId) + { + scriptId_ = scriptId; + } + + const std::string &GetFileName() const + { + return fileName_; + } + + void SetFileName(const std::string &fileName) + { + fileName_ = fileName; + } + + const std::string &GetUrl() const + { + return url_; + } + + void SetUrl(const std::string &url) + { + url_ = url; + } + + const std::string &GetHash() const + { + return hash_; + } + + void SetHash(const std::string &hash) + { + hash_ = hash; + } + + const std::string &GetScriptSource() const + { + return scriptSource_; + } + + void SetScriptSource(const std::string &scriptSource) + { + scriptSource_ = scriptSource; + } + + const std::string &GetSourceMapUrl() const + { + return sourceMapUrl_; + } + + void SetSourceMapUrl(const std::string &sourceMapUrl) + { + sourceMapUrl_ = sourceMapUrl; + } + + int32_t GetEndLine() const + { + return endLine_; + } + + void SetEndLine(int32_t endLine) + { + endLine_ = endLine; + } + +private: + NO_COPY_SEMANTIC(PtScript); + NO_MOVE_SEMANTIC(PtScript); + + ScriptId scriptId_ {0}; // start from 0, such as "0","1","2"... + std::string fileName_ {}; // binary file name, such as xx.bin + std::string url_ {}; // source file name, such as xx.js + std::string hash_ {}; // js source file hash code + std::string scriptSource_ {}; // js source code + std::string sourceMapUrl_ {}; // source map url + int32_t endLine_ {0}; // total line number of source file +}; +} // namespace panda::ecmascript::tooling +#endif diff --git a/tooling/base/pt_types.cpp b/tooling/base/pt_types.cpp new file mode 100644 index 00000000..0e7d5329 --- /dev/null +++ b/tooling/base/pt_types.cpp @@ -0,0 +1,2793 @@ +/* + * 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 "pt_types.h" + +namespace panda::ecmascript::tooling { +using ObjectType = RemoteObject::TypeName; +using ObjectSubType = RemoteObject::SubTypeName; +using ObjectClassName = RemoteObject::ClassName; + +const std::string ObjectType::Object = "object"; // NOLINT (readability-identifier-naming) +const std::string ObjectType::Function = "function"; // NOLINT (readability-identifier-naming) +const std::string ObjectType::Undefined = "undefined"; // NOLINT (readability-identifier-naming) +const std::string ObjectType::String = "string"; // NOLINT (readability-identifier-naming) +const std::string ObjectType::Number = "number"; // NOLINT (readability-identifier-naming) +const std::string ObjectType::Boolean = "boolean"; // NOLINT (readability-identifier-naming) +const std::string ObjectType::Symbol = "symbol"; // NOLINT (readability-identifier-naming) +const std::string ObjectType::Bigint = "bigint"; // NOLINT (readability-identifier-naming) +const std::string ObjectType::Wasm = "wasm"; // NOLINT (readability-identifier-naming) + +const std::string ObjectSubType::Array = "array"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Null = "null"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Node = "node"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Regexp = "regexp"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Date = "date"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Map = "map"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Set = "set"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Weakmap = "weakmap"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Weakset = "weakset"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Iterator = "iterator"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Generator = "generator"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Error = "error"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Proxy = "proxy"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Promise = "promise"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Typedarray = "typedarray"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Arraybuffer = "arraybuffer"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Dataview = "dataview"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::I32 = "i32"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::I64 = "i64"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::F32 = "f32"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::F64 = "f64"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::V128 = "v128"; // NOLINT (readability-identifier-naming) +const std::string ObjectSubType::Externref = "externref"; // NOLINT (readability-identifier-naming) + +const std::string ObjectClassName::Object = "Object"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Function = "Function"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Array = "Array"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Regexp = "RegExp"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Date = "Date"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Map = "Map"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Set = "Set"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Weakmap = "Weakmap"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Weakset = "Weakset"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::ArrayIterator = "ArrayIterator"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::StringIterator = "StringIterator"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::SetIterator = "SetIterator"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::MapIterator = "MapIterator"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Iterator = "Iterator"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Error = "Error"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Proxy = "Object"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Promise = "Promise"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Typedarray = "Typedarray"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Arraybuffer = "Arraybuffer"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Global = "global"; // NOLINT (readability-identifier-naming) +const std::string ObjectClassName::Generator = "Generator"; // NOLINT (readability-identifier-naming) + +const std::string RemoteObject::ObjectDescription = "Object"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::GlobalDescription = "global"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::ProxyDescription = "Proxy"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::PromiseDescription = "Promise"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::ArrayIteratorDescription = // NOLINT (readability-identifier-naming) + "ArrayIterator"; +const std::string RemoteObject::StringIteratorDescription = // NOLINT (readability-identifier-naming) + "StringIterator"; +const std::string RemoteObject::SetIteratorDescription = "SetIterator"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::MapIteratorDescription = "MapIterator"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::WeakRefDescription = "WeakRef"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::WeakMapDescription = "WeakMap"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::WeakSetDescription = "WeakSet"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::JSPrimitiveNumberDescription = // NOLINT (readability-identifier-naming) + "Number"; +const std::string RemoteObject::JSPrimitiveBooleanDescription = // NOLINT (readability-identifier-naming) + "Boolean"; +const std::string RemoteObject::JSPrimitiveStringDescription = // NOLINT (readability-identifier-naming) + "String"; +const std::string RemoteObject::JSPrimitiveSymbolDescription = // NOLINT (readability-identifier-naming) + "Symbol"; +const std::string RemoteObject::DateTimeFormatDescription = // NOLINT (readability-identifier-naming) + "DateTimeFormat"; +const std::string RemoteObject::JSIntlDescription = "Intl"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::NumberFormatDescription = "NumberFormat"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::CollatorDescription = "Collator"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::PluralRulesDescription = "PluralRules"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::JSLocaleDescription = "Locale"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::JSListFormatDescription = "ListFormat"; // NOLINT (readability-identifier-naming) +const std::string RemoteObject::JSRelativeTimeFormatDescription = // NOLINT (readability-identifier-naming) + "RelativeTimeFormat"; + +std::unique_ptr RemoteObject::FromTagged(const EcmaVM *ecmaVm, Local tagged) +{ + if (tagged->IsNull() || tagged->IsUndefined() || + tagged->IsBoolean() || tagged->IsNumber() || + tagged->IsBigInt()) { + return std::make_unique(ecmaVm, tagged); + } + if (tagged->IsString()) { + return std::make_unique(ecmaVm, Local(tagged)); + } + if (tagged->IsSymbol()) { + return std::make_unique(ecmaVm, Local(tagged)); + } + if (tagged->IsGeneratorFunction()) { + return std::make_unique(ecmaVm, Local(tagged)); + } + if (tagged->IsFunction()) { + return std::make_unique(ecmaVm, tagged); + } + if (tagged->IsArray(ecmaVm)) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Array, ObjectSubType::Array); + } + if (tagged->IsRegExp()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Regexp, ObjectSubType::Regexp); + } + if (tagged->IsDate()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Date, ObjectSubType::Date); + } + if (tagged->IsMap()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Map, ObjectSubType::Map); + } + if (tagged->IsWeakMap()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Weakmap, ObjectSubType::Weakmap); + } + if (tagged->IsSet()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Set, ObjectSubType::Set); + } + if (tagged->IsWeakSet()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Weakset, ObjectSubType::Weakset); + } + if (tagged->IsError()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Error, ObjectSubType::Error); + } + if (tagged->IsProxy()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Proxy, ObjectSubType::Proxy); + } + if (tagged->IsPromise()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Promise, ObjectSubType::Promise); + } + if (tagged->IsArrayBuffer()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Arraybuffer, + ObjectSubType::Arraybuffer); + } + if (tagged->IsArrayIterator()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::ArrayIterator); + } + if (tagged->IsStringIterator()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::StringIterator); + } + if (tagged->IsSetIterator()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::SetIterator, + ObjectSubType::Iterator); + } + if (tagged->IsMapIterator()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::MapIterator, + ObjectSubType::Iterator); + } + if (tagged->IsObject()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Object); + } + std::unique_ptr object = std::make_unique(); + object->SetType(ObjectType::Undefined); + return object; +} + +PrimitiveRemoteObject::PrimitiveRemoteObject(const EcmaVM *ecmaVm, Local tagged) +{ + if (tagged->IsNull()) { + SetType(ObjectType::Object).SetSubType(ObjectSubType::Null); + } else if (tagged->IsBoolean()) { + std::string description = tagged->IsTrue() ? "true" : "false"; + SetType(ObjectType::Boolean) + .SetValue(tagged) + .SetUnserializableValue(description) + .SetDescription(description); + } else if (tagged->IsUndefined()) { + SetType(ObjectType::Undefined); + } else if (tagged->IsNumber()) { + std::string description = tagged->ToString(ecmaVm)->ToString(); + SetType(ObjectType::Number) + .SetValue(tagged) + .SetUnserializableValue(description) + .SetDescription(description); + } else if (tagged->IsBigInt()) { + std::string description = tagged->ToString(ecmaVm)->ToString() + "n"; // n : BigInt literal postfix + SetType(ObjectType::Bigint) + .SetValue(tagged) + .SetUnserializableValue(description) + .SetDescription(description); + } +} + +StringRemoteObject::StringRemoteObject([[maybe_unused]] const EcmaVM *ecmaVm, Local tagged) +{ + std::string description = tagged->ToString(); + SetType(RemoteObject::TypeName::String) + .SetValue(tagged) + .SetUnserializableValue(description) + .SetDescription(description); +} + +SymbolRemoteObject::SymbolRemoteObject(const EcmaVM *ecmaVm, Local tagged) +{ + std::string description = DescriptionForSymbol(ecmaVm, tagged); + SetType(RemoteObject::TypeName::Symbol) + .SetValue(tagged) + .SetUnserializableValue(description) + .SetDescription(description); +} + +FunctionRemoteObject::FunctionRemoteObject(const EcmaVM *ecmaVm, Local tagged) +{ + std::string description = DescriptionForFunction(ecmaVm, tagged); + SetType(RemoteObject::TypeName::Function) + .SetClassName(RemoteObject::ClassName::Function) + .SetValue(tagged) + .SetUnserializableValue(description) + .SetDescription(description); +} + +GeneratorFunctionRemoteObject::GeneratorFunctionRemoteObject(const EcmaVM *ecmaVm, Local tagged) +{ + std::string description = DescriptionForGeneratorFunction(ecmaVm, tagged); + SetType(RemoteObject::TypeName::Function) + .SetClassName(RemoteObject::ClassName::Generator) + .SetValue(tagged) + .SetUnserializableValue(description) + .SetDescription(description); +} + +ObjectRemoteObject::ObjectRemoteObject(const EcmaVM *ecmaVm, Local tagged, + const std::string &classname) +{ + std::string description = DescriptionForObject(ecmaVm, tagged); + SetType(RemoteObject::TypeName::Object) + .SetClassName(classname) + .SetValue(tagged) + .SetUnserializableValue(description) + .SetDescription(description); +} + +ObjectRemoteObject::ObjectRemoteObject(const EcmaVM *ecmaVm, Local tagged, + const std::string &classname, const std::string &subtype) +{ + std::string description = DescriptionForObject(ecmaVm, tagged); + SetType(RemoteObject::TypeName::Object) + .SetSubType(subtype) + .SetClassName(classname) + .SetValue(tagged) + .SetUnserializableValue(description) + .SetDescription(description); +} + +std::string ObjectRemoteObject::DescriptionForObject(const EcmaVM *ecmaVm, Local tagged) +{ + if (tagged->IsArray(ecmaVm)) { + return DescriptionForArray(ecmaVm, Local(tagged)); + } + if (tagged->IsRegExp()) { + return DescriptionForRegexp(ecmaVm, Local(tagged)); + } + if (tagged->IsDate()) { + return DescriptionForDate(ecmaVm, Local(tagged)); + } + if (tagged->IsMap()) { + return DescriptionForMap(ecmaVm, Local(tagged)); + } + if (tagged->IsWeakMap()) { + return RemoteObject::WeakMapDescription; + } + if (tagged->IsSet()) { + return DescriptionForSet(ecmaVm, Local(tagged)); + } + if (tagged->IsWeakSet()) { + return RemoteObject::WeakSetDescription; + } + if (tagged->IsError()) { + return DescriptionForError(ecmaVm, tagged); + } + if (tagged->IsProxy()) { + return RemoteObject::ProxyDescription; + } + if (tagged->IsPromise()) { + return RemoteObject::PromiseDescription; + } + if (tagged->IsArrayIterator()) { + return DescriptionForArrayIterator(); + } + if (tagged->IsStringIterator()) { + return RemoteObject::StringIteratorDescription; + } + if (tagged->IsSetIterator()) { + return DescriptionForSetIterator(); + } + if (tagged->IsMapIterator()) { + return DescriptionForMapIterator(); + } + if (tagged->IsArrayBuffer()) { + return DescriptionForArrayBuffer(ecmaVm, Local(tagged)); + } + if (tagged->IsSharedArrayBuffer()) { + return DescriptionForSharedArrayBuffer(ecmaVm, Local(tagged)); + } + if (tagged->IsUint8Array()) { + return DescriptionForUint8Array(ecmaVm, Local(tagged)); + } + if (tagged->IsInt8Array()) { + return DescriptionForInt8Array(ecmaVm, Local(tagged)); + } + if (tagged->IsInt16Array()) { + return DescriptionForInt16Array(ecmaVm, Local(tagged)); + } + if (tagged->IsInt32Array()) { + return DescriptionForInt32Array(ecmaVm, Local(tagged)); + } + if (tagged->IsJSPrimitiveRef() && tagged->IsJSPrimitiveNumber()) { + return DescriptionForPrimitiveNumber(ecmaVm, tagged); + } + if (tagged->IsJSPrimitiveRef() && tagged->IsJSPrimitiveString()) { + return DescriptionForPrimitiveString(ecmaVm, tagged); + } + if (tagged->IsJSPrimitiveRef() && tagged->IsJSPrimitiveBoolean()) { + return DescriptionForPrimitiveBoolean(ecmaVm, tagged); + } + if (tagged->IsGeneratorObject()) { + return DescriptionForGeneratorObject(ecmaVm, tagged); + } + if (tagged->IsWeakRef()) { + return DescriptionForWeakRef(); + } + if (tagged->IsJSLocale()) { + return DescriptionForJSLocale(); + } + if (tagged->IsJSDateTimeFormat()) { + return DescriptionForDateTimeFormat(); + } + if (tagged->IsJSRelativeTimeFormat()) { + return DescriptionForJSRelativeTimeFormat(); + } + if (tagged->IsJSIntl()) { + return RemoteObject::JSIntlDescription; + } + if (tagged->IsJSNumberFormat()) { + return DescriptionForNumberFormat(); + } + if (tagged->IsJSCollator()) { + return DescriptionForCollator(); + } + if (tagged->IsJSPluralRules()) { + return DescriptionForPluralRules(); + } + if (tagged->IsJSListFormat()) { + return DescriptionForJSListFormat(); + } + return RemoteObject::ObjectDescription; +} + +std::string ObjectRemoteObject::DescriptionForArray(const EcmaVM *ecmaVm, Local tagged) +{ + std::string description = "Array(" + std::to_string(tagged->Length(ecmaVm)) + ")"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForRegexp(const EcmaVM *ecmaVm, Local tagged) +{ + std::string regExpSource = tagged->GetOriginalSource(ecmaVm)->ToString(); + std::string regExpFlags = tagged->GetOriginalFlags(); + return "/" + regExpSource + "/" + regExpFlags; +} + +std::string ObjectRemoteObject::DescriptionForDate(const EcmaVM *ecmaVm, Local tagged) +{ + std::string description = tagged->ToString(ecmaVm)->ToString(); + return description; +} + +std::string ObjectRemoteObject::DescriptionForMap(const EcmaVM *ecmaVm, Local tagged) +{ + int32_t len = tagged->GetSize(); + std::string description = "Map(" + std::to_string(len) + ")"; + if (!len) { + return description; + } + description += " {"; + char cPre = '\''; + for (int32_t i = 0; i < len; ++i) { + // add Key + Local jsVKey = tagged->GetKey(ecmaVm, i); + Local jsVValue = tagged->GetValue(ecmaVm, i); + if (jsVKey->IsObject()) { + description += "Object"; + } else if (jsVKey->IsString()) { + description += cPre + jsVKey->ToString(ecmaVm)->ToString() + cPre; + } else { + description += jsVKey->ToString(ecmaVm)->ToString(); + } + + description += " => "; + // add Value + if (jsVValue->IsObject()) { + description += "Object"; + } else if (jsVValue->IsString()) { + description += cPre + jsVValue->ToString(ecmaVm)->ToString() + cPre; + } else { + description += jsVValue->ToString(ecmaVm)->ToString(); + } + if (i == len - 1 || i >= 4) { // 4:The count of elements + description += len > 5 ? ", ..." : ""; // 5:The count of elements + break; + } + description += ", "; + } + description += "}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForSet(const EcmaVM *ecmaVm, Local tagged) +{ + int32_t len = tagged->GetSize(); + std::string description = ("Set(" + std::to_string(tagged->GetSize()) + ")"); + if (!len) { + return description; + } + description += " {"; + char cPre = '\''; + for (int32_t i = 0; i < len; ++i) { + // add Key + Local jsValue = tagged->GetValue(ecmaVm, i); + // add Value + if (jsValue->IsObject()) { + description += "Object"; + } else if (jsValue->IsString()) { + description += cPre + jsValue->ToString(ecmaVm)->ToString() + cPre; + } else { + description += jsValue->ToString(ecmaVm)->ToString(); + } + if (i == len - 1 || i >= 4) { // 4:The count of elements + description += len > 5 ? ", ..." : ""; // 5:The count of elements + break; + } + description += ", "; + } + description += "}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForError(const EcmaVM *ecmaVm, Local tagged) +{ + // add name + Local name = StringRef::NewFromUtf8(ecmaVm, "name"); + std::string strName = Local(tagged)->Get(ecmaVm, name)->ToString(ecmaVm)->ToString(); + // add message + Local message = StringRef::NewFromUtf8(ecmaVm, "message"); + std::string strMessage = Local(tagged)->Get(ecmaVm, message)->ToString(ecmaVm)->ToString(); + if (strMessage.empty()) { + return strName; + } else { + return strName + ": " + strMessage; + } +} + +std::string ObjectRemoteObject::DescriptionForArrayIterator() +{ + std::string description = RemoteObject::ArrayIteratorDescription + "{}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForSetIterator() +{ + std::string description = RemoteObject::SetIteratorDescription + "{}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForMapIterator() +{ + std::string description = RemoteObject::MapIteratorDescription + "{}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForArrayBuffer(const EcmaVM *ecmaVm, Local tagged) +{ + int32_t len = tagged->ByteLength(ecmaVm); + std::string description = ("ArrayBuffer(" + std::to_string(len) + ")"); + return description; +} + +std::string ObjectRemoteObject::DescriptionForSharedArrayBuffer(const EcmaVM *ecmaVm, Local tagged) +{ + int32_t len = tagged->ByteLength(ecmaVm); + std::string description = ("SharedArrayBuffer(" + std::to_string(len) + ")"); + return description; +} + +std::string ObjectRemoteObject::DescriptionForUint8Array(const EcmaVM *ecmaVm, Local tagged) +{ + int32_t len = static_cast(tagged->ByteLength(ecmaVm)); + std::string description = ("Uint8Array(" + std::to_string(len) + ")"); + return description; +} + +std::string ObjectRemoteObject::DescriptionForInt8Array(const EcmaVM *ecmaVm, Local tagged) +{ + int32_t len = static_cast(tagged->ByteLength(ecmaVm)); + std::string description = ("Int8Array(" + std::to_string(len) + ")"); + return description; +} + +std::string ObjectRemoteObject::DescriptionForInt16Array(const EcmaVM *ecmaVm, Local tagged) +{ + int32_t len = tagged->ByteLength(ecmaVm) / static_cast(NumberSize::BYTES_OF_16BITS); + std::string description = ("Int16Array(" + std::to_string(len) + ")"); + return description; +} + +std::string ObjectRemoteObject::DescriptionForInt32Array(const EcmaVM *ecmaVm, Local tagged) +{ + int32_t len = tagged->ByteLength(ecmaVm) / static_cast(NumberSize::BYTES_OF_32BITS); + std::string description = ("Int32Array(" + std::to_string(len) + ")"); + return description; +} + +std::string ObjectRemoteObject::DescriptionForPrimitiveNumber(const EcmaVM *ecmaVm, const Local &tagged) +{ + std::string strValue = tagged->ToString(ecmaVm)->ToString(); + std::string description = RemoteObject::JSPrimitiveNumberDescription + "{[[PrimitiveValue]]: " + strValue + "}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForPrimitiveString(const EcmaVM *ecmaVm, const Local &tagged) +{ + std::string strValue = tagged->ToString(ecmaVm)->ToString(); + std::string description = RemoteObject::JSPrimitiveStringDescription + "{[[PrimitiveValue]]: " + strValue + "}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForPrimitiveBoolean(const EcmaVM *ecmaVm, const Local &tagged) +{ + std::string strValue = tagged->ToString(ecmaVm)->ToString(); + std::string description = RemoteObject::JSPrimitiveBooleanDescription + "{[[PrimitiveValue]]: " + strValue + "}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForGeneratorObject(const EcmaVM *ecmaVm, const Local &tagged) +{ + Local genObjectRef = tagged->ToObject(ecmaVm); + // add Status + Local jsValueRef = genObjectRef->GetGeneratorState(ecmaVm); + std::string strState = genObjectRef->GetGeneratorState(ecmaVm)->ToString(ecmaVm)->ToString(); + // add FuncName + jsValueRef = genObjectRef->GetGeneratorFunction(ecmaVm); + Local name = StringRef::NewFromUtf8(ecmaVm, "name"); + std::string strFuncName = Local(jsValueRef)->Get(ecmaVm, name)->ToString(ecmaVm)->ToString(); + + std::string description = strFuncName + " {<" + strState + ">}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForWeakRef() +{ + std::string description = RemoteObject::WeakRefDescription + " {}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForDateTimeFormat() +{ + std::string description = RemoteObject::DateTimeFormatDescription + " {}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForNumberFormat() +{ + std::string description = RemoteObject::NumberFormatDescription + " {}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForCollator() +{ + std::string description = RemoteObject::CollatorDescription + " {}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForPluralRules() +{ + std::string description = RemoteObject::PluralRulesDescription + " {}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForJSLocale() +{ + std::string description = RemoteObject::JSLocaleDescription + " {}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForJSRelativeTimeFormat() +{ + std::string description = RemoteObject::JSRelativeTimeFormatDescription + " {}"; + return description; +} + +std::string ObjectRemoteObject::DescriptionForJSListFormat() +{ + std::string description = RemoteObject::JSListFormatDescription + " {}"; + return description; +} + +std::string SymbolRemoteObject::DescriptionForSymbol(const EcmaVM *ecmaVm, Local tagged) const +{ + std::string description = "Symbol(" + tagged->GetDescription(ecmaVm)->ToString() + ")"; + return description; +} + +std::string FunctionRemoteObject::DescriptionForFunction(const EcmaVM *ecmaVm, Local tagged) const +{ + std::string sourceCode; + if (tagged->IsNative(ecmaVm)) { + sourceCode = "[native code]"; + } else { + sourceCode = "[js code]"; + } + Local name = tagged->GetName(ecmaVm); + std::string description = "function " + name->ToString() + "( { " + sourceCode + " }"; + return description; +} + +std::string GeneratorFunctionRemoteObject::DescriptionForGeneratorFunction(const EcmaVM *ecmaVm, + Local tagged) const +{ + std::string sourceCode; + if (tagged->IsNative(ecmaVm)) { + sourceCode = "[native code]"; + } else { + sourceCode = "[js code]"; + } + Local name = tagged->GetName(ecmaVm); + std::string description = "function* " + name->ToString() + "( { " + sourceCode + " }"; + return description; +} + +std::unique_ptr RemoteObject::Create(const PtJson ¶ms) +{ + std::string error; + auto remoteObject = std::make_unique(); + Result ret; + + std::string type; + ret = params.GetString("type", &type); + if (ret == Result::SUCCESS) { + if (ObjectType::Valid(type)) { + remoteObject->type_ = std::move(type); + } else { + error += "'type' is invalid;"; + } + } else { + error += "Unknown 'type';"; + } + + std::string subType; + ret = params.GetString("subtype", &subType); + if (ret == Result::SUCCESS) { + if (ObjectSubType::Valid(subType)) { + remoteObject->subType_ = std::move(subType); + } else { + error += "'subtype' is invalid;"; + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'subtype';"; + } + + std::string className; + ret = params.GetString("className", &className); + if (ret == Result::SUCCESS) { + remoteObject->className_ = std::move(className); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'className';"; + } + + std::string unserializableValue; + ret = params.GetString("unserializableValue", &unserializableValue); + if (ret == Result::SUCCESS) { + remoteObject->unserializableValue_ = std::move(unserializableValue); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'unserializableValue';"; + } + + std::string description; + ret = params.GetString("description", &description); + if (ret == Result::SUCCESS) { + remoteObject->description_ = std::move(description); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'description';"; + } + + std::string objectId; + ret = params.GetString("objectId", &objectId); + if (ret == Result::SUCCESS) { + remoteObject->objectId_ = std::stoi(objectId); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'objectId';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "RemoteObject::Create " << error; + return nullptr; + } + + return remoteObject; +} + +std::unique_ptr RemoteObject::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("type", type_.c_str()); + if (subType_) { + result->Add("subtype", subType_->c_str()); + } + if (className_) { + result->Add("className", className_->c_str()); + } + if (unserializableValue_) { + result->Add("unserializableValue", unserializableValue_->c_str()); + } + if (description_) { + result->Add("description", description_->c_str()); + } + if (objectId_) { + result->Add("objectId", std::to_string(objectId_.value()).c_str()); + } + + return result; +} + +std::unique_ptr ExceptionDetails::Create(const PtJson ¶ms) +{ + std::string error; + auto exceptionDetails = std::make_unique(); + Result ret; + + int32_t exceptionId; + ret = params.GetInt("exceptionId", &exceptionId); + if (ret == Result::SUCCESS) { + exceptionDetails->exceptionId_ = exceptionId; + } else { + error += "Unknown 'exceptionId';"; + } + + std::string text; + ret = params.GetString("text", &text); + if (ret == Result::SUCCESS) { + exceptionDetails->text_ = std::move(text); + } else { + error += "Unknown 'text';"; + } + + int32_t lineNumber; + ret = params.GetInt("lineNumber", &lineNumber); + if (ret == Result::SUCCESS) { + exceptionDetails->lineNumber_ = lineNumber; + } else { + error += "Unknown 'lineNumber';"; + } + + int32_t columnNumber; + ret = params.GetInt("columnNumber", &columnNumber); + if (ret == Result::SUCCESS) { + exceptionDetails->columnNumber_ = columnNumber; + } else { + error += "Unknown 'columnNumber';"; + } + + std::string scriptId; + ret = params.GetString("scriptId", &scriptId); + if (ret == Result::SUCCESS) { + exceptionDetails->scriptId_ = std::stoi(scriptId); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'scriptId';"; + } + + std::string url; + ret = params.GetString("url", &url); + if (ret == Result::SUCCESS) { + exceptionDetails->url_ = std::move(url); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'url';"; + } + + std::unique_ptr exception; + ret = params.GetObject("exception", &exception); + if (ret == Result::SUCCESS) { + std::unique_ptr obj = RemoteObject::Create(*exception); + if (obj == nullptr) { + error += "'exception' format error;"; + } else { + exceptionDetails->exception_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'exception';"; + } + + int32_t executionContextId; + ret = params.GetInt("executionContextId", &executionContextId); + if (ret == Result::SUCCESS) { + exceptionDetails->executionContextId_ = executionContextId; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'executionContextId';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "ExceptionDetails::Create " << error; + return nullptr; + } + + return exceptionDetails; +} + +std::unique_ptr ExceptionDetails::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("exceptionId", exceptionId_); + result->Add("text", text_.c_str()); + result->Add("lineNumber", lineNumber_); + result->Add("columnNumber", columnNumber_); + + if (scriptId_) { + result->Add("scriptId", std::to_string(scriptId_.value()).c_str()); + } + if (url_) { + result->Add("url", url_->c_str()); + } + if (exception_) { + ASSERT(exception_.value() != nullptr); + result->Add("exception", exception_.value()->ToJson()); + } + if (executionContextId_) { + result->Add("executionContextId", executionContextId_.value()); + } + + return result; +} + +std::unique_ptr InternalPropertyDescriptor::Create(const PtJson ¶ms) +{ + std::string error; + auto internalPropertyDescriptor = std::make_unique(); + Result ret; + + std::string name; + ret = params.GetString("name", &name); + if (ret == Result::SUCCESS) { + internalPropertyDescriptor->name_ = std::move(name); + } else { + error += "Unknown 'name';"; + } + + std::unique_ptr value; + ret = params.GetObject("value", &value); + if (ret == Result::SUCCESS) { + std::unique_ptr obj = RemoteObject::Create(*value); + if (obj == nullptr) { + error += "'value' format error;"; + } else { + internalPropertyDescriptor->value_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'value';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "InternalPropertyDescriptor::Create " << error; + return nullptr; + } + + return internalPropertyDescriptor; +} + +std::unique_ptr InternalPropertyDescriptor::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("name", name_.c_str()); + if (value_) { + ASSERT(value_.value() != nullptr); + result->Add("value", value_.value()->ToJson()); + } + + return result; +} + +std::unique_ptr PrivatePropertyDescriptor::Create(const PtJson ¶ms) +{ + std::string error; + auto privatePropertyDescriptor = std::make_unique(); + Result ret; + + std::string name; + ret = params.GetString("name", &name); + if (ret == Result::SUCCESS) { + privatePropertyDescriptor->name_ = std::move(name); + } else { + error += "Unknown 'name';"; + } + + std::unique_ptr value; + ret = params.GetObject("value", &value); + std::unique_ptr obj; + if (ret == Result::SUCCESS) { + obj = RemoteObject::Create(*value); + if (obj == nullptr) { + error += "'value' format error;"; + } else { + privatePropertyDescriptor->value_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'value';"; + } + + std::unique_ptr get; + ret = params.GetObject("get", &get); + if (ret == Result::SUCCESS) { + obj = RemoteObject::Create(*get); + if (obj == nullptr) { + error += "'get' format error;"; + } else { + privatePropertyDescriptor->get_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'get';"; + } + + std::unique_ptr set; + ret = params.GetObject("set", &set); + if (ret == Result::SUCCESS) { + obj = RemoteObject::Create(*set); + if (obj == nullptr) { + error += "'set' format error;"; + } else { + privatePropertyDescriptor->set_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'set';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "PrivatePropertyDescriptor::Create " << error; + return nullptr; + } + + return privatePropertyDescriptor; +} + +std::unique_ptr PrivatePropertyDescriptor::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("name", name_.c_str()); + if (value_) { + ASSERT(value_.value() != nullptr); + result->Add("value", value_.value()->ToJson()); + } + if (get_) { + ASSERT(get_.value() != nullptr); + result->Add("get", get_.value()->ToJson()); + } + if (set_) { + ASSERT(set_.value() != nullptr); + result->Add("set", set_.value()->ToJson()); + } + + return result; +} + +std::unique_ptr PropertyDescriptor::FromProperty(const EcmaVM *ecmaVm, + Local name, const PropertyAttribute &property) +{ + std::unique_ptr debuggerProperty = std::make_unique(); + + std::string nameStr; + if (name->IsSymbol()) { + Local symbol(name); + nameStr = "Symbol(" + Local(name)->GetDescription(ecmaVm)->ToString() + ")"; + debuggerProperty->symbol_ = RemoteObject::FromTagged(ecmaVm, name); + } else { + nameStr = name->ToString(ecmaVm)->ToString(); + } + + debuggerProperty->name_ = nameStr; + if (property.HasValue()) { + debuggerProperty->value_ = RemoteObject::FromTagged(ecmaVm, property.GetValue(ecmaVm)); + } + if (property.HasWritable()) { + debuggerProperty->writable_ = property.IsWritable(); + } + if (property.HasGetter()) { + debuggerProperty->get_ = RemoteObject::FromTagged(ecmaVm, property.GetGetter(ecmaVm)); + } + if (property.HasSetter()) { + debuggerProperty->set_ = RemoteObject::FromTagged(ecmaVm, property.GetSetter(ecmaVm)); + } + debuggerProperty->configurable_ = property.IsConfigurable(); + debuggerProperty->enumerable_ = property.IsEnumerable(); + debuggerProperty->isOwn_ = true; + + return debuggerProperty; +} + +std::unique_ptr PropertyDescriptor::Create(const PtJson ¶ms) +{ + std::string error; + auto propertyDescriptor = std::make_unique(); + Result ret; + + std::string name; + ret = params.GetString("name", &name); + if (ret == Result::SUCCESS) { + propertyDescriptor->name_ = std::move(name); + } else { + error += "Unknown 'name';"; + } + + std::unique_ptr value; + std::unique_ptr obj; + ret = params.GetObject("value", &value); + if (ret == Result::SUCCESS) { + obj = RemoteObject::Create(*value); + if (obj == nullptr) { + error += "'value' format error;"; + } else { + propertyDescriptor->value_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'value';"; + } + + bool writable = false; + ret = params.GetBool("writable", &writable); + if (ret == Result::SUCCESS) { + propertyDescriptor->writable_ = writable; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'writable';"; + } + + std::unique_ptr get; + ret = params.GetObject("get", &get); + if (ret == Result::SUCCESS) { + obj = RemoteObject::Create(*get); + if (obj == nullptr) { + error += "'get' format error;"; + } else { + propertyDescriptor->get_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'get';"; + } + + std::unique_ptr set; + ret = params.GetObject("set", &set); + if (ret == Result::SUCCESS) { + obj = RemoteObject::Create(*set); + if (obj == nullptr) { + error += "'set' format error;"; + } else { + propertyDescriptor->set_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'set';"; + } + + bool configurable = false; + ret = params.GetBool("configurable", &configurable); + if (ret == Result::SUCCESS) { + propertyDescriptor->configurable_ = configurable; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'configurable';"; + } + + bool enumerable = false; + ret = params.GetBool("enumerable", &enumerable); + if (ret == Result::SUCCESS) { + propertyDescriptor->enumerable_ = enumerable; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'enumerable';"; + } + + bool wasThrown = false; + ret = params.GetBool("wasThrown", &wasThrown); + if (ret == Result::SUCCESS) { + propertyDescriptor->wasThrown_ = wasThrown; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'wasThrown';"; + } + + bool isOwn = false; + ret = params.GetBool("isOwn", &isOwn); + if (ret == Result::SUCCESS) { + propertyDescriptor->isOwn_ = isOwn; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'isOwn';"; + } + + std::unique_ptr symbol; + ret = params.GetObject("symbol", &symbol); + if (ret == Result::SUCCESS) { + obj = RemoteObject::Create(*symbol); + if (obj == nullptr) { + error += "'symbol' format error;"; + } else { + propertyDescriptor->symbol_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'symbol';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "PropertyDescriptor::Create " << error; + return nullptr; + } + + return propertyDescriptor; +} + +std::unique_ptr PropertyDescriptor::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("name", name_.c_str()); + if (value_) { + ASSERT(value_.value() != nullptr); + result->Add("value", value_.value()->ToJson()); + } + if (writable_) { + result->Add("writable", writable_.value()); + } + if (get_) { + ASSERT(get_.value() != nullptr); + result->Add("get", get_.value()->ToJson()); + } + if (set_) { + ASSERT(set_.value() != nullptr); + result->Add("set", set_.value()->ToJson()); + } + result->Add("configurable", configurable_); + result->Add("enumerable", enumerable_); + if (wasThrown_) { + result->Add("wasThrown", wasThrown_.value()); + } + if (isOwn_) { + result->Add("isOwn", isOwn_.value()); + } + if (symbol_) { + ASSERT(symbol_.value() != nullptr); + result->Add("symbol", symbol_.value()->ToJson()); + } + + return result; +} + +std::unique_ptr CallArgument::Create(const PtJson ¶ms) +{ + auto callArgument = std::make_unique(); + std::string error; + Result ret; + + std::string unserializableValue; + ret = params.GetString("unserializableValue", &unserializableValue); + if (ret == Result::SUCCESS) { + callArgument->unserializableValue_ = std::move(unserializableValue); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'unserializableValue';"; + } + std::string objectId; + ret = params.GetString("objectId", &objectId); + if (ret == Result::SUCCESS) { + callArgument->objectId_ = std::stoi(objectId); + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'objectId';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "CallArgument::Create " << error; + return nullptr; + } + + return callArgument; +} + +std::unique_ptr CallArgument::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + if (unserializableValue_) { + result->Add("unserializableValue", unserializableValue_->c_str()); + } + if (objectId_) { + result->Add("objectId", std::to_string(objectId_.value()).c_str()); + } + + return result; +} + +std::unique_ptr Location::Create(const PtJson ¶ms) +{ + auto location = std::make_unique(); + std::string error; + Result ret; + + std::string scriptId; + ret = params.GetString("scriptId", &scriptId); + if (ret == Result::SUCCESS) { + location->scriptId_ = std::stoi(scriptId); + } else { + error += "Unknown 'scriptId';"; + } + int32_t lineNumber; + ret = params.GetInt("lineNumber", &lineNumber); + if (ret == Result::SUCCESS) { + location->lineNumber_ = lineNumber; + } else { + error += "Unknown 'lineNumber';"; + } + int32_t columnNumber; + ret = params.GetInt("columnNumber", &columnNumber); + if (ret == Result::SUCCESS) { + location->columnNumber_ = columnNumber; + } else if (ret == Result::TYPE_ERROR) { // optional value + error += "Unknown 'columnNumber';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "Location::Create " << error; + return nullptr; + } + + return location; +} + +std::unique_ptr Location::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("scriptId", std::to_string(scriptId_).c_str()); + result->Add("lineNumber", lineNumber_); + if (columnNumber_) { + result->Add("columnNumber", columnNumber_.value()); + } + + return result; +} + +std::unique_ptr ScriptPosition::Create(const PtJson ¶ms) +{ + auto scriptPosition = std::make_unique(); + std::string error; + Result ret; + + int32_t lineNumber; + ret = params.GetInt("lineNumber", &lineNumber); + if (ret == Result::SUCCESS) { + scriptPosition->lineNumber_ = lineNumber; + } else { + error += "Unknown 'lineNumber';"; + } + int32_t columnNumber; + ret = params.GetInt("columnNumber", &columnNumber); + if (ret == Result::SUCCESS) { + scriptPosition->columnNumber_ = columnNumber; + } else { + error += "Unknown 'columnNumber';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "ScriptPosition::Create " << error; + return nullptr; + } + + return scriptPosition; +} + +std::unique_ptr ScriptPosition::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("lineNumber", lineNumber_); + result->Add("columnNumber", columnNumber_); + + return result; +} + +std::unique_ptr SearchMatch::Create(const PtJson ¶ms) +{ + std::string error; + auto locationSearch = std::make_unique(); + Result ret; + + int32_t lineNumber; + ret = params.GetInt("lineNumber", &lineNumber); + if (ret == Result::SUCCESS) { + locationSearch->lineNumber_ = lineNumber; + } else { + error += "Unknown 'lineNumber';"; + } + + std::string lineContent; + ret = params.GetString("lineContent", &lineContent); + if (ret == Result::SUCCESS) { + locationSearch->lineContent_ = std::move(lineContent); + } else { + error += "Unknown 'lineContent';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SearchMatch::Create " << error; + return nullptr; + } + + return locationSearch; +} + +std::unique_ptr SearchMatch::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("lineNumber", lineNumber_); + result->Add("lineContent", lineContent_.c_str()); + + return result; +} + +std::unique_ptr LocationRange::Create(const PtJson ¶ms) +{ + std::string error; + auto locationRange = std::make_unique(); + Result ret; + + std::string scriptId; + ret = params.GetString("scriptId", &scriptId); + if (ret == Result::SUCCESS) { + locationRange->scriptId_ = std::stoi(scriptId); + } else { + error += "Unknown 'scriptId';"; + } + + std::unique_ptr start; + std::unique_ptr obj; + ret = params.GetObject("start", &start); + if (ret == Result::SUCCESS) { + obj = ScriptPosition::Create(*start); + if (obj == nullptr) { + error += "'start' format error;"; + } else { + locationRange->start_ = std::move(obj); + } + } else { + error += "Unknown 'start';"; + } + + std::unique_ptr end; + ret = params.GetObject("end", &end); + if (ret == Result::SUCCESS) { + obj = ScriptPosition::Create(*end); + if (obj == nullptr) { + error += "'end' format error;"; + } else { + locationRange->end_ = std::move(obj); + } + } else { + error += "Unknown 'end';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "LocationRange::Create " << error; + return nullptr; + } + + return locationRange; +} + +std::unique_ptr LocationRange::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("scriptId", std::to_string(scriptId_).c_str()); + ASSERT(start_ != nullptr); + result->Add("start", start_->ToJson()); + ASSERT(end_ != nullptr); + result->Add("end", end_->ToJson()); + + return result; +} + +std::unique_ptr BreakLocation::Create(const PtJson ¶ms) +{ + std::string error; + auto breakLocation = std::make_unique(); + Result ret; + + std::string scriptId; + ret = params.GetString("scriptId", &scriptId); + if (ret == Result::SUCCESS) { + breakLocation->scriptId_ = std::stoi(scriptId); + } else { + error += "Unknown 'scriptId';"; + } + + int32_t lineNumber; + ret = params.GetInt("lineNumber", &lineNumber); + if (ret == Result::SUCCESS) { + breakLocation->lineNumber_ = lineNumber; + } else { + error += "Unknown 'lineNumber';"; + } + + int32_t columnNumber; + ret = params.GetInt("columnNumber", &columnNumber); + if (ret == Result::SUCCESS) { + breakLocation->columnNumber_ = columnNumber; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'columnNumber';"; + } + + std::string type; + ret = params.GetString("type", &type); + if (ret == Result::SUCCESS) { + if (BreakType::Valid(type)) { + breakLocation->type_ = std::move(type); + } else { + error += "'type' is invalid;"; + } + } else if (ret == Result::TYPE_ERROR) { + error += "type 'scriptId';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "Location::Create " << error; + return nullptr; + } + + return breakLocation; +} + +std::unique_ptr BreakLocation::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("scriptId", std::to_string(scriptId_).c_str()); + result->Add("lineNumber", lineNumber_); + if (columnNumber_) { + result->Add("columnNumber", columnNumber_.value()); + } + if (type_) { + result->Add("type", type_->c_str()); + } + + return result; +} + +std::unique_ptr Scope::Create(const PtJson ¶ms) +{ + std::string error; + auto scope = std::make_unique(); + Result ret; + + std::string type; + ret = params.GetString("type", &type); + if (ret == Result::SUCCESS) { + if (Scope::Type::Valid(type)) { + scope->type_ = std::move(type); + } else { + error += "'type' is invalid;"; + } + } else { + error += "Unknown 'type';"; + } + + std::unique_ptr object; + std::unique_ptr remoteObject; + ret = params.GetObject("object", &object); + if (ret == Result::SUCCESS) { + remoteObject = RemoteObject::Create(*object); + if (remoteObject == nullptr) { + error += "'object' format error;"; + } else { + scope->object_ = std::move(remoteObject); + } + } else { + error += "Unknown 'object';"; + } + + std::string name; + ret = params.GetString("name", &name); + if (ret == Result::SUCCESS) { + scope->name_ = std::move(name); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'name';"; + } + + std::unique_ptr startLocation; + std::unique_ptr obj; + ret = params.GetObject("startLocation", &startLocation); + if (ret == Result::SUCCESS) { + obj = Location::Create(*startLocation); + if (obj == nullptr) { + error += "'startLocation' format error;"; + } else { + scope->startLocation_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'startLocation';"; + } + + std::unique_ptr endLocation; + ret = params.GetObject("endLocation", &endLocation); + if (ret == Result::SUCCESS) { + obj = Location::Create(*endLocation); + if (obj == nullptr) { + error += "'endLocation' format error;"; + } else { + scope->endLocation_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'endLocation';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "Location::Create " << error; + return nullptr; + } + + return scope; +} + +std::unique_ptr Scope::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("type", type_.c_str()); + ASSERT(object_ != nullptr); + result->Add("object", object_->ToJson()); + if (name_) { + result->Add("name", name_->c_str()); + } + if (startLocation_) { + ASSERT(startLocation_.value() != nullptr); + result->Add("startLocation", startLocation_.value()->ToJson()); + } + if (endLocation_) { + ASSERT(endLocation_.value() != nullptr); + result->Add("endLocation", endLocation_.value()->ToJson()); + } + + return result; +} + +std::unique_ptr CallFrame::Create(const PtJson ¶ms) +{ + std::string error; + auto callFrame = std::make_unique(); + Result ret; + + std::string callFrameId; + ret = params.GetString("callFrameId", &callFrameId); + if (ret == Result::SUCCESS) { + callFrame->callFrameId_ = std::stoi(callFrameId); + } else { + error += "Unknown 'callFrameId';"; + } + + std::string functionName; + ret = params.GetString("functionName", &functionName); + if (ret == Result::SUCCESS) { + callFrame->functionName_ = std::move(functionName); + } else { + error += "Unknown 'functionName';"; + } + + std::unique_ptr functionLocation; + std::unique_ptr obj; + ret = params.GetObject("functionLocation", &functionLocation); + if (ret == Result::SUCCESS) { + obj = Location::Create(*functionLocation); + if (obj == nullptr) { + error += "'functionLocation' format error;"; + } else { + callFrame->functionLocation_ = std::move(obj); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'functionLocation';"; + } + + std::unique_ptr location; + ret = params.GetObject("location", &location); + if (ret == Result::SUCCESS) { + obj = Location::Create(*location); + if (obj == nullptr) { + error += "'location' format error;"; + } else { + callFrame->location_ = std::move(obj); + } + } else { + error += "Unknown 'location';"; + } + + std::string url; + ret = params.GetString("url", &url); + if (ret == Result::SUCCESS) { + callFrame->url_ = std::move(url); + } else { + error += "Unknown 'url';"; + } + + std::unique_ptr scopeChain; + ret = params.GetArray("scopeChain", &scopeChain); + if (ret == Result::SUCCESS) { + int32_t len = scopeChain->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr arrayEle = scopeChain->Get(i); + ASSERT(arrayEle != nullptr); + std::unique_ptr scope = Scope::Create(*arrayEle); + if (scope == nullptr) { + error += "'scopeChain' format error;"; + } else { + callFrame->scopeChain_.emplace_back(std::move(scope)); + } + } + } else { + error += "Unknown 'scopeChain';"; + } + + std::unique_ptr thisObj; + std::unique_ptr remoteObject; + ret = params.GetObject("this", &thisObj); + if (ret == Result::SUCCESS) { + remoteObject = RemoteObject::Create(*thisObj); + if (remoteObject == nullptr) { + error += "'this' format error;"; + } else { + callFrame->this_ = std::move(remoteObject); + } + } else { + error += "Unknown 'this';"; + } + + std::unique_ptr returnValue; + ret = params.GetObject("returnValue", &returnValue); + if (ret == Result::SUCCESS) { + remoteObject = RemoteObject::Create(*returnValue); + if (remoteObject == nullptr) { + error += "'returnValue' format error;"; + } else { + callFrame->returnValue_ = std::move(remoteObject); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'returnValue';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "CallFrame::Create " << error; + return nullptr; + } + + return callFrame; +} + +std::unique_ptr CallFrame::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("callFrameId", std::to_string(callFrameId_).c_str()); + result->Add("functionName", functionName_.c_str()); + + if (functionLocation_) { + ASSERT(functionLocation_.value() != nullptr); + result->Add("functionLocation", functionLocation_.value()->ToJson()); + } + ASSERT(location_ != nullptr); + result->Add("location", location_->ToJson()); + result->Add("url", url_.c_str()); + + size_t len = scopeChain_.size(); + std::unique_ptr values = PtJson::CreateArray(); + for (size_t i = 0; i < len; i++) { + ASSERT(scopeChain_[i] != nullptr); + std::unique_ptr scope = scopeChain_[i]->ToJson(); + values->Push(scope); + } + result->Add("scopeChain", values); + + ASSERT(this_ != nullptr); + result->Add("this", this_->ToJson()); + if (returnValue_) { + ASSERT(returnValue_.value() != nullptr); + result->Add("returnValue", returnValue_.value()->ToJson()); + } + + return result; +} + +std::unique_ptr SamplingHeapProfileSample::Create(const PtJson ¶ms) +{ + std::string error; + auto samplingHeapProfileSample = std::make_unique(); + Result ret; + + int32_t size; + ret = params.GetInt("size", &size); + if (ret == Result::SUCCESS) { + samplingHeapProfileSample->size_ = size; + } else { + error += "Unknown 'size';"; + } + int32_t nodeId; + ret = params.GetInt("nodeId", &nodeId); + if (ret == Result::SUCCESS) { + samplingHeapProfileSample->nodeId_ = nodeId; + } else { + error += "Unknown 'nodeId';"; + } + int32_t ordinal; + ret = params.GetInt("ordinal", &ordinal); + if (ret == Result::SUCCESS) { + samplingHeapProfileSample->ordinal_ = ordinal; + } else { + error += "Unknown 'ordinal';"; + } + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SamplingHeapProfileSample::Create " << error; + return nullptr; + } + + return samplingHeapProfileSample; +} + +std::unique_ptr SamplingHeapProfileSample::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("size", size_); + result->Add("nodeId", nodeId_); + result->Add("ordinal", ordinal_); + + return result; +} + +std::unique_ptr RuntimeCallFrame::Create(const PtJson ¶ms) +{ + std::string error; + auto runtimeCallFrame = std::make_unique(); + Result ret; + + std::string functionName; + ret = params.GetString("functionName", &functionName); + if (ret == Result::SUCCESS) { + runtimeCallFrame->functionName_ = std::move(functionName); + } else { + error += "Unknown 'functionName';"; + } + + std::string scriptId; + ret = params.GetString("scriptId", &scriptId); + if (ret == Result::SUCCESS) { + runtimeCallFrame->scriptId_ = std::move(scriptId); + } else { + error += "Unknown 'scriptId';"; + } + + std::string url; + ret = params.GetString("url", &url); + if (ret == Result::SUCCESS) { + runtimeCallFrame->url_ = std::move(url); + } else { + error += "Unknown 'url';"; + } + + int32_t lineNumber; + ret = params.GetInt("lineNumber", &lineNumber); + if (ret == Result::SUCCESS) { + runtimeCallFrame->lineNumber_ = lineNumber; + } else { + error += "Unknown 'lineNumber';"; + } + + int32_t columnNumber; + ret = params.GetInt("columnNumber", &columnNumber); + if (ret == Result::SUCCESS) { + runtimeCallFrame->columnNumber_ = columnNumber; + } else { + error += "Unknown 'columnNumber';"; + } + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "RuntimeCallFrame::Create " << error; + return nullptr; + } + + return runtimeCallFrame; +} + +std::unique_ptr RuntimeCallFrame::FromFrameInfo(const FrameInfo &cpuFrameInfo) +{ + auto runtimeCallFrame = std::make_unique(); + runtimeCallFrame->SetFunctionName(cpuFrameInfo.functionName); + runtimeCallFrame->SetScriptId(std::to_string(cpuFrameInfo.scriptId)); + runtimeCallFrame->SetColumnNumber(cpuFrameInfo.columnNumber); + runtimeCallFrame->SetLineNumber(cpuFrameInfo.lineNumber); + runtimeCallFrame->SetUrl(cpuFrameInfo.url); + return runtimeCallFrame; +} + +std::unique_ptr RuntimeCallFrame::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("functionName", functionName_.c_str()); + result->Add("scriptId", scriptId_.c_str()); + result->Add("url", url_.c_str()); + result->Add("lineNumber", lineNumber_); + result->Add("columnNumber", columnNumber_); + + return result; +} + +std::unique_ptr SamplingHeapProfileNode::Create(const PtJson ¶ms) +{ + std::string error; + auto samplingHeapProfileNode = std::make_unique(); + Result ret; + + std::unique_ptr callFrame; + ret = params.GetObject("callFrame", &callFrame); + if (ret == Result::SUCCESS) { + std::unique_ptr runtimeCallFrame = RuntimeCallFrame::Create(*callFrame); + if (runtimeCallFrame == nullptr) { + error += "'callFrame' format invalid;"; + } else { + samplingHeapProfileNode->callFrame_ = std::move(runtimeCallFrame); + } + } else { + error += "Unknown 'callFrame';"; + } + + int32_t selfSize; + ret = params.GetInt("selfSize", &selfSize); + if (ret == Result::SUCCESS) { + samplingHeapProfileNode->selfSize_ = selfSize; + } else { + error += "Unknown 'selfSize';"; + } + + int32_t id; + ret = params.GetInt("id", &id); + if (ret == Result::SUCCESS) { + samplingHeapProfileNode->id_ = id; + } else { + error += "Unknown 'id';"; + } + + std::unique_ptr children; + ret = params.GetArray("children", &children); + if (ret == Result::SUCCESS) { + int32_t len = children->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr arrayEle = children->Get(i); + ASSERT(arrayEle != nullptr); + std::unique_ptr node = SamplingHeapProfileNode::Create(*arrayEle); + if (node == nullptr) { + error += "'children' format invalid;"; + } else { + samplingHeapProfileNode->children_.emplace_back(std::move(node)); + } + } + } else { + error += "Unknown 'children';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SamplingHeapProfileNode::Create " << error; + return nullptr; + } + + return samplingHeapProfileNode; +} + +std::unique_ptr SamplingHeapProfileNode::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + ASSERT(callFrame_ != nullptr); + result->Add("callFrame", callFrame_->ToJson()); + result->Add("selfSize", selfSize_); + result->Add("id", id_); + + std::unique_ptr childrens = PtJson::CreateArray(); + size_t len = children_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(children_[i] != nullptr); + childrens->Push(children_[i]->ToJson()); + } + result->Add("children", childrens); + + return result; +} + +std::unique_ptr SamplingHeapProfile::Create(const PtJson ¶ms) +{ + std::string error; + auto samplingHeapProfile = std::make_unique(); + Result ret; + + std::unique_ptr head; + ret = params.GetObject("head", &head); + if (ret == Result::SUCCESS) { + std::unique_ptr pHead = SamplingHeapProfileNode::Create(*head); + if (pHead == nullptr) { + error += "'sample' format invalid;"; + } else { + samplingHeapProfile->head_ = std::move(pHead); + } + } else { + error += "Unknown 'head';"; + } + + std::unique_ptr samples; + ret = params.GetArray("samples", &samples); + if (ret == Result::SUCCESS) { + int32_t len = samples->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr arrayEle = samples->Get(i); + ASSERT(arrayEle != nullptr); + std::unique_ptr pSample = SamplingHeapProfileSample::Create(*arrayEle); + if (pSample == nullptr) { + error += "'sample' format invalid;"; + } else { + samplingHeapProfile->samples_.emplace_back(std::move(pSample)); + } + } + } else { + error += "Unknown 'samples';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SamplingHeapProfile::Create " << error; + return nullptr; + } + + return samplingHeapProfile; +} + +std::unique_ptr SamplingHeapProfile::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + ASSERT(head_ != nullptr); + result->Add("head", head_->ToJson()); + + std::unique_ptr samples = PtJson::CreateArray(); + size_t len = samples_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(samples_[i] != nullptr); + samples->Push(samples_[i]->ToJson()); + } + result->Add("samples", samples); + return result; +} + +std::unique_ptr PositionTickInfo::Create(const PtJson ¶ms) +{ + std::string error; + auto positionTickInfo = std::make_unique(); + Result ret; + + int32_t line; + ret = params.GetInt("line", &line); + if (ret == Result::SUCCESS) { + positionTickInfo->line_ = line; + } else { + error += "Unknown 'line';"; + } + + int32_t ticks; + ret = params.GetInt("ticks", &ticks); + if (ret == Result::SUCCESS) { + positionTickInfo->ticks_ = ticks; + } else { + error += "Unknown 'ticks';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "PositionTickInfo::Create " << error; + return nullptr; + } + + return positionTickInfo; +} + +std::unique_ptr PositionTickInfo::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("line", line_); + result->Add("ticks", ticks_); + + return result; +} + +std::unique_ptr ProfileNode::Create(const PtJson ¶ms) +{ + std::string error; + auto profileNode = std::make_unique(); + Result ret; + + int32_t id; + ret = params.GetInt("id", &id); + if (ret == Result::SUCCESS) { + profileNode->id_ = id; + } else { + error += "Unknown 'id';"; + } + + std::unique_ptr callFrame; + ret = params.GetObject("callFrame", &callFrame); + if (ret == Result::SUCCESS) { + std::unique_ptr runtimeCallFrame = RuntimeCallFrame::Create(*callFrame); + if (runtimeCallFrame == nullptr) { + error += "'callFrame' format invalid;"; + } else { + profileNode->callFrame_ = std::move(runtimeCallFrame); + } + } else { + error += "Unknown 'callFrame';"; + } + + int32_t hitCount; + ret = params.GetInt("hitCount", &hitCount); + if (ret == Result::SUCCESS) { + profileNode->hitCount_ = hitCount; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'hitCount';"; + } + + std::unique_ptr children; + ret = params.GetArray("children", &children); + if (ret == Result::SUCCESS) { + int32_t childrenLen = children->GetSize(); + for (int32_t i = 0; i < childrenLen; ++i) { + int32_t pChildren = children->Get(i)->GetInt(); + profileNode->children_.value().emplace_back(pChildren); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'children';"; + } + + std::unique_ptr positionTicks; + ret = params.GetArray("positionTicks", &positionTicks); + if (ret == Result::SUCCESS) { + int32_t positionTicksLen = positionTicks->GetSize(); + for (int32_t i = 0; i < positionTicksLen; ++i) { + std::unique_ptr arrayEle = positionTicks->Get(i); + ASSERT(arrayEle != nullptr); + std::unique_ptr tmpPositionTicks = PositionTickInfo::Create(*arrayEle); + if (tmpPositionTicks == nullptr) { + error += "'positionTicks' format invalid;"; + } else { + profileNode->positionTicks_.value().emplace_back(std::move(tmpPositionTicks)); + } + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'positionTicks';"; + } + + std::string deoptReason; + ret = params.GetString("deoptReason", &deoptReason); + if (ret == Result::SUCCESS) { + profileNode->deoptReason_ = std::move(deoptReason); + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'deoptReason';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "ProfileNode::Create " << error; + return nullptr; + } + + return profileNode; +} + +std::unique_ptr ProfileNode::FromCpuProfileNode(const CpuProfileNode &cpuProfileNode) +{ + auto profileNode = std::make_unique(); + profileNode->SetId(cpuProfileNode.id); + profileNode->SetHitCount(cpuProfileNode.hitCount); + + size_t childrenLen = cpuProfileNode.children.size(); + std::vector tmpChildren; + tmpChildren.reserve(childrenLen); + for (size_t i = 0; i < childrenLen; ++i) { + tmpChildren.push_back(cpuProfileNode.children[i]); + } + profileNode->SetChildren(tmpChildren); + profileNode->SetCallFrame(RuntimeCallFrame::FromFrameInfo(cpuProfileNode.codeEntry)); + return profileNode; +} + +std::unique_ptr ProfileNode::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("id", id_); + ASSERT(callFrame_ != nullptr); + result->Add("callFrame", callFrame_->ToJson()); + if (hitCount_) { + result->Add("hitCount", hitCount_.value()); + } + if (children_) { + std::unique_ptr childrens = PtJson::CreateArray(); + size_t len = children_->size(); + for (size_t i = 0; i < len; i++) { + childrens->Push(children_.value()[i]); + } + result->Add("children", childrens); + } + if (positionTicks_) { + std::unique_ptr positionTicks = PtJson::CreateArray(); + size_t len = positionTicks_->size(); + for (size_t i = 0; i < len; i++) { + ASSERT(positionTicks_.value()[i] != nullptr); + positionTicks->Push(positionTicks_.value()[i]->ToJson()); + } + result->Add("positionTicks", positionTicks); + } + + if (deoptReason_) { + result->Add("deoptReason", deoptReason_.value().c_str()); + } + + return result; +} + +std::unique_ptr Profile::Create(const PtJson ¶ms) +{ + std::string error; + auto profile = std::make_unique(); + Result ret; + + std::unique_ptr nodes; + ret = params.GetArray("nodes", &nodes); + if (ret == Result::SUCCESS) { + int32_t nodesLen = nodes->GetSize(); + for (int32_t i = 0; i < nodesLen; ++i) { + std::unique_ptr arrayEle = nodes->Get(i); + ASSERT(arrayEle != nullptr); + std::unique_ptr profileNode = ProfileNode::Create(*arrayEle); + if (profileNode == nullptr) { + error += "'nodes' format invalid;"; + } else { + profile->nodes_.emplace_back(std::move(profileNode)); + } + } + } else { + error += "Unknown 'nodes';"; + } + + int64_t tid; + ret = params.GetInt64("tid", &tid); + if (ret == Result::SUCCESS) { + profile->tid_ = tid; + } else { + error += "Unknown 'tid';"; + } + + int64_t startTime; + ret = params.GetInt64("startTime", &startTime); + if (ret == Result::SUCCESS) { + profile->startTime_ = startTime; + } else { + error += "Unknown 'startTime';"; + } + + int64_t endTime; + ret = params.GetInt64("endTime", &endTime); + if (ret == Result::SUCCESS) { + profile->endTime_ = endTime; + } else { + error += "Unknown 'endTime';"; + } + + std::unique_ptr samples; + ret = params.GetArray("samples", &samples); + if (ret == Result::SUCCESS) { + int32_t samplesLen = samples->GetSize(); + for (int32_t i = 0; i < samplesLen; ++i) { + int32_t pSamples = samples->Get(i)->GetInt(); + profile->samples_.value().emplace_back(pSamples); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'samples';"; + } + + std::unique_ptr timeDeltas; + ret = params.GetArray("timeDeltas", &timeDeltas); + if (ret == Result::SUCCESS) { + int32_t timeDeltasLen = timeDeltas->GetSize(); + for (int32_t i = 0; i < timeDeltasLen; ++i) { + int32_t pTimeDeltas = timeDeltas->Get(i)->GetInt(); + profile->timeDeltas_.value().emplace_back(pTimeDeltas); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'timeDeltas';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "Profile::Create " << error; + return nullptr; + } + + return profile; +} + +std::unique_ptr Profile::FromProfileInfo(const ProfileInfo &profileInfo) +{ + auto profile = std::make_unique(); + profile->SetTid(static_cast(profileInfo.tid)); + profile->SetStartTime(static_cast(profileInfo.startTime)); + profile->SetEndTime(static_cast(profileInfo.stopTime)); + size_t samplesLen = profileInfo.samples.size(); + std::vector tmpSamples; + tmpSamples.reserve(samplesLen); + for (size_t i = 0; i < samplesLen; ++i) { + tmpSamples.push_back(profileInfo.samples[i]); + } + profile->SetSamples(tmpSamples); + + size_t timeDeltasLen = profileInfo.timeDeltas.size(); + std::vector tmpTimeDeltas; + tmpTimeDeltas.reserve(timeDeltasLen); + for (size_t i = 0; i < timeDeltasLen; ++i) { + tmpTimeDeltas.push_back(profileInfo.timeDeltas[i]); + } + profile->SetTimeDeltas(tmpTimeDeltas); + + std::vector> profileNode; + size_t nodesLen = profileInfo.nodeCount; + for (size_t i = 0; i < nodesLen; ++i) { + const auto &cpuProfileNode = profileInfo.nodes[i]; + profileNode.push_back(ProfileNode::FromCpuProfileNode(cpuProfileNode)); + } + profile->SetNodes(std::move(profileNode)); + return profile; +} + +std::unique_ptr Profile::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("tid", tid_); + result->Add("startTime", startTime_); + result->Add("endTime", endTime_); + + std::unique_ptr nodes = PtJson::CreateArray(); + size_t nodesLen = nodes_.size(); + for (size_t i = 0; i < nodesLen; i++) { + ASSERT(nodes_[i] != nullptr); + nodes->Push(nodes_[i]->ToJson()); + } + result->Add("nodes", nodes); + + if (samples_) { + std::unique_ptr samples = PtJson::CreateArray(); + size_t samplesLen = samples_->size(); + for (size_t i = 0; i < samplesLen; i++) { + samples->Push(samples_.value()[i]); + } + result->Add("samples", samples); + } + + if (timeDeltas_) { + std::unique_ptr timeDeltas = PtJson::CreateArray(); + size_t timeDeltasLen = timeDeltas_->size(); + for (size_t i = 0; i < timeDeltasLen; i++) { + timeDeltas->Push(timeDeltas_.value()[i]); + } + result->Add("timeDeltas", timeDeltas); + } + + return result; +} + +std::unique_ptr Coverage::Create(const PtJson ¶ms) +{ + std::string error; + auto coverage = std::make_unique(); + Result ret; + + int32_t startOffset; + ret = params.GetInt("startOffset", &startOffset); + if (ret == Result::SUCCESS) { + coverage->startOffset_ = startOffset; + } else { + error += "Unknown 'startOffset';"; + } + + int32_t endOffset; + ret = params.GetInt("endOffset", &endOffset); + if (ret == Result::SUCCESS) { + coverage->endOffset_ = endOffset; + } else { + error += "Unknown 'endOffset';"; + } + + int32_t count; + ret = params.GetInt("count", &count); + if (ret == Result::SUCCESS) { + coverage->count_ = count; + } else { + error += "Unknown 'count';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "Coverage::Create " << error; + return nullptr; + } + + return coverage; +} + +std::unique_ptr Coverage::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("startOffset", startOffset_); + result->Add("endOffset", endOffset_); + result->Add("count", count_); + + return result; +} + +std::unique_ptr FunctionCoverage::Create(const PtJson ¶ms) +{ + std::string error; + auto functionCoverage = std::make_unique(); + Result ret; + + std::string functionName; + ret = params.GetString("functionName", &functionName); + if (ret == Result::SUCCESS) { + functionCoverage->functionName_ = std::move(functionName); + } else { + error += "Unknown 'functionName';"; + } + + std::unique_ptr ranges; + ret = params.GetArray("ranges", &ranges); + if (ret == Result::SUCCESS) { + int32_t len = ranges->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr arrayEle = ranges->Get(i); + ASSERT(arrayEle != nullptr); + std::unique_ptr pRanges = Coverage::Create(*arrayEle); + if (pRanges == nullptr) { + error += "'ranges' format invalid;"; + } else { + functionCoverage->ranges_.emplace_back(std::move(pRanges)); + } + } + } else { + error += "Unknown 'ranges';"; + } + + bool isBlockCoverage = false; + ret = params.GetBool("isBlockCoverage", &isBlockCoverage); + if (ret == Result::SUCCESS) { + functionCoverage->isBlockCoverage_ = isBlockCoverage; + } else { + error += "Unknown 'isBlockCoverage';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "FunctionCoverage::Create " << error; + return nullptr; + } + + return functionCoverage; +} + +std::unique_ptr FunctionCoverage::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("functionName", functionName_.c_str()); + + std::unique_ptr ranges = PtJson::CreateArray(); + size_t len = ranges_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(ranges_[i] != nullptr); + ranges->Push(ranges_[i]->ToJson()); + } + result->Add("ranges", ranges); + + result->Add("isBlockCoverage", isBlockCoverage_); + + return result; +} + +std::unique_ptr ScriptCoverage::Create(const PtJson ¶ms) +{ + std::string error; + auto scriptCoverage = std::make_unique(); + Result ret; + + std::string scriptId; + ret = params.GetString("scriptId", &scriptId); + if (ret == Result::SUCCESS) { + scriptCoverage->scriptId_ = std::move(scriptId); + } else { + error += "Unknown 'scriptId';"; + } + + std::string url; + ret = params.GetString("url", &url); + if (ret == Result::SUCCESS) { + scriptCoverage->url_ = std::move(url); + } else { + error += "Unknown 'url';"; + } + + std::unique_ptr functions; + ret = params.GetArray("functions", &functions); + if (ret == Result::SUCCESS) { + int32_t len = functions->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr arrayEle = functions->Get(i); + ASSERT(arrayEle != nullptr); + std::unique_ptr pFunctions = FunctionCoverage::Create(*arrayEle); + if (pFunctions == nullptr) { + error += "'functions' format invalid;"; + } else { + scriptCoverage->functions_.emplace_back(std::move(pFunctions)); + } + } + } else { + error += "Unknown 'functions';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "ScriptCoverage::Create " << error; + return nullptr; + } + + return scriptCoverage; +} + +std::unique_ptr ScriptCoverage::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("scriptId", scriptId_.c_str()); + result->Add("url", url_.c_str()); + + std::unique_ptr functions = PtJson::CreateArray(); + size_t len = functions_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(functions_[i] != nullptr); + functions->Push(functions_[i]->ToJson()); + } + result->Add("functions", functions); + + return result; +} + +std::unique_ptr TypeObject::Create(const PtJson ¶ms) +{ + std::string error; + auto typeObject = std::make_unique(); + Result ret; + + std::string name; + ret = params.GetString("name", &name); + if (ret == Result::SUCCESS) { + typeObject->name_ = std::move(name); + } else { + error += "Unknown 'name';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "TypeObject::Create " << error; + return nullptr; + } + + return typeObject; +} + +std::unique_ptr TypeObject::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("name", name_.c_str()); + + return result; +} + +std::unique_ptr TypeProfileEntry::Create(const PtJson ¶ms) +{ + std::string error; + auto typeProfileEntry = std::make_unique(); + Result ret; + + int32_t offset; + ret = params.GetInt("offset", &offset); + if (ret == Result::SUCCESS) { + typeProfileEntry->offset_ = offset; + } else { + error += "Unknown 'offset';"; + } + + std::unique_ptr types; + ret = params.GetArray("types", &types); + if (ret == Result::SUCCESS) { + int32_t len = types->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr arrayEle = types->Get(i); + ASSERT(arrayEle != nullptr); + std::unique_ptr pTypes = TypeObject::Create(*arrayEle); + if (pTypes == nullptr) { + error += "'types' format invalid;"; + } else { + typeProfileEntry->types_.emplace_back(std::move(pTypes)); + } + } + } else { + error += "Unknown 'types';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "TypeProfileEntry::Create " << error; + return nullptr; + } + + return typeProfileEntry; +} + +std::unique_ptr TypeProfileEntry::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("offset", offset_); + + std::unique_ptr types = PtJson::CreateArray(); + size_t len = types_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(types_[i] != nullptr); + types->Push(types_[i]->ToJson()); + } + result->Add("types", types); + + return result; +} + +std::unique_ptr ScriptTypeProfile::Create(const PtJson ¶ms) +{ + std::string error; + auto scriptTypeProfile = std::make_unique(); + Result ret; + + std::string scriptId; + ret = params.GetString("scriptId", &scriptId); + if (ret == Result::SUCCESS) { + scriptTypeProfile->scriptId_ = std::move(scriptId); + } else { + error += "Unknown 'scriptId';"; + } + + std::string url; + ret = params.GetString("url", &url); + if (ret == Result::SUCCESS) { + scriptTypeProfile->url_ = std::move(url); + } else { + error += "Unknown 'url';"; + } + + std::unique_ptr entries; + ret = params.GetArray("entries", &entries); + if (ret == Result::SUCCESS) { + int32_t len = entries->GetSize(); + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr arrayEle = entries->Get(i); + ASSERT(arrayEle != nullptr); + std::unique_ptr pEntries = TypeProfileEntry::Create(*arrayEle); + if (pEntries == nullptr) { + error += "'entries' format invalid;"; + } else { + scriptTypeProfile->entries_.emplace_back(std::move(pEntries)); + } + } + } else { + error += "Unknown 'entries';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "ScriptTypeProfile::Create " << error; + return nullptr; + } + + return scriptTypeProfile; +} + +std::unique_ptr ScriptTypeProfile::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + result->Add("scriptId", scriptId_.c_str()); + result->Add("url", url_.c_str()); + + std::unique_ptr entries = PtJson::CreateArray(); + size_t len = entries_.size(); + for (size_t i = 0; i < len; i++) { + ASSERT(entries_[i] != nullptr); + entries->Push(entries_[i]->ToJson()); + } + result->Add("entries", entries); + + return result; +} + +std::unique_ptr TraceConfig::Create(const PtJson ¶ms) +{ + std::string error; + auto traceConfig = std::make_unique(); + Result ret; + + std::string recordMode; + ret = params.GetString("recordMode", &recordMode); + if (ret == Result::SUCCESS) { + if (TraceConfig::RecordModeValues::Valid(recordMode)) { + traceConfig->recordMode_ = std::move(recordMode); + } else { + error += "'recordMode' is invalid;"; + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'recordMode';"; + } + + bool enableSampling = false; + ret = params.GetBool("enableSampling", &enableSampling); + if (ret == Result::SUCCESS) { + traceConfig->enableSampling_ = enableSampling; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'enableSampling';"; + } + + bool enableSystrace = false; + ret = params.GetBool("enableSystrace", &enableSystrace); + if (ret == Result::SUCCESS) { + traceConfig->enableSystrace_ = enableSystrace; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'enableSystrace';"; + } + + bool enableArgumentFilter = false; + ret = params.GetBool("enableArgumentFilter", &enableArgumentFilter); + if (ret == Result::SUCCESS) { + traceConfig->enableArgumentFilter_ = enableArgumentFilter; + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'enableArgumentFilter';"; + } + + std::unique_ptr includedCategories; + ret = params.GetArray("includedCategories", &includedCategories); + if (ret == Result::SUCCESS) { + int32_t includedCategoriesLen = includedCategories->GetSize(); + for (int32_t i = 0; i < includedCategoriesLen; ++i) { + std::string pIncludedCategories = includedCategories->Get(i)->GetString(); + traceConfig->includedCategories_.value().emplace_back(pIncludedCategories); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'includedCategories';"; + } + + std::unique_ptr excludedCategories; + ret = params.GetArray("excludedCategories", &excludedCategories); + if (ret == Result::SUCCESS) { + int32_t excludedCategoriesLen = excludedCategories->GetSize(); + for (int32_t i = 0; i < excludedCategoriesLen; ++i) { + std::string pExcludedCategories = excludedCategories->Get(i)->GetString(); + traceConfig->excludedCategories_.value().emplace_back(pExcludedCategories); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'excludedCategories';"; + } + + std::unique_ptr syntheticDelays; + ret = params.GetArray("syntheticDelays", &syntheticDelays); + if (ret == Result::SUCCESS) { + int32_t syntheticDelaysLen = includedCategories->GetSize(); + for (int32_t i = 0; i < syntheticDelaysLen; ++i) { + std::string pSyntheticDelays = syntheticDelays->Get(i)->GetString(); + traceConfig->syntheticDelays_.value().emplace_back(pSyntheticDelays); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'syntheticDelays';"; + } + + std::unique_ptr memoryDumpConfig; + ret = params.GetObject("memoryDumpConfig", &memoryDumpConfig); + if (ret == Result::SUCCESS) { + std::unique_ptr tmpMemory = std::move(memoryDumpConfig); + if (tmpMemory == nullptr) { + error += "'memoryDumpConfig' format error;"; + } else { + traceConfig->memoryDumpConfig_ = std::move(tmpMemory); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'memoryDumpConfig';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "TraceConfig::Create " << error; + return nullptr; + } + + return traceConfig; +} + +std::unique_ptr TraceConfig::ToJson() const +{ + std::unique_ptr result = PtJson::CreateObject(); + + if (recordMode_) { + result->Add("recordMode", recordMode_.value().c_str()); + } + + if (enableSampling_) { + result->Add("enableSampling", enableSampling_.value()); + } + + if (enableSystrace_) { + result->Add("enableSystrace", enableSystrace_.value()); + } + + if (enableArgumentFilter_) { + result->Add("enableArgumentFilter", enableArgumentFilter_.value()); + } + + if (includedCategories_) { + std::unique_ptr includedCategories = PtJson::CreateArray(); + size_t includedCategoriesLen = includedCategories_->size(); + for (size_t i = 0; i < includedCategoriesLen; i++) { + includedCategories->Push(includedCategories_.value()[i].c_str()); + } + result->Add("includedCategories", includedCategories); + } + + if (excludedCategories_) { + std::unique_ptr excludedCategories = PtJson::CreateArray(); + size_t excludedCategoriesLen = excludedCategories_->size(); + for (size_t i = 0; i < excludedCategoriesLen; i++) { + excludedCategories->Push(excludedCategories_.value()[i].c_str()); + } + result->Add("excludedCategories", excludedCategories); + } + + if (syntheticDelays_) { + std::unique_ptr syntheticDelays = PtJson::CreateArray(); + size_t syntheticDelaysLen = syntheticDelays_->size(); + for (size_t i = 0; i < syntheticDelaysLen; i++) { + syntheticDelays->Push(syntheticDelays_.value()[i].c_str()); + } + result->Add("syntheticDelays", syntheticDelays); + } + + if (memoryDumpConfig_) { + result->Add("functionLocation", memoryDumpConfig_.value()); + } + + return result; +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/base/pt_types.h b/tooling/base/pt_types.h new file mode 100644 index 00000000..22b51ee3 --- /dev/null +++ b/tooling/base/pt_types.h @@ -0,0 +1,2508 @@ +/* + * 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_BASE_PT_TYPES_H +#define ECMASCRIPT_TOOLING_BASE_PT_TYPES_H + +#include +#include + +#include "ecmascript/dfx/cpu_profiler/samples_record.h" +#include "ecmascript/tooling/backend/debugger_api.h" +#include "ecmascript/tooling/base/pt_json.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +// ========== Base types begin +class PtBaseTypes { +public: + PtBaseTypes() = default; + virtual ~PtBaseTypes() = default; + virtual std::unique_ptr ToJson() const = 0; + +private: + NO_COPY_SEMANTIC(PtBaseTypes); + NO_MOVE_SEMANTIC(PtBaseTypes); + + friend class ProtocolHandler; + friend class DebuggerImpl; +}; + +// ========== Debugger types begin +// Debugger.BreakpointId +using BreakpointId = std::string; +struct BreakpointDetails { + static BreakpointId ToString(const BreakpointDetails &metaData) + { + return "id:" + std::to_string(metaData.line_) + ":" + std::to_string(metaData.column_) + ":" + + metaData.url_; + } + + static bool ParseBreakpointId(const BreakpointId &id, BreakpointDetails *metaData) + { + auto lineStart = id.find(':'); + if (lineStart == std::string::npos) { + return false; + } + auto columnStart = id.find(':', lineStart + 1); + if (columnStart == std::string::npos) { + return false; + } + auto urlStart = id.find(':', columnStart + 1); + if (urlStart == std::string::npos) { + return false; + } + std::string lineStr = id.substr(lineStart + 1, columnStart - lineStart - 1); + std::string columnStr = id.substr(columnStart + 1, urlStart - columnStart - 1); + std::string url = id.substr(urlStart + 1); + metaData->line_ = std::stoi(lineStr); + metaData->column_ = std::stoi(columnStr); + metaData->url_ = url; + + return true; + } + + int32_t line_ {0}; + int32_t column_ {0}; + std::string url_ {}; +}; + +// Debugger.CallFrameId +using CallFrameId = int32_t; + +// ========== Runtime types begin +// Runtime.ScriptId +using ScriptId = int32_t; + +// Runtime.RemoteObjectId + +using RemoteObjectId = int32_t; + +// Runtime.ExecutionContextId +using ExecutionContextId = int32_t; + +// Runtime.UnserializableValue +using UnserializableValue = std::string; + +// Runtime.UniqueDebuggerId +using UniqueDebuggerId = int32_t; + +// Runtime.RemoteObject +class RemoteObject : public PtBaseTypes { +public: + RemoteObject() = default; + ~RemoteObject() override = default; + + static std::unique_ptr FromTagged(const EcmaVM *ecmaVm, Local tagged); + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + /* + * @see {#ObjectType} + */ + const std::string &GetType() const + { + return type_; + } + + RemoteObject &SetType(const std::string &type) + { + type_ = type; + return *this; + } + /* + * @see {#ObjectSubType} + */ + const std::string &GetSubType() const + { + ASSERT(HasSubType()); + return subType_.value(); + } + + RemoteObject &SetSubType(const std::string &type) + { + subType_ = type; + return *this; + } + + bool HasSubType() const + { + return subType_.has_value(); + } + + const std::string &GetClassName() const + { + ASSERT(HasClassName()); + return className_.value(); + } + + RemoteObject &SetClassName(const std::string &className) + { + className_ = className; + return *this; + } + + bool HasClassName() const + { + return className_.has_value(); + } + + Local GetValue() const + { + return value_.value_or(Local()); + } + + RemoteObject &SetValue(Local value) + { + value_ = value; + return *this; + } + + bool HasValue() const + { + return value_.has_value(); + } + + const UnserializableValue &GetUnserializableValue() const + { + ASSERT(HasUnserializableValue()); + return unserializableValue_.value(); + } + + RemoteObject &SetUnserializableValue(const UnserializableValue &unserializableValue) + { + unserializableValue_ = unserializableValue; + return *this; + } + + bool HasUnserializableValue() const + { + return unserializableValue_.has_value(); + } + + const std::string &GetDescription() const + { + ASSERT(HasDescription()); + return description_.value(); + } + + RemoteObject &SetDescription(const std::string &description) + { + description_ = description; + return *this; + } + + bool HasDescription() const + { + return description_.has_value(); + } + + RemoteObjectId GetObjectId() const + { + return objectId_.value_or(0); + } + + RemoteObject &SetObjectId(RemoteObjectId objectId) + { + objectId_ = objectId; + return *this; + } + + bool HasObjectId() const + { + return objectId_.has_value(); + } + + struct TypeName { + static const std::string Object; // NOLINT (readability-identifier-naming) + static const std::string Function; // NOLINT (readability-identifier-naming) + static const std::string Undefined; // NOLINT (readability-identifier-naming) + static const std::string String; // NOLINT (readability-identifier-naming) + static const std::string Number; // NOLINT (readability-identifier-naming) + static const std::string Boolean; // NOLINT (readability-identifier-naming) + static const std::string Symbol; // NOLINT (readability-identifier-naming) + static const std::string Bigint; // NOLINT (readability-identifier-naming) + static const std::string Wasm; // NOLINT (readability-identifier-naming) + static bool Valid(const std::string &type) + { + return type == Object || type == Function || type == Undefined || type == String || type == Number || + type == Boolean || type == Symbol || type == Bigint || type == Wasm; + } + }; + + struct SubTypeName { + static const std::string Array; // NOLINT (readability-identifier-naming) + static const std::string Null; // NOLINT (readability-identifier-naming) + static const std::string Node; // NOLINT (readability-identifier-naming) + static const std::string Regexp; // NOLINT (readability-identifier-naming) + static const std::string Date; // NOLINT (readability-identifier-naming) + static const std::string Map; // NOLINT (readability-identifier-naming) + static const std::string Set; // NOLINT (readability-identifier-naming) + static const std::string Weakmap; // NOLINT (readability-identifier-naming) + static const std::string Weakset; // NOLINT (readability-identifier-naming) + static const std::string Iterator; // NOLINT (readability-identifier-naming) + static const std::string Generator; // NOLINT (readability-identifier-naming) + static const std::string Error; // NOLINT (readability-identifier-naming) + static const std::string Proxy; // NOLINT (readability-identifier-naming) + static const std::string Promise; // NOLINT (readability-identifier-naming) + static const std::string Typedarray; // NOLINT (readability-identifier-naming) + static const std::string Arraybuffer; // NOLINT (readability-identifier-naming) + static const std::string Dataview; // NOLINT (readability-identifier-naming) + static const std::string I32; // NOLINT (readability-identifier-naming) + static const std::string I64; // NOLINT (readability-identifier-naming) + static const std::string F32; // NOLINT (readability-identifier-naming) + static const std::string F64; // NOLINT (readability-identifier-naming) + static const std::string V128; // NOLINT (readability-identifier-naming) + static const std::string Externref; // NOLINT (readability-identifier-naming) + static bool Valid(const std::string &type) + { + return type == Array || type == Null || type == Node || type == Regexp || type == Map || type == Set || + type == Weakmap || type == Iterator || type == Generator || type == Error || type == Proxy || + type == Promise || type == Typedarray || type == Arraybuffer || type == Dataview || type == I32 || + type == I64 || type == F32 || type == F64 || type == V128 || type == Externref; + } + }; + struct ClassName { + static const std::string Object; // NOLINT (readability-identifier-naming) + static const std::string Function; // NOLINT (readability-identifier-naming) + static const std::string Array; // NOLINT (readability-identifier-naming) + static const std::string Regexp; // NOLINT (readability-identifier-naming) + static const std::string Date; // NOLINT (readability-identifier-naming) + static const std::string Map; // NOLINT (readability-identifier-naming) + static const std::string Set; // NOLINT (readability-identifier-naming) + static const std::string Weakmap; // NOLINT (readability-identifier-naming) + static const std::string Weakset; // NOLINT (readability-identifier-naming) + static const std::string ArrayIterator; // NOLINT (readability-identifier-naming) + static const std::string StringIterator; // NOLINT (readability-identifier-naming) + static const std::string SetIterator; // NOLINT (readability-identifier-naming) + static const std::string MapIterator; // NOLINT (readability-identifier-naming) + static const std::string Iterator; // NOLINT (readability-identifier-naming) + static const std::string Error; // NOLINT (readability-identifier-naming) + static const std::string Proxy; // NOLINT (readability-identifier-naming) + static const std::string Promise; // NOLINT (readability-identifier-naming) + static const std::string Typedarray; // NOLINT (readability-identifier-naming) + static const std::string Arraybuffer; // NOLINT (readability-identifier-naming) + static const std::string Global; // NOLINT (readability-identifier-naming) + static const std::string Generator; // NOLINT (readability-identifier-naming) + static bool Valid(const std::string &type) + { + return type == Object || type == Array || type == Regexp || type == Date || type == Map || type == Set || + type == Weakmap || type == Weakset || type == ArrayIterator || type == StringIterator || + type == Error || type == SetIterator || type == MapIterator || type == Iterator || type == Proxy || + type == Promise || type == Typedarray || type == Arraybuffer || type == Function; + } + }; + static const std::string ObjectDescription; // NOLINT (readability-identifier-naming) + static const std::string GlobalDescription; // NOLINT (readability-identifier-naming) + static const std::string ProxyDescription; // NOLINT (readability-identifier-naming) + static const std::string PromiseDescription; // NOLINT (readability-identifier-naming) + static const std::string ArrayIteratorDescription; // NOLINT (readability-identifier-naming) + static const std::string StringIteratorDescription; // NOLINT (readability-identifier-naming) + static const std::string SetIteratorDescription; // NOLINT (readability-identifier-naming) + static const std::string MapIteratorDescription; // NOLINT (readability-identifier-naming) + static const std::string WeakRefDescription; // NOLINT (readability-identifier-naming) + static const std::string WeakMapDescription; // NOLINT (readability-identifier-naming) + static const std::string WeakSetDescription; // NOLINT (readability-identifier-naming) + static const std::string JSPrimitiveRefDescription; // NOLINT (readability-identifier-naming) + static const std::string JSPrimitiveNumberDescription; // NOLINT (readability-identifier-naming) + static const std::string JSPrimitiveBooleanDescription; // NOLINT (readability-identifier-naming) + static const std::string JSPrimitiveStringDescription; // NOLINT (readability-identifier-naming) + static const std::string JSPrimitiveSymbolDescription; // NOLINT (readability-identifier-naming) + static const std::string JSIntlDescription; // NOLINT (readability-identifier-naming) + static const std::string DateTimeFormatDescription; // NOLINT (readability-identifier-naming) + static const std::string NumberFormatDescription; // NOLINT (readability-identifier-naming) + static const std::string CollatorDescription; // NOLINT (readability-identifier-naming) + static const std::string PluralRulesDescription; // NOLINT (readability-identifier-naming) + static const std::string JSLocaleDescription; // NOLINT (readability-identifier-naming) + static const std::string JSListFormatDescription; // NOLINT (readability-identifier-naming) + static const std::string JSRelativeTimeFormatDescription; // NOLINT (readability-identifier-naming) + +private: + NO_COPY_SEMANTIC(RemoteObject); + NO_MOVE_SEMANTIC(RemoteObject); + + std::string type_ {}; + std::optional subType_ {}; + std::optional className_ {}; + std::optional> value_ {}; + std::optional unserializableValue_ {}; + std::optional description_ {}; + std::optional objectId_ {}; +}; + +class PrimitiveRemoteObject final : public RemoteObject { +public: + PrimitiveRemoteObject(const EcmaVM *ecmaVm, Local tagged); + ~PrimitiveRemoteObject() override = default; +}; + +class StringRemoteObject final : public RemoteObject { +public: + StringRemoteObject(const EcmaVM *ecmaVm, Local tagged); + ~StringRemoteObject() override = default; +}; + +class SymbolRemoteObject final : public RemoteObject { +public: + SymbolRemoteObject(const EcmaVM *ecmaVm, Local tagged); + ~SymbolRemoteObject() override = default; + +private: + std::string DescriptionForSymbol(const EcmaVM *ecmaVm, Local tagged) const; +}; + +class FunctionRemoteObject final : public RemoteObject { +public: + FunctionRemoteObject(const EcmaVM *ecmaVm, Local tagged); + ~FunctionRemoteObject() override = default; + +private: + std::string DescriptionForFunction(const EcmaVM *ecmaVm, Local tagged) const; +}; + +class GeneratorFunctionRemoteObject final : public RemoteObject { +public: + GeneratorFunctionRemoteObject(const EcmaVM *ecmaVm, Local tagged); + ~GeneratorFunctionRemoteObject() override = default; + +private: + std::string DescriptionForGeneratorFunction(const EcmaVM *ecmaVm, Local tagged) const; +}; + +class ObjectRemoteObject final : public RemoteObject { +public: + ObjectRemoteObject(const EcmaVM *ecmaVm, Local tagged, const std::string &classname); + ObjectRemoteObject(const EcmaVM *ecmaVm, Local tagged, const std::string &classname, + const std::string &subtype); + ~ObjectRemoteObject() override = default; + static std::string DescriptionForObject(const EcmaVM *ecmaVm, Local tagged); + +private: + enum NumberSize : uint8_t { BYTES_OF_16BITS = 2, BYTES_OF_32BITS = 4, BYTES_OF_64BITS = 8 }; + static std::string DescriptionForArray(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForRegexp(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForDate(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForMap(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForSet(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForError(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForArrayIterator(); + static std::string DescriptionForMapIterator(); + static std::string DescriptionForSetIterator(); + static std::string DescriptionForArrayBuffer(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForSharedArrayBuffer(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForUint8Array(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForInt8Array(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForInt16Array(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForInt32Array(const EcmaVM *ecmaVm, Local tagged); + static std::string DescriptionForPrimitiveNumber(const EcmaVM *ecmaVm, const Local &tagged); + static std::string DescriptionForPrimitiveString(const EcmaVM *ecmaVm, const Local &tagged); + static std::string DescriptionForPrimitiveBoolean(const EcmaVM *ecmaVm, const Local &tagged); + static std::string DescriptionForGeneratorObject(const EcmaVM *ecmaVm, const Local &tagged); + static std::string DescriptionForWeakRef(); + static std::string DescriptionForDateTimeFormat(); + static std::string DescriptionForNumberFormat(); + static std::string DescriptionForCollator(); + static std::string DescriptionForPluralRules(); + static std::string DescriptionForJSLocale(); + static std::string DescriptionForJSRelativeTimeFormat(); + static std::string DescriptionForJSListFormat(); +}; + +// Runtime.ExceptionDetails +class ExceptionDetails final : public PtBaseTypes { +public: + ExceptionDetails() = default; + ~ExceptionDetails() override = default; + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + int32_t GetExceptionId() const + { + return exceptionId_; + } + + ExceptionDetails &SetExceptionId(int32_t exceptionId) + { + exceptionId_ = exceptionId; + return *this; + } + + const std::string &GetText() const + { + return text_; + } + + ExceptionDetails &SetText(const std::string &text) + { + text_ = text; + return *this; + } + + int32_t GetLine() const + { + return lineNumber_; + } + + ExceptionDetails &SetLine(int32_t lineNumber) + { + lineNumber_ = lineNumber; + return *this; + } + + int32_t GetColumn() const + { + return columnNumber_; + } + + ExceptionDetails &SetColumn(int32_t columnNumber) + { + columnNumber_ = columnNumber; + return *this; + } + + ScriptId GetScriptId() const + { + return scriptId_.value_or(0); + } + + ExceptionDetails &SetScriptId(ScriptId scriptId) + { + scriptId_ = scriptId; + return *this; + } + + bool HasScriptId() const + { + return scriptId_.has_value(); + } + + const std::string &GetUrl() const + { + ASSERT(HasUrl()); + return url_.value(); + } + + ExceptionDetails &SetUrl(const std::string &url) + { + url_ = url; + return *this; + } + + bool HasUrl() const + { + return url_.has_value(); + } + + RemoteObject *GetException() const + { + if (exception_) { + return exception_->get(); + } + return nullptr; + } + + ExceptionDetails &SetException(std::unique_ptr exception) + { + exception_ = std::move(exception); + return *this; + } + + bool HasException() const + { + return exception_.has_value(); + } + + ExecutionContextId GetExecutionContextId() const + { + return executionContextId_.value_or(-1); + } + + ExceptionDetails &SetExecutionContextId(ExecutionContextId executionContextId) + { + executionContextId_ = executionContextId; + return *this; + } + + bool HasExecutionContextId() const + { + return executionContextId_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ExceptionDetails); + NO_MOVE_SEMANTIC(ExceptionDetails); + + int32_t exceptionId_ {0}; + std::string text_ {}; + int32_t lineNumber_ {0}; + int32_t columnNumber_ {0}; + std::optional scriptId_ {}; + std::optional url_ {}; + std::optional> exception_ {}; + std::optional executionContextId_ {0}; +}; + +// Runtime.InternalPropertyDescriptor +class InternalPropertyDescriptor final : public PtBaseTypes { +public: + InternalPropertyDescriptor() = default; + ~InternalPropertyDescriptor() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + std::string GetName() const + { + return name_; + } + + InternalPropertyDescriptor &SetName(const std::string &name) + { + name_ = name; + return *this; + } + + RemoteObject *GetValue() const + { + if (value_) { + return value_->get(); + } + return nullptr; + } + + InternalPropertyDescriptor &SetValue(std::unique_ptr value) + { + value_ = std::move(value); + return *this; + } + + bool HasValue() const + { + return value_.has_value(); + } + +private: + NO_COPY_SEMANTIC(InternalPropertyDescriptor); + NO_MOVE_SEMANTIC(InternalPropertyDescriptor); + + std::string name_ {}; + std::optional> value_ {}; +}; + +// Runtime.PrivatePropertyDescriptor +class PrivatePropertyDescriptor final : public PtBaseTypes { +public: + PrivatePropertyDescriptor() = default; + ~PrivatePropertyDescriptor() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + std::string GetName() const + { + return name_; + } + + PrivatePropertyDescriptor &SetName(const std::string &name) + { + name_ = name; + return *this; + } + + RemoteObject *GetValue() const + { + if (value_) { + return value_->get(); + } + return nullptr; + } + + PrivatePropertyDescriptor &SetValue(std::unique_ptr value) + { + value_ = std::move(value); + return *this; + } + + bool HasValue() const + { + return value_.has_value(); + } + + RemoteObject *GetGet() const + { + if (get_) { + return get_->get(); + } + return nullptr; + } + + PrivatePropertyDescriptor &SetGet(std::unique_ptr get) + { + get_ = std::move(get); + return *this; + } + + bool HasGet() const + { + return get_.has_value(); + } + + RemoteObject *GetSet() const + { + if (set_) { + return set_->get(); + } + return nullptr; + } + + PrivatePropertyDescriptor &SetSet(std::unique_ptr set) + { + set_ = std::move(set); + return *this; + } + + bool HasSet() const + { + return set_.has_value(); + } + +private: + NO_COPY_SEMANTIC(PrivatePropertyDescriptor); + NO_MOVE_SEMANTIC(PrivatePropertyDescriptor); + + std::string name_ {}; + std::optional> value_ {}; + std::optional> get_ {}; + std::optional> set_ {}; +}; + +// Runtime.PropertyDescriptor +class PropertyDescriptor final : public PtBaseTypes { +public: + PropertyDescriptor() = default; + ~PropertyDescriptor() override = default; + + static std::unique_ptr FromProperty(const EcmaVM *ecmaVm, Local name, + const PropertyAttribute &property); + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + std::string GetName() const + { + return name_; + } + + PropertyDescriptor &SetName(const std::string &name) + { + name_ = name; + return *this; + } + + RemoteObject *GetValue() const + { + if (value_) { + return value_->get(); + } + return nullptr; + } + + PropertyDescriptor &SetValue(std::unique_ptr value) + { + value_ = std::move(value); + return *this; + } + + bool HasValue() const + { + return value_.has_value(); + } + + bool GetWritable() const + { + return writable_.value_or(false); + } + + PropertyDescriptor &SetWritable(bool writable) + { + writable_ = writable; + return *this; + } + + bool HasWritable() const + { + return writable_.has_value(); + } + + RemoteObject *GetGet() const + { + if (get_) { + return get_->get(); + } + return nullptr; + } + + PropertyDescriptor &SetGet(std::unique_ptr get) + { + get_ = std::move(get); + return *this; + } + + bool HasGet() const + { + return get_.has_value(); + } + + RemoteObject *GetSet() const + { + if (set_) { + return set_->get(); + } + return nullptr; + } + + PropertyDescriptor &SetSet(std::unique_ptr set) + { + set_ = std::move(set); + return *this; + } + + bool HasSet() const + { + return set_.has_value(); + } + + bool GetConfigurable() const + { + return configurable_; + } + + PropertyDescriptor &SetConfigurable(bool configurable) + { + configurable_ = configurable; + return *this; + } + + bool GetEnumerable() const + { + return enumerable_; + } + + PropertyDescriptor &SetEnumerable(bool enumerable) + { + enumerable_ = enumerable; + return *this; + } + + bool GetWasThrown() const + { + return wasThrown_.value_or(false); + } + + PropertyDescriptor &SetWasThrown(bool wasThrown) + { + wasThrown_ = wasThrown; + return *this; + } + + bool HasWasThrown() const + { + return wasThrown_.has_value(); + } + + bool GetIsOwn() const + { + return isOwn_.value_or(false); + } + + PropertyDescriptor &SetIsOwn(bool isOwn) + { + isOwn_ = isOwn; + return *this; + } + + bool HasIsOwn() const + { + return isOwn_.has_value(); + } + + RemoteObject *GetSymbol() const + { + if (symbol_) { + return symbol_->get(); + } + return nullptr; + } + + PropertyDescriptor &SetSymbol(std::unique_ptr symbol) + { + symbol_ = std::move(symbol); + return *this; + } + + bool HasSymbol() const + { + return symbol_.has_value(); + } + +private: + NO_COPY_SEMANTIC(PropertyDescriptor); + NO_MOVE_SEMANTIC(PropertyDescriptor); + + std::string name_ {}; + std::optional> value_ {}; + std::optional writable_ {}; + std::optional> get_ {}; + std::optional> set_ {}; + bool configurable_ {false}; + bool enumerable_ {false}; + std::optional wasThrown_ {}; + std::optional isOwn_ {}; + std::optional> symbol_ {}; +}; + +// Runtime.CallArgument +class CallArgument final : public PtBaseTypes { +public: + CallArgument() = default; + ~CallArgument() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + Local GetValue() const + { + return value_.value_or(Local()); + } + + CallArgument &SetValue(Local value) + { + value_ = value; + return *this; + } + + bool HasValue() const + { + return value_.has_value(); + } + + const UnserializableValue &GetUnserializableValue() const + { + ASSERT(HasUnserializableValue()); + return unserializableValue_.value(); + } + + CallArgument &SetUnserializableValue(const UnserializableValue &unserializableValue) + { + unserializableValue_ = unserializableValue; + return *this; + } + + bool HasUnserializableValue() const + { + return unserializableValue_.has_value(); + } + + RemoteObjectId GetObjectId() const + { + return objectId_.value_or(0); + } + + CallArgument &SetObjectId(RemoteObjectId objectId) + { + objectId_ = objectId; + return *this; + } + + bool HasObjectId() const + { + return objectId_.has_value(); + } + +private: + NO_COPY_SEMANTIC(CallArgument); + NO_MOVE_SEMANTIC(CallArgument); + + std::optional> value_ {}; + std::optional unserializableValue_ {}; + std::optional objectId_ {}; +}; + +// ========== Debugger types begin +// Debugger.ScriptLanguage +struct ScriptLanguage { + static bool Valid(const std::string &language) + { + return language == JavaScript() || language == WebAssembly(); + } + static std::string JavaScript() + { + return "JavaScript"; + } + static std::string WebAssembly() + { + return "WebAssembly"; + } +}; + +// Debugger.Location +class Location : public PtBaseTypes { +public: + Location() = default; + ~Location() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + ScriptId GetScriptId() const + { + return scriptId_; + } + + Location &SetScriptId(ScriptId scriptId) + { + scriptId_ = scriptId; + return *this; + } + + int32_t GetLine() const + { + return lineNumber_; + } + + Location &SetLine(int32_t line) + { + lineNumber_ = line; + return *this; + } + + int32_t GetColumn() const + { + return columnNumber_.value_or(-1); + } + + Location &SetColumn(int32_t column) + { + columnNumber_ = column; + return *this; + } + + bool HasColumn() const + { + return columnNumber_.has_value(); + } + +private: + NO_COPY_SEMANTIC(Location); + NO_MOVE_SEMANTIC(Location); + + ScriptId scriptId_ {0}; + int32_t lineNumber_ {0}; + std::optional columnNumber_ {}; +}; + +// Debugger.ScriptPosition +class ScriptPosition : public PtBaseTypes { +public: + ScriptPosition() = default; + ~ScriptPosition() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + int32_t GetLine() const + { + return lineNumber_; + } + + ScriptPosition &SetLine(int32_t line) + { + lineNumber_ = line; + return *this; + } + + int32_t GetColumn() const + { + return columnNumber_; + } + + ScriptPosition &SetColumn(int32_t column) + { + columnNumber_ = column; + return *this; + } + +private: + NO_COPY_SEMANTIC(ScriptPosition); + NO_MOVE_SEMANTIC(ScriptPosition); + + int32_t lineNumber_ {0}; + int32_t columnNumber_ {0}; +}; + +// Debugger.SearchMatch +class SearchMatch : public PtBaseTypes { +public: + SearchMatch() = default; + ~SearchMatch() override = default; + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + +private: + NO_COPY_SEMANTIC(SearchMatch); + NO_MOVE_SEMANTIC(SearchMatch); + + int32_t lineNumber_ {0}; + std::string lineContent_ {}; +}; + +// Debugger.LocationRange +class LocationRange : public PtBaseTypes { +public: + LocationRange() = default; + ~LocationRange() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + ScriptId GetScriptId() const + { + return scriptId_; + } + + LocationRange &SetScriptId(ScriptId scriptId) + { + scriptId_ = scriptId; + return *this; + } + + ScriptPosition *GetStart() const + { + return start_.get(); + } + + LocationRange &SetStart(std::unique_ptr start) + { + start_ = std::move(start); + return *this; + } + + ScriptPosition *GetEnd() const + { + return end_.get(); + } + + LocationRange &SetEnd(std::unique_ptr end) + { + end_ = std::move(end); + return *this; + } + +private: + NO_COPY_SEMANTIC(LocationRange); + NO_MOVE_SEMANTIC(LocationRange); + + ScriptId scriptId_ {0}; + std::unique_ptr start_ {nullptr}; + std::unique_ptr end_ {nullptr}; +}; + +// Debugger.BreakLocation +class BreakLocation final : public PtBaseTypes { +public: + BreakLocation() = default; + ~BreakLocation() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + ScriptId GetScriptId() const + { + return scriptId_; + } + + BreakLocation &SetScriptId(ScriptId scriptId) + { + scriptId_ = scriptId; + return *this; + } + + int32_t GetLine() const + { + return lineNumber_; + } + + BreakLocation &SetLine(int32_t lineNumber) + { + lineNumber_ = lineNumber; + return *this; + } + + int32_t GetColumn() const + { + return columnNumber_.value_or(-1); + } + + BreakLocation &SetColumn(int32_t columnNumber) + { + columnNumber_ = columnNumber; + return *this; + } + + bool HasColumn() const + { + return columnNumber_.has_value(); + } + + /* + * @see {#BreakType} + */ + const std::string &GetType() const + { + ASSERT(HasType()); + return type_.value(); + } + + BreakLocation &SetType(const std::string &type) + { + type_ = type; + return *this; + } + + bool HasType() const + { + return type_.has_value(); + } + + struct Type { + static bool Valid(const std::string &type) + { + return type == DebuggerStatement() || type == Call() || type == Return(); + } + static std::string DebuggerStatement() + { + return "debuggerStatement"; + } + static std::string Call() + { + return "call"; + } + static std::string Return() + { + return "return"; + } + }; + +private: + NO_COPY_SEMANTIC(BreakLocation); + NO_MOVE_SEMANTIC(BreakLocation); + + ScriptId scriptId_ {0}; + int32_t lineNumber_ {0}; + std::optional columnNumber_ {}; + std::optional type_ {}; +}; +using BreakType = BreakLocation::Type; + +enum class ScopeType : uint8_t { + GLOBAL, + LOCAL, + WITH, + CLOSURE, + CATCH, + BLOCK, + SCRIPT, + EVAL, + MODULE, + WASM_EXPRESSION_STACK +}; + +// Debugger.Scope +class Scope final : public PtBaseTypes { +public: + Scope() = default; + ~Scope() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + /* + * @see {#Scope::Type} + */ + const std::string &GetType() const + { + return type_; + } + + Scope &SetType(const std::string &type) + { + type_ = type; + return *this; + } + + RemoteObject *GetObject() const + { + return object_.get(); + } + + Scope &SetObject(std::unique_ptr params) + { + object_ = std::move(params); + return *this; + } + + const std::string &GetName() const + { + ASSERT(HasName()); + return name_.value(); + } + + Scope &SetName(const std::string &name) + { + name_ = name; + return *this; + } + + bool HasName() const + { + return name_.has_value(); + } + + Location *GetStartLocation() const + { + if (startLocation_) { + return startLocation_->get(); + } + return nullptr; + } + + Scope &SetStartLocation(std::unique_ptr location) + { + startLocation_ = std::move(location); + return *this; + } + + bool HasStartLocation() const + { + return startLocation_.has_value(); + } + + Location *GetEndLocation() const + { + if (endLocation_) { + return endLocation_->get(); + } + return nullptr; + } + + Scope &SetEndLocation(std::unique_ptr location) + { + endLocation_ = std::move(location); + return *this; + } + + bool HasEndLocation() const + { + return endLocation_.has_value(); + } + + struct Type { + static bool Valid(const std::string &type) + { + return type == Global() || type == Local() || type == With() || type == Closure() || type == Catch() || + type == Block() || type == Script() || type == Eval() || type == Module() || + type == WasmExpressionStack(); + } + static std::string Global() + { + return "global"; + } + static std::string Local() + { + return "local"; + } + static std::string With() + { + return "with"; + } + static std::string Closure() + { + return "closure"; + } + static std::string Catch() + { + return "catch"; + } + static std::string Block() + { + return "block"; + } + static std::string Script() + { + return "script"; + } + static std::string Eval() + { + return "eval"; + } + static std::string Module() + { + return "module"; + } + static std::string WasmExpressionStack() + { + return "wasm-expression-stack"; + } + }; + +private: + NO_COPY_SEMANTIC(Scope); + NO_MOVE_SEMANTIC(Scope); + + std::string type_ {}; + std::unique_ptr object_ {nullptr}; + std::optional name_ {}; + std::optional> startLocation_ {}; + std::optional> endLocation_ {}; +}; + +// Debugger.CallFrame +class CallFrame final : public PtBaseTypes { +public: + CallFrame() = default; + ~CallFrame() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + CallFrameId GetCallFrameId() const + { + return callFrameId_; + } + + CallFrame &SetCallFrameId(CallFrameId callFrameId) + { + callFrameId_ = callFrameId; + return *this; + } + + const std::string &GetFunctionName() const + { + return functionName_; + } + + CallFrame &SetFunctionName(const std::string &functionName) + { + functionName_ = functionName; + return *this; + } + + Location *GetFunctionLocation() const + { + if (functionLocation_) { + return functionLocation_->get(); + } + return nullptr; + } + + CallFrame &SetFunctionLocation(std::unique_ptr location) + { + functionLocation_ = std::move(location); + return *this; + } + + bool HasFunctionLocation() const + { + return functionLocation_.has_value(); + } + + Location *GetLocation() const + { + return location_.get(); + } + + CallFrame &SetLocation(std::unique_ptr location) + { + location_ = std::move(location); + return *this; + } + + const std::string &GetUrl() const + { + return url_; + } + + CallFrame &SetUrl(const std::string &url) + { + url_ = url; + return *this; + } + + const std::vector> *GetScopeChain() const + { + return &scopeChain_; + } + + CallFrame &SetScopeChain(std::vector> scopeChain) + { + scopeChain_ = std::move(scopeChain); + return *this; + } + RemoteObject *GetThis() const + { + return this_.get(); + } + + CallFrame &SetThis(std::unique_ptr thisObj) + { + this_ = std::move(thisObj); + return *this; + } + + RemoteObject *GetReturnValue() const + { + if (returnValue_) { + return returnValue_->get(); + } + return nullptr; + } + + CallFrame &SetReturnValue(std::unique_ptr returnValue) + { + returnValue_ = std::move(returnValue); + return *this; + } + + bool HasReturnValue() const + { + return returnValue_.has_value(); + } + +private: + NO_COPY_SEMANTIC(CallFrame); + NO_MOVE_SEMANTIC(CallFrame); + + CallFrameId callFrameId_ {}; + std::string functionName_ {}; + std::optional> functionLocation_ {}; + std::unique_ptr location_ {nullptr}; + std::string url_ {}; + std::vector> scopeChain_ {}; + std::unique_ptr this_ {nullptr}; + std::optional> returnValue_ {}; +}; + +// ========== Heapprofiler types begin + +using HeapSnapshotObjectId = int32_t; + +class SamplingHeapProfileSample final : public PtBaseTypes { +public: + SamplingHeapProfileSample() = default; + ~SamplingHeapProfileSample() override = default; + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + SamplingHeapProfileSample &SetSize(int32_t size) + { + size_ = size; + return *this; + } + + int32_t GetSize() const + { + return size_; + } + + SamplingHeapProfileSample &SetNodeId(int32_t nodeId) + { + nodeId_ = nodeId; + return *this; + } + + int32_t GetNodeId() const + { + return nodeId_; + } + + SamplingHeapProfileSample &SetOrdinal(int32_t ordinal) + { + ordinal_ = ordinal; + return *this; + } + + int32_t GetOrdinal() const + { + return ordinal_; + } + +private: + NO_COPY_SEMANTIC(SamplingHeapProfileSample); + NO_MOVE_SEMANTIC(SamplingHeapProfileSample); + + int32_t size_ {0}; + int32_t nodeId_ {0}; + int32_t ordinal_ {0}; +}; + +class RuntimeCallFrame final : public PtBaseTypes { +public: + RuntimeCallFrame() = default; + ~RuntimeCallFrame() override = default; + static std::unique_ptr Create(const PtJson ¶ms); + static std::unique_ptr FromFrameInfo(const FrameInfo &cpuFrameInfo); + std::unique_ptr ToJson() const override; + + RuntimeCallFrame &SetFunctionName(const std::string &functionName) + { + functionName_ = functionName; + return *this; + } + + const std::string &GetFunctionName() const + { + return functionName_; + } + + RuntimeCallFrame &SetScriptId(const std::string &scriptId) + { + scriptId_ = scriptId; + return *this; + } + + const std::string &GetScriptId() const + { + return scriptId_; + } + + RuntimeCallFrame &SetUrl(const std::string &url) + { + url_ = url; + return *this; + } + + const std::string &GetUrl() const + { + return url_; + } + + RuntimeCallFrame &SetLineNumber(int32_t lineNumber) + { + lineNumber_ = lineNumber; + return *this; + } + + int32_t GetLineNumber() const + { + return lineNumber_; + } + + RuntimeCallFrame &SetColumnNumber(int32_t columnNumber) + { + columnNumber_ = columnNumber; + return *this; + } + + int32_t GetColumnNumber() const + { + return columnNumber_; + } + +private: + NO_COPY_SEMANTIC(RuntimeCallFrame); + NO_MOVE_SEMANTIC(RuntimeCallFrame); + + std::string functionName_ {}; + std::string scriptId_ {}; + std::string url_ {}; + int32_t lineNumber_ {0}; + int32_t columnNumber_ {0}; +}; + +class SamplingHeapProfileNode final : public PtBaseTypes { +public: + SamplingHeapProfileNode() = default; + ~SamplingHeapProfileNode() override = default; + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + SamplingHeapProfileNode &SetCallFrame(std::unique_ptr callFrame) + { + callFrame_ = std::move(callFrame); + return *this; + } + + RuntimeCallFrame *GetCallFrame() const + { + return callFrame_.get(); + } + + SamplingHeapProfileNode &SetSelfSize(int32_t selfSize) + { + selfSize_ = selfSize; + return *this; + } + + int32_t GetSelfSize() const + { + return selfSize_; + } + + SamplingHeapProfileNode &SetId(int32_t id) + { + id_ = id; + return *this; + } + + int32_t GetId() const + { + return id_; + } + + SamplingHeapProfileNode &SetChildren(std::vector> children) + { + children_ = std::move(children); + return *this; + } + + const std::vector> *GetChildren() const + { + return &children_; + } + +private: + NO_COPY_SEMANTIC(SamplingHeapProfileNode); + NO_MOVE_SEMANTIC(SamplingHeapProfileNode); + + std::unique_ptr callFrame_ {nullptr}; + int32_t selfSize_ {0}; + int32_t id_ {0}; + std::vector> children_ {}; +}; + +class SamplingHeapProfile final : public PtBaseTypes { +public: + SamplingHeapProfile() = default; + ~SamplingHeapProfile() override = default; + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + SamplingHeapProfile &SetHead(std::unique_ptr head) + { + head_ = std::move(head); + return *this; + } + + SamplingHeapProfileNode *GetHead() const + { + return head_.get(); + } + + SamplingHeapProfile &SetSamples(std::vector> samples) + { + samples_ = std::move(samples); + return *this; + } + + const std::vector> *GetSamples() const + { + return &samples_; + } + +private: + NO_COPY_SEMANTIC(SamplingHeapProfile); + NO_MOVE_SEMANTIC(SamplingHeapProfile); + + std::unique_ptr head_ {nullptr}; + std::vector> samples_ {}; +}; + +// ========== Profiler types begin +// Profiler.PositionTickInfo +class PositionTickInfo final : public PtBaseTypes { +public: + PositionTickInfo() = default; + ~PositionTickInfo() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + int32_t GetLine() const + { + return line_; + } + + PositionTickInfo &SetLine(int32_t line) + { + line_ = line; + return *this; + } + + int32_t GetTicks() const + { + return ticks_; + } + + PositionTickInfo &SetTicks(int32_t ticks) + { + ticks_ = ticks; + return *this; + } + +private: + NO_COPY_SEMANTIC(PositionTickInfo); + NO_MOVE_SEMANTIC(PositionTickInfo); + int32_t line_ {0}; + int32_t ticks_ {0}; +}; + +// Profiler.ProfileNode +class ProfileNode final : public PtBaseTypes { +public: + ProfileNode() = default; + ~ProfileNode() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + static std::unique_ptr FromCpuProfileNode(const CpuProfileNode &cpuProfileNode); + std::unique_ptr ToJson() const override; + + int32_t GetId() const + { + return id_; + } + + ProfileNode &SetId(int32_t id) + { + id_ = id; + return *this; + } + + RuntimeCallFrame *GetCallFrame() const + { + return callFrame_.get(); + } + + ProfileNode &SetCallFrame(std::unique_ptr callFrame) + { + callFrame_ = std::move(callFrame); + return *this; + } + + int32_t GetHitCount() const + { + ASSERT(HasHitCount()); + return hitCount_.value(); + } + + ProfileNode &SetHitCount(int32_t hitCount) + { + hitCount_ = hitCount; + return *this; + } + + bool HasHitCount() const + { + return hitCount_.has_value(); + } + + const std::vector *GetChildren() const + { + if (children_) { + return &children_.value(); + } + return nullptr; + } + + ProfileNode &SetChildren(std::vector children) + { + children_ = std::move(children); + return *this; + } + + bool HasChildren() const + { + return children_.has_value(); + } + + const std::vector> *GetPositionTicks() const + { + if (positionTicks_) { + return &positionTicks_.value(); + } + return nullptr; + } + + ProfileNode &SetPositionTicks(std::vector> positionTicks) + { + positionTicks_ = std::move(positionTicks); + return *this; + } + + bool HasPositionTicks() const + { + return positionTicks_.has_value(); + } + + const std::string &GetDeoptReason() const + { + ASSERT(HasDeoptReason()); + return deoptReason_.value(); + } + + ProfileNode &SetDeoptReason(const std::string &deoptReason) + { + deoptReason_ = deoptReason; + return *this; + } + + bool HasDeoptReason() const + { + return deoptReason_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ProfileNode); + NO_MOVE_SEMANTIC(ProfileNode); + int32_t id_ {0}; + std::unique_ptr callFrame_ {nullptr}; + std::optional hitCount_ {0}; + std::optional> children_ {}; + std::optional>> positionTicks_ {}; + std::optional deoptReason_ {}; +}; + +// Profiler.Profile +class Profile final : public PtBaseTypes { +public: + Profile() = default; + ~Profile() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + static std::unique_ptr FromProfileInfo(const ProfileInfo &profileInfo); + std::unique_ptr ToJson() const override; + + int64_t GetTid() const + { + return tid_; + } + + Profile &SetTid(int64_t tid) + { + tid_ = tid; + return *this; + } + + int64_t GetStartTime() const + { + return startTime_; + } + + Profile &SetStartTime(int64_t startTime) + { + startTime_ = startTime; + return *this; + } + + int64_t GetEndTime() const + { + return endTime_; + } + + Profile &SetEndTime(int64_t endTime) + { + endTime_ = endTime; + return *this; + } + + const std::vector> *GetNodes() const + { + return &nodes_; + } + + Profile &SetNodes(std::vector> nodes) + { + nodes_ = std::move(nodes); + return *this; + } + + const std::vector *GetSamples() const + { + if (samples_) { + return &samples_.value(); + } + return nullptr; + } + + Profile &SetSamples(std::vector samples) + { + samples_ = std::move(samples); + return *this; + } + + bool HasSamples() const + { + return samples_.has_value(); + } + + const std::vector *GetTimeDeltas() const + { + if (timeDeltas_) { + return &timeDeltas_.value(); + } + return nullptr; + } + + Profile &SetTimeDeltas(std::vector timeDeltas) + { + timeDeltas_ = std::move(timeDeltas); + return *this; + } + + bool HasTimeDeltas() const + { + return timeDeltas_.has_value(); + } + +private: + NO_COPY_SEMANTIC(Profile); + NO_MOVE_SEMANTIC(Profile); + + int64_t tid_ {0}; + int64_t startTime_ {0}; + int64_t endTime_ {0}; + std::vector> nodes_ {}; + std::optional> samples_ {}; + std::optional> timeDeltas_ {}; +}; + +// Profiler.Coverage +class Coverage final : public PtBaseTypes { +public: + Coverage() = default; + ~Coverage() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + int32_t GetStartOffset() const + { + return startOffset_; + } + + Coverage &SetStartOffset(int32_t startOffset) + { + startOffset_ = startOffset; + return *this; + } + + int32_t GetEndOffset() const + { + return endOffset_; + } + + Coverage &SetEndOffset(int32_t endOffset) + { + endOffset_ = endOffset; + return *this; + } + + int32_t GetCount() const + { + return count_; + } + + Coverage &SetCount(int32_t count) + { + count_ = count; + return *this; + } + +private: + NO_COPY_SEMANTIC(Coverage); + NO_MOVE_SEMANTIC(Coverage); + + int32_t startOffset_ {0}; + int32_t endOffset_ {0}; + int32_t count_ {0}; +}; + +// Profiler.FunctionCoverage +class FunctionCoverage final : public PtBaseTypes { +public: + FunctionCoverage() = default; + ~FunctionCoverage() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + const std::string &GetFunctionName() const + { + return functionName_; + } + + FunctionCoverage &SetFunctionName(const std::string &functionName) + { + functionName_ = functionName; + return *this; + } + + const std::vector> *GetRanges() const + { + return &ranges_; + } + + FunctionCoverage &SetFunctions(std::vector> ranges) + { + ranges_ = std::move(ranges); + return *this; + } + + bool GetIsBlockCoverage() const + { + return isBlockCoverage_; + } + + FunctionCoverage &SetisBlockCoverage(bool isBlockCoverage) + { + isBlockCoverage_ = isBlockCoverage; + return *this; + } + +private: + NO_COPY_SEMANTIC(FunctionCoverage); + NO_MOVE_SEMANTIC(FunctionCoverage); + + std::string functionName_ {}; + std::vector> ranges_ {}; + bool isBlockCoverage_ {}; +}; + +// Profiler.ScriptCoverage +// Profiler.GetBestEffortCoverage and Profiler.TakePreciseCoverage share this return value type +class ScriptCoverage final : public PtBaseTypes { +public: + ScriptCoverage() = default; + ~ScriptCoverage() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + const std::string &GetScriptId() const + { + return scriptId_; + } + + ScriptCoverage &SetScriptId(const std::string &scriptId) + { + scriptId_ = scriptId; + return *this; + } + + const std::string &GetUrl() const + { + return url_; + } + + ScriptCoverage &SetUrl(const std::string &url) + { + url_ = url; + return *this; + } + + const std::vector> *GetFunctions() const + { + return &functions_; + } + + ScriptCoverage &SetFunctions(std::vector> functions) + { + functions_ = std::move(functions); + return *this; + } + +private: + NO_COPY_SEMANTIC(ScriptCoverage); + NO_MOVE_SEMANTIC(ScriptCoverage); + + std::string scriptId_ {}; + std::string url_ {}; + std::vector> functions_ {}; +}; + +// Profiler.TypeObject +class TypeObject final : public PtBaseTypes { +public: + TypeObject() = default; + ~TypeObject() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + const std::string &GetName() const + { + return name_; + } + + TypeObject &SetName(const std::string &name) + { + name_ = name; + return *this; + } + +private: + NO_COPY_SEMANTIC(TypeObject); + NO_MOVE_SEMANTIC(TypeObject); + + std::string name_ {}; +}; + +// Profiler.TypeProfileEntry +class TypeProfileEntry final : public PtBaseTypes { +public: + TypeProfileEntry() = default; + ~TypeProfileEntry() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + int32_t GetOffset() const + { + return offset_; + } + + TypeProfileEntry &SetOffset(int32_t offset) + { + offset_ = offset; + return *this; + } + + const std::vector> *GetTypes() const + { + return &types_; + } + + TypeProfileEntry &SetTypes(std::vector> types) + { + types_ = std::move(types); + return *this; + } + +private: + NO_COPY_SEMANTIC(TypeProfileEntry); + NO_MOVE_SEMANTIC(TypeProfileEntry); + + int32_t offset_ {0}; + std::vector> types_ {}; +}; + +// Profiler.ScriptTypeProfile +class ScriptTypeProfile final : public PtBaseTypes { +public: + ScriptTypeProfile() = default; + ~ScriptTypeProfile() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + const std::string &GetScriptId() const + { + return scriptId_; + } + + ScriptTypeProfile &SetScriptId(const std::string &scriptId) + { + scriptId_ = scriptId; + return *this; + } + + const std::string &GetUrl() const + { + return url_; + } + + ScriptTypeProfile &SetUrl(const std::string &url) + { + url_ = url; + return *this; + } + + const std::vector> *GetEntries() const + { + return &entries_; + } + + ScriptTypeProfile &SetEntries(std::vector> entries) + { + entries_ = std::move(entries); + return *this; + } + +private: + NO_COPY_SEMANTIC(ScriptTypeProfile); + NO_MOVE_SEMANTIC(ScriptTypeProfile); + + std::string scriptId_ {}; + std::string url_ {}; + std::vector> entries_ {}; +}; + +// ========== Tracing types begin +// Tracing.MemoryDumpConfig +using MemoryDumpConfig = PtJson; + +// Tracing.MemoryDumpLevelOfDetail +using MemoryDumpLevelOfDetail = std::string; +struct MemoryDumpLevelOfDetailValues { + static bool Valid(const std::string &values) + { + return values == Background() || values == Light() || values == Detailed(); + } + static std::string Background() + { + return "background"; + } + static std::string Light() + { + return "light"; + } + static std::string Detailed() + { + return "detailed"; + } +}; + +// Tracing.StreamCompression +using StreamCompression = std::string; +struct StreamCompressionValues { + static bool Valid(const std::string &values) + { + return values == None() || values == Gzip(); + } + static std::string None() + { + return "none"; + } + static std::string Gzip() + { + return "gzip"; + } +}; + +// Tracing.StreamFormat +using StreamFormat = std::string; +struct StreamFormatValues { + static bool Valid(const std::string &values) + { + return values == Json() || values == Proto(); + } + static std::string Json() + { + return "json"; + } + static std::string Proto() + { + return "proto"; + } +}; + +// Tracing.TraceConfig +class TraceConfig final : public PtBaseTypes { +public: + TraceConfig() = default; + ~TraceConfig() override = default; + + static std::unique_ptr Create(const PtJson ¶ms); + std::unique_ptr ToJson() const override; + + std::string GetRecordMode() const + { + return recordMode_.value(); + } + + TraceConfig &SetRecordMode(std::string recordMode) + { + recordMode_ = recordMode; + return *this; + } + + bool HasRecordMode() const + { + return recordMode_.has_value(); + } + + struct RecordModeValues { + static bool Valid(const std::string &values) + { + return values == RecordUntilFull() || values == RecordContinuously() || + values == RecordAsMuchAsPossible() || values == EchoToConsole(); + } + static std::string RecordUntilFull() + { + return "recordUntilFull"; + } + static std::string RecordContinuously() + { + return "recordContinuously"; + } + static std::string RecordAsMuchAsPossible() + { + return "recordAsMuchAsPossible"; + } + static std::string EchoToConsole() + { + return "echoToConsole"; + } + }; + + bool GetEnableSampling() const + { + return enableSampling_.value(); + } + + TraceConfig &SetEnableSampling(bool enableSampling) + { + enableSampling_ = enableSampling; + return *this; + } + + bool HasEnableSampling() const + { + return enableSampling_.has_value(); + } + + bool GetEnableSystrace() const + { + return enableSystrace_.value(); + } + + TraceConfig &SetEnableSystrace(bool enableSystrace) + { + enableSystrace_ = enableSystrace; + return *this; + } + + bool HasEnableSystrace() const + { + return enableSystrace_.has_value(); + } + + bool GetEnableArgumentFilter() const + { + return enableArgumentFilter_.value(); + } + + TraceConfig &SetEnableArgumentFilter(bool enableArgumentFilter) + { + enableArgumentFilter_ = enableArgumentFilter; + return *this; + } + + bool HasEnableArgumentFilter() const + { + return enableArgumentFilter_.has_value(); + } + + const std::vector *GetIncludedCategories() const + { + if (includedCategories_) { + return &includedCategories_.value(); + } + return nullptr; + } + + TraceConfig &SetIncludedCategories(std::vector includedCategories) + { + includedCategories_ = includedCategories; + return *this; + } + + bool HasIncludedCategories() const + { + return includedCategories_.has_value(); + } + + const std::vector *GetExcludedCategories() const + { + if (excludedCategories_) { + return &excludedCategories_.value(); + } + return nullptr; + } + + TraceConfig &SetExcludedCategories(std::vector excludedCategories) + { + excludedCategories_ = excludedCategories; + return *this; + } + + bool HasExcludedCategories() const + { + return excludedCategories_.has_value(); + } + + const std::vector *GetSyntheticDelays() const + { + if (syntheticDelays_) { + return &syntheticDelays_.value(); + } + return nullptr; + } + + TraceConfig &SetSyntheticDelays(std::vector syntheticDelays) + { + syntheticDelays_ = syntheticDelays; + return *this; + } + + bool HasSyntheticDelays() const + { + return syntheticDelays_.has_value(); + } + +private: + NO_COPY_SEMANTIC(TraceConfig); + NO_MOVE_SEMANTIC(TraceConfig); + + std::optional recordMode_ {}; + std::optional enableSampling_ {}; + std::optional enableSystrace_ {}; + std::optional enableArgumentFilter_ {}; + std::optional> includedCategories_ {}; + std::optional> excludedCategories_ {}; + std::optional> syntheticDelays_ {}; + std::optional> memoryDumpConfig_ {}; +}; + +// Tracing.TracingBackend +using TracingBackend = std::string; +struct TracingBackendValues { + static bool Valid(const std::string &values) + { + return values == Auto() || values == Chrome() || values == System(); + } + static std::string Auto() + { + return "auto"; + } + static std::string Chrome() + { + return "chrome"; + } + static std::string System() + { + return "system"; + } +}; +} // namespace panda::ecmascript::tooling +#endif diff --git a/tooling/debugger_service.cpp b/tooling/debugger_service.cpp new file mode 100644 index 00000000..da1c86d6 --- /dev/null +++ b/tooling/debugger_service.cpp @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/debugger_service.h" + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/tooling/protocol_handler.h" +#include "ecmascript/tooling/interface/js_debugger_manager.h" + +namespace panda::ecmascript::tooling { +void InitializeDebugger(::panda::ecmascript::EcmaVM *vm, + const std::function &onResponse) +{ + ProtocolHandler *handler = vm->GetJsDebuggerManager()->GetDebuggerHandler(); + if (handler != nullptr) { + LOG_DEBUGGER(ERROR) << "JS debugger was initialized"; + return; + } + vm->GetJsDebuggerManager()->SetDebuggerHandler(new ProtocolHandler(onResponse, vm)); +} + +void UninitializeDebugger(::panda::ecmascript::EcmaVM *vm) +{ + ProtocolHandler *handler = vm->GetJsDebuggerManager()->GetDebuggerHandler(); + delete handler; + vm->GetJsDebuggerManager()->SetDebuggerHandler(nullptr); +} + +void WaitForDebugger(const ::panda::ecmascript::EcmaVM *vm) +{ + ProtocolHandler *handler = vm->GetJsDebuggerManager()->GetDebuggerHandler(); + if (LIKELY(handler != nullptr)) { + handler->WaitForDebugger(); + } +} + +void OnMessage(const ::panda::ecmascript::EcmaVM *vm, std::string &&message) +{ + ProtocolHandler *handler = vm->GetJsDebuggerManager()->GetDebuggerHandler(); + if (LIKELY(handler != nullptr)) { + handler->DispatchCommand(std::move(message)); + } +} + +void ProcessMessage(const ::panda::ecmascript::EcmaVM *vm) +{ + ProtocolHandler *handler = vm->GetJsDebuggerManager()->GetDebuggerHandler(); + if (LIKELY(handler != nullptr)) { + handler->ProcessCommand(); + } +} + +int32_t GetDispatchStatus(const ::panda::ecmascript::EcmaVM *vm) +{ + ProtocolHandler *handler = vm->GetJsDebuggerManager()->GetDebuggerHandler(); + if (LIKELY(handler != nullptr)) { + return handler->GetDispatchStatus(); + } + return ProtocolHandler::DispatchStatus::UNKNOWN; +} +} // namespace panda::ecmascript::tooling \ No newline at end of file diff --git a/tooling/debugger_service.h b/tooling/debugger_service.h new file mode 100644 index 00000000..f9047d2b --- /dev/null +++ b/tooling/debugger_service.h @@ -0,0 +1,55 @@ +/* + * 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_DEBUGGER_SERVICE_H +#define ECMASCRIPT_TOOLING_DEBUGGER_SERVICE_H + +#include +#include + +#include "ecmascript/common.h" + +namespace panda::ecmascript { +class EcmaVM; +} // namespace panda::ecmascript + +namespace panda::ecmascript::tooling { +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* End of #ifdef __cplusplus */ + +PUBLIC_API void InitializeDebugger(::panda::ecmascript::EcmaVM *vm, + const std::function &onResponse); + +PUBLIC_API void UninitializeDebugger(::panda::ecmascript::EcmaVM *vm); + +PUBLIC_API void OnMessage(const ::panda::ecmascript::EcmaVM *vm, std::string &&message); + +PUBLIC_API void WaitForDebugger(const ::panda::ecmascript::EcmaVM *vm); + +PUBLIC_API void ProcessMessage(const ::panda::ecmascript::EcmaVM *vm); + +PUBLIC_API int32_t GetDispatchStatus(const ::panda::ecmascript::EcmaVM *vm); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* End of #ifdef __cplusplus */ +} // panda::ecmascript::tooling + +#endif \ No newline at end of file diff --git a/tooling/dispatcher.cpp b/tooling/dispatcher.cpp new file mode 100644 index 00000000..f1420f1f --- /dev/null +++ b/tooling/dispatcher.cpp @@ -0,0 +1,172 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/dispatcher.h" + +#include "ecmascript/tooling/agent/debugger_impl.h" +#include "ecmascript/tooling/agent/runtime_impl.h" +#ifdef ECMASCRIPT_SUPPORT_HEAPPROFILER +#include "ecmascript/tooling/agent/heapprofiler_impl.h" +#endif +#ifdef ECMASCRIPT_SUPPORT_CPUPROFILER +#include "ecmascript/tooling/agent/profiler_impl.h" +#endif +#include "ecmascript/tooling/agent/tracing_impl.h" +#include "ecmascript/tooling/protocol_channel.h" + +namespace panda::ecmascript::tooling { +DispatchRequest::DispatchRequest(const std::string &message) +{ + std::unique_ptr json = PtJson::Parse(message); + if (json == nullptr) { + code_ = RequestCode::JSON_PARSE_ERROR; + LOG_DEBUGGER(ERROR) << "json parse error"; + return; + } + if (!json->IsObject()) { + code_ = RequestCode::PARAMS_FORMAT_ERROR; + LOG_DEBUGGER(ERROR) << "json parse format error"; + json->ReleaseRoot(); + return; + } + + Result ret; + int32_t callId; + ret = json->GetInt("id", &callId); + if (ret != Result::SUCCESS) { + code_ = RequestCode::PARSE_ID_ERROR; + LOG_DEBUGGER(ERROR) << "parse id error"; + return; + } + callId_ = callId; + + std::string wholeMethod; + ret = json->GetString("method", &wholeMethod); + if (ret != Result::SUCCESS) { + code_ = RequestCode::PARSE_METHOD_ERROR; + LOG_DEBUGGER(ERROR) << "parse method error"; + return; + } + std::string::size_type length = wholeMethod.length(); + std::string::size_type indexPoint; + indexPoint = wholeMethod.find_first_of('.', 0); + if (indexPoint == std::string::npos || indexPoint == 0 || indexPoint == length - 1) { + code_ = RequestCode::METHOD_FORMAT_ERROR; + LOG_DEBUGGER(ERROR) << "method format error: " << wholeMethod; + return; + } + domain_ = wholeMethod.substr(0, indexPoint); + method_ = wholeMethod.substr(indexPoint + 1, length); + + LOG_DEBUGGER(DEBUG) << "id: " << callId_ << ", domain: " << domain_ << ", method: " << method_; + + std::unique_ptr params; + ret = json->GetObject("params", ¶ms); + if (ret == Result::NOT_EXIST) { + return; + } + if (ret == Result::TYPE_ERROR) { + code_ = RequestCode::PARAMS_FORMAT_ERROR; + LOG_DEBUGGER(ERROR) << "params format error"; + return; + } + params_ = std::move(params); +} + +DispatchRequest::~DispatchRequest() +{ + params_->ReleaseRoot(); +} + +DispatchResponse DispatchResponse::Create(ResponseCode code, const std::string &msg) +{ + DispatchResponse response; + response.code_ = code; + response.errorMsg_ = msg; + return response; +} + +DispatchResponse DispatchResponse::Create(std::optional error) +{ + DispatchResponse response; + if (error.has_value()) { + response.code_ = ResponseCode::NOK; + response.errorMsg_ = error.value(); + } + return response; +} + +DispatchResponse DispatchResponse::Ok() +{ + return DispatchResponse(); +} + +DispatchResponse DispatchResponse::Fail(const std::string &message) +{ + DispatchResponse response; + response.code_ = ResponseCode::NOK; + response.errorMsg_ = message; + return response; +} + +void DispatcherBase::SendResponse(const DispatchRequest &request, const DispatchResponse &response, + const PtBaseReturns &result) +{ + if (channel_ != nullptr) { + channel_->SendResponse(request, response, result); + } +} + +Dispatcher::Dispatcher(const EcmaVM *vm, ProtocolChannel *channel) +{ + // profiler +#ifdef ECMASCRIPT_SUPPORT_CPUPROFILER + auto profiler = std::make_unique(vm, channel); + dispatchers_["Profiler"] = + std::make_unique(channel, std::move(profiler)); +#endif +#ifdef ECMASCRIPT_SUPPORT_HEAPPROFILER + auto heapProfiler = std::make_unique(vm, channel); + dispatchers_["HeapProfiler"] = + std::make_unique(channel, std::move(heapProfiler)); +#endif + auto tracing = std::make_unique(vm, channel); + dispatchers_["Tracing"] = + std::make_unique(channel, std::move(tracing)); + + // debugger + auto runtime = std::make_unique(vm, channel); + auto debugger = std::make_unique(vm, channel, runtime.get()); + dispatchers_["Runtime"] = + std::make_unique(channel, std::move(runtime)); + dispatchers_["Debugger"] = + std::make_unique(channel, std::move(debugger)); +} + +void Dispatcher::Dispatch(const DispatchRequest &request) +{ + if (!request.IsValid()) { + LOG_DEBUGGER(ERROR) << "Unknown request"; + return; + } + const std::string &domain = request.GetDomain(); + auto dispatcher = dispatchers_.find(domain); + if (dispatcher != dispatchers_.end()) { + dispatcher->second->Dispatch(request); + } else { + LOG_DEBUGGER(ERROR) << "unknown domain: " << domain; + } +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/dispatcher.h b/tooling/dispatcher.h new file mode 100644 index 00000000..b247ba8b --- /dev/null +++ b/tooling/dispatcher.h @@ -0,0 +1,147 @@ +/* + * 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_DISPATCHER_H +#define ECMASCRIPT_TOOLING_DISPATCHER_H + +#include +#include + +#include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/tooling/backend/js_debugger_interface.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +class ProtocolChannel; +class PtBaseReturns; +class PtBaseEvents; + +enum class RequestCode : uint8_t { + OK = 0, + NOK, + + // Json parse errors + JSON_PARSE_ERROR, + PARSE_ID_ERROR, + ID_FORMAT_ERROR, + PARSE_METHOD_ERROR, + METHOD_FORMAT_ERROR, + PARSE_PARAMS_ERROR, + PARAMS_FORMAT_ERROR +}; + +enum class ResponseCode : uint8_t { OK, NOK }; + +class DispatchRequest { +public: + explicit DispatchRequest(const std::string &message); + ~DispatchRequest(); + + bool IsValid() const + { + return code_ == RequestCode::OK; + } + int32_t GetCallId() const + { + return callId_; + } + const PtJson &GetParams() const + { + return *params_; + } + const std::string &GetDomain() const + { + return domain_; + } + const std::string &GetMethod() const + { + return method_; + } + +private: + int32_t callId_ = -1; + std::string domain_ {}; + std::string method_ {}; + std::unique_ptr params_ = std::make_unique(); + RequestCode code_ {RequestCode::OK}; + std::string errorMsg_ {}; +}; + +class DispatchResponse { +public: + bool IsOk() const + { + return code_ == ResponseCode::OK; + } + + ResponseCode GetError() const + { + return code_; + } + + const std::string &GetMessage() const + { + return errorMsg_; + } + + static DispatchResponse Create(ResponseCode code, const std::string &msg = ""); + static DispatchResponse Create(std::optional error); + static DispatchResponse Ok(); + static DispatchResponse Fail(const std::string &message); + + ~DispatchResponse() = default; + +private: + DispatchResponse() = default; + + ResponseCode code_ {ResponseCode::OK}; + std::string errorMsg_ {}; +}; + +class DispatcherBase { +public: + explicit DispatcherBase(ProtocolChannel *channel) : channel_(channel) {} + virtual ~DispatcherBase() + { + channel_ = nullptr; + }; + virtual void Dispatch(const DispatchRequest &request) = 0; + +protected: + void SendResponse(const DispatchRequest &request, const DispatchResponse &response, + const PtBaseReturns &result = PtBaseReturns()); + +private: + ProtocolChannel *channel_ {nullptr}; + + NO_COPY_SEMANTIC(DispatcherBase); + NO_MOVE_SEMANTIC(DispatcherBase); +}; + +class Dispatcher { +public: + explicit Dispatcher(const EcmaVM *vm, ProtocolChannel *channel); + ~Dispatcher() = default; + void Dispatch(const DispatchRequest &request); + +private: + std::unordered_map> dispatchers_ {}; + + NO_COPY_SEMANTIC(Dispatcher); + NO_MOVE_SEMANTIC(Dispatcher); +}; +} // namespace panda::ecmascript::tooling +#endif diff --git a/tooling/interface/file_stream.cpp b/tooling/interface/file_stream.cpp new file mode 100644 index 00000000..f374487a --- /dev/null +++ b/tooling/interface/file_stream.cpp @@ -0,0 +1,130 @@ +/* + * 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/file_stream.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ecmascript/log_wrapper.h" + +namespace panda::ecmascript { +FileStream::FileStream(const std::string &fileName) +{ + Initialize(fileName); +} + +void FileStream::EndOfStream() +{ + if (Good()) { + fileStream_.close(); + } +} + +bool FileStream::Good() +{ + return fileStream_.good(); +} + +void FileStream::Initialize(const std::string &fileName) +{ + // check file name + std::pair realPath = FilePathValid(fileName); + if (!realPath.first) { + LOG_ECMA(ERROR) << "FileStream: check file path failed"; + fileStream_.close(); + return; + } + + fileStream_.open(realPath.second.c_str(), std::ios::out); + if (fileStream_.fail()) { + LOG_ECMA(ERROR) << "FileStream: open file failed"; + } +} + +std::pair FileStream::FilePathValid(const std::string &fileName) +{ + if (fileName.empty() || fileName.size() > PATH_MAX) { + return std::make_pair(false, ""); + } + char resolvedPath[PATH_MAX] = {0}; + auto result = realpath(fileName.c_str(), resolvedPath); + if (result == resolvedPath || errno == ENOENT) { + return std::make_pair(true, std::string(resolvedPath)); + } + return std::make_pair(false, ""); +} + +// Writes the chunk of data into the stream +bool FileStream::WriteChunk(char *data, int32_t size) +{ + if (fileStream_.fail()) { + return false; + } + + std::string str; + str.resize(size); + for (int32_t i = 0; i < size; ++i) { + str[i] = data[i]; + } + + fileStream_ << str; + + return true; +} + +void FileDescriptorStream::EndOfStream() +{ + if (Good()) { + close(fd_); + } +} + +bool FileDescriptorStream::Good() +{ + return fd_ > 0; +} + +// Writes the chunk of data into the stream +bool FileDescriptorStream::WriteChunk(char *data, int32_t size) +{ + if (fd_ < 0) { + return false; + } + + std::string str; + str.resize(size); + for (int32_t i = 0; i < size; ++i) { + str[i] = data[i]; + } + int ret = dprintf(fd_, "%s", str.c_str()); + if (ret < 0) { + LOG_ECMA(ERROR) << "Write FD print failed, ret" << ret; + return false; + } + ret = fsync(fd_); + if (ret < 0) { + LOG_ECMA(ERROR) << "Write FD file failed, ret" << ret; + return false; + } + return true; +} +} diff --git a/tooling/interface/file_stream.h b/tooling/interface/file_stream.h new file mode 100644 index 00000000..0d284e4b --- /dev/null +++ b/tooling/interface/file_stream.h @@ -0,0 +1,88 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_INTERFACE_FILE_STREAM_H +#define ECMASCRIPT_TOOLING_INTERFACE_FILE_STREAM_H + +#include +#include +#include +#include + +#include "ecmascript/tooling/interface/stream.h" +#include "ecmascript/mem/mem_common.h" + +namespace panda::ecmascript { +class FileStream : public Stream { +public: + FileStream(const std::string &fileName); + ~FileStream() override = default; + + void EndOfStream() override; + + // Get chunk's size + int GetSize() override + { + const static int fileChunkSize = static_cast(10_KB); + return fileChunkSize; + } + + // Writes the chunk of data into the stream + bool WriteChunk(char* data, int32_t size) override; + bool Good() override; + void UpdateHeapStats([[maybe_unused]] HeapStat* data, [[maybe_unused]] int32_t count) override + { + } + void UpdateLastSeenObjectId([[maybe_unused]] int32_t lastSeenObjectId) override + { + } + +private: + void Initialize(const std::string &fileName); + std::pair FilePathValid(const std::string &fileName); + + std::fstream fileStream_; +}; + +class FileDescriptorStream : public Stream { +public: + explicit FileDescriptorStream(int32_t fd): fd_(fd) {} + ~FileDescriptorStream() override = default; + + void EndOfStream() override; + + // Get chunk's size + int GetSize() override + { + const static int fileChunkSize = static_cast(10_KB); + return fileChunkSize; + } + + // Writes the chunk of data into the stream + bool WriteChunk(char *data, int32_t size) override; + bool Good() override; + void UpdateHeapStats([[maybe_unused]] HeapStat* data, [[maybe_unused]] int32_t count) override + { + } + void UpdateLastSeenObjectId([[maybe_unused]] int32_t lastSeenObjectId) override + { + } + +private: + int32_t fd_; +}; +} // namespace panda::ecmascript::tooling + +#endif // ECMASCRIPT_TOOLING_INTERFACE_FILE_STREAM_H diff --git a/tooling/interface/js_debugger_manager.h b/tooling/interface/js_debugger_manager.h new file mode 100644 index 00000000..a3ee9c94 --- /dev/null +++ b/tooling/interface/js_debugger_manager.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUGGER_MANAGER_H +#define ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUGGER_MANAGER_H + +#include "ecmascript/interpreter/frame_handler.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/tooling/interface/notification_manager.h" + +#include "libpandabase/os/library_loader.h" + +namespace panda::ecmascript::tooling { +class ProtocolHandler; +class JsDebuggerManager { +public: + using LibraryHandle = os::library_loader::LibraryHandle; + using ObjectUpdaterFunc = + std::function)>; + using SingleStepperFunc = std::function; + + JsDebuggerManager() = default; + ~JsDebuggerManager() + { + delete notificationManager_; + } + + NO_COPY_SEMANTIC(JsDebuggerManager); + NO_MOVE_SEMANTIC(JsDebuggerManager); + + void Initialize(const EcmaVM *vm) + { + notificationManager_ = new NotificationManager(); + jsThread_ = vm->GetJSThread(); + } + + NotificationManager *GetNotificationManager() const + { + return notificationManager_; + } + + void SetDebugMode(bool isDebugMode) + { + if (isDebugMode_ == isDebugMode) { + return; + } + + isDebugMode_ = isDebugMode; + if (jsThread_ != nullptr && jsThread_->IsAsmInterpreter()) { + jsThread_->CheckSwitchDebuggerBCStub(); + } + } + + bool IsDebugMode() const + { + return isDebugMode_; + } + + void SetMixedDebugEnabled(bool enabled) + { + isMixedDebugEnabled_ = enabled; + } + + bool IsMixedDebugEnabled() const + { + return isMixedDebugEnabled_; + } + + void SetDebuggerHandler(ProtocolHandler *debuggerHandler) + { + debuggerHandler_ = debuggerHandler; + } + + ProtocolHandler *GetDebuggerHandler() const + { + return debuggerHandler_; + } + + void SetDebugLibraryHandle(LibraryHandle handle) + { + debuggerLibraryHandle_ = std::move(handle); + } + + const LibraryHandle &GetDebugLibraryHandle() const + { + return debuggerLibraryHandle_; + } + + void SetEvalFrameHandler(std::shared_ptr frameHandler) + { + frameHandler_ = frameHandler; + } + + const std::shared_ptr &GetEvalFrameHandler() const + { + return frameHandler_; + } + + void SetLocalScopeUpdater(ObjectUpdaterFunc *updaterFunc) + { + updaterFunc_ = updaterFunc; + } + + void NotifyLocalScopeUpdated(std::string_view varName, Local value) + { + if (updaterFunc_ != nullptr) { + (*updaterFunc_)(frameHandler_.get(), varName, value); + } + } + + void SetStepperFunc(SingleStepperFunc *stepperFunc) + { + stepperFunc_ = stepperFunc; + } + + void ClearSingleStepper() + { + if (stepperFunc_ != nullptr) { + (*stepperFunc_)(); + } + } + +private: + bool isDebugMode_ {false}; + bool isMixedDebugEnabled_ { false }; + ProtocolHandler *debuggerHandler_ {nullptr}; + LibraryHandle debuggerLibraryHandle_ {nullptr}; + NotificationManager *notificationManager_ {nullptr}; + ObjectUpdaterFunc *updaterFunc_ {nullptr}; + SingleStepperFunc *stepperFunc_ {nullptr}; + JSThread *jsThread_ {nullptr}; + std::shared_ptr frameHandler_; +}; +} // panda::ecmascript::tooling + +#endif // ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUGGER_MANAGER_H \ No newline at end of file diff --git a/tooling/interface/notification_manager.h b/tooling/interface/notification_manager.h new file mode 100644 index 00000000..cac5b092 --- /dev/null +++ b/tooling/interface/notification_manager.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_INTERFACE_NOTIFICATION_MANAGER_H +#define ECMASCRIPT_TOOLING_INTERFACE_NOTIFICATION_MANAGER_H + +#include + +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript::tooling { +class RuntimeListener { +public: + RuntimeListener() = default; + virtual ~RuntimeListener() = default; + DEFAULT_COPY_SEMANTIC(RuntimeListener); + DEFAULT_MOVE_SEMANTIC(RuntimeListener); + + virtual void LoadModule(std::string_view name, std::string_view) = 0; + + virtual void BytecodePcChanged(JSThread *thread, JSHandle method, + uint32_t bc_offset) = 0; + virtual void VmStart() = 0; + virtual void VmDeath() = 0; + virtual void PendingJobEntry() = 0; + virtual void NativeCalling(const void *nativeAddress) = 0; +}; + +class NotificationManager { +public: + NotificationManager() = default; + ~NotificationManager() = default; + NO_COPY_SEMANTIC(NotificationManager); + NO_MOVE_SEMANTIC(NotificationManager); + + void AddListener(RuntimeListener *listener) + { + listener_ = listener; + } + void RemoveListener() + { + listener_ = nullptr; + } + + void LoadModuleEvent(std::string_view name, std::string_view entryPoint) const + { + if (UNLIKELY(listener_ != nullptr)) { + listener_->LoadModule(name, entryPoint); + } + } + + void BytecodePcChangedEvent(JSThread *thread, Method *method, uint32_t bcOffset) const + { + if (UNLIKELY(listener_ != nullptr)) { + JSHandle methodHandle(thread, method); + listener_->BytecodePcChanged(thread, methodHandle, bcOffset); + } + } + + void NativeCallingEvent(const void *nativeAddress) const + { + if (UNLIKELY(listener_ != nullptr)) { + listener_->NativeCalling(nativeAddress); + } + } + + void PendingJobEntryEvent() const + { + if (UNLIKELY(listener_ != nullptr)) { + listener_->PendingJobEntry(); + } + } + + void VmStartEvent() const + { + if (UNLIKELY(listener_ != nullptr)) { + listener_->VmStart(); + } + } + void VmDeathEvent() const + { + if (UNLIKELY(listener_ != nullptr)) { + listener_->VmDeath(); + } + } + +private: + RuntimeListener *listener_ {nullptr}; +}; +} // panda::ecmascript::tooling + +#endif // ECMASCRIPT_TOOLING_INTERFACE_NOTIFICATION_MANAGER_H \ No newline at end of file diff --git a/tooling/interface/progress.h b/tooling/interface/progress.h new file mode 100644 index 00000000..8ef98ebb --- /dev/null +++ b/tooling/interface/progress.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_INTERFACE_PROGRESS_H +#define ECMASCRIPT_TOOLING_INTERFACE_PROGRESS_H + +#include + +namespace panda::ecmascript { +class Progress { +public: + virtual ~Progress() = default; + + virtual void ReportProgress(int32_t done, int32_t total) = 0; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_TOOLING_INTERFACE_PROGRESS_H diff --git a/tooling/interface/stream.h b/tooling/interface/stream.h new file mode 100644 index 00000000..81d7dd42 --- /dev/null +++ b/tooling/interface/stream.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_INTERFACE_STREAM_H +#define ECMASCRIPT_TOOLING_INTERFACE_STREAM_H + +namespace panda::ecmascript { +class HeapStat { +public: + HeapStat(int32_t index, int32_t count, int32_t size) + : index_(index), count_(count), size_(size) {} + ~HeapStat() = default; + + int32_t index_; + int32_t count_; + int32_t size_; +}; + +class Stream { +public: + virtual ~Stream() = default; + + virtual void EndOfStream() = 0; + + // Get chunk's size + virtual int GetSize() = 0; + + // Writes the chunk of data into the stream + virtual bool WriteChunk(char *data, int32_t size) = 0; + virtual bool Good() = 0; + virtual void UpdateHeapStats(HeapStat* data, int32_t count) = 0; + virtual void UpdateLastSeenObjectId(int32_t lastSeenObjectId) = 0; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_TOOLING_INTERFACE_STREAM_H diff --git a/tooling/protocol_channel.h b/tooling/protocol_channel.h new file mode 100644 index 00000000..3c2d7ba2 --- /dev/null +++ b/tooling/protocol_channel.h @@ -0,0 +1,43 @@ +/* + * 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_PROTOCOL_CHANNEL_H +#define ECMASCRIPT_TOOLING_PROTOCOL_CHANNEL_H + +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/dispatcher.h" + +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +class ProtocolChannel { +public: + ProtocolChannel() = default; + virtual ~ProtocolChannel() = default; + + virtual void WaitForDebugger() = 0; + virtual void RunIfWaitingForDebugger() = 0; + virtual void SendResponse(const DispatchRequest &request, const DispatchResponse &response, + const PtBaseReturns &result) = 0; + virtual void SendNotification(const PtBaseEvents &events) = 0; + +private: + NO_COPY_SEMANTIC(ProtocolChannel); + NO_MOVE_SEMANTIC(ProtocolChannel); +}; +} // namespace panda::ecmascript::tooling + +#endif // ECMASCRIPT_TOOLING_PROTOCOL_CHANNEL_H \ No newline at end of file diff --git a/tooling/protocol_handler.cpp b/tooling/protocol_handler.cpp new file mode 100644 index 00000000..edb70ff8 --- /dev/null +++ b/tooling/protocol_handler.cpp @@ -0,0 +1,131 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/protocol_handler.h" + +#include "ecmascript/tooling/agent/debugger_impl.h" + +namespace panda::ecmascript::tooling { +void ProtocolHandler::WaitForDebugger() +{ + waitingForDebugger_ = true; + ProcessCommand(); +} + +void ProtocolHandler::RunIfWaitingForDebugger() +{ + waitingForDebugger_ = false; +} + +void ProtocolHandler::DispatchCommand(std::string &&msg) +{ + LOG_DEBUGGER(DEBUG) << "ProtocolHandler::DispatchCommand: " << msg; + std::unique_lock queueLock(requestLock_); + requestQueue_.push(std::move(msg)); + requestQueueCond_.notify_one(); +} + +// called after DispatchCommand +int32_t ProtocolHandler::GetDispatchStatus() +{ + if (isDispatchingMessage_ || waitingForDebugger_) { + return DispatchStatus::DISPATCHING; + } + std::unique_lock queueLock(requestLock_); + if (requestQueue_.empty()) { + return DispatchStatus::DISPATCHED; + } + return DispatchStatus::UNKNOWN; +} + +void ProtocolHandler::ProcessCommand() +{ + std::queue dispatchingQueue; + do { + { + std::unique_lock queueLock(requestLock_); + if (requestQueue_.empty()) { + if (!waitingForDebugger_) { + return; + } + requestQueueCond_.wait(queueLock); + } + requestQueue_.swap(dispatchingQueue); + } + + isDispatchingMessage_ = true; + while (!dispatchingQueue.empty()) { + std::string msg = std::move(dispatchingQueue.front()); + dispatchingQueue.pop(); + + [[maybe_unused]] LocalScope scope(vm_); + auto exception = DebuggerApi::GetAndClearException(vm_); + dispatcher_.Dispatch(DispatchRequest(msg)); + DebuggerApi::SetException(vm_, exception); + } + isDispatchingMessage_ = false; + } while (true); +} + +void ProtocolHandler::SendResponse(const DispatchRequest &request, const DispatchResponse &response, + const PtBaseReturns &result) +{ + LOG_DEBUGGER(INFO) << "ProtocolHandler::SendResponse: " + << (response.IsOk() ? "success" : "failed: " + response.GetMessage()); + + std::unique_ptr reply = PtJson::CreateObject(); + reply->Add("id", request.GetCallId()); + std::unique_ptr resultObj; + if (response.IsOk()) { + resultObj = result.ToJson(); + } else { + resultObj = CreateErrorReply(response); + } + reply->Add("result", resultObj); + SendReply(*reply); + reply->ReleaseRoot(); +} + +void ProtocolHandler::SendNotification(const PtBaseEvents &events) +{ + LOG_DEBUGGER(DEBUG) << "ProtocolHandler::SendNotification: " << events.GetName(); + std::unique_ptr reply = events.ToJson(); + SendReply(*reply); + reply->ReleaseRoot(); +} + +void ProtocolHandler::SendReply(const PtJson &reply) +{ + std::string str = reply.Stringify(); + if (str.empty()) { + LOG_DEBUGGER(ERROR) << "ProtocolHandler::SendReply: json stringify error"; + return; + } + + callback_(reinterpret_cast(vm_), str); +} + +std::unique_ptr ProtocolHandler::CreateErrorReply(const DispatchResponse &response) +{ + std::unique_ptr result = PtJson::CreateObject(); + + if (!response.IsOk()) { + result->Add("code", static_cast(response.GetError())); + result->Add("message", response.GetMessage().c_str()); + } + + return result; +} +} // namespace panda::ecmascript::tooling diff --git a/tooling/protocol_handler.h b/tooling/protocol_handler.h new file mode 100644 index 00000000..1a0cb129 --- /dev/null +++ b/tooling/protocol_handler.h @@ -0,0 +1,68 @@ +/* + * 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_PROTOCOL_HANDLER_H +#define ECMASCRIPT_TOOLING_PROTOCOL_HANDLER_H + +#include +#include +#include +#include + +#include "ecmascript/tooling/protocol_channel.h" + +namespace panda::ecmascript::tooling { +class ProtocolHandler final : public ProtocolChannel { +public: + enum DispatchStatus : int32_t { + UNKNOWN = 0, + DISPATCHING, + DISPATCHED + }; + + ProtocolHandler(std::function callback, const EcmaVM *vm) + : callback_(std::move(callback)), dispatcher_(vm, this), vm_(vm) {} + ~ProtocolHandler() override = default; + + void WaitForDebugger() override; + void RunIfWaitingForDebugger() override; + void ProcessCommand(); + void DispatchCommand(std::string &&msg); + int32_t GetDispatchStatus(); + + void SendResponse(const DispatchRequest &request, const DispatchResponse &response, + const PtBaseReturns &result) override; + void SendNotification(const PtBaseEvents &events) override; + +private: + NO_MOVE_SEMANTIC(ProtocolHandler); + NO_COPY_SEMANTIC(ProtocolHandler); + std::unique_ptr CreateErrorReply(const DispatchResponse &response); + void SendReply(const PtJson &reply); + + std::function callback_; + Dispatcher dispatcher_; + + bool waitingForDebugger_ {false}; + const EcmaVM *vm_ {nullptr}; + + std::condition_variable requestQueueCond_; + std::queue requestQueue_; + std::mutex requestLock_; + std::atomic isDispatchingMessage_ {false}; +}; +} // namespace panda::ecmascript::tooling + +#endif diff --git a/tooling/test/BUILD.gn b/tooling/test/BUILD.gn new file mode 100644 index 00000000..fd120a75 --- /dev/null +++ b/tooling/test/BUILD.gn @@ -0,0 +1,206 @@ +# 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. + +import("//arkcompiler/ets_frontend/ts2panda/ts2abc_config.gni") +import("//arkcompiler/ets_runtime/js_runtime_config.gni") +import("//arkcompiler/ets_runtime/test/test_helper.gni") +import("//build/ohos.gni") +import("//build/test.gni") + +module_output_path = "arkcompiler/ets_runtime" + +config("debug_api_test") { + visibility = [ ":*" ] + + ldflags = [ "-Wl,-rpath=\$ORIGIN/" ] + + configs = [ "//arkcompiler/ets_runtime:ecma_test_config" ] + + include_dirs = [ + "//arkcompiler/ets_runtime", + "//arkcompiler/ets_runtime/ecmascript/tooling/test", + ] +} + +test_js_path = "//arkcompiler/ets_runtime/ecmascript/tooling/test/testcases/js/" + +# add new js test file here and modify test/resource/js_runtime/ohos_test.xml +test_js_files = [ + "arrow_func", + "async_func", + "exception", + "range_error", + "sample", + "step", + "syntax_exception", + "throw_exception", +] + +foreach(file, test_js_files) { + ts2abc_gen_abc("gen_${file}_abc") { + test_js = "${test_js_path}${file}.js" + test_abc = "$target_out_dir/${file}.abc" + extra_visibility = [ ":*" ] # Only targets in this file can depend on this. + src_js = rebase_path(test_js) + dst_file = rebase_path(test_abc) + extra_args = [ "--debug" ] + + in_puts = [ test_js ] + out_puts = [ test_abc ] + } +} + +source_set("debugger_entry_set") { + sources = [ "entry/test_debugger_entry.cpp" ] + + public_configs = [ ":debug_api_test" ] + + deps = [ ":jsdebugtest" ] + + foreach(file, test_js_files) { + deps += [ ":gen_${file}_abc" ] + } +} + +ohos_shared_library("debugger_entry") { + deps = [ ":debugger_entry_set" ] + + install_enable = false + + output_extension = "so" + subsystem_name = "test" +} + +source_set("jsdebugtest_set") { + sources = [ + "utils/test_entry.cpp", + "utils/test_extractor.cpp", + "utils/test_list.cpp", + "utils/test_util.cpp", + ] + + public_configs = [ + ":debug_api_test", + "//arkcompiler/ets_runtime/ecmascript/tooling:ark_ecma_debugger_config", + ] + + test_abc_dir = "/data/test/" + target_label = get_label_info(":${target_name}", "label_with_toolchain") + target_toolchain = get_label_info(target_label, "toolchain") + if (target_toolchain == host_toolchain) { + test_abc_dir = rebase_path(target_out_dir) + } + + defines = [ "DEBUGGER_ABC_DIR=\"${test_abc_dir}/\"" ] + + if (is_ohos && is_standard_system) { + if (enable_hilog) { + defines += [ "ENABLE_HILOG" ] + include_dirs = + [ "//base/hiviewdfx/hilog/interfaces/native/innerkits/include" ] + } + } + + deps = [ + "//arkcompiler/ets_runtime:libark_jsruntime_test", + "//arkcompiler/ets_runtime/ecmascript/tooling:libark_ecma_debugger_test", + ] +} + +ohos_shared_library("jsdebugtest") { + deps = [ ":jsdebugtest_set" ] + + if (is_ohos && is_standard_system) { + if (enable_hilog) { + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + } + } + + install_enable = false + + output_extension = "so" + subsystem_name = "test" +} + +host_unittest_action("DebuggerEntryTest") { + module_out_path = module_output_path + + sources = [ + # test file + "debugger_entry_test.cpp", + ] + + cflags_cc = [ "-Wno-gnu-zero-variadic-macro-arguments" ] + + defines = [ "DEBUGGER_TEST_LIBRARY=\"libdebugger_entry.so\"" ] + + configs = [ ":debug_api_test" ] + + deps = [ + ":debugger_entry_resource", + ":jsdebugtest", + "//arkcompiler/ets_runtime:libark_jsruntime_test", + "//arkcompiler/ets_runtime/ecmascript/tooling:libark_ecma_debugger_test", + ] +} + +host_unittest_action("DebuggerTest") { + module_out_path = module_output_path + + sources = [ + # test file + "debugger_commands_test.cpp", + "debugger_events_test.cpp", + "debugger_params_test.cpp", + "debugger_returns_test.cpp", + "debugger_script_test.cpp", + "debugger_types_test.cpp", + "js_pt_hooks_test.cpp", + "pt_base64_test.cpp", + "pt_json_test.cpp", + ] + + configs = [ "//arkcompiler/ets_runtime:ecma_test_config" ] + + deps = [ + "//arkcompiler/ets_runtime:libark_jsruntime_test", + "//arkcompiler/ets_runtime/ecmascript/tooling:libark_ecma_debugger_test", + sdk_libc_secshared_dep, + ] +} + +group("debugger_entry_resource") { + testonly = true + + deps = [ ":debugger_entry" ] +} + +group("unittest") { + testonly = true + + # deps file + deps = [ + ":DebuggerEntryTest", + ":DebuggerTest", + ] +} + +group("host_unittest") { + testonly = true + + # deps file + deps = [ + ":DebuggerEntryTestAction", + ":DebuggerTestAction", + ] +} diff --git a/tooling/test/debugger_commands_test.cpp b/tooling/test/debugger_commands_test.cpp new file mode 100644 index 00000000..43e04f30 --- /dev/null +++ b/tooling/test/debugger_commands_test.cpp @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/debugger_service.h" +#include "ecmascript/tooling/dispatcher.h" +#include "ecmascript/tooling/backend/js_debugger.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::tooling; + +namespace panda::test { +class DebuggerCommandsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + Logger::InitializeStdLogging(Logger::Level::FATAL, LoggerComponentMaskAll); + } + + static void TearDownTestCase() + { + Logger::InitializeStdLogging(Logger::Level::ERROR, LoggerComponentMaskAll); + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(ecmaVm, thread, scope); + // Main logic is JSON parser, so not need trigger GC to decrease execute time + ecmaVm->SetEnableForceGC(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope); + } + +protected: + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(DebuggerCommandsTest, CreateDebuggerTest) +{ + std::unique_ptr debugger = std::make_unique(ecmaVm); + ASSERT_NE(debugger, nullptr); +} +} // namespace panda::test \ No newline at end of file diff --git a/tooling/test/debugger_entry_test.cpp b/tooling/test/debugger_entry_test.cpp new file mode 100644 index 00000000..53373d8e --- /dev/null +++ b/tooling/test/debugger_entry_test.cpp @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/test/utils/test_list.h" + +namespace panda::ecmascript::tooling::test { +using panda::test::TestHelper; + +class DebuggerEntryTest : public testing::TestWithParam { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + SetCurrentTestName(GetParam()); + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + JSNApi::StartDebugger(DEBUGGER_TEST_LIBRARY, instance, true); + } + + void TearDown() override + { + JSNApi::StopDebugger(instance); + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + EcmaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_P_L0(DebuggerEntryTest, DebuggerSuite) +{ + std::string testName = GetCurrentTestName(); + std::cout << "Running " << testName << std::endl; + ASSERT_NE(instance, nullptr); + auto [pandaFile, entryPoint] = GetTestEntryPoint(testName); + auto res = JSNApi::Execute(instance, pandaFile.c_str(), entryPoint.c_str()); + ASSERT_TRUE(res); +} + +INSTANTIATE_TEST_SUITE_P(DebugAbcTest, DebuggerEntryTest, testing::ValuesIn(GetTestList())); +} // namespace panda::ecmascript::tooling::test diff --git a/tooling/test/debugger_events_test.cpp b/tooling/test/debugger_events_test.cpp new file mode 100644 index 00000000..b78e20ce --- /dev/null +++ b/tooling/test/debugger_events_test.cpp @@ -0,0 +1,436 @@ +/* + * 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. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/dispatcher.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::tooling; +using ObjectType = RemoteObject::TypeName; +using ObjectSubType = RemoteObject::SubTypeName; +using ObjectClassName = RemoteObject::ClassName; + +namespace panda::test { +class DebuggerEventsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + Logger::InitializeStdLogging(Logger::Level::FATAL, LoggerComponentMaskAll); + } + + static void TearDownTestCase() + { + Logger::InitializeStdLogging(Logger::Level::ERROR, LoggerComponentMaskAll); + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(ecmaVm, thread, scope); + // Main logic is JSON parser, so not need trigger GC to decrease execute time + ecmaVm->SetEnableForceGC(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope); + } + +protected: + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(DebuggerEventsTest, BreakpointResolvedToJsonTest) +{ + BreakpointResolved breakpointResolved; + + auto location = std::make_unique(); + location->SetScriptId(2).SetLine(99); + breakpointResolved.SetBreakpointId("00").SetLocation(std::move(location)); + + std::unique_ptr json; + ASSERT_EQ(breakpointResolved.ToJson()->GetObject("params", &json), Result::SUCCESS); + std::string breakpointId; + ASSERT_EQ(json->GetString("breakpointId", &breakpointId), Result::SUCCESS); + EXPECT_EQ(breakpointId, "00"); + + std::unique_ptr locationJson; + ASSERT_EQ(json->GetObject("location", &locationJson), Result::SUCCESS); + std::string scriptId; + ASSERT_EQ(locationJson->GetString("scriptId", &scriptId), Result::SUCCESS); + EXPECT_EQ(scriptId, "2"); + int32_t lineNumber; + ASSERT_EQ(locationJson->GetInt("lineNumber", &lineNumber), Result::SUCCESS); + EXPECT_EQ(lineNumber, 99); +} + +HWTEST_F_L0(DebuggerEventsTest, PausedToJsonTest) +{ + Paused paused; + std::vector> v; + paused.SetCallFrames(std::move(v)) + .SetReason(PauseReason::EXCEPTION); + + std::unique_ptr json = paused.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + std::string reason; + ASSERT_EQ(params->GetString("reason", &reason), Result::SUCCESS); + EXPECT_EQ("exception", reason); + std::unique_ptr callFrames; + ASSERT_EQ(params->GetArray("callFrames", &callFrames), Result::SUCCESS); +} + +HWTEST_F_L0(DebuggerEventsTest, ResumedToJsonTest) +{ + Resumed resumed; + std::unique_ptr json = resumed.ToJson(); + + std::string method; + ASSERT_EQ(json->GetString("method", &method), Result::SUCCESS); + EXPECT_EQ(resumed.GetName(), method); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); +} + +HWTEST_F_L0(DebuggerEventsTest, ScriptFailedToParseToJsonTest) +{ + ScriptFailedToParse parsed; + parsed.SetScriptId(100) + .SetUrl("use/test.js") + .SetStartLine(0) + .SetStartColumn(4) + .SetEndLine(10) + .SetEndColumn(10) + .SetExecutionContextId(2) + .SetHash("hash0001") + .SetSourceMapURL("usr/") + .SetHasSourceURL(true) + .SetIsModule(true) + .SetLength(34) + .SetCodeOffset(432) + .SetScriptLanguage("JavaScript") + .SetEmbedderName("hh"); + + std::unique_ptr json = parsed.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + std::string tmpStr; + ASSERT_EQ(params->GetString("scriptId", &tmpStr), Result::SUCCESS); + EXPECT_EQ("100", tmpStr); + + ASSERT_EQ(params->GetString("url", &tmpStr), Result::SUCCESS); + EXPECT_EQ("use/test.js", tmpStr); + + int tmpInt; + ASSERT_EQ(params->GetInt("startLine", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 0); + + ASSERT_EQ(params->GetInt("startColumn", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 4); + + ASSERT_EQ(params->GetInt("endLine", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); + + ASSERT_EQ(params->GetInt("endColumn", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); + + ASSERT_EQ(params->GetInt("executionContextId", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 2); + + ASSERT_EQ(params->GetString("hash", &tmpStr), Result::SUCCESS); + EXPECT_EQ("hash0001", tmpStr); + + ASSERT_EQ(params->GetString("sourceMapURL", &tmpStr), Result::SUCCESS); + EXPECT_EQ("usr/", tmpStr); + + bool tmpBool; + ASSERT_EQ(params->GetBool("hasSourceURL", &tmpBool), Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ASSERT_EQ(params->GetBool("isModule", &tmpBool), Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ASSERT_EQ(params->GetInt("length", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 34); + + ASSERT_EQ(params->GetInt("codeOffset", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 432); + + ASSERT_EQ(params->GetString("scriptLanguage", &tmpStr), Result::SUCCESS); + EXPECT_EQ("JavaScript", tmpStr); + + ASSERT_EQ(params->GetString("embedderName", &tmpStr), Result::SUCCESS); + EXPECT_EQ("hh", tmpStr); +} + +HWTEST_F_L0(DebuggerEventsTest, ScriptParsedToJsonTest) +{ + ScriptParsed parsed; + parsed.SetScriptId(10) + .SetUrl("use/test.js") + .SetStartLine(0) + .SetStartColumn(4) + .SetEndLine(10) + .SetEndColumn(10) + .SetExecutionContextId(2) + .SetHash("hash0001") + .SetIsLiveEdit(true) + .SetSourceMapURL("usr/") + .SetHasSourceURL(true) + .SetIsModule(true) + .SetLength(34) + .SetCodeOffset(432) + .SetScriptLanguage("JavaScript") + .SetEmbedderName("hh"); + + std::unique_ptr json = parsed.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + std::string tmpStr; + ASSERT_EQ(params->GetString("scriptId", &tmpStr), Result::SUCCESS); + EXPECT_EQ("10", tmpStr); + + ASSERT_EQ(params->GetString("url", &tmpStr), Result::SUCCESS); + EXPECT_EQ("use/test.js", tmpStr); + + int tmpInt; + ASSERT_EQ(params->GetInt("startLine", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 0); + + ASSERT_EQ(params->GetInt("startColumn", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 4); + + ASSERT_EQ(params->GetInt("endLine", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); + + ASSERT_EQ(params->GetInt("endColumn", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); + + ASSERT_EQ(params->GetInt("executionContextId", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 2); + + ASSERT_EQ(params->GetString("hash", &tmpStr), Result::SUCCESS); + EXPECT_EQ("hash0001", tmpStr); + + bool tmpBool; + ASSERT_EQ(params->GetBool("isLiveEdit", &tmpBool), Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ASSERT_EQ(params->GetString("sourceMapURL", &tmpStr), Result::SUCCESS); + EXPECT_EQ("usr/", tmpStr); + + ASSERT_EQ(params->GetBool("hasSourceURL", &tmpBool), Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ASSERT_EQ(params->GetBool("isModule", &tmpBool), Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ASSERT_EQ(params->GetInt("length", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 34); + + ASSERT_EQ(params->GetInt("codeOffset", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 432); + + ASSERT_EQ(params->GetString("scriptLanguage", &tmpStr), Result::SUCCESS); + EXPECT_EQ("JavaScript", tmpStr); + + ASSERT_EQ(params->GetString("embedderName", &tmpStr), Result::SUCCESS); + EXPECT_EQ("hh", tmpStr); +} + +HWTEST_F_L0(DebuggerEventsTest, ConsoleProfileFinishedToJsonTest) +{ + ConsoleProfileFinished consoleProfileFinished; + + auto location = std::make_unique(); + location->SetScriptId(13).SetLine(20); + std::vector> v; + auto profile = std::make_unique(); + profile->SetNodes(std::move(v)) + .SetStartTime(0) + .SetEndTime(15) + .SetSamples(std::vector{}) + .SetTimeDeltas(std::vector{}); + consoleProfileFinished.SetId("11").SetLocation(std::move(location)).SetProfile(std::move(profile)).SetTitle("001"); + + std::unique_ptr json = consoleProfileFinished.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + std::string tmpStr; + ASSERT_EQ(params->GetString("id", &tmpStr), Result::SUCCESS); + EXPECT_EQ("11", tmpStr); + + std::unique_ptr tmpJson; + ASSERT_EQ(params->GetObject("location", &tmpJson), Result::SUCCESS); + ASSERT_EQ(params->GetObject("profile", &tmpJson), Result::SUCCESS); + + ASSERT_EQ(params->GetString("title", &tmpStr), Result::SUCCESS); + EXPECT_EQ("001", tmpStr); +} + +HWTEST_F_L0(DebuggerEventsTest, ConsoleProfileStartedToJsonTest) +{ + ConsoleProfileStarted consoleProfileStarted; + + auto location = std::make_unique(); + location->SetScriptId(17).SetLine(30); + consoleProfileStarted.SetId("12").SetLocation(std::move(location)).SetTitle("002"); + + std::unique_ptr json = consoleProfileStarted.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + std::string tmpStr; + ASSERT_EQ(params->GetString("id", &tmpStr), Result::SUCCESS); + EXPECT_EQ("12", tmpStr); + + std::unique_ptr tmpJson = consoleProfileStarted.GetLocation()->ToJson(); + ASSERT_EQ(tmpJson->GetString("scriptId", &tmpStr), Result::SUCCESS); + EXPECT_EQ("17", tmpStr); + int tmpInt; + ASSERT_EQ(tmpJson->GetInt("lineNumber", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 30); + + ASSERT_EQ(params->GetString("title", &tmpStr), Result::SUCCESS); + EXPECT_EQ("002", tmpStr); +} + +HWTEST_F_L0(DebuggerEventsTest, PreciseCoverageDeltaUpdateToJsonTest) +{ + PreciseCoverageDeltaUpdate preciseCoverageDeltaUpdate; + + std::vector> v; + preciseCoverageDeltaUpdate.SetOccasion("percise") + .SetResult(std::move(v)) + .SetTimestamp(77); + + std::unique_ptr json = preciseCoverageDeltaUpdate.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + int64_t tmpInt; + ASSERT_EQ(params->GetInt64("timestamp", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 77); + + std::string tmpStr; + ASSERT_EQ(params->GetString("occasion", &tmpStr), Result::SUCCESS); + EXPECT_EQ("percise", tmpStr); + + std::unique_ptr tmpArray; + ASSERT_EQ(params->GetArray("result", &tmpArray), Result::SUCCESS); +} + +HWTEST_F_L0(DebuggerEventsTest, HeapStatsUpdateToJsonTest) +{ + HeapStatsUpdate heapStatsUpdate; + heapStatsUpdate.SetStatsUpdate(std::vector {}); + + std::unique_ptr json = heapStatsUpdate.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + std::unique_ptr tmpArray; + ASSERT_EQ(params->GetArray("statsUpdate", &tmpArray), Result::SUCCESS); +} + +HWTEST_F_L0(DebuggerEventsTest, LastSeenObjectIdToJsonTest) +{ + LastSeenObjectId lastSeenObjectId; + lastSeenObjectId.SetLastSeenObjectId(10).SetTimestamp(77); + + std::unique_ptr json = lastSeenObjectId.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + int64_t tmpInt64; + ASSERT_EQ(params->GetInt64("timestamp", &tmpInt64), Result::SUCCESS); + EXPECT_EQ(tmpInt64, 77); + + int tmpInt; + ASSERT_EQ(params->GetInt("lastSeenObjectId", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); +} + +HWTEST_F_L0(DebuggerEventsTest, ReportHeapSnapshotProgressToJsonTest) +{ + ReportHeapSnapshotProgress reportHeapSnapshotProgress; + reportHeapSnapshotProgress.SetDone(10).SetTotal(100); + + std::unique_ptr json = reportHeapSnapshotProgress.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + int tmpInt; + ASSERT_EQ(params->GetInt("done", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); + + ASSERT_EQ(params->GetInt("total", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 100); +} + +HWTEST_F_L0(DebuggerEventsTest, BufferUsageToJsonTest) +{ + BufferUsage bufferUsage; + bufferUsage.SetPercentFull(17).SetEventCount(15).SetValue(12); + + std::unique_ptr json = bufferUsage.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + int tmpInt; + ASSERT_EQ(params->GetInt("percentFull", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 17); + + ASSERT_EQ(params->GetInt("eventCount", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 15); + + ASSERT_EQ(params->GetInt("value", &tmpInt), Result::SUCCESS); + EXPECT_EQ(tmpInt, 12); +} + +HWTEST_F_L0(DebuggerEventsTest, TracingCompleteToJsonTest) +{ + TracingComplete tracingComplete; + auto traceFormat = std::make_unique(); + auto streamCompression = std::make_unique(); + tracingComplete.SetDataLossOccurred(true) + .SetTraceFormat(std::move(traceFormat)) + .SetStreamCompression(std::move(streamCompression)); + + std::unique_ptr json = tracingComplete.ToJson(); + std::unique_ptr params; + ASSERT_EQ(json->GetObject("params", ¶ms), Result::SUCCESS); + + bool tmpBool; + ASSERT_EQ(params->GetBool("dataLossOccurred", &tmpBool), Result::SUCCESS); + + std::string tmpStr; + ASSERT_EQ(params->GetString("traceFormat", &tmpStr), Result::SUCCESS); + ASSERT_EQ(params->GetString("streamCompression", &tmpStr), Result::SUCCESS); +} +} // namespace panda::test \ No newline at end of file diff --git a/tooling/test/debugger_params_test.cpp b/tooling/test/debugger_params_test.cpp new file mode 100644 index 00000000..6b2e83a0 --- /dev/null +++ b/tooling/test/debugger_params_test.cpp @@ -0,0 +1,476 @@ +/* + * 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. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/dispatcher.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::tooling; + +namespace panda::test { +// Duplicate name of panda::ecmascript::PropertyDescriptor in js_object-inl.h +using panda::ecmascript::tooling::PropertyDescriptor; + +using ObjectType = RemoteObject::TypeName; +using ObjectSubType = RemoteObject::SubTypeName; +using ObjectClassName = RemoteObject::ClassName; + +class DebuggerParamsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + Logger::InitializeStdLogging(Logger::Level::FATAL, LoggerComponentMaskAll); + } + + static void TearDownTestCase() + { + Logger::InitializeStdLogging(Logger::Level::ERROR, LoggerComponentMaskAll); + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(ecmaVm, thread, scope); + // Main logic is JSON parser, so not need trigger GC to decrease execute time + ecmaVm->SetEnableForceGC(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope); + } + +protected: + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(DebuggerParamsTest, EnableParamsCreateTest) +{ + std::string msg; + std::unique_ptr enableParams; + + // abnormal + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + enableParams = EnableParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(enableParams, nullptr); + EXPECT_FALSE(enableParams->HasMaxScriptsCacheSize()); + + // normal + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"maxScriptsCacheSize":100}})"; + enableParams = EnableParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(enableParams, nullptr); + EXPECT_EQ(enableParams->GetMaxScriptsCacheSize(), 100); +} + +HWTEST_F_L0(DebuggerParamsTest, StartSamplingParamsCreateTest) +{ + std::string msg; + std::unique_ptr startSamplingData; + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + startSamplingData = StartSamplingParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(startSamplingData, nullptr); + EXPECT_EQ(startSamplingData->GetSamplingInterval(), 32768); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + startSamplingData = StartSamplingParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(startSamplingData, nullptr); + EXPECT_EQ(startSamplingData->GetSamplingInterval(), 32768); + + // abnormal params of params.sub-key=["samplingInterval":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"samplingInterval":true}})"; + startSamplingData = StartSamplingParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(startSamplingData, nullptr); + + // abnormal params of params.sub-key=["samplingInterval":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"samplingInterval":"Test"}})"; + startSamplingData = StartSamplingParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(startSamplingData, nullptr); + + // abnormal params of params.sub-key = [ "size"=100,"nodeId"=1,"ordinal"=10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"samplingInterval":1000}})"; + startSamplingData = StartSamplingParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(startSamplingData, nullptr); + EXPECT_EQ(startSamplingData->GetSamplingInterval(), 1000); +} + +HWTEST_F_L0(DebuggerParamsTest, StartTrackingHeapObjectsParamsCreateTest) +{ + std::string msg; + std::unique_ptr objectData; + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + objectData = StartTrackingHeapObjectsParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + ASSERT_FALSE(objectData->GetTrackAllocations()); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + objectData = StartTrackingHeapObjectsParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + ASSERT_FALSE(objectData->GetTrackAllocations()); + + // abnormal params of params.sub-key=["trackAllocations":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"trackAllocations":10}})"; + objectData = StartTrackingHeapObjectsParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of params.sub-key=["trackAllocations":"Test"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"trackAllocations":"Test"}})"; + objectData = StartTrackingHeapObjectsParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of params.sub-key=["trackAllocations":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"trackAllocations":true}})"; + objectData = StartTrackingHeapObjectsParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + ASSERT_TRUE(objectData->GetTrackAllocations()); +} + +HWTEST_F_L0(DebuggerParamsTest, StopTrackingHeapObjectsParamsCreateTest) +{ + std::string msg; + std::unique_ptr objectData; + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + objectData = StopTrackingHeapObjectsParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + ASSERT_FALSE(objectData->GetReportProgress()); + ASSERT_FALSE(objectData->GetTreatGlobalObjectsAsRoots()); + ASSERT_FALSE(objectData->GetCaptureNumericValue()); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + objectData = StopTrackingHeapObjectsParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + ASSERT_FALSE(objectData->GetReportProgress()); + ASSERT_FALSE(objectData->GetTreatGlobalObjectsAsRoots()); + ASSERT_FALSE(objectData->GetCaptureNumericValue()); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "reportProgress":10, + "treatGlobalObjectsAsRoots":10, + "captureNumericValue":10}})"; + objectData = StopTrackingHeapObjectsParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "reportProgress":"Test", + "treatGlobalObjectsAsRoots":"Test", + "captureNumericValue":"Test"}})"; + objectData = StopTrackingHeapObjectsParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "reportProgress":true, + "treatGlobalObjectsAsRoots":true, + "captureNumericValue":true}})"; + objectData = StopTrackingHeapObjectsParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + ASSERT_TRUE(objectData->GetReportProgress()); + ASSERT_TRUE(objectData->GetTreatGlobalObjectsAsRoots()); + ASSERT_TRUE(objectData->GetCaptureNumericValue()); +} + +HWTEST_F_L0(DebuggerParamsTest, AddInspectedHeapObjectParamsCreateTest) +{ + std::string msg; + std::unique_ptr objectData; + + // abnormal params of null msg + msg = std::string() + R"({})"; + objectData = AddInspectedHeapObjectParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + objectData = AddInspectedHeapObjectParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + objectData = AddInspectedHeapObjectParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + objectData = AddInspectedHeapObjectParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of params.sub-key=["heapObjectId":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"heapObjectId":10}})"; + objectData = AddInspectedHeapObjectParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of params.sub-key=["heapObjectId":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"heapObjectId":true}})"; + objectData = AddInspectedHeapObjectParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of params.sub-key=["heapObjectId":“10”] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"heapObjectId":"10"}})"; + objectData = AddInspectedHeapObjectParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + EXPECT_EQ(objectData->GetHeapObjectId(), 10); +} + +HWTEST_F_L0(DebuggerParamsTest, GetHeapObjectIdParamsCreateTest) +{ + std::string msg; + std::unique_ptr objectData; + + // abnormal params of params.sub-key=["objectId":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"objectId":10}})"; + objectData = GetHeapObjectIdParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of params.sub-key=["objectId":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"objectId":true}})"; + objectData = GetHeapObjectIdParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of params.sub-key=["objectId":“10”] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"objectId":"10"}})"; + objectData = GetHeapObjectIdParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + EXPECT_EQ((int)objectData->GetObjectId(), 10); +} + +HWTEST_F_L0(DebuggerParamsTest, GetObjectByHeapObjectIdParamsCreateTest) +{ + std::string msg; + std::unique_ptr objectData; + + // abnormal params of params.sub-key=["objectId":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"objectId":10}})"; + objectData = GetObjectByHeapObjectIdParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of params.sub-key=["objectId":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"objectId":"10", "objectGroup":10}})"; + objectData = GetObjectByHeapObjectIdParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of params.sub-key=["objectId":“10”] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"objectId":"10"}})"; + objectData = GetObjectByHeapObjectIdParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + EXPECT_EQ((int)objectData->GetObjectId(), 10); + ASSERT_FALSE(objectData->HasObjectGroup()); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"objectId":"10", "objectGroup":"groupname"}})"; + objectData = GetObjectByHeapObjectIdParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + EXPECT_EQ((int)objectData->GetObjectId(), 10); + EXPECT_EQ(objectData->GetObjectGroup(), "groupname"); +} + +HWTEST_F_L0(DebuggerParamsTest, StartPreciseCoverageParamCreateTest) +{ + std::string msg; + std::unique_ptr objectData; + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + objectData = StartPreciseCoverageParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + objectData = StartPreciseCoverageParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callCount":8, + "detailed":8, + "allowTriggeredUpdates":8}})"; + objectData = StartPreciseCoverageParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callCount":"Test", + "detailed":"Test", + "allowTriggeredUpdates":"Test"}})"; + objectData = StartPreciseCoverageParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callCount":true, + "detailed":true, + "allowTriggeredUpdates":true}})"; + objectData = StartPreciseCoverageParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + ASSERT_TRUE(objectData->GetCallCount()); + ASSERT_TRUE(objectData->GetDetailed()); + ASSERT_TRUE(objectData->GetAllowTriggeredUpdates()); +} + +HWTEST_F_L0(DebuggerParamsTest, SetSamplingIntervalParamsCreateTest) +{ + std::string msg; + std::unique_ptr objectData; + + // abnormal params of null msg + msg = std::string() + R"({})"; + objectData = SetSamplingIntervalParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + objectData = SetSamplingIntervalParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + objectData = SetSamplingIntervalParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + objectData = SetSamplingIntervalParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "interval":"500"}})"; + objectData = SetSamplingIntervalParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"interval":500}})"; + objectData = SetSamplingIntervalParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + EXPECT_EQ(objectData->GetInterval(), 500); +} + +HWTEST_F_L0(DebuggerParamsTest, RecordClockSyncMarkerParamsCreateTest) +{ + std::string msg; + std::unique_ptr objectData; + + // abnormal params of null msg + msg = std::string() + R"({})"; + objectData = RecordClockSyncMarkerParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + objectData = RecordClockSyncMarkerParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + objectData = RecordClockSyncMarkerParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + objectData = RecordClockSyncMarkerParams::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(objectData, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"syncId":"101"}})"; + objectData = RecordClockSyncMarkerParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + EXPECT_EQ(objectData->GetSyncId(), "101"); +} + +HWTEST_F_L0(DebuggerParamsTest, RequestMemoryDumpParamsCreateTest) +{ + std::string msg; + std::unique_ptr objectData; + + // abnormal params of null msg + msg = std::string() + R"({})"; + objectData = RequestMemoryDumpParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + objectData = RequestMemoryDumpParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + objectData = RequestMemoryDumpParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + objectData = RequestMemoryDumpParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"deterministic":true, + "levelOfDetail":"background"}})"; + objectData = RequestMemoryDumpParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + ASSERT_TRUE(objectData->GetDeterministic()); + EXPECT_EQ(objectData->GetLevelOfDetail(), "background"); +} + +HWTEST_F_L0(DebuggerParamsTest, StartParamsCreateTest) +{ + std::string msg; + std::unique_ptr objectData; + + // abnormal params of null msg + msg = std::string() + R"({})"; + objectData = StartParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + objectData = StartParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + objectData = StartParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + objectData = StartParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"categories":"filter1", + "options":"1", "bufferUsageReportingInterval":11, "transferMode":"ReportEvents", "streamFormat":"json", + "streamCompression":"none", "traceConfig": {"recordMode":"recordUntilFull"}, "perfettoConfig":"categories", + "tracingBackend":"auto"}})"; + objectData = StartParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(objectData, nullptr); + EXPECT_EQ(objectData->GetCategories(), "filter1"); + EXPECT_EQ(objectData->GetOptions(), "1"); + EXPECT_EQ(objectData->GetBufferUsageReportingInterval(), 11); + EXPECT_EQ(objectData->GetTransferMode(), "ReportEvents"); + EXPECT_EQ(objectData->GetStreamFormat(), "json"); + EXPECT_EQ(objectData->GetStreamCompression(), "none"); + TraceConfig *traceConfig = objectData->GetTraceConfig(); + ASSERT_NE(traceConfig, nullptr); + EXPECT_EQ(traceConfig->GetRecordMode(), "recordUntilFull"); + EXPECT_EQ(objectData->GetPerfettoConfig(), "categories"); + EXPECT_EQ(objectData->GetTracingBackend(), "auto"); +} +} // namespace panda::test diff --git a/tooling/test/debugger_returns_test.cpp b/tooling/test/debugger_returns_test.cpp new file mode 100644 index 00000000..8ea78952 --- /dev/null +++ b/tooling/test/debugger_returns_test.cpp @@ -0,0 +1,402 @@ +/* + * 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. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/dispatcher.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::tooling; + +namespace panda::test { +// Duplicate name of panda::ecmascript::PropertyDescriptor in js_object-inl.h +using panda::ecmascript::tooling::PropertyDescriptor; +using ObjectType = RemoteObject::TypeName; +using ObjectSubType = RemoteObject::SubTypeName; +using ObjectClassName = RemoteObject::ClassName; + +class DebuggerReturnsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(ecmaVm, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope); + } + +protected: + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(DebuggerReturnsTest, EnableReturnsToJsonTest) +{ + std::unique_ptr enableReturns = std::make_unique(100U); + ASSERT_NE(enableReturns, nullptr); + + std::string debuggerId; + ASSERT_EQ(enableReturns->ToJson()->GetString("debuggerId", &debuggerId), Result::SUCCESS); + EXPECT_EQ(debuggerId, "100"); +} + +HWTEST_F_L0(DebuggerReturnsTest, SetBreakpointByUrlReturnsToJsonTest) +{ + auto locations = std::vector>(); + std::unique_ptr location = std::make_unique(); + location->SetScriptId(1).SetLine(99); + locations.emplace_back(std::move(location)); + std::unique_ptr setBreakpointByUrlReturns + = std::make_unique("11", std::move(locations)); + ASSERT_NE(setBreakpointByUrlReturns, nullptr); + std::string id; + ASSERT_EQ(setBreakpointByUrlReturns->ToJson()->GetString("breakpointId", &id), Result::SUCCESS); + EXPECT_EQ(id, "11"); + + std::unique_ptr locationsJson; + ASSERT_EQ(setBreakpointByUrlReturns->ToJson()->GetArray("locations", &locationsJson), Result::SUCCESS); + ASSERT_NE(locationsJson, nullptr); + EXPECT_EQ(locationsJson->GetSize(), 1); +} + +HWTEST_F_L0(DebuggerReturnsTest, EvaluateOnCallFrameReturnsToJsonTest) +{ + std::unique_ptr result = std::make_unique(); + result->SetType("idle"); + std::unique_ptr exceptionDetails = std::make_unique(); + exceptionDetails->SetExceptionId(12); + std::unique_ptr evaluateOnCallFrameReturns + = std::make_unique(std::move(result), std::move(exceptionDetails)); + ASSERT_NE(evaluateOnCallFrameReturns, nullptr); + std::unique_ptr json; + ASSERT_EQ(evaluateOnCallFrameReturns->ToJson()->GetObject("result", &json), Result::SUCCESS); + std::string type; + ASSERT_EQ(json->GetString("type", &type), Result::SUCCESS); + EXPECT_EQ(type, "idle"); + + std::unique_ptr tmpJson; + ASSERT_EQ(evaluateOnCallFrameReturns->ToJson()->GetObject("exceptionDetails", &tmpJson), Result::SUCCESS); + int32_t exceptionId; + ASSERT_EQ(tmpJson->GetInt("exceptionId", &exceptionId), Result::SUCCESS); + EXPECT_EQ(exceptionId, 12); +} + +HWTEST_F_L0(DebuggerReturnsTest, GetPossibleBreakpointsReturnsToJsonTest) +{ + auto locations = std::vector>(); + std::unique_ptr breakLocation = std::make_unique(); + breakLocation->SetScriptId(11).SetLine(1).SetColumn(44).SetType("idel5"); + locations.emplace_back(std::move(breakLocation)); + std::unique_ptr getPossibleBreakpointsReturns = std::make_unique + (std::move(locations)); + + std::unique_ptr locationsJson; + ASSERT_EQ(getPossibleBreakpointsReturns->ToJson()->GetArray("locations", &locationsJson), Result::SUCCESS); + ASSERT_NE(locationsJson, nullptr); + EXPECT_EQ(locationsJson->GetSize(), 1); +} + +HWTEST_F_L0(DebuggerReturnsTest, GetScriptSourceReturnsToJsonTest) +{ + std::unique_ptr getScriptSourceReturns = std::make_unique + ("source_1", "bytecode_1"); + ASSERT_NE(getScriptSourceReturns, nullptr); + + std::string scriptSource; + ASSERT_EQ(getScriptSourceReturns->ToJson()->GetString("scriptSource", &scriptSource), Result::SUCCESS); + EXPECT_EQ(scriptSource, "source_1"); + + std::string bytecode; + ASSERT_EQ(getScriptSourceReturns->ToJson()->GetString("bytecode", &bytecode), Result::SUCCESS); + EXPECT_EQ(bytecode, "bytecode_1"); +} + +HWTEST_F_L0(DebuggerReturnsTest, RestartFrameReturnsToJsonTest) +{ + auto callFrames = std::vector>(); + std::unique_ptr callFrame = std::make_unique(); + std::unique_ptr location = std::make_unique(); + location->SetScriptId(13).SetLine(16); + + std::unique_ptr res = std::make_unique(); + res->SetType("idle2"); + + callFrame->SetCallFrameId(55); + callFrame->SetLocation(std::move(location)); + callFrame->SetThis(std::move(res)); + callFrames.emplace_back(std::move(callFrame)); + std::unique_ptr restartFrameReturns = std::make_unique + (std::move(callFrames)); + + std::unique_ptr json; + ASSERT_EQ(restartFrameReturns->ToJson()->GetArray("callFrames", &json), Result::SUCCESS); + ASSERT_NE(json, nullptr); + EXPECT_EQ(json->GetSize(), 1); +} + +HWTEST_F_L0(DebuggerReturnsTest, SetBreakpointReturnsToJsonTest) +{ + std::unique_ptr location = std::make_unique(); + std::unique_ptr setBreakpointReturns + = std::make_unique("breakpointId_1", std::move(location)); + ASSERT_NE(setBreakpointReturns, nullptr); + + std::string breakpointId; + ASSERT_EQ(setBreakpointReturns->ToJson()->GetString("breakpointId", &breakpointId), Result::SUCCESS); + EXPECT_EQ(breakpointId, "breakpointId_1"); + + std::unique_ptr tmpJson; + ASSERT_EQ(setBreakpointReturns->ToJson()->GetObject("actualLocation", &tmpJson), Result::SUCCESS); +} + +HWTEST_F_L0(DebuggerReturnsTest, SetInstrumentationBreakpointReturnsToJsonTest) +{ + std::unique_ptr setInstrumentationBreakpointReturns + = std::make_unique("111"); + ASSERT_NE(setInstrumentationBreakpointReturns, nullptr); + + std::string breakpointId; + ASSERT_EQ(setInstrumentationBreakpointReturns->ToJson()->GetString("breakpointId", &breakpointId), + Result::SUCCESS); + EXPECT_EQ(breakpointId, "111"); +} + +HWTEST_F_L0(DebuggerReturnsTest, SetScriptSourceReturnsToJsonTest) +{ + auto callFrames = std::vector>(); + std::unique_ptr callFrame = std::make_unique(); + std::unique_ptr location = std::make_unique(); + location->SetScriptId(3).SetLine(36); + + std::unique_ptr res = std::make_unique(); + res->SetType("idle5"); + + callFrame->SetCallFrameId(33); + callFrame->SetLocation(std::move(location)); + callFrame->SetThis(std::move(res)); + callFrames.emplace_back(std::move(callFrame)); + std::unique_ptr setScriptSourceReturns = std::make_unique + (std::move(callFrames)); + + std::unique_ptr json; + ASSERT_EQ(setScriptSourceReturns->ToJson()->GetArray("callFrames", &json), Result::SUCCESS); + ASSERT_NE(json, nullptr); + EXPECT_EQ(json->GetSize(), 1); +} + +HWTEST_F_L0(DebuggerReturnsTest, GetPropertiesReturnsToJsonTest) +{ + auto descriptor = std::vector>(); + std::unique_ptr propertyDescriptor = std::make_unique(); + propertyDescriptor->SetName("filename1").SetConfigurable(true).SetEnumerable(true); + descriptor.emplace_back(std::move(propertyDescriptor)); + std::unique_ptr getPropertiesReturns = std::make_unique + (std::move(descriptor)); + ASSERT_NE(getPropertiesReturns, nullptr); + + std::unique_ptr json; + ASSERT_EQ(getPropertiesReturns->ToJson()->GetArray("result", &json), Result::SUCCESS); + ASSERT_NE(json, nullptr); + EXPECT_EQ(json->GetSize(), 1); +} + +HWTEST_F_L0(DebuggerReturnsTest, CallFunctionOnReturnsToJsonTest) +{ + std::unique_ptr result = std::make_unique(); + result->SetType("idle2"); + std::unique_ptr exceptionDetails = std::make_unique(); + std::unique_ptr callFunctionOnReturns + = std::make_unique(std::move(result), std::move(exceptionDetails)); + ASSERT_NE(callFunctionOnReturns, nullptr); + std::unique_ptr json; + ASSERT_EQ(callFunctionOnReturns->ToJson()->GetObject("result", &json), Result::SUCCESS); + std::string type; + ASSERT_EQ(json->GetString("type", &type), Result::SUCCESS); + EXPECT_EQ(type, "idle2"); + + std::unique_ptr tmpJson; + ASSERT_EQ(callFunctionOnReturns->ToJson()->GetObject("exceptionDetails", &tmpJson), Result::SUCCESS); +} + +HWTEST_F_L0(DebuggerReturnsTest, StopSamplingReturnsToJsonTest) +{ + auto res = std::vector>(); + std::unique_ptr runtime = std::make_unique(); + std::unique_ptr node = std::make_unique(); + node->SetCallFrame(std::move(runtime)); + std::unique_ptr profile = std::make_unique(); + profile->SetHead(std::move(node)); + profile->SetSamples(std::move(res)); + std::unique_ptr stopSamplingReturns = + std::make_unique(std::move(profile)); + ASSERT_NE(stopSamplingReturns, nullptr); + + std::unique_ptr json; + ASSERT_EQ(stopSamplingReturns->ToJson()->GetObject("profile", &json), Result::SUCCESS); +} + +HWTEST_F_L0(DebuggerReturnsTest, GetHeapObjectIdReturnsToJsonTest) +{ + std::unique_ptr getHeapObjectIdReturns = std::make_unique(10); + ASSERT_NE(getHeapObjectIdReturns, nullptr); + + std::string heapSnapshotObjectId; + ASSERT_EQ(getHeapObjectIdReturns->ToJson()->GetString("heapSnapshotObjectId", &heapSnapshotObjectId), + Result::SUCCESS); + EXPECT_EQ(heapSnapshotObjectId, "10"); +} + +HWTEST_F_L0(DebuggerReturnsTest, GetObjectByHeapObjectIdReturnsToJsonTest) +{ + std::unique_ptr remoteObjectResult = std::make_unique(); + remoteObjectResult->SetType("idle5"); + std::unique_ptr getObjectByHeapObjectIdReturns = + std::make_unique(std::move(remoteObjectResult)); + ASSERT_NE(getObjectByHeapObjectIdReturns, nullptr); + + std::unique_ptr json; + ASSERT_EQ(getObjectByHeapObjectIdReturns->ToJson()->GetObject("result", &json), Result::SUCCESS); + std::string type; + ASSERT_EQ(json->GetString("type", &type), Result::SUCCESS); + EXPECT_EQ(type, "idle5"); +} + +HWTEST_F_L0(DebuggerReturnsTest, StopReturnsToJsonTest) +{ + std::unique_ptr profile = std::make_unique(); + std::unique_ptr stopReturns= std::make_unique(std::move(profile)); + ASSERT_NE(stopReturns, nullptr); + + std::unique_ptr json; + ASSERT_EQ(stopReturns->ToJson()->GetObject("profile", &json), Result::SUCCESS); +} + +HWTEST_F_L0(DebuggerReturnsTest, GetHeapUsageReturnsToJsonTest) +{ + double usedSize = 1; + double totalSize = 1; + std::unique_ptr getHeapUsageReturns = + std::make_unique(usedSize, totalSize); + ASSERT_NE(getHeapUsageReturns, nullptr); + + double pUsedSize; + ASSERT_EQ(getHeapUsageReturns->ToJson()->GetDouble("usedSize", &pUsedSize), Result::SUCCESS); + EXPECT_EQ(pUsedSize, 1); + + double pTotalSize; + ASSERT_EQ(getHeapUsageReturns->ToJson()->GetDouble("totalSize", &pTotalSize), Result::SUCCESS); + EXPECT_EQ(pTotalSize, 1); +} + +HWTEST_F_L0(DebuggerReturnsTest, GetBestEffortCoverageReturnsToJsonTest) +{ + auto result = std::vector>(); + std::unique_ptr scriptCoverage = std::make_unique(); + std::unique_ptr getBestEffortCoverageReturns = + std::make_unique(std::move(result)); + + std::unique_ptr json; + ASSERT_EQ(getBestEffortCoverageReturns->ToJson()->GetArray("result", &json), Result::SUCCESS); + ASSERT_NE(json, nullptr); + EXPECT_EQ(json->GetSize(), 0); +} + +HWTEST_F_L0(DebuggerReturnsTest, StartPreciseCoverageReturnsToJsonTest) +{ + std::unique_ptr startPreciseCoverageReturns + = std::make_unique(1001); + ASSERT_NE(startPreciseCoverageReturns, nullptr); + + int32_t timestamp; + ASSERT_EQ(startPreciseCoverageReturns->ToJson()->GetInt("timestamp", ×tamp), Result::SUCCESS); + EXPECT_EQ(timestamp, 1001); +} + +HWTEST_F_L0(DebuggerReturnsTest, TakePreciseCoverageReturnsToJsonTest) +{ + auto coverage = std::vector>(); + std::unique_ptr takePreciseCoverageReturns = + std::make_unique(std::move(coverage), 1001); + ASSERT_NE(takePreciseCoverageReturns, nullptr); + + std::unique_ptr json; + ASSERT_EQ(takePreciseCoverageReturns->ToJson()->GetArray("result", &json), Result::SUCCESS); + ASSERT_NE(json, nullptr); + EXPECT_EQ(json->GetSize(), 0); + + int32_t timestamp; + ASSERT_EQ(takePreciseCoverageReturns->ToJson()->GetInt("timestamp", ×tamp), Result::SUCCESS); + EXPECT_EQ(timestamp, 1001); +} + +HWTEST_F_L0(DebuggerReturnsTest, TakeTypeProfileturnsToJsonTest) +{ + auto result = std::vector>(); + std::unique_ptr scriptTypeProfile = std::make_unique(); + std::unique_ptr takeTypeProfileturns = std::make_unique + (std::move(result)); + + std::unique_ptr json; + ASSERT_EQ(takeTypeProfileturns->ToJson()->GetArray("result", &json), Result::SUCCESS); + ASSERT_NE(json, nullptr); + EXPECT_EQ(json->GetSize(), 0); +} + +HWTEST_F_L0(DebuggerReturnsTest, GetCategoriesReturnsToJsonTest) +{ + auto result = std::vector(); + std::unique_ptr getCategoriesReturns = std::make_unique + (std::move(result)); + + std::unique_ptr json; + ASSERT_EQ(getCategoriesReturns->ToJson()->GetArray("categories", &json), Result::SUCCESS); + ASSERT_NE(json, nullptr); + EXPECT_EQ(json->GetSize(), 0); +} + +HWTEST_F_L0(DebuggerReturnsTest, RequestMemoryDumpReturnsToJsonTest) +{ + std::unique_ptr requestMemoryDumpReturns + = std::make_unique("123", true); + ASSERT_NE(requestMemoryDumpReturns, nullptr); + + std::string dumpGuid; + ASSERT_EQ(requestMemoryDumpReturns->ToJson()->GetString("dumpGuid", &dumpGuid), + Result::SUCCESS); + EXPECT_EQ(dumpGuid, "123"); + + bool success; + ASSERT_EQ(requestMemoryDumpReturns->ToJson()->GetBool("success", &success), Result::SUCCESS); + ASSERT_TRUE(success); +} +} // namespace panda::test \ No newline at end of file diff --git a/tooling/test/debugger_script_test.cpp b/tooling/test/debugger_script_test.cpp new file mode 100644 index 00000000..cc8ab425 --- /dev/null +++ b/tooling/test/debugger_script_test.cpp @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/debugger_service.h" +#include "ecmascript/tooling/dispatcher.h" +#include "ecmascript/tooling/backend/js_debugger.h" +#include "ecmascript/tooling/base/pt_script.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::tooling; + +namespace panda::test { +class DebuggerScriptTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(ecmaVm, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope); + } + +protected: + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(DebuggerScriptTest, ScriptIdTest) +{ + std::unique_ptr script = std::make_unique(1, "name_1", "url_1", "source_1"); + script->SetScriptId(100); + ASSERT_EQ(script->GetScriptId(), 100); +} + +HWTEST_F_L0(DebuggerScriptTest, FileNameTest) +{ + std::unique_ptr script = std::make_unique(1, "name_1", "url_1", "source_1"); + script->SetFileName("xx"); + ASSERT_EQ(script->GetFileName(), "xx"); +} + +HWTEST_F_L0(DebuggerScriptTest, UrlTest) +{ + std::unique_ptr script = std::make_unique(1, "name_1", "url_1", "source_1"); + script->SetUrl("121"); + ASSERT_EQ(script->GetUrl(), "121"); +} + +HWTEST_F_L0(DebuggerScriptTest, HashTest) +{ + std::unique_ptr script = std::make_unique(1, "name_1", "url_1", "source_1"); + script->SetHash("111"); + ASSERT_EQ(script->GetHash(), "111"); +} + +HWTEST_F_L0(DebuggerScriptTest, ScriptSourceTest) +{ + std::unique_ptr script = std::make_unique(1, "name_1", "url_1", "source_1"); + script->SetScriptSource("a=1"); + ASSERT_EQ(script->GetScriptSource(), "a=1"); +} + +HWTEST_F_L0(DebuggerScriptTest, SourceMapUrlTest) +{ + std::unique_ptr script = std::make_unique(1, "name_1", "url_1", "source_1"); + script->SetSourceMapUrl("192"); + ASSERT_EQ(script->GetSourceMapUrl(), "192"); +} + +HWTEST_F_L0(DebuggerScriptTest, EndLineTest) +{ + std::unique_ptr script = std::make_unique(1, "name_1", "url_1", "source_1"); + script->SetEndLine(200); + ASSERT_EQ(script->GetEndLine(), 200); +} +} // namespace panda::test \ No newline at end of file diff --git a/tooling/test/debugger_types_test.cpp b/tooling/test/debugger_types_test.cpp new file mode 100644 index 00000000..738891ea --- /dev/null +++ b/tooling/test/debugger_types_test.cpp @@ -0,0 +1,2642 @@ +/* + * 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. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/dispatcher.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::tooling; + +namespace panda::test { + +// Duplicate name of panda::ecmascript::PropertyDescriptor in js_object-inl.h +using panda::ecmascript::tooling::PropertyDescriptor; + +using ObjectType = RemoteObject::TypeName; +using ObjectSubType = RemoteObject::SubTypeName; +using ObjectClassName = RemoteObject::ClassName; + +class DebuggerTypesTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + Logger::InitializeStdLogging(Logger::Level::FATAL, LoggerComponentMaskAll); + } + + static void TearDownTestCase() + { + Logger::InitializeStdLogging(Logger::Level::ERROR, LoggerComponentMaskAll); + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(ecmaVm, thread, scope); + // Main logic is JSON parser, so not need trigger GC to decrease execute time + ecmaVm->SetEnableForceGC(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope); + } + +protected: + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(DebuggerTypesTest, RemoteObjectCreateTest) +{ + std::string msg; + std::unique_ptr remoteObject; + + // abnormal params of null msg + msg = std::string() + R"({})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = 100, ] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":100}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = [ "sub": "test" ] }, ] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":100}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", ] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + R"("}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + + // abnormal params of params.sub-key = [ type = "object", subtype = "unknown"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","subtype":"unknown"}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = "object", subtype = 100] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","subtype":100}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", subtype = "array"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","subtype":")" + ObjectSubType::Array + R"("}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasSubType()); + EXPECT_EQ(ObjectSubType::Array, remoteObject->GetSubType()); + + // abnormal params of params.sub-key = [ type = "object", className = 100] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","className":100}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = "object", className = {"xx":"yy"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","className":{"xx":"yy"}}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", className = "TestClass"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","className":"TestClass"}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasClassName()); + EXPECT_EQ("TestClass", remoteObject->GetClassName()); + + // normal params of params.sub-key = [ type = "object", value = 100] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","value":100}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + + // normal params of params.sub-key = [ type = "object", value = {"xx":"yy"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","value":{"xx":"yy"}}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + + // normal params of params.sub-key = [ type = "object", value = "Test"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","value":"Test"}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + + // abnormal params of params.sub-key = [ type = "object", unserializableValue = 100] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","unserializableValue":100}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = "object", unserializableValue = {"xx":"yy"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","unserializableValue":{"xx":"yy"}}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", unserializableValue = "TestClass"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","unserializableValue":"Test"}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasUnserializableValue()); + EXPECT_EQ("Test", remoteObject->GetUnserializableValue()); + + // abnormal params of params.sub-key = [ type = "object", description = 100] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","description":100}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = "object", description = {"xx":"yy"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","description":{"xx":"yy"}}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", description = "Test"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","description":"Test"}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasDescription()); + EXPECT_EQ("Test", remoteObject->GetDescription()); + + // abnormal params of params.sub-key = [ type = "object", objectId = 100] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","objectId":100}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = "object", objectId = {"xx":"yy"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","objectId":{"xx":"yy"}}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", objectId = "id_1"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","objectId":"1"}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasObjectId()); + EXPECT_EQ(remoteObject->GetObjectId(), 1); +} + +HWTEST_F_L0(DebuggerTypesTest, RemoteObjectToJsonTest) +{ + std::string msg; + std::unique_ptr remoteObject; + std::string tmpStr; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(", "subtype":")" + ObjectSubType::Array + + R"(","className":"TestClass","value":100, "unserializableValue":"Test","description":"Test","objectId":"1"}})"; + remoteObject = RemoteObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + auto objJson = remoteObject->ToJson(); + + ret = objJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), tmpStr); + + ret = objJson->GetString("subtype", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectSubType::Array.c_str()), tmpStr); + + ret = objJson->GetString("className", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("TestClass", tmpStr); + + ret = objJson->GetString("unserializableValue", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("Test", tmpStr); + + ret = objJson->GetString("description", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("Test", tmpStr); + + ret = objJson->GetString("objectId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("1", tmpStr); +} + +HWTEST_F_L0(DebuggerTypesTest, ExceptionDetailsCreateTest) +{ + std::string msg; + std::unique_ptr exceptionMetaData; + + // abnormal params of null msg + msg = std::string() + R"({})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId="Test","text"="text0","lineNumber"=10,"columnNumber"=20] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":"Test","text":"text0","lineNumber":10,"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId={"xx":"yy"},"text"="text0","lineNumber"=10,"columnNumber"=20] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":{"xx":"yy"},"text":"text0","lineNumber":10,"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"=10,"lineNumber"=10,"columnNumber"=20] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":10,"lineNumber":10,"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"=["text0"],"lineNumber"=10,"columnNumber"=20] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":["text0"],"lineNumber":10,"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"="text0","lineNumber"="10","columnNumber"=20] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":"10","columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"="text0","lineNumber"=["10"],"columnNumber"=20] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":["10"],"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"="20"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":"20"}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=["20"]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":["20"]}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // normal params of params.sub-key = [ exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + EXPECT_EQ(exceptionMetaData->GetExceptionId(), 3); + EXPECT_EQ("text0", exceptionMetaData->GetText()); + EXPECT_EQ(exceptionMetaData->GetLine(), 10); + EXPECT_EQ(exceptionMetaData->GetColumn(), 20); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"scriptId"=10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"scriptId":10}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"scriptId"=["10"]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"scriptId":["10"]}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // normal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"scriptId"="id0"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"scriptId":"0"}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + EXPECT_EQ(exceptionMetaData->GetExceptionId(), 3); + EXPECT_EQ("text0", exceptionMetaData->GetText()); + EXPECT_EQ(exceptionMetaData->GetLine(), 10); + EXPECT_EQ(exceptionMetaData->GetColumn(), 20); + EXPECT_EQ(exceptionMetaData->GetScriptId(), 0); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"url"=10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"url":10}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"url"=["10"]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"url":["10"]}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // normal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"url"="url0"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"url":"url0"}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + EXPECT_EQ(exceptionMetaData->GetExceptionId(), 3); + EXPECT_EQ("text0", exceptionMetaData->GetText()); + EXPECT_EQ(exceptionMetaData->GetLine(), 10); + EXPECT_EQ(exceptionMetaData->GetColumn(), 20); + EXPECT_EQ("url0", exceptionMetaData->GetUrl()); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"exception"=10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"exception":10}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"exception"=["10"]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"exception":["10"]}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // normal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"exception"={}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"exception":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Error + R"("}}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + EXPECT_EQ(exceptionMetaData->GetExceptionId(), 3); + EXPECT_EQ("text0", exceptionMetaData->GetText()); + EXPECT_EQ(exceptionMetaData->GetLine(), 10); + EXPECT_EQ(exceptionMetaData->GetColumn(), 20); + RemoteObject *exception = exceptionMetaData->GetException(); + ASSERT_NE(exception, nullptr); + EXPECT_EQ(exception->GetType(), ObjectType::Object); + EXPECT_EQ(exception->GetSubType(), ObjectSubType::Error); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"executionContextId"="10"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"executionContextId":"10"}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"executionContextId"=["10"]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"executionContextId":["10"]}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // normal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"executionContextId"=2] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"executionContextId":2}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + EXPECT_EQ(exceptionMetaData->GetExceptionId(), 3); + EXPECT_EQ("text0", exceptionMetaData->GetText()); + EXPECT_EQ(exceptionMetaData->GetLine(), 10); + EXPECT_EQ(exceptionMetaData->GetColumn(), 20); + EXPECT_EQ(exceptionMetaData->GetExecutionContextId(), 2); +} + +HWTEST_F_L0(DebuggerTypesTest, ExceptionDetailsToJsonTest) +{ + std::string msg; + std::unique_ptr exceptionMetaData; + std::string tmpStr; + int32_t tmpInt; + std::unique_ptr tmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":5,"text":"text0","lineNumber":10,"columnNumber":20,"scriptId":"100","url":"url0", + "exception":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Error + R"("},"executionContextId":30}})"; + exceptionMetaData = ExceptionDetails::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + auto objJson = exceptionMetaData->ToJson(); + + ret = objJson->GetInt("exceptionId", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 5); + + ret = objJson->GetString("text", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("text0", tmpStr); + + ret = objJson->GetInt("lineNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); + + ret = objJson->GetInt("columnNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 20); + + ret = objJson->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("100", tmpStr); + + ret = objJson->GetString("url", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("url0", tmpStr); + + ret = objJson->GetObject("exception", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), tmpStr); + ret = tmpJson->GetString("subtype", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectSubType::Error.c_str()), tmpStr); + + ret = objJson->GetInt("executionContextId", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 30); +} + +HWTEST_F_L0(DebuggerTypesTest, InternalPropertyDescriptorCreateTest) +{ + std::string msg; + std::unique_ptr internalPropertyDescriptor; + + // abnormal params of null msg + msg = std::string() + R"({})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":"name8"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":99}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":"name8"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":99}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":"name8","value":99] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":99}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":99] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":99}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":[99]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":[99]}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name7"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name7"}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(internalPropertyDescriptor, nullptr); + EXPECT_EQ("name7", internalPropertyDescriptor->GetName()); + + // abnormal params of unknown params.sub-key=["name":"name8","value":{"type":"object","subtype":"map"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":"99"}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":"name8","value":{"type":"object","subtype":"wrong"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":{"type":")" + + ObjectType::Object + R"(","subtype":"wrong"}}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","value":{"type":"object","subtype":"map"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Map + R"("}}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(internalPropertyDescriptor, nullptr); + EXPECT_EQ("name8", internalPropertyDescriptor->GetName()); + ASSERT_TRUE(internalPropertyDescriptor->HasValue()); + RemoteObject *value = internalPropertyDescriptor->GetValue(); + ASSERT_NE(value, nullptr); + EXPECT_EQ(value->GetType(), ObjectType::Object); + EXPECT_EQ(value->GetSubType(), ObjectSubType::Map); +} + +HWTEST_F_L0(DebuggerTypesTest, InternalPropertyDescriptorToJsonTest) +{ + std::string msg; + std::unique_ptr internalPropertyDescriptor; + std::string tmpStr; + std::unique_ptr tmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Map + R"("}}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(internalPropertyDescriptor, nullptr); + auto objJson = internalPropertyDescriptor->ToJson(); + + ret = objJson->GetString("name", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("name8", tmpStr); + + ret = objJson->GetObject("value", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), tmpStr); + ret = tmpJson->GetString("subtype", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectSubType::Map.c_str()), tmpStr); +} + +HWTEST_F_L0(DebuggerTypesTest, PropertyDescriptorCreateTest) +{ + std::string msg; + std::unique_ptr propertyDescriptor; + + // abnormal params of null msg + msg = std::string() + R"({})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":10,"configurable":true,"enumerable":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":10,"configurable":true,"enumerable":true,"value":10}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":["name85"],"configurable":true,"enumerable":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":["name85"],"configurable":true,"enumerable":true,"value":10}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":10,"enumerable":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":10,"enumerable":true}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":"true","enumerable":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":"true","enumerable":true}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":10}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":"true"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":"true"}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"value":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"value":10}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"value":{"ee":"11"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"value":{"ee":"11"}}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"value":{..}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"value":{"type":")" + + ObjectType::Symbol + R"("}}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + RemoteObject *value = propertyDescriptor->GetValue(); + ASSERT_NE(value, nullptr); + EXPECT_EQ(value->GetType(), ObjectType::Symbol); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"writable":98] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"writable":98}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"writable":[true]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"writable":[true]}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"writable":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"writable":true}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + ASSERT_TRUE(propertyDescriptor->GetWritable()); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"get":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"get":10}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"get":[10]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"get":[10]}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"get":{}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"get":{"type":")" + + ObjectType::Function + R"("}}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + RemoteObject *get = propertyDescriptor->GetGet(); + ASSERT_NE(get, nullptr); + EXPECT_EQ(get->GetType(), ObjectType::Function); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"set":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"set":10}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"set":[10]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"set":[10]}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"set":{}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"set":{"type":")" + + ObjectType::String + R"("}}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + RemoteObject *set = propertyDescriptor->GetSet(); + ASSERT_NE(set, nullptr); + EXPECT_EQ(set->GetType(), ObjectType::String); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"wasThrown":98] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"wasThrown":98}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"wasThrown":[true]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"wasThrown":[true]}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"wasThrown":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"wasThrown":true}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + ASSERT_TRUE(propertyDescriptor->GetWasThrown()); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"isOwn":98] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"isOwn":98}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"isOwn":[true]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"isOwn":[true]}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"isOwn":true] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"isOwn":true}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + ASSERT_TRUE(propertyDescriptor->GetIsOwn()); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true, "symbol":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"symbol":10}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true, "symbol":[10]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"symbol":[10]}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true, "symbol":{}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"symbol":{"type":")" + + ObjectType::Wasm + R"("}}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + RemoteObject *symbol = propertyDescriptor->GetSymbol(); + ASSERT_NE(symbol, nullptr); + EXPECT_EQ(symbol->GetType(), ObjectType::Wasm); +} + +HWTEST_F_L0(DebuggerTypesTest, PropertyDescriptorToJsonTest) +{ + std::string msg; + std::unique_ptr propertyDescriptor; + std::string tmpStr; + std::unique_ptr tmpJson; + bool tmpBool; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Map + R"("}, + "writable":true,"get":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Regexp + R"("},"set":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Generator + + R"("},"configurable":true,"enumerable":true,"wasThrown":true,"isOwn":true,"symbol":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Proxy + R"("}}})"; + propertyDescriptor = PropertyDescriptor::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + auto objJson = propertyDescriptor->ToJson(); + + ret = objJson->GetString("name", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("name8", tmpStr); + + ret = objJson->GetObject("value", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), tmpStr); + ret = tmpJson->GetString("subtype", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectSubType::Map.c_str()), tmpStr); + + ret = objJson->GetBool("writable", &tmpBool); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ret = objJson->GetObject("get", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), tmpStr); + ret = tmpJson->GetString("subtype", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectSubType::Regexp.c_str()), tmpStr); + + ret = objJson->GetObject("set", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), tmpStr); + ret = tmpJson->GetString("subtype", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectSubType::Generator.c_str()), tmpStr); + + ret = objJson->GetBool("configurable", &tmpBool); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ret = objJson->GetBool("enumerable", &tmpBool); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ret = objJson->GetBool("wasThrown", &tmpBool); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ret = objJson->GetBool("isOwn", &tmpBool); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ret = objJson->GetObject("symbol", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), tmpStr); + ret = tmpJson->GetString("subtype", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectSubType::Proxy.c_str()), tmpStr); +} + +HWTEST_F_L0(DebuggerTypesTest, LocationCreateTest) +{ + std::string msg; + std::unique_ptr location; + + // abnormal params of null msg + msg = std::string() + R"({})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of params.sub-key=["scriptId":10,"lineNumber":99] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":10,"lineNumber":99 + }})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of params.sub-key=["scriptId":["id3"],"lineNumber":99] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":["id3"],"lineNumber":99 + }})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of params.sub-key=["scriptId":"222","lineNumber":"99"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"222","lineNumber":"99" + }})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of params.sub-key=["scriptId":"222","lineNumber":[99]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"222","lineNumber":[99] + }})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // normal params of params.sub-key=["scriptId":"2","lineNumber":99,"columnNumber":138] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"222","lineNumber":899,"columnNumber":138 + }})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(location, nullptr); + EXPECT_EQ(location->GetScriptId(), 222); + EXPECT_EQ(location->GetLine(), 899); + EXPECT_EQ(location->GetColumn(), 138); + + // normal params of params.sub-key=["scriptId":"2122","lineNumber":8299] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"2122","lineNumber":8299 + }})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(location, nullptr); + EXPECT_EQ(location->GetScriptId(), 2122); + EXPECT_EQ(location->GetLine(), 8299); +} + +HWTEST_F_L0(DebuggerTypesTest, LocationToJsonTest) +{ + std::string msg; + std::unique_ptr location; + std::string tmpStr; + int32_t tmpInt; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"2","lineNumber":99,"columnNumber":18 + }})"; + location = Location::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(location, nullptr); + auto objJson = location->ToJson(); + + ret = objJson->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("2", tmpStr); + + ret = objJson->GetInt("lineNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 99); + + ret = objJson->GetInt("columnNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 18); +} + +HWTEST_F_L0(DebuggerTypesTest, BreakLocationCreateTest) +{ + std::string msg; + std::unique_ptr breakLocation; + + // abnormal params of null msg + msg = std::string() + R"({})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":10,"lineNumber":99] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":10,"lineNumber":99 + }})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":["id3"],"lineNumber":99] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":["id3"],"lineNumber":99 + }})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":"222","lineNumber":"99"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"222","lineNumber":"99" + }})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":"222","lineNumber":[99]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"222","lineNumber":[99] + }})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":"2","lineNumber":99,"columnNumber":"18"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"222","lineNumber":899,"columnNumber":"18" + }})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":"2","lineNumber":99,"columnNumber":"18","type":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"222","lineNumber":899,"columnNumber":"18","type":10 + }})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":"2","lineNumber":99,"columnNumber":"18","type":"ee"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"222","lineNumber":899,"columnNumber":"18","type":"ee" + }})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // normal params of params.sub-key=["scriptId":"2","lineNumber":99,"columnNumber":138,"type":"return"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"222","lineNumber":899,"columnNumber":138,"type":"return" + }})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(breakLocation, nullptr); + EXPECT_EQ(breakLocation->GetScriptId(), 222); + EXPECT_EQ(breakLocation->GetLine(), 899); + EXPECT_EQ(breakLocation->GetColumn(), 138); + EXPECT_EQ("return", breakLocation->GetType()); + + // normal params of params.sub-key=["scriptId":"2122","lineNumber":8299] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"2122","lineNumber":8299 + }})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(breakLocation, nullptr); + EXPECT_EQ(breakLocation->GetScriptId(), 2122); + EXPECT_EQ(breakLocation->GetLine(), 8299); +} + +HWTEST_F_L0(DebuggerTypesTest, BreakLocationToJsonTest) +{ + std::string msg; + std::unique_ptr breakLocation; + std::string tmpStr; + int32_t tmpInt; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"12","lineNumber":919,"columnNumber":148,"type":"call" + }})"; + breakLocation = BreakLocation::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(breakLocation, nullptr); + auto objJson = breakLocation->ToJson(); + + ret = objJson->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("12", tmpStr); + + ret = objJson->GetInt("lineNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 919); + + ret = objJson->GetInt("columnNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 148); + + ret = objJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("call", tmpStr); +} + +HWTEST_F_L0(DebuggerTypesTest, ScopeCreateTest) +{ + std::string msg; + std::unique_ptr scope; + + // abnormal params of null msg + msg = std::string() + R"({})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"ss","object":{..}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"ss","object":{"type":")" + + ObjectType::Bigint + R"("}}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":12,"object":{..}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":12,"object":{"type":")" + + ObjectType::Bigint + R"("}}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":10}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"ww":")" + + ObjectType::Bigint + R"("}}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..},"name":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"name":10}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..},"name":["10"]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"name":["10"]}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // normal params of params.sub-key=["type":"global","object":{..},"name":"name128"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"name":"name117"}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(scope, nullptr); + EXPECT_EQ("name117", scope->GetName()); + + // abnormal params of params.sub-key=["type":"global","object":{..},"startLocation":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"startLocation":10}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..},"startLocation":{"12":"34"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"startLocation":{"12":"34"}}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..},"endLocation":10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"endLocation":10}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..},"endLocation":{"12":"34"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"endLocation":{"12":"34"}}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // normal params of params.sub-key=["type":"global","object":{..}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("}}})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(scope, nullptr); + EXPECT_EQ("global", scope->GetType()); + RemoteObject *object = scope->GetObject(); + ASSERT_NE(object, nullptr); + EXPECT_EQ(object->GetType(), ObjectType::Bigint); +} + +HWTEST_F_L0(DebuggerTypesTest, ScopeToJsonTest) +{ + std::string msg; + std::unique_ptr scope; + std::string tmpStr; + int32_t tmpInt; + std::unique_ptr tmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Dataview + R"("},"name":"name9", + "startLocation":{"scriptId":"2","lineNumber":99}, + "endLocation":{"scriptId":"13","lineNumber":146} + }})"; + scope = Scope::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(scope, nullptr); + auto objJson = scope->ToJson(); + + ret = objJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("global", tmpStr); + + ret = objJson->GetObject("object", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), tmpStr); + ret = tmpJson->GetString("subtype", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectSubType::Dataview.c_str()), tmpStr); + + ret = objJson->GetString("name", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("name9", tmpStr); + + ret = objJson->GetObject("startLocation", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("2", tmpStr); + ret = tmpJson->GetInt("lineNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 99); + + ret = objJson->GetObject("endLocation", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("13", tmpStr); + ret = tmpJson->GetInt("lineNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 146); +} + +HWTEST_F_L0(DebuggerTypesTest, CallFrameCreateTest) +{ + std::string msg; + std::unique_ptr callFrame; + + // abnormal params of null msg + msg = std::string() + R"({})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":10,"functionName":"name0", + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":["0"],"functionName":"name0", + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":10, + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":["name0"], + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0","functionLocation":10, + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0","functionLocation":{"scriptId11":"id5","lineNumber":19}, + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", + "location":{"scriptId2":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", + "location":10,"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", + "location":{"scriptId":"5","lineNumber":19},"url":10,"scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", + "location":{"scriptId":"5","lineNumber":19},"url":{"url":"url7"},"scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", "location":{"scriptId":"5","lineNumber":19}, + "url":"url7","scopeChain":10,"this":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + {"type":"22","object":{"type":")" + + ObjectType::Object + R"("}},"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":10}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"11":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}, + "returnValue":10}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}, + "returnValue":{"type":"object","subtype":"11"}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // normal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0", + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(callFrame, nullptr); + EXPECT_EQ(callFrame->GetCallFrameId(), 0); + EXPECT_EQ("name0", callFrame->GetFunctionName()); + ASSERT_FALSE(callFrame->HasFunctionLocation()); + Location *location = callFrame->GetLocation(); + EXPECT_EQ(location->GetScriptId(), 5); + EXPECT_EQ(location->GetLine(), 19); + EXPECT_EQ("url7", callFrame->GetUrl()); + const std::vector> *scopeChain = callFrame->GetScopeChain(); + EXPECT_EQ(scopeChain->size(), 2U); + RemoteObject *thisObj = callFrame->GetThis(); + ASSERT_NE(thisObj, nullptr); + EXPECT_EQ(thisObj->GetType(), ObjectType::Object); + EXPECT_EQ(thisObj->GetSubType(), ObjectSubType::V128); + ASSERT_FALSE(callFrame->HasReturnValue()); + + // normal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"10","functionName":"name0","functionLocation":{"scriptId":"3","lineNumber":16}, + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + + R"("},"returnValue":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::I32 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(callFrame, nullptr); + EXPECT_EQ(callFrame->GetCallFrameId(), 10); + EXPECT_EQ("name0", callFrame->GetFunctionName()); + Location *functionLocation = callFrame->GetFunctionLocation(); + EXPECT_EQ(functionLocation->GetScriptId(), 3); + EXPECT_EQ(functionLocation->GetLine(), 16); + location = callFrame->GetLocation(); + EXPECT_EQ(location->GetScriptId(), 5); + EXPECT_EQ(location->GetLine(), 19); + EXPECT_EQ("url7", callFrame->GetUrl()); + scopeChain = callFrame->GetScopeChain(); + EXPECT_EQ(scopeChain->size(), 2U); + thisObj = callFrame->GetThis(); + ASSERT_NE(thisObj, nullptr); + EXPECT_EQ(thisObj->GetType(), ObjectType::Object); + EXPECT_EQ(thisObj->GetSubType(), ObjectSubType::V128); + RemoteObject *returnObj = callFrame->GetReturnValue(); + ASSERT_NE(returnObj, nullptr); + EXPECT_EQ(returnObj->GetType(), ObjectType::Object); + EXPECT_EQ(returnObj->GetSubType(), ObjectSubType::I32); +} + +HWTEST_F_L0(DebuggerTypesTest, CallFrameToJsonTest) +{ + std::string msg; + std::unique_ptr callFrame; + std::string tmpStr; + int32_t tmpInt; + std::unique_ptr tmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"0","functionName":"name0","functionLocation":{"scriptId":"3","lineNumber":16}, + "location":{"scriptId":"5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Iterator + + R"("},"returnValue":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::I64 + R"("}}})"; + callFrame = CallFrame::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(callFrame, nullptr); + auto objJson = callFrame->ToJson(); + + ret = objJson->GetString("callFrameId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("0", tmpStr); + + ret = objJson->GetString("functionName", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("name0", tmpStr); + + ret = objJson->GetObject("functionLocation", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("3", tmpStr); + ret = tmpJson->GetInt("lineNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 16); + + ret = objJson->GetObject("location", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("5", tmpStr); + ret = tmpJson->GetInt("lineNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 19); + + ret = objJson->GetString("url", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ("url7", tmpStr); + + ret = objJson->GetArray("scopeChain", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + EXPECT_EQ(tmpJson->GetSize(), 2); + + ret = objJson->GetObject("this", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), tmpStr); + ret = tmpJson->GetString("subtype", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectSubType::Iterator.c_str()), tmpStr); + + ret = objJson->GetObject("returnValue", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("type", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), tmpStr); + ret = tmpJson->GetString("subtype", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(std::string(ObjectSubType::I64.c_str()), tmpStr); +} + +HWTEST_F_L0(DebuggerTypesTest, SamplingHeapProfileSampleCreateTest) +{ + std::string msg; + std::unique_ptr object; + + // abnormal params of null msg + msg = std::string() + R"({})"; + object = SamplingHeapProfileSample::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + object = SamplingHeapProfileSample::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + object = SamplingHeapProfileSample::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + object = SamplingHeapProfileSample::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of params.sub-key = [ "size"="Test","nodeId"="Test","ordinal"="Test"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "size":"Test","nodeId":"Test","ordinal":"Test"}})"; + object = SamplingHeapProfileSample::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of params.sub-key = [ "size"={"xx":"yy"},"nodeId"={"xx":"yy"},"ordinal"={"xx":"yy"}] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "size":{"xx":"yy"},"nodeId":{"xx":"yy"},"ordinal":{"xx":"yy"}}})"; + object = SamplingHeapProfileSample::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of params.sub-key = [ "size"=100,"nodeId"=1,"ordinal"=10] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"size":100,"nodeId":1,"ordinal":10}})"; + object = SamplingHeapProfileSample::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(object, nullptr); + EXPECT_EQ(object->GetSize(), 100); + EXPECT_EQ(object->GetNodeId(), 1); + EXPECT_EQ(object->GetOrdinal(), 10); +} + +HWTEST_F_L0(DebuggerTypesTest, SamplingHeapProfileSampleToJsonTest) +{ + std::string msg; + std::unique_ptr samplingHeapProfileSampleData; + std::string tmpStr; + int32_t tmpInt; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"size":100,"nodeId":1,"ordinal":10}})"; + samplingHeapProfileSampleData = + SamplingHeapProfileSample::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(samplingHeapProfileSampleData, nullptr); + auto json = samplingHeapProfileSampleData->ToJson(); + + ret = json->GetInt("size", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 100); + ret = json->GetInt("nodeId", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 1); + ret = json->GetInt("ordinal", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); +} + +HWTEST_F_L0(DebuggerTypesTest, SamplingHeapProfileNodeCreateTest) +{ + std::string msg; + std::unique_ptr object; + + // abnormal params of null msg + msg = std::string() + R"({})"; + object = SamplingHeapProfileNode::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + object = SamplingHeapProfileNode::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + object = SamplingHeapProfileNode::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + object = SamplingHeapProfileNode::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrame": {"functionName":"Create", "scriptId":"10", "url":"url3", "lineNumber":100, "columnNumber":20}, + "selfSize":10, + "id":5, + "children":[] + }})"; + object = SamplingHeapProfileNode::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(object, nullptr); + RuntimeCallFrame *runTimeCallFrame = object->GetCallFrame(); + ASSERT_NE(runTimeCallFrame, nullptr); + EXPECT_EQ(runTimeCallFrame->GetFunctionName(), "Create"); + EXPECT_EQ(runTimeCallFrame->GetScriptId(), "10"); + EXPECT_EQ(runTimeCallFrame->GetUrl(), "url3"); + EXPECT_EQ(runTimeCallFrame->GetLineNumber(), 100); + EXPECT_EQ(runTimeCallFrame->GetColumnNumber(), 20); + + EXPECT_EQ(object->GetSelfSize(), 10); + EXPECT_EQ(object->GetId(), 5); + const std::vector> *children = object->GetChildren(); + ASSERT_NE(children, nullptr); + EXPECT_EQ((int)children->size(), 0); +} + +HWTEST_F_L0(DebuggerTypesTest, SamplingHeapProfileNodeToJsonTest) +{ + std::string msg; + std::unique_ptr samplingHeapProfileNode; + std::string tmpStr; + std::unique_ptr tmpJson; + int32_t tmpInt; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrame": {"functionName":"Create", "scriptId":"10", "url":"url3", "lineNumber":100, "columnNumber":20}, + "selfSize":10, + "id":5, + "children":[] + }})"; + samplingHeapProfileNode = SamplingHeapProfileNode::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(samplingHeapProfileNode, nullptr); + auto json = samplingHeapProfileNode->ToJson(); + + ret = json->GetObject("callFrame", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("functionName", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "Create"); + ret = tmpJson->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "10"); + ret = tmpJson->GetString("url", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "url3"); + + ret = json->GetInt("selfSize", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); + ret = json->GetInt("id", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 5); + ret = json->GetArray("children", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + EXPECT_EQ(tmpJson->GetSize(), 0); +} + +HWTEST_F_L0(DebuggerTypesTest, SamplingHeapProfileCreateTest) +{ + std::string msg; + std::unique_ptr object; + + // abnormal params of null msg + msg = std::string() + R"({})"; + object = SamplingHeapProfile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + object = SamplingHeapProfile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + object = SamplingHeapProfile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + object = SamplingHeapProfile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(object, nullptr); + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "head": { + "callFrame": {"functionName":"Create", "scriptId":"10", "url":"url3", "lineNumber":100, "columnNumber":20}, + "selfSize":10, + "id":5, + "children":[] + }, + "samples":[{"size":100, "nodeId":1, "ordinal":10}] + }})"; + object = SamplingHeapProfile::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(object, nullptr); + SamplingHeapProfileNode *head = object->GetHead(); + ASSERT_NE(head, nullptr); + + RuntimeCallFrame *runTimeCallFrame = head->GetCallFrame(); + ASSERT_NE(runTimeCallFrame, nullptr); + EXPECT_EQ(runTimeCallFrame->GetFunctionName(), "Create"); + EXPECT_EQ(runTimeCallFrame->GetScriptId(), "10"); + EXPECT_EQ(runTimeCallFrame->GetUrl(), "url3"); + EXPECT_EQ(runTimeCallFrame->GetLineNumber(), 100); + EXPECT_EQ(runTimeCallFrame->GetColumnNumber(), 20); + + EXPECT_EQ(head->GetSelfSize(), 10); + EXPECT_EQ(head->GetId(), 5); + const std::vector> *children = head->GetChildren(); + ASSERT_NE(children, nullptr); + EXPECT_EQ((int)children->size(), 0); + + const std::vector> *samples = object->GetSamples(); + ASSERT_NE(samples, nullptr); + EXPECT_EQ((int)samples->size(), 1); + EXPECT_EQ(samples->data()->get()->GetSize(), 100); + EXPECT_EQ(samples->data()->get()->GetNodeId(), 1); + EXPECT_EQ(samples->data()->get()->GetOrdinal(), 10); +} + +HWTEST_F_L0(DebuggerTypesTest, SamplingHeapProfileToJsonTest) +{ + std::string msg; + std::unique_ptr samplingHeapProfile; + std::string tmpStr; + int32_t tmpInt; + std::unique_ptr tmpJson; + std::unique_ptr varTmpJson; + std::unique_ptr exTmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "head": { + "callFrame": {"functionName":"Create", "scriptId":"10", "url":"url3", "lineNumber":100, "columnNumber":20}, + "selfSize":10, + "id":5, + "children":[] + }, + "samples":[{"size":100, "nodeId":1, "ordinal":10}] + }})"; + + samplingHeapProfile = SamplingHeapProfile::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(samplingHeapProfile, nullptr); + auto json = samplingHeapProfile->ToJson(); + + ret = json->GetObject("head", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetObject("callFrame", &varTmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(varTmpJson, nullptr); + ret = varTmpJson->GetString("functionName", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "Create"); + ret = varTmpJson->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "10"); + ret = varTmpJson->GetString("url", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "url3"); + + ret = tmpJson->GetInt("selfSize", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); + ret = tmpJson->GetInt("id", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 5); + ret = tmpJson->GetArray("children", &exTmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(exTmpJson, nullptr); + EXPECT_EQ(exTmpJson->GetSize(), 0); + + ret = json->GetArray("samples", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + EXPECT_EQ(tmpJson->GetSize(), 1); +} + +HWTEST_F_L0(DebuggerTypesTest, PositionTickInfoCreateTest) +{ + std::string msg; + std::unique_ptr positionTickInfo; + + // abnormal params of null msg + msg = std::string() + R"({})"; + positionTickInfo = PositionTickInfo::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(positionTickInfo, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + positionTickInfo = PositionTickInfo::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(positionTickInfo, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + positionTickInfo = PositionTickInfo::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(positionTickInfo, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + positionTickInfo = PositionTickInfo::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(positionTickInfo, nullptr); + + // abnormal params of params.sub-key=["line":11,"ticks":99] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "line":"11","ticks":99}})"; + positionTickInfo = PositionTickInfo::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(positionTickInfo, nullptr); + + // abnormal params of params.sub-key=["line":"11","ticks":"99"] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "line":"11","ticks":"99"}})"; + positionTickInfo = PositionTickInfo::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(positionTickInfo, nullptr); + + // abnormal params of params.sub-key=["line":[11],"ticks":[99]] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "line":[11],"ticks":[99]}})"; + positionTickInfo = PositionTickInfo::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(positionTickInfo, nullptr); + + // normal params of params.sub-key=["line":11,"ticks":99] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"line":1,"ticks":0}})"; + positionTickInfo = PositionTickInfo::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(positionTickInfo, nullptr); + EXPECT_EQ(positionTickInfo->GetLine(), 1); + EXPECT_EQ(positionTickInfo->GetTicks(), 0); +} + +HWTEST_F_L0(DebuggerTypesTest, PositionTickInfoToJsonTest) +{ + std::string msg; + std::unique_ptr positionTickInfo; + int32_t tmpInt; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"line":1,"ticks":0}})"; + positionTickInfo = PositionTickInfo::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(positionTickInfo, nullptr); + auto json = positionTickInfo->ToJson(); + + ret = json->GetInt("line", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 1); + + ret = json->GetInt("ticks", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 0); +} + +HWTEST_F_L0(DebuggerTypesTest, ProfileNodeCreateTest) +{ + std::string msg; + std::unique_ptr profileNode; + + // abnormal params of null msg + msg = std::string() + R"({})"; + profileNode = ProfileNode::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(profileNode, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + profileNode = ProfileNode::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(profileNode, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + profileNode = ProfileNode::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(profileNode, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + profileNode = ProfileNode::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(profileNode, nullptr); + + // normal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "id":10, + "callFrame": {"functionName":"name0", "scriptId":"12", "url":"url15", "lineNumber":11, "columnNumber":20}, + "hitCount":15,"children":[],"positionTicks":[],"deoptReason":"yyy"}})"; + profileNode = ProfileNode::Create(DispatchRequest(msg).GetParams()); + + ASSERT_NE(profileNode, nullptr); + EXPECT_EQ(profileNode->GetId(), 10); + RuntimeCallFrame *runTimeCallFrame = profileNode->GetCallFrame(); + ASSERT_NE(runTimeCallFrame, nullptr); + EXPECT_EQ(runTimeCallFrame->GetFunctionName(), "name0"); + EXPECT_EQ(runTimeCallFrame->GetScriptId(), "12"); + EXPECT_EQ(runTimeCallFrame->GetUrl(), "url15"); + EXPECT_EQ(runTimeCallFrame->GetLineNumber(), 11); + EXPECT_EQ(runTimeCallFrame->GetColumnNumber(), 20); + + EXPECT_EQ(profileNode->GetHitCount(), 15); + EXPECT_EQ(profileNode->GetDeoptReason(), "yyy"); +} + +HWTEST_F_L0(DebuggerTypesTest, ProfileNodeToJsonTest) +{ + std::string msg; + std::unique_ptr profilenode; + std::string tmpStr; + int32_t tmpInt; + std::unique_ptr tmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "id":10, + "callFrame": {"functionName":"name0", "scriptId":"12", "url":"url15", "lineNumber":11, "columnNumber":20}, + "hitCount":15,"children":[],"positionTicks":[],"deoptReason":"yyy"}})"; + profilenode = ProfileNode::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(profilenode, nullptr); + auto json = profilenode->ToJson(); + + ret = json->GetInt("id", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); + + ret = json->GetObject("callFrame", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + ret = tmpJson->GetString("functionName", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "name0"); + ret = tmpJson->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "12"); + ret = tmpJson->GetString("url", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "url15"); + ret = tmpJson->GetInt("lineNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 11); + ret = tmpJson->GetInt("columnNumber", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 20); + + ret = json->GetInt("hitCount", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 15); + + ret = json->GetString("deoptReason", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "yyy"); +} + +HWTEST_F_L0(DebuggerTypesTest, ProfileCreateTest) +{ + std::string msg; + std::unique_ptr profile; + + // abnormal params of null msg + msg = std::string() + R"({})"; + profile = Profile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(profile, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + profile = Profile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(profile, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + profile = Profile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(profile, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + profile = Profile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(profile, nullptr); + + // abnormal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "tid":1000, "startTime":10, "endTime":25, "nodes":[{"id":12, + "callFrame": {"functionName":"Create", "scriptId":"10", "url":"url3", "lineNumber":100, "columnNumber":20}}], + "samples":[],"timeDeltas":[]}})"; + profile = Profile::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(profile, nullptr); + + EXPECT_EQ(profile->GetTid(), 1000LL); + EXPECT_EQ(profile->GetStartTime(), 10LL); + EXPECT_EQ(profile->GetEndTime(), 25LL); + const std::vector> *profileNode = profile->GetNodes(); + ASSERT_NE(profileNode, nullptr); + EXPECT_EQ((int)profileNode->size(), 1); +} + +HWTEST_F_L0(DebuggerTypesTest, ProfileToJsonTest) +{ + std::string msg; + std::unique_ptr profile; + std::string tmpStr; + int32_t tmpInt; + std::unique_ptr tmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "tid":1000, "startTime":10, "endTime":25, "nodes":[{"id":12, + "callFrame": {"functionName":"Create", "scriptId":"10", "url":"url3", "lineNumber":100, "columnNumber":20}}], + "samples":[],"timeDeltas":[]}})"; + profile = Profile::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(profile, nullptr); + auto json = profile->ToJson(); + + ret = json->GetInt("tid", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 1000); + + ret = json->GetInt("startTime", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 10); + + ret = json->GetInt("endTime", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 25); +} + +HWTEST_F_L0(DebuggerTypesTest, CoverageCreateTest) +{ + std::string msg; + std::unique_ptr coverage; + + // abnormal params of null msg + msg = std::string() + R"({})"; + coverage = Coverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(coverage, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + coverage = Coverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(coverage, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + coverage = Coverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(coverage, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + coverage = Coverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(coverage, nullptr); + + // normal params of params.sub-key=["startOffset":0,"endOffset":5,"count":13] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "startOffset":0,"endOffset":13,"count":13}})"; + coverage = Coverage::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(coverage, nullptr); + EXPECT_EQ(coverage->GetStartOffset(), 0); + EXPECT_EQ(coverage->GetEndOffset(), 13); + EXPECT_EQ(coverage->GetCount(), 13); +} + +HWTEST_F_L0(DebuggerTypesTest, CoverageToJsonTest) +{ + std::string msg; + std::unique_ptr coverage; + std::string tmpStr; + int32_t tmpInt; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "startOffset":0,"endOffset":13,"count":13}})"; + coverage = Coverage::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(coverage, nullptr); + auto json = coverage->ToJson(); + + ret = json->GetInt("startOffset", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 0); + + ret = json->GetInt("endOffset", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 13); + + ret = json->GetInt("count", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 13); +} + +HWTEST_F_L0(DebuggerTypesTest, FunctionCoverageCreateTest) +{ + std::string msg; + std::unique_ptr functionCoverage; + + // abnormal params of null msg + msg = std::string() + R"({})"; + functionCoverage = FunctionCoverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(functionCoverage, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + functionCoverage = FunctionCoverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(functionCoverage, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + functionCoverage = FunctionCoverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(functionCoverage, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + functionCoverage = FunctionCoverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(functionCoverage, nullptr); + + // normal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "functionName":"Create0","ranges":[{"startOffset":0,"endOffset":13,"count":13}],"isBlockCoverage":true}})"; + functionCoverage = FunctionCoverage::Create(DispatchRequest(msg).GetParams()); + + ASSERT_NE(functionCoverage, nullptr); + EXPECT_EQ(functionCoverage->GetFunctionName(), "Create0"); + const std::vector> *ranges = functionCoverage->GetRanges(); + ASSERT_NE(ranges, nullptr); + EXPECT_EQ((int)ranges->size(), 1); + ASSERT_TRUE(functionCoverage->GetIsBlockCoverage()); +} + +HWTEST_F_L0(DebuggerTypesTest, FunctionCoverageToJsonTest) +{ + std::string msg; + std::unique_ptr functionCoverage; + std::string tmpStr; + bool tmpBool; + std::unique_ptr tmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "functionName":"Create0","ranges":[{"startOffset":0,"endOffset":13,"count":13}],"isBlockCoverage":true}})"; + functionCoverage = FunctionCoverage::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(functionCoverage, nullptr); + auto json = functionCoverage->ToJson(); + + ret = json->GetString("functionName", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "Create0"); + + ret = json->GetArray("ranges", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + EXPECT_EQ(tmpJson->GetSize(), 1); + + ret = json->GetBool("isBlockCoverage", &tmpBool); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_TRUE(tmpBool); +} + +HWTEST_F_L0(DebuggerTypesTest, ScriptCoverageCreateTest) +{ + std::string msg; + std::unique_ptr scriptCoverage; + + // abnormal params of null msg + msg = std::string() + R"({})"; + scriptCoverage = ScriptCoverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scriptCoverage, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + scriptCoverage = ScriptCoverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scriptCoverage, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + scriptCoverage = ScriptCoverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scriptCoverage, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + scriptCoverage = ScriptCoverage::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scriptCoverage, nullptr); + + // normal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"1001", + "url":"url17", + "functions":[{"functionName":"Create0", + "ranges":[{"startOffset":0, "endOffset":13, "count":13}], + "isBlockCoverage":true}]}})"; + scriptCoverage = ScriptCoverage::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(scriptCoverage, nullptr); + EXPECT_EQ(scriptCoverage->GetScriptId(), "1001"); + EXPECT_EQ(scriptCoverage->GetUrl(), "url17"); + const std::vector> *functions = scriptCoverage->GetFunctions(); + ASSERT_NE(functions, nullptr); + EXPECT_EQ((int)functions->size(), 1); +} + +HWTEST_F_L0(DebuggerTypesTest, ScriptCoverageToJsonTest) +{ + std::string msg; + std::unique_ptr scriptCoverage; + std::string tmpStr; + std::unique_ptr tmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"1001", + "url":"url17", + "functions": [{"functionName":"Create0", + "ranges": [{"startOffset":0, "endOffset":13, "count":13}], + "isBlockCoverage":true}]}})"; + scriptCoverage = ScriptCoverage::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(scriptCoverage, nullptr); + auto json = scriptCoverage->ToJson(); + + ret = json->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "1001"); + + ret = json->GetString("url", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "url17"); + + ret = json->GetArray("functions", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + EXPECT_EQ(tmpJson->GetSize(), 1); +} + +HWTEST_F_L0(DebuggerTypesTest, TypeObjectCreateTest) +{ + std::string msg; + std::unique_ptr typeObject; + + // abnormal params of null msg + msg = std::string() + R"({})"; + typeObject = TypeObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(typeObject, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + typeObject = TypeObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(typeObject, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + typeObject = TypeObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(typeObject, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + typeObject = TypeObject::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(typeObject, nullptr); + + // normal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"Create1"}})"; + typeObject = TypeObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(typeObject, nullptr); + EXPECT_EQ(typeObject->GetName(), "Create1"); +} + +HWTEST_F_L0(DebuggerTypesTest, TypeObjectToJsonTest) +{ + std::string msg; + std::unique_ptr typeObject; + std::string tmpStr; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"Create1"}})"; + typeObject = TypeObject::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(typeObject, nullptr); + auto json = typeObject->ToJson(); + + ret = json->GetString("name", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "Create1"); +} + +HWTEST_F_L0(DebuggerTypesTest, TypeProfileEntryCreateTest) +{ + std::string msg; + std::unique_ptr typeProfileEntry; + + // abnormal params of null msg + msg = std::string() + R"({})"; + typeProfileEntry = TypeProfileEntry::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(typeProfileEntry, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + typeProfileEntry = TypeProfileEntry::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(typeProfileEntry, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + typeProfileEntry = TypeProfileEntry::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(typeProfileEntry, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + typeProfileEntry = TypeProfileEntry::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(typeProfileEntry, nullptr); + + // normal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "offset":11,"types":[{"name":"Create1"}]}})"; + typeProfileEntry = TypeProfileEntry::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(typeProfileEntry, nullptr); + EXPECT_EQ(typeProfileEntry->GetOffset(), 11); + const std::vector> *typeObject = typeProfileEntry->GetTypes(); + ASSERT_NE(typeObject, nullptr); + EXPECT_EQ((int)typeObject->size(), 1); +} + +HWTEST_F_L0(DebuggerTypesTest, TypeProfileEntryToJsonTest) +{ + std::string msg; + std::unique_ptr typeProfileEntry; + int32_t tmpInt; + std::unique_ptr tmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "offset":11,"types":[{"name":"Create1"}]}})"; + typeProfileEntry = TypeProfileEntry::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(typeProfileEntry, nullptr); + auto json = typeProfileEntry->ToJson(); + + ret = json->GetInt("offset", &tmpInt); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpInt, 11); + + ret = json->GetArray("types", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + EXPECT_EQ(tmpJson->GetSize(), 1); +} + +HWTEST_F_L0(DebuggerTypesTest, ScriptTypeProfileCreateTest) +{ + std::string msg; + std::unique_ptr scriptTypeProfile; + + // abnormal params of null msg + msg = std::string() + R"({})"; + scriptTypeProfile = ScriptTypeProfile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scriptTypeProfile, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + scriptTypeProfile = ScriptTypeProfile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scriptTypeProfile, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + scriptTypeProfile = ScriptTypeProfile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scriptTypeProfile, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + scriptTypeProfile = ScriptTypeProfile::Create(DispatchRequest(msg).GetParams()); + EXPECT_EQ(scriptTypeProfile, nullptr); + + // normal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"122","url":"url15","entries":[{"offset":11,"types":[{"name":"Create1"}]}]}})"; + scriptTypeProfile = ScriptTypeProfile::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(scriptTypeProfile, nullptr); + EXPECT_EQ(scriptTypeProfile->GetScriptId(), "122"); + EXPECT_EQ(scriptTypeProfile->GetUrl(), "url15"); + const std::vector> *typeProfileEntry = scriptTypeProfile->GetEntries(); + ASSERT_NE(typeProfileEntry, nullptr); + EXPECT_EQ((int)typeProfileEntry->size(), 1); +} + +HWTEST_F_L0(DebuggerTypesTest, ScriptTypeProfileToJsonTest) +{ + std::string msg; + std::unique_ptr scriptTypeProfile; + std::string tmpStr; + std::unique_ptr tmpJson; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"122","url":"url15","entries":[{"offset":11,"types":[{"name":"Create1"}]}]}})"; + scriptTypeProfile = ScriptTypeProfile::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(scriptTypeProfile, nullptr); + auto json = scriptTypeProfile->ToJson(); + + ret = json->GetString("scriptId", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "122"); + + ret = json->GetString("url", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "url15"); + + ret = json->GetArray("entries", &tmpJson); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_NE(tmpJson, nullptr); + EXPECT_EQ(tmpJson->GetSize(), 1); +} + +HWTEST_F_L0(DebuggerTypesTest, TraceConfigCreateTest) +{ + std::string msg; + std::unique_ptr traceConfig; + + // abnormal params of null msg + msg = std::string() + R"({})"; + traceConfig = TraceConfig::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(traceConfig, nullptr); + + // abnormal params of unexist key params + msg = std::string() + R"({"id":0,"method":"Debugger.Test"})"; + traceConfig = TraceConfig::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(traceConfig, nullptr); + + // abnormal params of null params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + traceConfig = TraceConfig::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(traceConfig, nullptr); + + // abnormal params of unknown params.sub-key + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + traceConfig = TraceConfig::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(traceConfig, nullptr); + + // normal params of params.sub-key=[..] + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "recordMode":"recordUntilFull", "enableSampling":true, "enableSystrace":true, + "enableArgumentFilter":true}})"; + traceConfig = TraceConfig::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(traceConfig, nullptr); + + EXPECT_EQ(traceConfig->GetRecordMode(), "recordUntilFull"); + ASSERT_TRUE(traceConfig->GetEnableSampling()); + ASSERT_TRUE(traceConfig->GetEnableSystrace()); + ASSERT_TRUE(traceConfig->GetEnableArgumentFilter()); +} + +HWTEST_F_L0(DebuggerTypesTest, TraceConfigToJsonTest) +{ + std::string msg; + std::unique_ptr traceConfig; + std::string tmpStr; + std::unique_ptr tmpJson; + bool tmpBool; + Result ret; + + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{ + "recordMode":"recordUntilFull", "enableSampling":true, "enableSystrace":true, + "enableArgumentFilter":true}})"; + traceConfig = TraceConfig::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(traceConfig, nullptr); + auto json = traceConfig->ToJson(); + + ret = json->GetString("recordMode", &tmpStr); + EXPECT_EQ(ret, Result::SUCCESS); + EXPECT_EQ(tmpStr, "recordUntilFull"); + + ret = json->GetBool("enableSampling", &tmpBool); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ret = json->GetBool("enableSystrace", &tmpBool); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_TRUE(tmpBool); + + ret = json->GetBool("enableArgumentFilter", &tmpBool); + EXPECT_EQ(ret, Result::SUCCESS); + ASSERT_TRUE(tmpBool); +} +} // namespace panda::test diff --git a/tooling/test/entry/test_debugger_entry.cpp b/tooling/test/entry/test_debugger_entry.cpp new file mode 100644 index 00000000..e10f6501 --- /dev/null +++ b/tooling/test/entry/test_debugger_entry.cpp @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/test/utils/test_entry.h" + +namespace panda::ecmascript::tooling::test { +extern "C" bool StartDebug(const std::string &name, EcmaVM *vm, bool isDebugMode) +{ + return StartDebuggerImpl(name, vm, isDebugMode); +} + +extern "C" bool StopDebug(const std::string &name) +{ + return StopDebuggerImpl(name); +} +} // namespace panda::ecmascript::tooling::test diff --git a/tooling/test/js_pt_hooks_test.cpp b/tooling/test/js_pt_hooks_test.cpp new file mode 100644 index 00000000..32b22c13 --- /dev/null +++ b/tooling/test/js_pt_hooks_test.cpp @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/agent/debugger_impl.h" +#include "ecmascript/tooling/backend/js_pt_hooks.h" +#include "ecmascript/tooling/backend/js_debugger.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/dispatcher.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::tooling; + +namespace panda::test { +class JSPtHooksTest : public testing::Test { +public: + using EntityId = panda_file::File::EntityId; + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(ecmaVm, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope); + } + +protected: + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(JSPtHooksTest, BreakpointTest) +{ + auto debugger = std::make_unique(ecmaVm, nullptr, nullptr); + std::unique_ptr jspthooks = std::make_unique(debugger.get()); + const char *pandaFile = " "; + EntityId methodId(0); + uint32_t bytecodeOffset = 0; + JSPtLocation ptLocation1(pandaFile, methodId, bytecodeOffset); + jspthooks->Breakpoint(ptLocation1); + ASSERT_NE(jspthooks, nullptr); +} + +HWTEST_F_L0(JSPtHooksTest, LoadModuleTest) +{ + auto debugger = std::make_unique(ecmaVm, nullptr, nullptr); + std::unique_ptr jspthooks = std::make_unique(debugger.get()); + jspthooks->LoadModule("pandafile/test.abc", "func_main_0"); + ASSERT_NE(jspthooks, nullptr); +} + +HWTEST_F_L0(JSPtHooksTest, ExceptionTest) +{ + auto debugger = std::make_unique(ecmaVm, nullptr, nullptr); + std::unique_ptr jspthooks = std::make_unique(debugger.get()); + const char *pandaFile = " "; + EntityId methodId(0); + uint32_t bytecodeOffset = 0; + JSPtLocation ptLocation2(pandaFile, methodId, bytecodeOffset); + jspthooks->Exception(ptLocation2); + ASSERT_NE(jspthooks, nullptr); +} + +HWTEST_F_L0(JSPtHooksTest, SingleStepTest) +{ + auto debugger = std::make_unique(ecmaVm, nullptr, nullptr); + std::unique_ptr jspthooks = std::make_unique(debugger.get()); + const char *pandaFile = " "; + EntityId methodId(0); + uint32_t bytecodeOffset = 0; + JSPtLocation ptLocation4(pandaFile, methodId, bytecodeOffset); + ASSERT_NE(jspthooks, nullptr); +} + +HWTEST_F_L0(JSPtHooksTest, VmStartTest) +{ + auto debugger = std::make_unique(ecmaVm, nullptr, nullptr); + std::unique_ptr jspthooks = std::make_unique(debugger.get()); + jspthooks->VmStart(); + ASSERT_NE(jspthooks, nullptr); +} + +HWTEST_F_L0(JSPtHooksTest, VmDeathTest) +{ + auto debugger = std::make_unique(ecmaVm, nullptr, nullptr); + std::unique_ptr jspthooks = std::make_unique(debugger.get()); + jspthooks->VmDeath(); + ASSERT_NE(jspthooks, nullptr); +} +} \ No newline at end of file diff --git a/tooling/test/pt_base64_test.cpp b/tooling/test/pt_base64_test.cpp new file mode 100644 index 00000000..fdd7e446 --- /dev/null +++ b/tooling/test/pt_base64_test.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 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/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_base64.h" + +using namespace panda::ecmascript::tooling; + +namespace panda::test { +class PtBase64Test : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + } + + void TearDown() override + { + } +}; + +HWTEST_F_L0(PtBase64Test, ShortTextTest) +{ + std::string dec = "Hello"; + std::string enc; + std::string des; + uint32_t len = PtBase64::Encode(dec, enc); + EXPECT_EQ(static_cast(len), 8); + EXPECT_EQ(enc, "SGVsbG8="); + + len = PtBase64::Decode(enc, des); + EXPECT_EQ(static_cast(len), 5); + EXPECT_EQ(des, "Hello"); +} + +HWTEST_F_L0(PtBase64Test, LongTextTest) +{ + std::string enc = "SWYgeW91IGNhbiBzZWUgdGhpcyBtZXNzYWdlLCBpdCBtZWFucyB0aGF0IFB0QmFzZTY0RGVjb2RlIHdvcmtzIHdlbGw="; + std::string dec = "If you can see this message, it means that PtBase64Decode works well"; + std::string des; + + uint32_t len = PtBase64::Encode(dec, des); + EXPECT_EQ(static_cast(len), 92); + EXPECT_EQ(enc, des); + + len = PtBase64::Decode(enc, des); + EXPECT_EQ(static_cast(len), 68); + EXPECT_EQ(des, dec); +} + +HWTEST_F_L0(PtBase64Test, ErrorTextTest) +{ + std::string src = "SGVsbG8=="; + std::string des; + PtBase64 ptBase64; + uint32_t len = ptBase64.Decode(src, des); + EXPECT_EQ(static_cast(len), 0); + EXPECT_EQ(des, ""); + + len = PtBase64::Encode("", des); + EXPECT_EQ(static_cast(len), 0); + EXPECT_EQ(des, ""); +} +} \ No newline at end of file diff --git a/tooling/test/pt_json_test.cpp b/tooling/test/pt_json_test.cpp new file mode 100644 index 00000000..05997474 --- /dev/null +++ b/tooling/test/pt_json_test.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 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/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_json.h" + +using namespace panda::ecmascript::tooling; + +namespace panda::test { +class PtJsonTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + } + + void TearDown() override + { + } +}; + +HWTEST_F_L0(PtJsonTest, FalseTest) +{ + std::string str = "false"; + std::unique_ptr json = PtJson::Parse(str.c_str()); + ASSERT_TRUE(json->IsBool()); + EXPECT_FALSE(json->GetBool()); + EXPECT_EQ(json->Stringify(), str); + json->ReleaseRoot(); +} + +HWTEST_F_L0(PtJsonTest, TrueTest) +{ + std::string str = "true"; + std::unique_ptr json = PtJson::Parse(str.c_str()); + ASSERT_TRUE(json->IsBool()); + EXPECT_TRUE(json->GetBool()); + EXPECT_EQ(json->Stringify(), str); + json->ReleaseRoot(); +} + +HWTEST_F_L0(PtJsonTest, IntTest) +{ + std::string str = "100"; + std::unique_ptr json = PtJson::Parse(str.c_str()); + ASSERT_TRUE(json->IsNumber()); + EXPECT_EQ(json->GetInt(), 100); + EXPECT_EQ(json->Stringify(), str); + json->ReleaseRoot(); +} + +HWTEST_F_L0(PtJsonTest, Int64Test) +{ + std::string str = "123456789012345"; + std::unique_ptr json = PtJson::Parse(str.c_str()); + ASSERT_TRUE(json->IsNumber()); + EXPECT_EQ(json->GetInt64(), 123456789012345); + EXPECT_EQ(json->Stringify(), str); + json->ReleaseRoot(); +} + +HWTEST_F_L0(PtJsonTest, DoubleTest) +{ + std::string str = "12345.6789"; + std::unique_ptr json = PtJson::Parse(str.c_str()); + ASSERT_TRUE(json->IsNumber()); + EXPECT_EQ(json->GetDouble(), 12345.6789); + EXPECT_EQ(json->Stringify(), str); + json->ReleaseRoot(); +} + +HWTEST_F_L0(PtJsonTest, StringTest) +{ + std::string str = "\"abcdefg\""; + std::unique_ptr json = PtJson::Parse(str.c_str()); + ASSERT_TRUE(json->IsString()); + EXPECT_EQ(json->GetString(), "abcdefg"); + EXPECT_EQ(json->Stringify(), str); + json->ReleaseRoot(); +} + +HWTEST_F_L0(PtJsonTest, ArrayTest1) +{ + std::string str = "[\"a\",\"b\",200]"; + std::unique_ptr json = PtJson::Parse(str.c_str()); + ASSERT_TRUE(json->IsArray()); + EXPECT_EQ(json->GetSize(), 3); + EXPECT_EQ(json->Get(0)->GetString(), "a"); + EXPECT_EQ(json->Get(1)->GetString(), "b"); + EXPECT_EQ(json->Get(2)->GetInt(), 200); + EXPECT_EQ(json->Stringify(), str); + json->ReleaseRoot(); +} + +HWTEST_F_L0(PtJsonTest, ArrayTest2) +{ + std::string str = "[\"a\",\"b\",200,10.5,{}]"; + std::unique_ptr json = PtJson::Parse(str.c_str()); + ASSERT_TRUE(json->IsArray()); + EXPECT_EQ(json->GetSize(), 5); + EXPECT_EQ(json->Get(0)->GetString(), "a"); + EXPECT_EQ(json->Get(1)->GetString(), "b"); + EXPECT_EQ(json->Get(2)->GetInt(), 200); + EXPECT_EQ(json->Get(3)->GetDouble(), 10.5); + EXPECT_TRUE(json->Get(4)->IsObject()); + EXPECT_EQ(json->Stringify(), str); + json->ReleaseRoot(); +} + +HWTEST_F_L0(PtJsonTest, ObjectTest) +{ + auto child1 = PtJson::CreateObject(); + child1->Add("ch", "child_1"); + ASSERT_TRUE(child1->Contains("ch")); + + auto child2 = PtJson::CreateObject(); + child2->Add("ch", "child_2"); + ASSERT_TRUE(child2->Contains("ch")); + + auto arr = PtJson::CreateArray(); + arr->Push(100); + EXPECT_EQ(arr->GetSize(), 1); + + auto root = PtJson::CreateObject(); + root->Add("a", false); + root->Add("b", 100); + root->Add("c", 100.2); + root->Add("d", static_cast(200)); + root->Add("e", "abc"); + root->Add("f", child2); + root->Add("g", arr); + + bool b; + int32_t i32; + int64_t i64; + double d; + std::string str; + std::unique_ptr json; + ASSERT_EQ(root->GetBool("a", &b), Result::SUCCESS); + EXPECT_FALSE(b); + ASSERT_EQ(root->GetInt("b", &i32), Result::SUCCESS); + EXPECT_EQ(i32, 100); + ASSERT_EQ(root->GetDouble("c", &d), Result::SUCCESS); + EXPECT_EQ(d, 100.2); + ASSERT_EQ(root->GetInt64("d", &i64), Result::SUCCESS); + EXPECT_EQ(i64, static_cast(200)); + ASSERT_EQ(root->GetString("e", &str), Result::SUCCESS); + EXPECT_EQ(str, "abc"); + ASSERT_EQ(root->GetObject("f", &json), Result::SUCCESS); + ASSERT_EQ(json->GetString("ch", &str), Result::SUCCESS); + EXPECT_EQ(str, "child_2"); + ASSERT_EQ(root->GetArray("g", &json), Result::SUCCESS); + ASSERT_TRUE(json->IsArray()); + EXPECT_EQ(json->Get(0)->GetInt(), 100); + + EXPECT_EQ(root->Stringify(), + "{\"a\":false,\"b\":100,\"c\":100.2,\"d\":200,\"e\":\"abc\",\"f\":{\"ch\":\"child_2\"},\"g\":[100]}"); + root->ReleaseRoot(); +} +} \ No newline at end of file diff --git a/tooling/test/testcases/js/arrow_func.js b/tooling/test/testcases/js/arrow_func.js new file mode 100644 index 00000000..75e9b5fa --- /dev/null +++ b/tooling/test/testcases/js/arrow_func.js @@ -0,0 +1,23 @@ +/* + * 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. + */ + +var sum = (num1, num2) => { + num1 = num1 + 1; + print(num1); + num2 = num2 + 1; + print(num2); + return num1 + num2; + }; +print(sum(1,2)); \ No newline at end of file diff --git a/tooling/test/testcases/js/async_func.js b/tooling/test/testcases/js/async_func.js new file mode 100644 index 00000000..f9c0ac64 --- /dev/null +++ b/tooling/test/testcases/js/async_func.js @@ -0,0 +1,33 @@ +/* + * 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. + */ + +async function helloAsync(){ + print ("helloAsync log 1"); + print ("helloAsync log 2"); + print ("helloAsync log 3"); + print ("helloAsync log 4"); + return "helloAsync"; +} + +print("main test 1"); +print(helloAsync()); +print("main test 2"); +print("main test 3"); + +helloAsync().then(v=>{ + print(v); + print("helloAsync then end!"); +}) +print("main test end!"); \ No newline at end of file diff --git a/tooling/test/testcases/js/exception.js b/tooling/test/testcases/js/exception.js new file mode 100644 index 00000000..764a9192 --- /dev/null +++ b/tooling/test/testcases/js/exception.js @@ -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. + */ + +function exception_func() { + var a = 'js_test'; + var b = false; var c = b(a); print(a); print(b) + print(c) +} + +print("exception test begin") +exception_func() +print("exception test end") diff --git a/tooling/test/testcases/js/range_error.js b/tooling/test/testcases/js/range_error.js new file mode 100644 index 00000000..51b82854 --- /dev/null +++ b/tooling/test/testcases/js/range_error.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 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. + */ + +function rangeErrorException() { + let a = new Array(-1); +} + +print("begin"); +rangeErrorException(); +print("end"); \ No newline at end of file diff --git a/tooling/test/testcases/js/sample.js b/tooling/test/testcases/js/sample.js new file mode 100644 index 00000000..d4796cf4 --- /dev/null +++ b/tooling/test/testcases/js/sample.js @@ -0,0 +1,30 @@ +/* + * 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. + */ + +function count_to_ten() { + var a = 1; + a = 2; + a = 3; + a = 4; + a = 5; + a = 6; + a = 7; + a = 8; + a = 9; + a = 10; +} + +count_to_ten(); +count_to_ten(); \ No newline at end of file diff --git a/tooling/test/testcases/js/step.js b/tooling/test/testcases/js/step.js new file mode 100644 index 00000000..3e2214eb --- /dev/null +++ b/tooling/test/testcases/js/step.js @@ -0,0 +1,98 @@ +/* + * Copyright (c) 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. + */ + +print("testStart"); + +async function testAwait() { + return new Promise(resolve => { + resolve("test"); + }); +} +function testFunction() { + var a = 1; + a = 2; + a = 3; + a = 4; + nextFunction(); + a = 5; + a = 6; + a = 7; + a = 8; + a = 9; + a = 10; + a = 12; + async function asyncTest() { + var c = 1; + c = 2; + return 'test'; + } + a = 13; + a = 14; + asyncTest(); + a = 15; + a = 16; + a = 17; + a = 18; + a = 19; + a = 20; + async function asyncTest2() { + var d = 1; + d = 2; + var result = await testAwait(); + d = 3; + return result; + } + asyncTest2(); + a = 21; + a = 22; + a = 23; + if (a == 23) { + a = 100; + } + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + print("->testFunction end, a:" + a); +} +function nextFunction() { + print("->nextFunction()"); + var c = 1; + c = 2; + c = 3; + c = 4; + c = 5; + c = 6; + c = 7; +} +print("->testFunction()"); +var b = 1; +b = 2; +b = 3; +b = 4; +testFunction(); +b = 5; +b = 6; +b = 7; +b = 8; +b = 9; +b = 10; +b = 11; +b = 12; +b = 13; +print("->testEnd"); diff --git a/tooling/test/testcases/js/syntax_exception.js b/tooling/test/testcases/js/syntax_exception.js new file mode 100644 index 00000000..899ab230 --- /dev/null +++ b/tooling/test/testcases/js/syntax_exception.js @@ -0,0 +1,29 @@ +/* + * Copyright (c) 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. + */ + +function countSyntaxException() { + var sum = 0; + try { + adddlert("hello! "); + } + catch(err) { + print(err); + sum += 1; + } + return sum; +} +print("begin"); +countSyntaxException(); +print("end"); \ No newline at end of file diff --git a/tooling/test/testcases/js/throw_exception.js b/tooling/test/testcases/js/throw_exception.js new file mode 100644 index 00000000..6d2b9f0d --- /dev/null +++ b/tooling/test/testcases/js/throw_exception.js @@ -0,0 +1,34 @@ +/* + * Copyright (c) 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. + */ + +function countThrowException() { + var sum = 0; + print(sum) + try { + throw 'error'; + print("error"); + } + catch(err) { + print("catch: " + err); + sum += 1; + } + return sum; +} +print("begin"); +print(countThrowException()); +print("end"); + + + diff --git a/tooling/test/testcases/js_breakpoint_arrow_test.h b/tooling/test/testcases/js_breakpoint_arrow_test.h new file mode 100644 index 00000000..baf68e7d --- /dev/null +++ b/tooling/test/testcases/js_breakpoint_arrow_test.h @@ -0,0 +1,86 @@ +/* + * 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_BREAKPOINT_ARROW_TEST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_ARROW_TEST_H + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsBreakpointArrowTest : public TestEvents { +public: + JsBreakpointArrowTest() + { + vmStart = [this] { + location_ = TestUtil::GetLocation(18, 0, pandaFile_.c_str()); // 18: breakpointer line + ASSERT_TRUE(location_.GetMethodId().IsValid()); + return true; + }; + + breakpoint = [this](const JSPtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_); + ++breakpointCounter_; + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + ASSERT_TRUE(debugger_->NotifyScriptParsed(0, pandaFile_)); + auto condFuncRef = FunctionRef::Undefined(vm_); + auto ret = debugInterface_->SetBreakpoint(location_, condFuncRef); + ASSERT_TRUE(ret); + return true; + }; + + scenario = [this]() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + TestUtil::WaitForBreakpoint(location_); + TestUtil::Continue(); + auto ret = debugInterface_->RemoveBreakpoint(location_); + ASSERT_TRUE(ret); + ASSERT_EXITED(); + return true; + }; + + vmDeath = [this]() { + ASSERT_EQ(breakpointCounter_, 1U); // 1: break point + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + ~JsBreakpointArrowTest() = default; + +private: + std::string pandaFile_ = DEBUGGER_ABC_DIR "arrow_func.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + JSPtLocation location_ {nullptr, JSPtLocation::EntityId(0), 0}; + size_t breakpointCounter_ = 0; +}; + +std::unique_ptr GetJsBreakpointArrowTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_ARROW_TEST_H diff --git a/tooling/test/testcases/js_breakpoint_async_test.h b/tooling/test/testcases/js_breakpoint_async_test.h new file mode 100644 index 00000000..17917e19 --- /dev/null +++ b/tooling/test/testcases/js_breakpoint_async_test.h @@ -0,0 +1,88 @@ +/* + * 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_BREAKPOINT_ASYNC_TEST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_ASYNC_TEST_H + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsBreakpointAsyncTest : public TestEvents { +public: + JsBreakpointAsyncTest() + { + vmStart = [this] { + location_ = TestUtil::GetLocation(18, 0, pandaFile_.c_str()); // 18: breakpointer line + ASSERT_TRUE(location_.GetMethodId().IsValid()); + return true; + }; + + breakpoint = [this](const JSPtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_); + ++breakpointCounter_; + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + ASSERT_TRUE(debugger_->NotifyScriptParsed(0, pandaFile_)); + auto condFuncRef = FunctionRef::Undefined(vm_); + auto ret = debugInterface_->SetBreakpoint(location_, condFuncRef); + ASSERT_TRUE(ret); + return true; + }; + + scenario = [this]() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + TestUtil::WaitForBreakpoint(location_); + TestUtil::Continue(); + TestUtil::WaitForBreakpoint(location_); + TestUtil::Continue(); + auto ret = debugInterface_->RemoveBreakpoint(location_); + ASSERT_TRUE(ret); + ASSERT_EXITED(); + return true; + }; + + vmDeath = [this]() { + ASSERT_EQ(breakpointCounter_, 2U); // 2: break point counter + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + ~JsBreakpointAsyncTest() = default; + +private: + std::string pandaFile_ = DEBUGGER_ABC_DIR "async_func.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + JSPtLocation location_ {nullptr, JSPtLocation::EntityId(0), 0}; + size_t breakpointCounter_ = 0; +}; + +std::unique_ptr GetJsBreakpointAsyncTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_ASYNC_TEST_H diff --git a/tooling/test/testcases/js_breakpoint_test.h b/tooling/test/testcases/js_breakpoint_test.h new file mode 100644 index 00000000..5bbf2db6 --- /dev/null +++ b/tooling/test/testcases/js_breakpoint_test.h @@ -0,0 +1,88 @@ +/* + * 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_BREAKPOINT_TEST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_TEST_H + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsBreakpointTest : public TestEvents { +public: + JsBreakpointTest() + { + vmStart = [this] { + location_ = TestUtil::GetLocation(22, 0, pandaFile_.c_str()); // 22: breakpointer line + ASSERT_TRUE(location_.GetMethodId().IsValid()); + return true; + }; + + breakpoint = [this](const JSPtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_); + ++breakpointCounter_; + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + ASSERT_TRUE(debugger_->NotifyScriptParsed(0, pandaFile_)); + auto condFuncRef = FunctionRef::Undefined(vm_); + auto ret = debugInterface_->SetBreakpoint(location_, condFuncRef); + ASSERT_TRUE(ret); + return true; + }; + + scenario = [this]() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + TestUtil::WaitForBreakpoint(location_); + TestUtil::Continue(); + TestUtil::WaitForBreakpoint(location_); + TestUtil::Continue(); + auto ret = debugInterface_->RemoveBreakpoint(location_); + ASSERT_TRUE(ret); + ASSERT_EXITED(); + return true; + }; + + vmDeath = [this]() { + ASSERT_EQ(breakpointCounter_, 2U); // 2: break point counter + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + ~JsBreakpointTest() = default; + +private: + std::string pandaFile_ = DEBUGGER_ABC_DIR "sample.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + JSPtLocation location_ {nullptr, JSPtLocation::EntityId(0), 0}; + size_t breakpointCounter_ = 0; +}; + +std::unique_ptr GetJsBreakpointTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_TEST_H diff --git a/tooling/test/testcases/js_exception_test.h b/tooling/test/testcases/js_exception_test.h new file mode 100644 index 00000000..860e76b2 --- /dev/null +++ b/tooling/test/testcases/js_exception_test.h @@ -0,0 +1,113 @@ +/* + * 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_EXCEPTION_TEST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_EXCEPTION_TEST_H + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsExceptionTest : public TestEvents { +public: + JsExceptionTest() + { + vmStart = [this] { + location_ = TestUtil::GetLocation(22, 0, pandaFile_.c_str()); // 22:breakpointer line + ASSERT_TRUE(location_.GetMethodId().IsValid()); + return true; + }; + + breakpoint = [this](const JSPtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_); + ++breakpointCounter_; + std::vector> callFrames; + ASSERT_TRUE(debugger_->GenerateCallFrames(&callFrames)); + ASSERT_TRUE(callFrames.size() > 0); + auto jsLocation = callFrames[0]->GetLocation(); + ASSERT_TRUE(jsLocation != nullptr); + ASSERT_EQ(jsLocation->GetLine(), 22); // 22: breakpoint line + ASSERT_EQ(jsLocation->GetColumn(), 0); // 0: breakpoint column + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); + return true; + }; + + exception = [this](const JSPtLocation &location) { + auto sourceLocation = TestUtil::GetSourceLocation(location, pandaFile_.c_str()); + ASSERT_EQ(sourceLocation.line, 17); // 17 : exception line + ASSERT_EQ(sourceLocation.column, 27); // 27 : exception column + ++exceptionCounter_; + std::vector> callFrames; + ASSERT_TRUE(debugger_->GenerateCallFrames(&callFrames)); + ASSERT_TRUE(callFrames.size() > 0); + auto jsLocation = callFrames[0]->GetLocation(); + ASSERT_TRUE(jsLocation != nullptr); + ASSERT_EQ(jsLocation->GetLine(), 17); // 17 : exception line + ASSERT_EQ(jsLocation->GetColumn(), 27); // 27 : exception column + TestUtil::SuspendUntilContinue(DebugEvent::EXCEPTION, location); + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + ASSERT_TRUE(debugger_->NotifyScriptParsed(0, pandaFile_)); + auto condFuncRef = FunctionRef::Undefined(vm_); + auto ret = debugInterface_->SetBreakpoint(location_, condFuncRef); + ASSERT_TRUE(ret); + return true; + }; + + scenario = [this]() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + TestUtil::WaitForBreakpoint(location_); + TestUtil::Continue(); + TestUtil::WaitForException(); + TestUtil::Continue(); + auto ret = debugInterface_->RemoveBreakpoint(location_); + ASSERT_TRUE(ret); + ASSERT_EXITED(); + return true; + }; + + vmDeath = [this]() { + ASSERT_EQ(breakpointCounter_, 1U); // 1: break point counter + ASSERT_EQ(exceptionCounter_, 1U); // 1: exception counter + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + ~JsExceptionTest() = default; + +private: + std::string pandaFile_ = DEBUGGER_ABC_DIR "exception.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + JSPtLocation location_ {nullptr, JSPtLocation::EntityId(0), 0}; + size_t breakpointCounter_ = 0; + size_t exceptionCounter_ = 0; +}; + +std::unique_ptr GetJsExceptionTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_EXCEPTION_TEST_H diff --git a/tooling/test/testcases/js_range_error_test.h b/tooling/test/testcases/js_range_error_test.h new file mode 100644 index 00000000..260f7342 --- /dev/null +++ b/tooling/test/testcases/js_range_error_test.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_RANGE_ERROR_TEST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_RANGE_ERROR_TEST_H + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsRangeErrorTest : public TestEvents { +public: + JsRangeErrorTest() + { + vmStart = [this] { + location_ = TestUtil::GetLocation(20, 0, pandaFile_.c_str()); // 20: breakpointer line + ASSERT_TRUE(location_.GetMethodId().IsValid()); + return true; + }; + + breakpoint = [this](const JSPtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_); + ++breakpointCounter_; + std::vector> callFrames; + ASSERT_TRUE(debugger_->GenerateCallFrames(&callFrames)); + ASSERT_TRUE(callFrames.size() > 0); + auto jsLocation = callFrames[0]->GetLocation(); + ASSERT_TRUE(jsLocation != nullptr); + ASSERT_EQ(jsLocation->GetLine(), 20); // 22: breakpoint line + ASSERT_EQ(jsLocation->GetColumn(), 0); // 0: breakpoint column + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); + return true; + }; + + exception = [this](const JSPtLocation &location) { + auto sourceLocation = TestUtil::GetSourceLocation(location, pandaFile_.c_str()); + ASSERT_EQ(sourceLocation.line, 16); // 16: exception line + ASSERT_EQ(sourceLocation.column, 12); // 12: exception column + ++exceptionCounter_; + std::vector> callFrames; + ASSERT_TRUE(debugger_->GenerateCallFrames(&callFrames)); + ASSERT_TRUE(callFrames.size() > 0); + auto jsLocation = callFrames[0]->GetLocation(); + ASSERT_TRUE(jsLocation != nullptr); + ASSERT_EQ(jsLocation->GetLine(), 16); // 16: exception line + ASSERT_EQ(jsLocation->GetColumn(), 12); // 12: exception column + TestUtil::SuspendUntilContinue(DebugEvent::EXCEPTION, location); + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + ASSERT_TRUE(debugger_->NotifyScriptParsed(0, pandaFile_)); + auto condFuncRef = FunctionRef::Undefined(vm_); + auto ret = debugInterface_->SetBreakpoint(location_, condFuncRef); + ASSERT_TRUE(ret); + return true; + }; + + scenario = [this]() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + TestUtil::WaitForBreakpoint(location_); + TestUtil::Continue(); + TestUtil::WaitForException(); + TestUtil::Continue(); + auto ret = debugInterface_->RemoveBreakpoint(location_); + ASSERT_TRUE(ret); + ASSERT_EXITED(); + return true; + }; + + vmDeath = [this]() { + ASSERT_EQ(breakpointCounter_, 1U); // 1: break point counter + ASSERT_EQ(exceptionCounter_, 1U); // 1: exception counter + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + ~JsRangeErrorTest() = default; + +private: + std::string pandaFile_ = DEBUGGER_ABC_DIR "range_error.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + JSPtLocation location_ {nullptr, JSPtLocation::EntityId(0), 0}; + size_t breakpointCounter_ = 0; + size_t exceptionCounter_ = 0; +}; + +std::unique_ptr GetJsRangeErrorTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_RANGE_ERROR_TEST_H diff --git a/tooling/test/testcases/js_single_step_test.h b/tooling/test/testcases/js_single_step_test.h new file mode 100644 index 00000000..33753f3f --- /dev/null +++ b/tooling/test/testcases/js_single_step_test.h @@ -0,0 +1,105 @@ +/* + * 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::ecmascript::tooling::test { +class JsSingleStepTest : public TestEvents { +public: + JsSingleStepTest() + { + vmStart = [this] { + locationStart_ = TestUtil::GetLocation(19, 0, pandaFile_.c_str()); // 19: line number + locationEnd_ = TestUtil::GetLocation(22, 0, pandaFile_.c_str()); // 22: line number + return true; + }; + + vmDeath = [this]() { + ASSERT_NE(stepCounter_, 0); // 0: step counter + ASSERT_EQ(breakpointCounter_, 2); // 2: break point counter + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + auto condFuncRef = FunctionRef::Undefined(vm_); + auto ret = debugInterface_->SetBreakpoint(locationEnd_, condFuncRef); + ASSERT_TRUE(ret); + return true; + }; + + breakpoint = [this](const JSPtLocation &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](const JSPtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + if (!collectSteps_) { + if (locationStart_ == location) { + collectSteps_ = true; + } + return false; + } + + ASSERT_NE(bytecodeOffset_, location.GetBytecodeOffset()); + locationStep_ = location; + stepCounter_++; + bytecodeOffset_ = location.GetBytecodeOffset(); + return false; + }; + + scenario = []() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + +private: + std::string pandaFile_ = DEBUGGER_ABC_DIR "sample.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + JSPtLocation locationStart_ {nullptr, JSPtLocation::EntityId(0), 0}; + JSPtLocation locationEnd_ {nullptr, JSPtLocation::EntityId(0), 0}; + JSPtLocation locationStep_ {nullptr, JSPtLocation::EntityId(0), 0}; + int32_t stepCounter_ = 0; + int32_t breakpointCounter_ = 0; + bool collectSteps_ = false; + uint32_t bytecodeOffset_ = std::numeric_limits::max(); +}; + +std::unique_ptr GetJsSingleStepTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_SINGLE_STEP_TEST_H diff --git a/tooling/test/testcases/js_step_into_test.h b/tooling/test/testcases/js_step_into_test.h new file mode 100644 index 00000000..7a581130 --- /dev/null +++ b/tooling/test/testcases/js_step_into_test.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_INTO_TEST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_INTO_TEST_H + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsStepIntoTest : public TestEvents { +public: + JsStepIntoTest() + { + vmStart = [this] { + // line number for breakpoint array + size_t breakpoint[POINTER_SIZE][LINE_COLUMN] = + {{84, 0}, {87, 0}, {27, 0}, {79, 0}, {42, 0}, {38, 0}, {56, 0}, {60, 0}, {96, 0}, {54, 0}}; + // line number for stepinto array + size_t stepInto[STEP_SIZE][LINE_COLUMN] = + {{85, 5}, {23, 0}, {73, 0}, {80, 0}, {36, 0}, {43, 0}, {50, 0}, {61, 0}, {97, 15}}; + SetJSPtLocation(breakpoint[0], POINTER_SIZE, pointerLocations_); + SetJSPtLocation(stepInto[0], STEP_SIZE, stepLocations_); + return true; + }; + + vmDeath = [this]() { + ASSERT_EQ(breakpointCounter_, pointerLocations_.size()); + ASSERT_EQ(stepCompleteCounter_, stepLocations_.size()); + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + debugger_->NotifyScriptParsed(0, moduleName.data()); + auto condFuncRef = FunctionRef::Undefined(vm_); + for (auto &iter : pointerLocations_) { + auto ret = debugInterface_->SetBreakpoint(iter, condFuncRef); + ASSERT_TRUE(ret); + } + return true; + }; + + breakpoint = [this](const JSPtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, pointerLocations_.at(breakpointCounter_)); + ++breakpointCounter_; + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); + debugger_->StepInto(StepIntoParams()); + return true; + }; + + singleStep = [this](const JSPtLocation &location) { + if (debugger_->NotifySingleStep(location)) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, stepLocations_.at(stepCompleteCounter_)); + stepCompleteCounter_++; + TestUtil::SuspendUntilContinue(DebugEvent::STEP_COMPLETE, location); + return true; + } + return false; + }; + + scenario = [this]() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + size_t index = 0; + while (index < POINTER_SIZE) { + TestUtil::WaitForBreakpoint(pointerLocations_.at(index)); + TestUtil::Continue(); + if (index < STEP_SIZE) { + TestUtil::WaitForStepComplete(stepLocations_.at(index)); + TestUtil::Continue(); + } + ++index; + } + ASSERT_EXITED(); + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + +private: + static constexpr size_t LINE_COLUMN = 2; + static constexpr size_t POINTER_SIZE = 10; + static constexpr size_t STEP_SIZE = 9; + + std::string pandaFile_ = DEBUGGER_ABC_DIR "step.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + size_t breakpointCounter_ = 0; + size_t stepCompleteCounter_ = 0; + std::vector pointerLocations_; + std::vector stepLocations_; + + void SetJSPtLocation(size_t *arr, size_t number, std::vector &locations) + { + for (size_t i = 0; i < number; i++) { + JSPtLocation location = + TestUtil::GetLocation(arr[i * LINE_COLUMN], arr[i * LINE_COLUMN + 1], pandaFile_.c_str()); + locations.push_back(location); + } + }; +}; + +std::unique_ptr GetJsStepIntoTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_INTO_TEST_H diff --git a/tooling/test/testcases/js_step_out_test.h b/tooling/test/testcases/js_step_out_test.h new file mode 100644 index 00000000..408d5bb6 --- /dev/null +++ b/tooling/test/testcases/js_step_out_test.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_OUT_TEST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_OUT_TEST_H + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsStepOutTest : public TestEvents { +public: + JsStepOutTest() + { + vmStart = [this] { + // 74、36: line number for breakpoint array + size_t breakpoint[5][2] = {{74, 0}, {36, 0}, {50, 0}, {61, 0}, {96, 0}}; + // 28: line number for stepinto array + size_t stepOut[4][2] = {{28, 0}, {43, 0}, {57, 0}, {88, 5}}; + SetJSPtLocation(breakpoint[0], POINTER_SIZE, pointerLocations_); + SetJSPtLocation(stepOut[0], STEP_SIZE, stepLocations_); + return true; + }; + + vmDeath = [this]() { + ASSERT_EQ(breakpointCounter_, pointerLocations_.size()); // size: break point counter + ASSERT_EQ(stepCompleteCounter_, stepLocations_.size()); // size: step complete counter + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + debugger_->NotifyScriptParsed(0, moduleName.data()); + auto condFuncRef = FunctionRef::Undefined(vm_); + for (auto &iter : pointerLocations_) { + auto ret = debugInterface_->SetBreakpoint(iter, condFuncRef); + ASSERT_TRUE(ret); + } + return true; + }; + + breakpoint = [this](const JSPtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, pointerLocations_.at(breakpointCounter_)); + ++breakpointCounter_; + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); + debugger_->StepOut(); + return true; + }; + + singleStep = [this](const JSPtLocation &location) { + if (debugger_->NotifySingleStep(location)) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, stepLocations_.at(stepCompleteCounter_)); + stepCompleteCounter_++; + TestUtil::SuspendUntilContinue(DebugEvent::STEP_COMPLETE, location); + return true; + } + return false; + }; + + scenario = [this]() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + size_t index = 0; + while (index < pointerLocations_.size()) { + TestUtil::WaitForBreakpoint(pointerLocations_.at(index)); + TestUtil::Continue(); + if (index < STEP_SIZE) { + TestUtil::WaitForStepComplete(stepLocations_.at(index)); + TestUtil::Continue(); + } + ++index; + } + ASSERT_EXITED(); + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + +private: + static constexpr size_t LINE_COLUMN = 2; + static constexpr size_t POINTER_SIZE = 5; + static constexpr size_t STEP_SIZE = 4; + + std::string pandaFile_ = DEBUGGER_ABC_DIR "step.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + JSPtLocation location1_ {nullptr, JSPtLocation::EntityId(0), 0}; + JSPtLocation location2_ {nullptr, JSPtLocation::EntityId(0), 0}; + size_t breakpointCounter_ = 0; + size_t stepCompleteCounter_ = 0; + std::vector pointerLocations_; + std::vector stepLocations_; + + void SetJSPtLocation(size_t *arr, size_t number, std::vector &locations) + { + for (size_t i = 0; i < number; i++) { + JSPtLocation location_ = + TestUtil::GetLocation(arr[i * LINE_COLUMN], arr[i * LINE_COLUMN + 1], pandaFile_.c_str()); + locations.push_back(location_); + } + }; +}; + +std::unique_ptr GetJsStepOutTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_OUT_TEST_H diff --git a/tooling/test/testcases/js_step_over_test.h b/tooling/test/testcases/js_step_over_test.h new file mode 100644 index 00000000..e6a97bf2 --- /dev/null +++ b/tooling/test/testcases/js_step_over_test.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_OVER_TEST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_OVER_TEST_H + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsStepOverTest : public TestEvents { +public: + JsStepOverTest() + { + vmStart = [this] { + // 24、27: line number for breakpoint array + size_t breakpoint[8][2] = {{24, 0}, {27, 0}, {36, 0}, {50, 0}, {60, 0}, {90, 0}, {96, 0}, {54, 0}}; + // 25、28: line number for stepinto array + size_t stepOver[7][2] = {{25, 5}, {28, 0}, {37, 0}, {51, 0}, {61, 0}, {91, 5}, {97, 15}}; + SetJSPtLocation(breakpoint[0], POINTER_SIZE, pointerLocations_); + SetJSPtLocation(stepOver[0], STEP_SIZE, stepLocations_); + return true; + }; + + vmDeath = [this]() { + ASSERT_EQ(breakpointCounter_, pointerLocations_.size()); // size: break point counter + ASSERT_EQ(stepCompleteCounter_, stepLocations_.size()); // size: step complete counter + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + debugger_->NotifyScriptParsed(0, moduleName.data()); + auto condFuncRef = FunctionRef::Undefined(vm_); + for (auto &iter : pointerLocations_) { + auto ret = debugInterface_->SetBreakpoint(iter, condFuncRef); + ASSERT_TRUE(ret); + } + return true; + }; + + breakpoint = [this](const JSPtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, pointerLocations_.at(breakpointCounter_)); + ++breakpointCounter_; + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); + debugger_->StepOver(StepOverParams()); + return true; + }; + + singleStep = [this](const JSPtLocation &location) { + if (debugger_->NotifySingleStep(location)) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, stepLocations_.at(stepCompleteCounter_)); + stepCompleteCounter_++; + TestUtil::SuspendUntilContinue(DebugEvent::STEP_COMPLETE, location); + return true; + } + return false; + }; + + scenario = [this]() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + size_t index = 0; + while (index < pointerLocations_.size()) { + TestUtil::WaitForBreakpoint(pointerLocations_.at(index)); + TestUtil::Continue(); + if (index < STEP_SIZE) { + TestUtil::WaitForStepComplete(stepLocations_.at(index)); + TestUtil::Continue(); + } + ++index; + } + ASSERT_EXITED(); + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + +private: + static constexpr size_t LINE_COLUMN = 2; + static constexpr size_t POINTER_SIZE = 8; + static constexpr size_t STEP_SIZE = 7; + + std::string pandaFile_ = DEBUGGER_ABC_DIR "step.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + JSPtLocation location1_ {nullptr, JSPtLocation::EntityId(0), 0}; + JSPtLocation location2_ {nullptr, JSPtLocation::EntityId(0), 0}; + size_t breakpointCounter_ = 0; + size_t stepCompleteCounter_ = 0; + std::vector pointerLocations_; + std::vector stepLocations_; + + void SetJSPtLocation(size_t *arr, size_t number, std::vector &locations) + { + for (size_t i = 0; i < number; i++) { + JSPtLocation location_ = + TestUtil::GetLocation(arr[i * LINE_COLUMN], arr[i * LINE_COLUMN + 1], pandaFile_.c_str()); + locations.push_back(location_); + } + }; +}; + +std::unique_ptr GetJsStepOverTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_OVER_TEST_H diff --git a/tooling/test/testcases/js_syntax_exception_test.h b/tooling/test/testcases/js_syntax_exception_test.h new file mode 100644 index 00000000..6e2503d7 --- /dev/null +++ b/tooling/test/testcases/js_syntax_exception_test.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_SYNTAX_EXCEPTION_TEST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_SYNTAX_EXCEPTION_TEST_H + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsSyntaxExceptionTest : public TestEvents { +public: + JsSyntaxExceptionTest() + { + vmStart = [this] { + location_ = TestUtil::GetLocation(27, 0, pandaFile_.c_str()); // 27: breakpointer line + ASSERT_TRUE(location_.GetMethodId().IsValid()); + return true; + }; + + breakpoint = [this](const JSPtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_); + ++breakpointCounter_; + std::vector> callFrames; + ASSERT_TRUE(debugger_->GenerateCallFrames(&callFrames)); + ASSERT_TRUE(callFrames.size() > 0); + auto jsLocation = callFrames[0]->GetLocation(); + ASSERT_TRUE(jsLocation != nullptr); + ASSERT_EQ(jsLocation->GetLine(), 27); // 27: breakpoint line + ASSERT_EQ(jsLocation->GetColumn(), 0); // 0: breakpoint column + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); + return true; + }; + + exception = [this](const JSPtLocation &location) { + auto sourceLocation = TestUtil::GetSourceLocation(location, pandaFile_.c_str()); + ASSERT_EQ(sourceLocation.line, 18); // 18: exception line + ASSERT_EQ(sourceLocation.column, 8); // 8: exception column + ++exceptionCounter_; + std::vector> callFrames; + ASSERT_TRUE(debugger_->GenerateCallFrames(&callFrames)); + ASSERT_TRUE(callFrames.size() > 0); + auto jsLocation = callFrames[0]->GetLocation(); + ASSERT_TRUE(jsLocation != nullptr); + ASSERT_EQ(jsLocation->GetLine(), 18); // 18: exception line + ASSERT_EQ(jsLocation->GetColumn(), 8); // 8: exception column + TestUtil::SuspendUntilContinue(DebugEvent::EXCEPTION, location); + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + ASSERT_TRUE(debugger_->NotifyScriptParsed(0, pandaFile_)); + auto condFuncRef = FunctionRef::Undefined(vm_); + auto ret = debugInterface_->SetBreakpoint(location_, condFuncRef); + ASSERT_TRUE(ret); + return true; + }; + + scenario = [this]() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + TestUtil::WaitForBreakpoint(location_); + TestUtil::Continue(); + TestUtil::WaitForException(); + TestUtil::Continue(); + auto ret = debugInterface_->RemoveBreakpoint(location_); + ASSERT_TRUE(ret); + ASSERT_EXITED(); + return true; + }; + + vmDeath = [this]() { + ASSERT_EQ(breakpointCounter_, 1U); // 1: break point counter + ASSERT_EQ(exceptionCounter_, 1U); // 1: exception counter + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + ~JsSyntaxExceptionTest() = default; + +private: + std::string pandaFile_ = DEBUGGER_ABC_DIR "syntax_exception.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + JSPtLocation location_ {nullptr, JSPtLocation::EntityId(0), 0}; + size_t breakpointCounter_ = 0; + size_t exceptionCounter_ = 0; +}; + +std::unique_ptr GetJsSyntaxExceptionTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_SYNTAX_EXCEPTION_TEST_H diff --git a/tooling/test/testcases/js_throw_exception_test.h b/tooling/test/testcases/js_throw_exception_test.h new file mode 100644 index 00000000..fafc398c --- /dev/null +++ b/tooling/test/testcases/js_throw_exception_test.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_THROW_EXCEPTION_TEST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_THROW_EXCEPTION_TEST_H + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsThrowExceptionTest : public TestEvents { +public: + JsThrowExceptionTest() + { + vmStart = [this] { + location_ = TestUtil::GetLocation(28, 0, pandaFile_.c_str()); // 28: breakpointer line + std::cout<<"vmStart1"<> callFrames; + ASSERT_TRUE(debugger_->GenerateCallFrames(&callFrames)); + ASSERT_TRUE(callFrames.size() > 0); + auto jsLocation = callFrames[0]->GetLocation(); + ASSERT_TRUE(jsLocation != nullptr); + ASSERT_EQ(jsLocation->GetLine(), 28); // 28: breakpoint line + ASSERT_EQ(jsLocation->GetColumn(), 0); // 0: breakpoint column + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); + return true; + }; + + exception = [this](const JSPtLocation &location) { + auto sourceLocation = TestUtil::GetSourceLocation(location, pandaFile_.c_str()); + ASSERT_EQ(sourceLocation.line, 19); // 19: exception line + ASSERT_EQ(sourceLocation.column, 8); // 8: exception column + ++exceptionCounter_; + std::vector> callFrames; + ASSERT_TRUE(debugger_->GenerateCallFrames(&callFrames)); + ASSERT_TRUE(callFrames.size() > 0); + auto jsLocation = callFrames[0]->GetLocation(); + ASSERT_TRUE(jsLocation != nullptr); + ASSERT_EQ(jsLocation->GetLine(), 19); // 19: exception line + ASSERT_EQ(jsLocation->GetColumn(), 8); // 8: exception column + ++exceptionCounter_; + TestUtil::SuspendUntilContinue(DebugEvent::EXCEPTION, location); + return true; + }; + + loadModule = [this](std::string_view moduleName) { + TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); + ASSERT_EQ(moduleName, pandaFile_); + ASSERT_TRUE(debugger_->NotifyScriptParsed(0, pandaFile_)); + auto condFuncRef = FunctionRef::Undefined(vm_); + auto ret = debugInterface_->SetBreakpoint(location_, condFuncRef); + ASSERT_TRUE(ret); + return true; + }; + + scenario = [this]() { + TestUtil::WaitForLoadModule(); + TestUtil::Continue(); + TestUtil::WaitForBreakpoint(location_); + TestUtil::Continue(); + TestUtil::WaitForException(); + TestUtil::Continue(); + auto ret = debugInterface_->RemoveBreakpoint(location_); + ASSERT_TRUE(ret); + ASSERT_EXITED(); + return true; + }; + + vmDeath = [this]() { + ASSERT_EQ(breakpointCounter_, 1U); // 1: break point counter + ASSERT_EQ(exceptionCounter_, 2U); // 2: exception counter + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + ~JsThrowExceptionTest() = default; + +private: + std::string pandaFile_ = DEBUGGER_ABC_DIR "throw_exception.abc"; + std::string entryPoint_ = "_GLOBAL::func_main_0"; + JSPtLocation location_ {nullptr, JSPtLocation::EntityId(0), 0}; + size_t breakpointCounter_ = 0; + size_t exceptionCounter_ = 0; +}; + +std::unique_ptr GetJsThrowExceptionTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_THROW_EXCEPTION_TEST_H diff --git a/tooling/test/utils/test_entry.cpp b/tooling/test/utils/test_entry.cpp new file mode 100644 index 00000000..4c9e1b07 --- /dev/null +++ b/tooling/test/utils/test_entry.cpp @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/test/utils/test_entry.h" + +#include + +#include "ecmascript/tooling/test/utils/test_hooks.h" +#include "ecmascript/tooling/test/utils/test_list.h" + +namespace panda::ecmascript::tooling::test { +static std::thread g_debuggerThread; +static std::unique_ptr g_hooks = nullptr; + +bool StartDebuggerImpl([[maybe_unused]] const std::string &name, EcmaVM *vm, [[maybe_unused]] bool isDebugMode) +{ + std::string testName = GetCurrentTestName(); + g_hooks = std::make_unique(testName, vm); + g_debuggerThread = std::thread([] { + TestUtil::WaitForInit(); + g_hooks->Run(); + }); + return true; +} + +bool StopDebuggerImpl([[maybe_unused]] const std::string &name) +{ + g_hooks->TerminateTest(); + g_debuggerThread.join(); + g_hooks.reset(); + return true; +} +} // namespace panda::ecmascript::tooling::test diff --git a/tooling/test/utils/test_entry.h b/tooling/test/utils/test_entry.h new file mode 100644 index 00000000..7625844f --- /dev/null +++ b/tooling/test/utils/test_entry.h @@ -0,0 +1,29 @@ +/* + * 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 + +#include + +namespace panda::ecmascript { +class EcmaVM; +namespace tooling::test { +bool StartDebuggerImpl(const std::string &name, EcmaVM *vm, bool isDebugMode); +bool StopDebuggerImpl(const std::string &name); +} // namespace tooling::test +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_ENTRY_H \ No newline at end of file diff --git a/tooling/test/utils/test_events.h b/tooling/test/utils/test_events.h new file mode 100644 index 00000000..f61bff21 --- /dev/null +++ b/tooling/test/utils/test_events.h @@ -0,0 +1,67 @@ +/* + * 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_EVENTS_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EVENTS_H + +#include + +#include "ecmascript/tooling/backend/js_pt_hooks.h" + +namespace panda::ecmascript::tooling::test { +using BreakpointCallback = std::function; +using LoadModuleCallback = std::function; +using ExceptionCallback = std::function; +using SingleStepCallback = std::function; +using VmStartCallback = std::function; +using VmDeathCallback = std::function; +using Scenario = std::function; + +enum class DebugEvent { + BREAKPOINT, + LOAD_MODULE, + PAUSED, + STEP_COMPLETE, + EXCEPTION, + METHOD_ENTRY, + SINGLE_STEP, + VM_START, + VM_INITIALIZATION, + VM_DEATH, + UNINITIALIZED +}; + +std::ostream &operator<<(std::ostream &out, DebugEvent value); + +struct TestEvents { + BreakpointCallback breakpoint; + LoadModuleCallback loadModule; + ExceptionCallback exception; + SingleStepCallback singleStep; + VmStartCallback vmStart; + VmDeathCallback vmDeath; + + Scenario scenario; + const EcmaVM *vm_ {nullptr}; + JSDebugger *debugInterface_ {nullptr}; + DebuggerImpl *debugger_ {nullptr}; + TestEvents(); + virtual ~TestEvents() = default; + + virtual std::pair GetEntryPoint() = 0; +}; +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EVENTS_H diff --git a/tooling/test/utils/test_extractor.cpp b/tooling/test/utils/test_extractor.cpp new file mode 100644 index 00000000..381c0a4c --- /dev/null +++ b/tooling/test/utils/test_extractor.cpp @@ -0,0 +1,57 @@ +/* + * 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. + */ +// Taken from panda-tools/panda-debugger/debugger + +#include "ecmascript/tooling/test/utils/test_extractor.h" + +#include "libpandabase/utils/leb128.h" +#include "libpandabase/utils/utf.h" +#include "libpandafile/class_data_accessor-inl.h" +#include "libpandafile/code_data_accessor-inl.h" +#include "libpandafile/debug_data_accessor-inl.h" +#include "libpandafile/helpers.h" +#include "libpandafile/method_data_accessor-inl.h" +#include "libpandafile/proto_data_accessor-inl.h" + +namespace panda::ecmascript::tooling::test { +std::pair TestExtractor::GetBreakpointAddress(const SourceLocation &sourceLocation) +{ + EntityId retId = EntityId(); + uint32_t retOffset = 0; + auto callbackFunc = [&retId, &retOffset](File::EntityId id, uint32_t offset) -> bool { + retId = id; + retOffset = offset; + return true; + }; + MatchWithLocation(callbackFunc, sourceLocation.line, sourceLocation.column); + return {retId, retOffset}; +} + +SourceLocation TestExtractor::GetSourceLocation(EntityId methodId, uint32_t bytecodeOffset) +{ + SourceLocation location {GetSourceFile(methodId), 0, 0}; + auto callbackLineFunc = [&location](int32_t line) -> bool { + location.line = line; + return true; + }; + auto callbackColumnFunc = [&location](int32_t column) -> bool { + location.column = column; + return true; + }; + MatchLineWithOffset(callbackLineFunc, methodId, bytecodeOffset); + MatchColumnWithOffset(callbackColumnFunc, methodId, bytecodeOffset); + return location; +} +} // namespace panda::ecmascript::tooling::test diff --git a/tooling/test/utils/test_extractor.h b/tooling/test/utils/test_extractor.h new file mode 100644 index 00000000..5cc9bedc --- /dev/null +++ b/tooling/test/utils/test_extractor.h @@ -0,0 +1,52 @@ +/* + * 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_EXTRACTOR_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EXTRACTOR_H + +#include "ecmascript/tooling/backend/js_pt_extractor.h" + +namespace panda::ecmascript::tooling::test { +using EntityId = panda_file::File::EntityId; + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +struct SourceLocation { + std::string path; // NOLINT(misc-non-private-member-variables-in-classes) + int32_t line; // NOLINT(misc-non-private-member-variables-in-classes) + int32_t column; + + bool operator==(const SourceLocation &other) const + { + return path == other.path && line == other.line && column == other.column; + } + + bool IsValid() const + { + return !path.empty(); + } +}; + +class TestExtractor : public JSPtExtractor { +public: + explicit TestExtractor(const JSPandaFile *pandaFile) : JSPtExtractor(pandaFile) {} + ~TestExtractor() = default; + + std::pair GetBreakpointAddress(const SourceLocation &sourceLocation); + + SourceLocation GetSourceLocation(EntityId methodId, uint32_t bytecodeOffset); +}; +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EXTRACTOR_H diff --git a/tooling/test/utils/test_hooks.h b/tooling/test/utils/test_hooks.h new file mode 100644 index 00000000..9ee2ee4f --- /dev/null +++ b/tooling/test/utils/test_hooks.h @@ -0,0 +1,123 @@ +/* + * 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_HOOKS_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_HOOKS_H + +#include "ecmascript/tooling/agent/debugger_impl.h" +#include "ecmascript/tooling/backend/js_pt_hooks.h" +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class TestHooks : public PtHooks { +public: + TestHooks(const std::string &testName, const EcmaVM *vm) : vm_(vm) + { + runtime_ = std::make_unique(vm, nullptr); + debugger_ = std::make_unique(vm, nullptr, runtime_.get()); + testName_ = testName; + test_ = TestUtil::GetTest(testName); + test_->vm_ = vm; + test_->debugger_ = debugger_.get(); + test_->debugInterface_ = debugger_->jsDebugger_; + debugInterface_ = debugger_->jsDebugger_; + TestUtil::Reset(); + debugInterface_->RegisterHooks(this); + } + + void Run() + { + if (test_->scenario) { + test_->scenario(); + } + } + + void Breakpoint(const JSPtLocation &location) override + { + if (test_->breakpoint) { + test_->breakpoint(location); + } + } + + void LoadModule(std::string_view panda_file_name, [[maybe_unused]]std::string_view entryPoint) override + { + if (test_->loadModule) { + test_->loadModule(panda_file_name); + } + } + + void Exception(const JSPtLocation &location) override + { + if (test_->exception) { + Local exception = DebuggerApi::GetAndClearException(vm_); + + test_->exception(location); + + if (!exception->IsHole()) { + DebuggerApi::SetException(vm_, exception); + } + } + } + + bool SingleStep(const JSPtLocation &location) override + { + if (test_->singleStep) { + return test_->singleStep(location); + } + return false; + } + + void VmDeath() override + { + if (test_->vmDeath) { + test_->vmDeath(); + } + TestUtil::Event(DebugEvent::VM_DEATH); + } + + void VmStart() override + { + if (test_->vmStart) { + test_->vmStart(); + } + TestUtil::Event(DebugEvent::VM_START); + } + + void PendingJobEntry() override {} + + void NativeCalling(const void *nativeAddress) override {} + + void TerminateTest() + { + debugInterface_->UnregisterHooks(); + if (TestUtil::IsTestFinished()) { + return; + } + LOG_DEBUGGER(FATAL) << "Test " << testName_ << " failed"; + } + + ~TestHooks() = default; + +private: + const EcmaVM *vm_ {nullptr}; + std::unique_ptr runtime_ {nullptr}; + std::unique_ptr debugger_ {nullptr}; + JSDebugger *debugInterface_; + std::string testName_; + TestEvents *test_; +}; +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_HOOKS_H diff --git a/tooling/test/utils/test_list.cpp b/tooling/test/utils/test_list.cpp new file mode 100644 index 00000000..50471a82 --- /dev/null +++ b/tooling/test/utils/test_list.cpp @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/test/utils/test_list.h" + +#include "ecmascript/tooling/test/utils/test_util.h" + +// testcase list +#include "ecmascript/tooling/test/testcases/js_breakpoint_test.h" +#include "ecmascript/tooling/test/testcases/js_breakpoint_arrow_test.h" +#include "ecmascript/tooling/test/testcases/js_breakpoint_async_test.h" +#include "ecmascript/tooling/test/testcases/js_exception_test.h" +#include "ecmascript/tooling/test/testcases/js_range_error_test.h" +#include "ecmascript/tooling/test/testcases/js_single_step_test.h" +#include "ecmascript/tooling/test/testcases/js_step_into_test.h" +#include "ecmascript/tooling/test/testcases/js_step_over_test.h" +#include "ecmascript/tooling/test/testcases/js_step_out_test.h" +#include "ecmascript/tooling/test/testcases/js_syntax_exception_test.h" +#include "ecmascript/tooling/test/testcases/js_throw_exception_test.h" + +namespace panda::ecmascript::tooling::test { +static std::string g_currentTestName = ""; + +static void RegisterTests() +{ + // Register testcases + TestUtil::RegisterTest("JsExceptionTest", GetJsExceptionTest()); + TestUtil::RegisterTest("JsSingleStepTest", GetJsSingleStepTest()); + TestUtil::RegisterTest("JsBreakpointTest", GetJsBreakpointTest()); + TestUtil::RegisterTest("JsBreakpointAsyncTest", GetJsBreakpointAsyncTest()); + TestUtil::RegisterTest("JsBreakpointArrowTest", GetJsBreakpointArrowTest()); + TestUtil::RegisterTest("JsRangeErrorTest", GetJsRangeErrorTest()); + TestUtil::RegisterTest("JsSyntaxExceptionTest", GetJsSyntaxExceptionTest()); + TestUtil::RegisterTest("JsThrowExceptionTest", GetJsThrowExceptionTest()); + TestUtil::RegisterTest("JsStepIntoTest", GetJsStepIntoTest()); + TestUtil::RegisterTest("JsStepOverTest", GetJsStepOverTest()); + TestUtil::RegisterTest("JsStepOutTest", GetJsStepOutTest()); +} + +std::vector GetTestList() +{ + RegisterTests(); + std::vector res; + + auto &tests = TestUtil::GetTests(); + for (const auto &entry : tests) { + res.push_back(entry.first.c_str()); + } + return res; +} + +void SetCurrentTestName(const std::string &testName) +{ + g_currentTestName = testName; +} + +std::string GetCurrentTestName() +{ + return g_currentTestName; +} + +std::pair GetTestEntryPoint(const std::string &testName) +{ + return TestUtil::GetTest(testName)->GetEntryPoint(); +} +} // namespace panda::ecmascript::tooling::test diff --git a/tooling/test/utils/test_list.h b/tooling/test/utils/test_list.h new file mode 100644 index 00000000..b7d17047 --- /dev/null +++ b/tooling/test/utils/test_list.h @@ -0,0 +1,34 @@ +/* + * 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_LIST_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_LIST_H + +#include +#include + +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/c_string.h" + +namespace panda::ecmascript::tooling::test { +std::vector GetTestList(); + +void SetCurrentTestName(const std::string &testName); +std::string GetCurrentTestName(); + +std::pair GetTestEntryPoint(const std::string &testName); +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_LIST_H diff --git a/tooling/test/utils/test_util.cpp b/tooling/test/utils/test_util.cpp new file mode 100644 index 00000000..2ca7534d --- /dev/null +++ b/tooling/test/utils/test_util.cpp @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#include "ecmascript/tooling/test/utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +TestMap TestUtil::testMap_; +os::memory::Mutex TestUtil::eventMutex_; +os::memory::ConditionVariable TestUtil::eventCv_; +DebugEvent TestUtil::lastEvent_ = DebugEvent::UNINITIALIZED; +bool TestUtil::initialized_ = false; +os::memory::Mutex TestUtil::suspendMutex_; +os::memory::ConditionVariable TestUtil::suspendCv_; +bool TestUtil::suspended_ = false; +JSPtLocation TestUtil::lastEventLocation_("", EntityId(0), 0); + +std::ostream &operator<<(std::ostream &out, DebugEvent value) +{ + const char *s = nullptr; + +#define ADD_CASE(entry) \ + case (entry): \ + s = #entry; \ + break + + switch (value) { + ADD_CASE(DebugEvent::BREAKPOINT); + ADD_CASE(DebugEvent::LOAD_MODULE); + ADD_CASE(DebugEvent::PAUSED); + ADD_CASE(DebugEvent::EXCEPTION); + ADD_CASE(DebugEvent::STEP_COMPLETE); + ADD_CASE(DebugEvent::METHOD_ENTRY); + ADD_CASE(DebugEvent::SINGLE_STEP); + ADD_CASE(DebugEvent::VM_START); + ADD_CASE(DebugEvent::VM_INITIALIZATION); + ADD_CASE(DebugEvent::VM_DEATH); + ADD_CASE(DebugEvent::UNINITIALIZED); + default: { + ASSERT(false && "Unknown DebugEvent"); // NOLINT + } + } + +#undef ADD_CASE + + return out << s; +} + +std::ostream &operator<<(std::ostream &out, std::nullptr_t) +{ + return out << "nullptr"; +} + +TestEvents::TestEvents() +{ + scenario = []() { + ASSERT_EXITED(); + return true; + }; +} +} // namespace panda::ecmascript::tooling::test diff --git a/tooling/test/utils/test_util.h b/tooling/test/utils/test_util.h new file mode 100644 index 00000000..22f3c017 --- /dev/null +++ b/tooling/test/utils/test_util.h @@ -0,0 +1,289 @@ +/* + * 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_UTIL_H +#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H + +#include "ecmascript/jspandafile/js_pandafile_manager.h" +#include "ecmascript/tooling/agent/debugger_impl.h" +#include "ecmascript/tooling/backend/js_debugger.h" +#include "ecmascript/tooling/test/utils/test_events.h" +#include "ecmascript/tooling/test/utils/test_extractor.h" +#include "os/mutex.h" + +namespace panda::ecmascript::tooling::test { +template, class KeyEqual = std::equal_to> +using CUnorderedMap = panda::ecmascript::CUnorderedMap; +using TestMap = CUnorderedMap>; + +class TestUtil { +public: + static void RegisterTest(const std::string &testName, std::unique_ptr test) + { + testMap_.insert({testName, std::move(test)}); + } + + static TestEvents *GetTest(const std::string &name) + { + auto iter = std::find_if(testMap_.begin(), testMap_.end(), [&name](auto &it) { + return it.first == name; + }); + if (iter != testMap_.end()) { + return iter->second.get(); + } + LOG_DEBUGGER(FATAL) << "Test " << name << " not found"; + return nullptr; + } + + static void WaitForBreakpoint(JSPtLocation location) + { + auto predicate = [&location]() REQUIRES(eventMutex_) { return lastEventLocation_ == location; }; + auto onSuccess = []() REQUIRES(eventMutex_) { + // Need to reset location, because we might want to stop at the same point + lastEventLocation_ = JSPtLocation("", EntityId(0), 0); + }; + + WaitForEvent(DebugEvent::BREAKPOINT, predicate, onSuccess); + } + + static bool WaitForExit() + { + return WaitForEvent(DebugEvent::VM_DEATH, + []() REQUIRES(eventMutex_) { + return lastEvent_ == DebugEvent::VM_DEATH; + }, [] {}); + } + + static void WaitForStepComplete(JSPtLocation location) + { + auto predicate = [&location]() REQUIRES(eventMutex_) { return lastEventLocation_ == location; }; + auto onSuccess = []() REQUIRES(eventMutex_) { + // Need to reset location, because we might want to stop at the same point + lastEventLocation_ = JSPtLocation("", EntityId(0), 0); + }; + + WaitForEvent(DebugEvent::STEP_COMPLETE, predicate, onSuccess); + } + + static bool WaitForException() + { + auto predicate = []() REQUIRES(eventMutex_) { return lastEvent_ == DebugEvent::EXCEPTION; }; + return WaitForEvent(DebugEvent::EXCEPTION, predicate, [] {}); + } + + static bool WaitForInit() + { + return WaitForEvent(DebugEvent::VM_START, + []() REQUIRES(eventMutex_) { + return initialized_; + }, [] {}); + } + + static bool WaitForLoadModule() + { + auto predicate = []() REQUIRES(eventMutex_) { return lastEvent_ == DebugEvent::LOAD_MODULE; }; + return WaitForEvent(DebugEvent::LOAD_MODULE, predicate, [] {}); + } + + static void Event(DebugEvent event, JSPtLocation location = JSPtLocation("", EntityId(0), 0)) + { + LOG_DEBUGGER(DEBUG) << "Occurred event " << event; + os::memory::LockHolder holder(eventMutex_); + lastEvent_ = event; + lastEventLocation_ = location; + if (event == DebugEvent::VM_START) { + initialized_ = true; + } + eventCv_.Signal(); + } + + static void Reset() + { + os::memory::LockHolder lock(eventMutex_); + initialized_ = false; + lastEvent_ = DebugEvent::VM_START; + } + + static TestMap &GetTests() + { + return testMap_; + } + + static bool IsTestFinished() + { + os::memory::LockHolder lock(eventMutex_); + return lastEvent_ == DebugEvent::VM_DEATH; + } + + static JSPtLocation GetLocation(int32_t line, int32_t column, const char *pandaFile) + { + auto jsPandaFile = ::panda::ecmascript::JSPandaFileManager::GetInstance()->OpenJSPandaFile(pandaFile); + if (jsPandaFile == nullptr) { + return JSPtLocation("", EntityId(0), 0); + } + TestExtractor extractor(jsPandaFile); + auto [id, offset] = extractor.GetBreakpointAddress({"", line, column}); + return JSPtLocation(pandaFile, id, offset); + } + + static SourceLocation GetSourceLocation(const JSPtLocation &location, const char *pandaFile) + { + auto jsPandaFile = ::panda::ecmascript::JSPandaFileManager::GetInstance()->OpenJSPandaFile(pandaFile); + if (jsPandaFile == nullptr) { + return SourceLocation(); + } + TestExtractor extractor(jsPandaFile); + return extractor.GetSourceLocation(location.GetMethodId(), location.GetBytecodeOffset()); + } + + static bool SuspendUntilContinue(DebugEvent reason, JSPtLocation location = JSPtLocation("", EntityId(0), 0)) + { + os::memory::LockHolder lock(suspendMutex_); + suspended_ = true; + + // Notify the debugger thread about the suspend event + Event(reason, location); + + // Wait for continue + while (suspended_) { + constexpr uint64_t TIMEOUT_MSEC = 10000U; + bool timeExceeded = suspendCv_.TimedWait(&suspendMutex_, TIMEOUT_MSEC); + if (timeExceeded) { + LOG_DEBUGGER(FATAL) << "Time limit exceeded while suspend"; + return false; + } + } + + return true; + } + + static bool Continue() + { + os::memory::LockHolder lock(suspendMutex_); + suspended_ = false; + suspendCv_.Signal(); + return true; + } + +private: + template + static bool WaitForEvent(DebugEvent event, Predicate predicate, OnSuccessAction action) + { + os::memory::LockHolder holder(eventMutex_); + while (!predicate()) { + if (lastEvent_ == DebugEvent::VM_DEATH) { + return false; + } + constexpr uint64_t TIMEOUT_MSEC = 10000U; + bool timeExceeded = eventCv_.TimedWait(&eventMutex_, TIMEOUT_MSEC); + if (timeExceeded) { + LOG_DEBUGGER(FATAL) << "Time limit exceeded while waiting " << event; + return false; + } + } + action(); + return true; + } + + static TestMap testMap_; + static os::memory::Mutex eventMutex_; + static os::memory::ConditionVariable eventCv_ GUARDED_BY(eventMutex_); + static DebugEvent lastEvent_ GUARDED_BY(eventMutex_); + static JSPtLocation lastEventLocation_ GUARDED_BY(eventMutex_); + static os::memory::Mutex suspendMutex_; + static os::memory::ConditionVariable suspendCv_ GUARDED_BY(suspendMutex_); + static bool suspended_ GUARDED_BY(suspendMutex_); + static bool initialized_ GUARDED_BY(eventMutex_); +}; + +std::ostream &operator<<(std::ostream &out, std::nullptr_t); + +#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_TRUE(cond) \ + do { \ + auto res = (cond); \ + if (!res) { \ + ASSERT_FAIL_(res, true, #cond, "true", "equal to"); \ + } \ + } while (0) + +#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) +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H -- Gitee From e140cbcf1f3c8eddc84c8e1c96130d604ffd8eda Mon Sep 17 00:00:00 2001 From: Gymee Date: Thu, 15 Sep 2022 20:55:53 +0800 Subject: [PATCH 2/2] migrate tooling from ets_runtime Issue: #I5RV8U Signed-off-by: Gymee Change-Id: Id6583124ecd9be0fe96390f90a36f2486b55b717 --- BUILD.gn | 145 +++++++ bundle.json | 6 +- test/fuzztest/BUILD.gn | 22 ++ .../dispatchprotocolmessage_fuzzer/BUILD.gn | 44 +++ .../corpus/init | 14 + .../dispatchprotocolmessage_fuzzer.cpp | 45 +++ .../dispatchprotocolmessage_fuzzer.h | 51 +-- .../project.xml | 25 ++ .../initializedebugger_fuzzer/BUILD.gn | 44 +++ .../initializedebugger_fuzzer/corpus/init | 14 + .../initializedebugger_fuzzer.cpp | 46 +++ .../initializedebugger_fuzzer.h | 69 +--- .../initializedebugger_fuzzer/project.xml | 25 ++ test/resource/tooling/ohos_test.xml | 38 ++ test/test_helper.gni | 69 ++++ toolchain.gni | 9 + tooling/BUILD.gn | 5 +- tooling/agent/debugger_impl.cpp | 77 ++-- tooling/agent/debugger_impl.h | 24 +- tooling/agent/heapprofiler_impl.cpp | 2 +- tooling/agent/heapprofiler_impl.h | 18 +- tooling/agent/profiler_impl.cpp | 7 +- tooling/agent/profiler_impl.h | 7 +- tooling/agent/runtime_impl.cpp | 7 +- tooling/agent/runtime_impl.h | 4 +- tooling/agent/tracing_impl.cpp | 7 +- tooling/agent/tracing_impl.h | 7 +- tooling/backend/debugger_api.cpp | 374 ------------------ tooling/backend/debugger_api.h | 101 ----- tooling/backend/debugger_executor.cpp | 6 +- tooling/backend/js_debugger.cpp | 183 --------- tooling/backend/js_debugger.h | 152 ------- tooling/backend/js_debugger_interface.h | 133 ------- tooling/backend/js_pt_extractor.h | 129 ------ tooling/backend/js_pt_hooks.cpp | 4 +- tooling/backend/js_pt_hooks.h | 7 +- tooling/backend/js_pt_location.h | 67 ---- ...pt_extractor.cpp => js_single_stepper.cpp} | 40 +- tooling/backend/js_single_stepper.h | 68 ++++ tooling/base/pt_base64.cpp | 2 +- tooling/base/pt_events.cpp | 2 +- tooling/base/pt_events.h | 6 +- tooling/base/pt_json.cpp | 2 +- tooling/base/pt_method.h | 74 ---- tooling/base/pt_params.cpp | 4 +- tooling/base/pt_params.h | 2 +- tooling/base/pt_returns.cpp | 2 +- tooling/base/pt_returns.h | 2 +- tooling/base/pt_script.cpp | 5 +- tooling/base/pt_script.h | 2 +- tooling/base/pt_types.h | 5 +- tooling/debugger_service.cpp | 7 +- tooling/dispatcher.cpp | 14 +- tooling/dispatcher.h | 5 +- tooling/interface/file_stream.cpp | 130 ------ tooling/interface/file_stream.h | 88 ----- tooling/interface/js_debugger_manager.h | 149 ------- tooling/interface/notification_manager.h | 104 ----- tooling/protocol_channel.h | 6 +- tooling/protocol_handler.cpp | 4 +- tooling/protocol_handler.h | 2 +- tooling/test/BUILD.gn | 26 +- tooling/test/debugger_commands_test.cpp | 11 +- tooling/test/debugger_entry_test.cpp | 2 +- tooling/test/debugger_events_test.cpp | 6 +- tooling/test/debugger_params_test.cpp | 7 +- tooling/test/debugger_returns_test.cpp | 7 +- tooling/test/debugger_script_test.cpp | 14 +- tooling/test/debugger_types_test.cpp | 5 +- tooling/test/entry/test_debugger_entry.cpp | 2 +- tooling/test/js_pt_hooks_test.cpp | 13 +- tooling/test/pt_base64_test.cpp | 2 +- tooling/test/pt_json_test.cpp | 2 +- .../test/testcases/js_breakpoint_arrow_test.h | 2 +- .../test/testcases/js_breakpoint_async_test.h | 2 +- tooling/test/testcases/js_breakpoint_test.h | 2 +- tooling/test/testcases/js_exception_test.h | 2 +- tooling/test/testcases/js_range_error_test.h | 2 +- tooling/test/testcases/js_single_step_test.h | 2 +- tooling/test/testcases/js_step_into_test.h | 2 +- tooling/test/testcases/js_step_out_test.h | 2 +- tooling/test/testcases/js_step_over_test.h | 2 +- .../test/testcases/js_syntax_exception_test.h | 2 +- .../test/testcases/js_throw_exception_test.h | 2 +- tooling/test/utils/test_entry.cpp | 6 +- tooling/test/utils/test_events.h | 2 +- tooling/test/utils/test_extractor.cpp | 4 +- tooling/test/utils/test_extractor.h | 6 +- tooling/test/utils/test_hooks.h | 6 +- tooling/test/utils/test_list.cpp | 26 +- tooling/test/utils/test_util.cpp | 2 +- tooling/test/utils/test_util.h | 10 +- 92 files changed, 904 insertions(+), 1989 deletions(-) create mode 100644 BUILD.gn create mode 100644 test/fuzztest/BUILD.gn create mode 100644 test/fuzztest/dispatchprotocolmessage_fuzzer/BUILD.gn create mode 100644 test/fuzztest/dispatchprotocolmessage_fuzzer/corpus/init create mode 100644 test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.cpp rename tooling/interface/progress.h => test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.h (52%) create mode 100644 test/fuzztest/dispatchprotocolmessage_fuzzer/project.xml create mode 100644 test/fuzztest/initializedebugger_fuzzer/BUILD.gn create mode 100644 test/fuzztest/initializedebugger_fuzzer/corpus/init create mode 100644 test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.cpp rename tooling/interface/stream.h => test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.h (34%) create mode 100644 test/fuzztest/initializedebugger_fuzzer/project.xml create mode 100755 test/resource/tooling/ohos_test.xml create mode 100644 test/test_helper.gni delete mode 100644 tooling/backend/debugger_api.cpp delete mode 100644 tooling/backend/debugger_api.h delete mode 100644 tooling/backend/js_debugger.cpp delete mode 100644 tooling/backend/js_debugger.h delete mode 100644 tooling/backend/js_debugger_interface.h delete mode 100644 tooling/backend/js_pt_extractor.h delete mode 100644 tooling/backend/js_pt_location.h rename tooling/backend/{js_pt_extractor.cpp => js_single_stepper.cpp} (66%) create mode 100644 tooling/backend/js_single_stepper.h delete mode 100644 tooling/base/pt_method.h delete mode 100644 tooling/interface/file_stream.cpp delete mode 100644 tooling/interface/file_stream.h delete mode 100644 tooling/interface/js_debugger_manager.h delete mode 100644 tooling/interface/notification_manager.h diff --git a/BUILD.gn b/BUILD.gn new file mode 100644 index 00000000..1ab08af3 --- /dev/null +++ b/BUILD.gn @@ -0,0 +1,145 @@ +# 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. + +import("//arkcompiler/toolchain/toolchain.gni") +import("//build/ohos.gni") + +config("ark_toolchain_common_config") { + defines = [ "PANDA_ENABLE_LTO" ] + cflags_cc = [ + "-Wall", + "-Wshadow", + "-Werror", + "-Wextra", + "-pedantic", + "-Wno-invalid-offsetof", + "-Wno-gnu-statement-expression", + "-pipe", + "-Wdate-time", + "-funwind-tables", + "-fasynchronous-unwind-tables", + "-Wformat=2", + ] + + if (is_linux) { + defines += [ + "PANDA_TARGET_UNIX", + "PANDA_TARGET_LINUX", + "PANDA_USE_FUTEX", + ] + } else if (is_mingw) { + cflags_cc += [ + "-std=c++17", + "-Wno-ignored-attributes", + ] + defines += [ + "PANDA_TARGET_WINDOWS", + "_CRTBLD", + "__LIBMSVCRT__", + ] + } else if (is_mac) { + defines += [ + "PANDA_TARGET_UNIX", + "PANDA_TARGET_MACOS", + ] + } else if (target_os == "android") { + defines += [ + "PANDA_TARGET_ANDROID", + "PANDA_TARGET_UNIX", + "PANDA_USE_FUTEX", + ] + } else { + defines += [ + "PANDA_TARGET_UNIX", + "PANDA_USE_FUTEX", + ] + if (!is_standard_system && (current_cpu != "arm" || is_wearable_product)) { + defines += [ "PANDA_TARGET_MOBILE" ] + } + } + + if (is_debug) { + cflags_cc += [ + "-O0", + "-ggdb3", + ] + } else { + defines += [ "NDEBUG" ] + } +} + +config("ark_toolchain_public_config") { + defines = [] + if (!is_mingw && !is_mac) { + defines += [ + "ECMASCRIPT_SUPPORT_CPUPROFILER", + "ECMASCRIPT_SUPPORT_HEAPPROFILER", + "ECMASCRIPT_SUPPORT_SNAPSHOT", + "ECMASCRIPT_SUPPORT_DEBUGGER", + ] + } else if (target_os == "android") { + defines += [ + "ECMASCRIPT_SUPPORT_CPUPROFILER", + "ECMASCRIPT_SUPPORT_DEBUGGER", + ] + } + + include_dirs = [ + # Dependent on runtime_core include + "$ark_root", + "$js_root", + "$toolchain_root", + ] +} + +# ecmascript unit testcase config +config("toolchain_test_config") { + visibility = [ ":*" ] + + configs = [ + "$toolchain_root:ark_toolchain_public_config", + "$toolchain_root:ark_toolchain_common_config", + ] + + ldflags = [ "-Wl,-rpath=\$ORIGIN/" ] +} + +group("ark_toolchain_packages") { + deps = [] + if (host_os != "mac") { + deps += [ + "//arkcompiler/toolchain/inspector:ark_debugger", + "//arkcompiler/toolchain/tooling:libark_ecma_debugger", + ] + } +} + +group("ark_toolchain_unittest") { + testonly = true + deps = [] + if (host_os != "mac") { + deps += [ "//arkcompiler/toolchain/tooling/test:unittest" ] + if (is_ohos && is_standard_system) { + deps += [ "//arkcompiler/toolchain/test/fuzztest:fuzztest" ] + } + } +} + +group("ark_toolchain_host_unittest") { + testonly = true + deps = [] + if (host_os != "mac") { + # js unittest + deps += [ "//arkcompiler/toolchain/tooling/test:host_unittest" ] + } +} diff --git a/bundle.json b/bundle.json index 13730834..e735a971 100644 --- a/bundle.json +++ b/bundle.json @@ -26,10 +26,12 @@ }, "build": { "sub_component": [ - "//arkcompiler/toolchain/inspector:ark_debugger" + "//arkcompiler/toolchain:ark_toolchain_packages" ], "inner_kits": [], - "test": [] + "test": [ + "//arkcompiler/toolchain:ark_toolchain_unittest" + ] } } } diff --git a/test/fuzztest/BUILD.gn b/test/fuzztest/BUILD.gn new file mode 100644 index 00000000..0746cd73 --- /dev/null +++ b/test/fuzztest/BUILD.gn @@ -0,0 +1,22 @@ +# Copyright (c) 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. + +group("fuzztest") { + testonly = true + deps = [] + + deps += [ + "dispatchprotocolmessage_fuzzer:fuzztest", + "initializedebugger_fuzzer:fuzztest", + ] +} diff --git a/test/fuzztest/dispatchprotocolmessage_fuzzer/BUILD.gn b/test/fuzztest/dispatchprotocolmessage_fuzzer/BUILD.gn new file mode 100644 index 00000000..a1a6254e --- /dev/null +++ b/test/fuzztest/dispatchprotocolmessage_fuzzer/BUILD.gn @@ -0,0 +1,44 @@ +# Copyright (c) 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. + +##################################hydra-fuzz################################### +# import("//arkcompiler/ets_runtime/js_runtime_config.gni") +import("//arkcompiler/toolchain/test/test_helper.gni") +import("//build/config/features.gni") +import("//build/ohos.gni") +import("//build/test.gni") + +##################################fuzztest##################################### +ohos_fuzztest("DispatchProtocolMessageFuzzTest") { + module_out_path = "arkcompiler/toolchain" + + fuzz_config_file = + "//arkcompiler/toolchain/test/fuzztest/dispatchprotocolmessage_fuzzer" + + sources = [ "dispatchprotocolmessage_fuzzer.cpp" ] + + configs = [ "//arkcompiler/toolchain:toolchain_test_config" ] + + deps = [ + "//arkcompiler/ets_runtime:libark_jsruntime", + "//arkcompiler/toolchain/tooling:libark_ecma_debugger_set", + "//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog", + sdk_libc_secshared_dep, + ] +} + +group("fuzztest") { + testonly = true + deps = [] + deps += [ ":DispatchProtocolMessageFuzzTest" ] +} diff --git a/test/fuzztest/dispatchprotocolmessage_fuzzer/corpus/init b/test/fuzztest/dispatchprotocolmessage_fuzzer/corpus/init new file mode 100644 index 00000000..b9a20c8e --- /dev/null +++ b/test/fuzztest/dispatchprotocolmessage_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# 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. + +FUZZ \ No newline at end of file diff --git a/test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.cpp b/test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.cpp new file mode 100644 index 00000000..bff19b9e --- /dev/null +++ b/test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 "dispatchprotocolmessage_fuzzer.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "tooling/debugger_service.h" + +using namespace panda; +using namespace panda::ecmascript; +using namespace panda::ecmascript::tooling; + +namespace OHOS { + void DispatchProtocolMessageFuzzTest(const uint8_t* data, size_t size) + { + RuntimeOption option; + option.SetLogLevel(RuntimeOption::LOG_LEVEL::ERROR); + auto vm = JSNApi::CreateJSVM(option); + if (size <= 0) { + return; + } + std::string message(data, data+size); + OnMessage(vm, std::move(message)); + JSNApi::DestroyJSVM(vm); + } +} + +// Fuzzer entry point. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + // Run your code on data. + OHOS::DispatchProtocolMessageFuzzTest(data, size); + return 0; +} diff --git a/tooling/interface/progress.h b/test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.h similarity index 52% rename from tooling/interface/progress.h rename to test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.h index 8ef98ebb..002d0073 100644 --- a/tooling/interface/progress.h +++ b/test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.h @@ -1,30 +1,21 @@ -/* - * 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. - */ - -#ifndef ECMASCRIPT_TOOLING_INTERFACE_PROGRESS_H -#define ECMASCRIPT_TOOLING_INTERFACE_PROGRESS_H - -#include - -namespace panda::ecmascript { -class Progress { -public: - virtual ~Progress() = default; - - virtual void ReportProgress(int32_t done, int32_t total) = 0; -}; -} // namespace panda::ecmascript - -#endif // ECMASCRIPT_TOOLING_INTERFACE_PROGRESS_H +/* + * Copyright (c) 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. + */ + +#ifndef DISPATCHPROTOCOLMESSAGE_FUZZER_H +#define DISPATCHPROTOCOLMESSAGE_FUZZER_H + +#define FUZZ_PROJECT_NAME "dispatchprotocolmessage_fuzzer" + +#endif \ No newline at end of file diff --git a/test/fuzztest/dispatchprotocolmessage_fuzzer/project.xml b/test/fuzztest/dispatchprotocolmessage_fuzzer/project.xml new file mode 100644 index 00000000..f7de9064 --- /dev/null +++ b/test/fuzztest/dispatchprotocolmessage_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 300 + + 4096 + + \ No newline at end of file diff --git a/test/fuzztest/initializedebugger_fuzzer/BUILD.gn b/test/fuzztest/initializedebugger_fuzzer/BUILD.gn new file mode 100644 index 00000000..cfde7f60 --- /dev/null +++ b/test/fuzztest/initializedebugger_fuzzer/BUILD.gn @@ -0,0 +1,44 @@ +# Copyright (c) 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. + +#####################################hydra-fuzz############################### +# import("//arkcompiler/ets_runtime/js_runtime_config.gni") +import("//arkcompiler/toolchain/test/test_helper.gni") +import("//build/config/features.gni") +import("//build/ohos.gni") +import("//build/test.gni") + +####################################fuzztest################################## +ohos_fuzztest("InitializeDebuggerFuzzTest") { + module_out_path = "arkcompiler/toolchain" + + fuzz_config_file = + "//arkcompiler/toolchain/test/fuzztest/initializedebugger_fuzzer" + + sources = [ "initializedebugger_fuzzer.cpp" ] + + configs = [ "//arkcompiler/toolchain:toolchain_test_config" ] + + deps = [ + "//arkcompiler/ets_runtime:libark_jsruntime", + "//arkcompiler/toolchain/tooling:libark_ecma_debugger_set", + "//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog", + sdk_libc_secshared_dep, + ] +} + +group("fuzztest") { + testonly = true + deps = [] + deps += [ ":InitializeDebuggerFuzzTest" ] +} diff --git a/test/fuzztest/initializedebugger_fuzzer/corpus/init b/test/fuzztest/initializedebugger_fuzzer/corpus/init new file mode 100644 index 00000000..b9a20c8e --- /dev/null +++ b/test/fuzztest/initializedebugger_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# 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. + +FUZZ \ No newline at end of file diff --git a/test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.cpp b/test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.cpp new file mode 100644 index 00000000..ab64a4ef --- /dev/null +++ b/test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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 "initializedebugger_fuzzer.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "tooling/debugger_service.h" + +using namespace panda; +using namespace panda::ecmascript; +using namespace panda::ecmascript::tooling; + +namespace OHOS { + void InitializeDebuggerFuzzTest(const uint8_t* data, size_t size) + { + RuntimeOption option; + option.SetLogLevel(RuntimeOption::LOG_LEVEL::ERROR); + auto vm = JSNApi::CreateJSVM(option); + using OnResponseType = const std::function; + OnResponseType onResponse = [data, size](const void *d, [[maybe_unused]] const std::string &s) -> void { + d = data + size; + }; + InitializeDebugger(vm, onResponse); + UninitializeDebugger(vm); + JSNApi::DestroyJSVM(vm); + } +} + +// Fuzzer entry point. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + // Run your code on data. + OHOS::InitializeDebuggerFuzzTest(data, size); + return 0; +} \ No newline at end of file diff --git a/tooling/interface/stream.h b/test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.h similarity index 34% rename from tooling/interface/stream.h rename to test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.h index 81d7dd42..2c2299cf 100644 --- a/tooling/interface/stream.h +++ b/test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.h @@ -1,48 +1,21 @@ -/* - * 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. - */ - -#ifndef ECMASCRIPT_TOOLING_INTERFACE_STREAM_H -#define ECMASCRIPT_TOOLING_INTERFACE_STREAM_H - -namespace panda::ecmascript { -class HeapStat { -public: - HeapStat(int32_t index, int32_t count, int32_t size) - : index_(index), count_(count), size_(size) {} - ~HeapStat() = default; - - int32_t index_; - int32_t count_; - int32_t size_; -}; - -class Stream { -public: - virtual ~Stream() = default; - - virtual void EndOfStream() = 0; - - // Get chunk's size - virtual int GetSize() = 0; - - // Writes the chunk of data into the stream - virtual bool WriteChunk(char *data, int32_t size) = 0; - virtual bool Good() = 0; - virtual void UpdateHeapStats(HeapStat* data, int32_t count) = 0; - virtual void UpdateLastSeenObjectId(int32_t lastSeenObjectId) = 0; -}; -} // namespace panda::ecmascript - -#endif // ECMASCRIPT_TOOLING_INTERFACE_STREAM_H +/* + * Copyright (c) 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. + */ + +#ifndef INITIALIZEDEBUGGER_FUZZER_H +#define INITIALIZEDEBUGGER_FUZZER_H + +#define FUZZ_PROJECT_NAME "initializedebugger_fuzzer" + +#endif \ No newline at end of file diff --git a/test/fuzztest/initializedebugger_fuzzer/project.xml b/test/fuzztest/initializedebugger_fuzzer/project.xml new file mode 100644 index 00000000..1020c7b4 --- /dev/null +++ b/test/fuzztest/initializedebugger_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 300 + + 4096 + + diff --git a/test/resource/tooling/ohos_test.xml b/test/resource/tooling/ohos_test.xml new file mode 100755 index 00000000..5a3cdfc9 --- /dev/null +++ b/test/resource/tooling/ohos_test.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + diff --git a/test/test_helper.gni b/test/test_helper.gni new file mode 100644 index 00000000..85064946 --- /dev/null +++ b/test/test_helper.gni @@ -0,0 +1,69 @@ +# 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. + +import("//arkcompiler/ets_frontend/es2panda/es2abc_config.gni") +import("//arkcompiler/ets_frontend/ts2panda/ts2abc_config.gni") +import("//arkcompiler/ets_runtime/js_runtime_config.gni") +import("//build/ohos.gni") +import("//build/test.gni") + +if (is_standard_system) { + _icu_path_ = "thirdparty/icu" +} else { + _icu_path_ = "global/i18n" +} + +template("host_unittest_action") { + _target_name_ = "${target_name}" + + # unittest for running on OpenHarmony device + ohos_unittest(_target_name_) { + resource_config_file = + "//arkcompiler/toolchain/test/resource/tooling/ohos_test.xml" + forward_variables_from(invoker, "*") + } + + _module_out_path_ = invoker.module_out_path + + # unittest for running on host + action("${_target_name_}Action") { + testonly = true + + _host_test_target_ = ":${_target_name_}(${host_toolchain})" + _root_out_dir_ = get_label_info(_host_test_target_, "root_out_dir") + + deps = [ _host_test_target_ ] + + script = "//arkcompiler/ets_runtime/script/run_ark_executable.py" + + args = [ + "--script-file", + rebase_path(_root_out_dir_) + + "/tests/unittest/${_module_out_path_}/${_target_name_}", + "--expect-output", + "0", + "--env-path", + rebase_path(_root_out_dir_) + "/ark/ark:" + rebase_path(_root_out_dir_) + + "/ark/ark_js_runtime:" + rebase_path(_root_out_dir_) + "/test/test:" + + rebase_path(_root_out_dir_) + "/${_icu_path_}:" + + rebase_path("//prebuilts/clang/ohos/linux-x86_64/llvm/lib/"), + "--timeout-limit", + "1200", + ] + + inputs = [ + "$_root_out_dir_/tests/unittest/${_module_out_path_}/${_target_name_}", + ] + outputs = [ "$target_out_dir/${_target_name_}/" ] + } +} diff --git a/toolchain.gni b/toolchain.gni index 991c3d65..7cd13cfb 100644 --- a/toolchain.gni +++ b/toolchain.gni @@ -12,3 +12,12 @@ # limitations under the License. toolchain_root = "//arkcompiler//toolchain" +ark_root = "//arkcompiler/runtime_core" +js_root = "//arkcompiler/ets_runtime" +third_party_gn_path = "//third_party" + +# For OpenHarmony build, always link with the static lib: +sdk_libc_secshared_dep = + "$third_party_gn_path/bounds_checking_function:libsec_static" +sdk_libc_secshared_config = + "$third_party_gn_path/bounds_checking_function:libsec_public_config" diff --git a/tooling/BUILD.gn b/tooling/BUILD.gn index 5bb81c79..8031de1f 100644 --- a/tooling/BUILD.gn +++ b/tooling/BUILD.gn @@ -22,7 +22,6 @@ config("ark_ecma_debugger_config") { include_dirs = [ ".", - "//third_party/boost", "//third_party/cJSON", ] } @@ -32,8 +31,8 @@ debugger_sources = [ "agent/runtime_impl.cpp", "agent/tracing_impl.cpp", "backend/debugger_executor.cpp", - "backend/js_pt_extractor.cpp", "backend/js_pt_hooks.cpp", + "backend/js_single_stepper.cpp", "base/pt_base64.cpp", "base/pt_events.cpp", "base/pt_json.cpp", @@ -114,7 +113,7 @@ ohos_shared_library("libark_ecma_debugger") { if (!is_standard_system) { relative_install_dir = "ark" } - part_name = "ark_js_runtime" + part_name = "toolchain" subsystem_name = "ark" } diff --git a/tooling/agent/debugger_impl.cpp b/tooling/agent/debugger_impl.cpp index 5eb6b325..07bdf972 100644 --- a/tooling/agent/debugger_impl.cpp +++ b/tooling/agent/debugger_impl.cpp @@ -13,25 +13,26 @@ * limitations under the License. */ -#include "ecmascript/tooling/agent/debugger_impl.h" +#include "agent/debugger_impl.h" + +#include "base/pt_base64.h" +#include "base/pt_events.h" +#include "base/pt_params.h" +#include "base/pt_returns.h" +#include "base/pt_types.h" +#include "backend/debugger_executor.h" +#include "dispatcher.h" +#include "protocol_handler.h" + #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/napi/jsnapi_helper.h" -#include "ecmascript/tooling/base/pt_base64.h" -#include "ecmascript/tooling/base/pt_events.h" -#include "ecmascript/tooling/base/pt_params.h" -#include "ecmascript/tooling/base/pt_returns.h" -#include "ecmascript/tooling/base/pt_types.h" -#include "ecmascript/tooling/backend/debugger_executor.h" -#include "ecmascript/tooling/dispatcher.h" -#include "ecmascript/tooling/protocol_handler.h" - namespace panda::ecmascript::tooling { using namespace std::placeholders; using ObjectType = RemoteObject::TypeName; using ObjectSubType = RemoteObject::SubTypeName; using ObjectClassName = RemoteObject::ClassName; -using StepperType = JSPtExtractor::SingleStepper::Type; +using StepperType = SingleStepper::Type; #ifdef DEBUGGER_TEST const std::string DATA_APP_PATH = "/"; @@ -82,7 +83,7 @@ bool DebuggerImpl::NotifyScriptParsed(ScriptId scriptId, const std::string &file return false; } - JSPtExtractor *extractor = GetExtractor(jsPandaFile); + DebugInfoExtractor *extractor = GetExtractor(jsPandaFile); if (extractor == nullptr) { LOG_DEBUGGER(ERROR) << "NotifyScriptParsed: Unsupported file: " << fileName; return false; @@ -150,7 +151,7 @@ bool DebuggerImpl::NotifySingleStep(const JSPtLocation &location) bool DebuggerImpl::IsSkipLine(const JSPtLocation &location) { - JSPtExtractor *extractor = nullptr; + DebugInfoExtractor *extractor = nullptr; auto scriptFunc = [this, &extractor](PtScript *script) -> bool { extractor = GetExtractor(script->GetUrl()); return true; @@ -161,7 +162,7 @@ bool DebuggerImpl::IsSkipLine(const JSPtLocation &location) } auto callbackFunc = [](int32_t line) -> bool { - return line == JSPtExtractor::SPECIAL_LINE_MARK; + return line == DebugInfoExtractor::SPECIAL_LINE_MARK; }; File::EntityId methodId = location.GetMethodId(); uint32_t offset = location.GetBytecodeOffset(); @@ -197,7 +198,7 @@ void DebuggerImpl::NotifyPaused(std::optional location, PauseReaso std::vector hitBreakpoints; if (location.has_value()) { BreakpointDetails detail; - JSPtExtractor *extractor = nullptr; + DebugInfoExtractor *extractor = nullptr; auto scriptFunc = [this, &extractor, &detail](PtScript *script) -> bool { detail.url_ = script->GetUrl(); extractor = GetExtractor(detail.url_); @@ -618,7 +619,7 @@ DispatchResponse DebuggerImpl::GetPossibleBreakpoints(const GetPossibleBreakpoin if (iter == scripts_.end()) { return DispatchResponse::Fail("Unknown file name."); } - JSPtExtractor *extractor = GetExtractor(iter->second->GetUrl()); + DebugInfoExtractor *extractor = GetExtractor(iter->second->GetUrl()); if (extractor == nullptr) { LOG_DEBUGGER(ERROR) << "GetPossibleBreakpoints: extractor is null"; return DispatchResponse::Fail("Unknown file name."); @@ -664,7 +665,7 @@ DispatchResponse DebuggerImpl::RemoveBreakpoint(const RemoveBreakpointParams &pa if (!BreakpointDetails::ParseBreakpointId(id, &metaData)) { return DispatchResponse::Fail("Parse breakpoint id failed"); } - JSPtExtractor *extractor = GetExtractor(metaData.url_); + DebugInfoExtractor *extractor = GetExtractor(metaData.url_); if (extractor == nullptr) { LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: extractor is null"; return DispatchResponse::Fail("Unknown file name."); @@ -715,7 +716,7 @@ DispatchResponse DebuggerImpl::SetBreakpointByUrl(const SetBreakpointByUrlParams int32_t columnNumber = params.GetColumn(); auto condition = params.HasCondition() ? params.GetCondition() : std::optional {}; - JSPtExtractor *extractor = GetExtractor(url); + DebugInfoExtractor *extractor = GetExtractor(url); if (extractor == nullptr) { LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: extractor is null"; return DispatchResponse::Fail("Unknown file name."); @@ -774,39 +775,33 @@ DispatchResponse DebuggerImpl::SetPauseOnExceptions(const SetPauseOnExceptionsPa DispatchResponse DebuggerImpl::StepInto([[maybe_unused]] const StepIntoParams ¶ms) { - JSPtExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_)); - if (extractor == nullptr) { - LOG_DEBUGGER(ERROR) << "StepOver: extractor is null"; - return DispatchResponse::Fail("Unknown file name."); + singleStepper_ = SingleStepper::GetStepIntoStepper(vm_); + if (singleStepper_ == nullptr) { + LOG_DEBUGGER(ERROR) << "StepInto: singleStepper is null"; + return DispatchResponse::Fail("Failed to StepInto"); } - singleStepper_ = extractor->GetStepIntoStepper(vm_); - frontend_.Resumed(vm_); return DispatchResponse::Ok(); } DispatchResponse DebuggerImpl::StepOut() { - JSPtExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_)); - if (extractor == nullptr) { - LOG_DEBUGGER(ERROR) << "StepOut: extractor is null"; - return DispatchResponse::Fail("Unknown file name."); + singleStepper_ = SingleStepper::GetStepOutStepper(vm_); + if (singleStepper_ == nullptr) { + LOG_DEBUGGER(ERROR) << "StepOut: singleStepper is null"; + return DispatchResponse::Fail("Failed to StepOut"); } - singleStepper_ = extractor->GetStepOutStepper(vm_); - frontend_.Resumed(vm_); return DispatchResponse::Ok(); } DispatchResponse DebuggerImpl::StepOver([[maybe_unused]] const StepOverParams ¶ms) { - JSPtExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_)); - if (extractor == nullptr) { - LOG_DEBUGGER(ERROR) << "StepOver: extractor is null"; - return DispatchResponse::Fail("Unknown file name."); + singleStepper_ = SingleStepper::GetStepOverStepper(vm_); + if (singleStepper_ == nullptr) { + LOG_DEBUGGER(ERROR) << "StepOver: singleStepper is null"; + return DispatchResponse::Fail("Failed to StepOver"); } - singleStepper_ = extractor->GetStepOverStepper(vm_); - frontend_.Resumed(vm_); return DispatchResponse::Ok(); } @@ -850,12 +845,12 @@ std::string DebuggerImpl::Trim(const std::string &str) return ret; } -JSPtExtractor *DebuggerImpl::GetExtractor(const JSPandaFile *jsPandaFile) +DebugInfoExtractor *DebuggerImpl::GetExtractor(const JSPandaFile *jsPandaFile) { return JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); } -JSPtExtractor *DebuggerImpl::GetExtractor(const std::string &url) +DebugInfoExtractor *DebuggerImpl::GetExtractor(const std::string &url) { auto iter = extractors_.find(url); if (iter == extractors_.end()) { @@ -900,7 +895,7 @@ bool DebuggerImpl::GenerateCallFrame(CallFrame *callFrame, { Method *method = DebuggerApi::GetMethod(frameHandler); auto methodId = method->GetMethodId(); - JSPtExtractor *extractor = GetExtractor(method->GetJSPandaFile()); + DebugInfoExtractor *extractor = GetExtractor(method->GetJSPandaFile()); if (extractor == nullptr) { LOG_DEBUGGER(ERROR) << "GenerateCallFrame: extractor is null"; return false; @@ -959,7 +954,7 @@ std::unique_ptr DebuggerImpl::GetLocalScopeChain(const FrameHandler *fram Method *method = DebuggerApi::GetMethod(frameHandler); auto methodId = method->GetMethodId(); const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); - JSPtExtractor *extractor = GetExtractor(jsPandaFile); + DebugInfoExtractor *extractor = GetExtractor(jsPandaFile); if (extractor == nullptr) { LOG_DEBUGGER(ERROR) << "GetScopeChain: extractor is null"; return localScope; @@ -1116,7 +1111,7 @@ std::optional DebuggerImpl::CmptEvaluateValue(CallFrameId callFrame Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Native Frame not support."))); return "Native Frame not support."; } - JSPtExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_)); + DebugInfoExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_)); if (extractor == nullptr) { *result = RemoteObject::FromTagged(vm_, Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Internal error."))); diff --git a/tooling/agent/debugger_impl.h b/tooling/agent/debugger_impl.h index 341a65d0..8877f0df 100644 --- a/tooling/agent/debugger_impl.h +++ b/tooling/agent/debugger_impl.h @@ -16,14 +16,14 @@ #ifndef ECMASCRIPT_TOOLING_AGENT_DEBUGGER_IMPL_H #define ECMASCRIPT_TOOLING_AGENT_DEBUGGER_IMPL_H -#include "ecmascript/tooling/agent/runtime_impl.h" -#include "ecmascript/tooling/backend/js_pt_hooks.h" -#include "ecmascript/tooling/base/pt_method.h" -#include "ecmascript/tooling/base/pt_params.h" -#include "ecmascript/tooling/backend/js_pt_extractor.h" -#include "ecmascript/tooling/dispatcher.h" -#include "ecmascript/tooling/interface/js_debugger_manager.h" - +#include "agent/runtime_impl.h" +#include "backend/js_pt_hooks.h" +#include "base/pt_params.h" +#include "backend/js_single_stepper.h" +#include "dispatcher.h" + +#include "ecmascript/debugger/js_debugger_manager.h" +#include "ecmascript/debugger/js_pt_method.h" #include "libpandabase/macros.h" namespace panda::ecmascript::tooling { @@ -138,8 +138,8 @@ private: NO_MOVE_SEMANTIC(DebuggerImpl); std::string Trim(const std::string &str); - JSPtExtractor *GetExtractor(const JSPandaFile *jsPandaFile); - JSPtExtractor *GetExtractor(const std::string &url); + DebugInfoExtractor *GetExtractor(const JSPandaFile *jsPandaFile); + DebugInfoExtractor *GetExtractor(const std::string &url); std::optional CmptEvaluateValue(CallFrameId callFrameId, const std::string &expression, std::unique_ptr *result); bool GenerateCallFrame(CallFrame *callFrame, const FrameHandler *frameHandler, CallFrameId frameId); @@ -183,11 +183,11 @@ private: std::unique_ptr hooks_ {nullptr}; JSDebugger *jsDebugger_ {nullptr}; - std::unordered_map extractors_ {}; + std::unordered_map extractors_ {}; std::unordered_map> scripts_ {}; PauseOnExceptionsState pauseOnException_ {PauseOnExceptionsState::NONE}; bool pauseOnNextByteCode_ {false}; - std::unique_ptr singleStepper_ {nullptr}; + std::unique_ptr singleStepper_ {nullptr}; std::unordered_map scopeObjects_ {}; std::vector> callFrameHandlers_; diff --git a/tooling/agent/heapprofiler_impl.cpp b/tooling/agent/heapprofiler_impl.cpp index 8111a6cd..b87babf2 100644 --- a/tooling/agent/heapprofiler_impl.cpp +++ b/tooling/agent/heapprofiler_impl.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "ecmascript/tooling/agent/heapprofiler_impl.h" +#include "agent/heapprofiler_impl.h" namespace panda::ecmascript::tooling { void HeapProfilerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) diff --git a/tooling/agent/heapprofiler_impl.h b/tooling/agent/heapprofiler_impl.h index b88fce3e..80eca16f 100644 --- a/tooling/agent/heapprofiler_impl.h +++ b/tooling/agent/heapprofiler_impl.h @@ -16,16 +16,16 @@ #ifndef ECMASCRIPT_TOOLING_AGENT_HEAPPROFILER_IMPL_H #define ECMASCRIPT_TOOLING_AGENT_HEAPPROFILER_IMPL_H -#include "ecmascript/tooling/base/pt_params.h" -#include "ecmascript/tooling/base/pt_events.h" -#include "ecmascript/tooling/base/pt_returns.h" -#include "ecmascript/tooling/dispatcher.h" -#include "ecmascript/tooling/interface/stream.h" -#include "ecmascript/tooling/interface/progress.h" -#include "ecmascript/tooling/protocol_handler.h" -#include "ecmascript/tooling/protocol_channel.h" +#include "base/pt_params.h" +#include "base/pt_events.h" +#include "base/pt_returns.h" +#include "dispatcher.h" +#include "protocol_handler.h" +#include "protocol_channel.h" + +#include "ecmascript/dfx/hprof/progress.h" +#include "ecmascript/dfx/hprof/stream.h" #include "ecmascript/napi/include/dfx_jsnapi.h" - #include "libpandabase/macros.h" #include diff --git a/tooling/agent/profiler_impl.cpp b/tooling/agent/profiler_impl.cpp index b0541493..0cfca719 100644 --- a/tooling/agent/profiler_impl.cpp +++ b/tooling/agent/profiler_impl.cpp @@ -13,11 +13,12 @@ * limitations under the License. */ -#include "ecmascript/tooling/agent/profiler_impl.h" +#include "agent/profiler_impl.h" + +#include "base/pt_events.h" +#include "protocol_channel.h" #include "ecmascript/napi/include/dfx_jsnapi.h" -#include "ecmascript/tooling/base/pt_events.h" -#include "ecmascript/tooling/protocol_channel.h" namespace panda::ecmascript::tooling { void ProfilerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) diff --git a/tooling/agent/profiler_impl.h b/tooling/agent/profiler_impl.h index bd76841b..ec165dee 100644 --- a/tooling/agent/profiler_impl.h +++ b/tooling/agent/profiler_impl.h @@ -16,10 +16,11 @@ #ifndef ECMASCRIPT_TOOLING_AGENT_PROFILER_IMPL_H #define ECMASCRIPT_TOOLING_AGENT_PROFILER_IMPL_H +#include "base/pt_params.h" +#include "base/pt_returns.h" +#include "dispatcher.h" + #include "ecmascript/dfx/cpu_profiler/samples_record.h" -#include "ecmascript/tooling/base/pt_params.h" -#include "ecmascript/tooling/base/pt_returns.h" -#include "ecmascript/tooling/dispatcher.h" #include "libpandabase/macros.h" namespace panda::ecmascript::tooling { diff --git a/tooling/agent/runtime_impl.cpp b/tooling/agent/runtime_impl.cpp index 87334692..d0c38342 100644 --- a/tooling/agent/runtime_impl.cpp +++ b/tooling/agent/runtime_impl.cpp @@ -13,13 +13,14 @@ * limitations under the License. */ -#include "ecmascript/tooling/agent/runtime_impl.h" +#include "agent/runtime_impl.h" #include +#include "base/pt_returns.h" +#include "protocol_channel.h" + #include "ecmascript/napi/include/dfx_jsnapi.h" -#include "ecmascript/tooling/base/pt_returns.h" -#include "ecmascript/tooling/protocol_channel.h" namespace panda::ecmascript::tooling { void RuntimeImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) diff --git a/tooling/agent/runtime_impl.h b/tooling/agent/runtime_impl.h index e0a5044f..cf6b0c8a 100644 --- a/tooling/agent/runtime_impl.h +++ b/tooling/agent/runtime_impl.h @@ -17,8 +17,8 @@ #define ECMASCRIPT_TOOLING_AGENT_RUNTIME_IMPL_H -#include "ecmascript/tooling/base/pt_params.h" -#include "ecmascript/tooling/dispatcher.h" +#include "base/pt_params.h" +#include "dispatcher.h" #include "libpandabase/macros.h" diff --git a/tooling/agent/tracing_impl.cpp b/tooling/agent/tracing_impl.cpp index 8de90cc4..11124c84 100644 --- a/tooling/agent/tracing_impl.cpp +++ b/tooling/agent/tracing_impl.cpp @@ -13,11 +13,12 @@ * limitations under the License. */ -#include "ecmascript/tooling/agent/tracing_impl.h" +#include "agent/tracing_impl.h" + +#include "base/pt_events.h" +#include "protocol_channel.h" #include "ecmascript/napi/include/dfx_jsnapi.h" -#include "ecmascript/tooling/base/pt_events.h" -#include "ecmascript/tooling/protocol_channel.h" namespace panda::ecmascript::tooling { void TracingImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) diff --git a/tooling/agent/tracing_impl.h b/tooling/agent/tracing_impl.h index 41e5f864..e2595feb 100644 --- a/tooling/agent/tracing_impl.h +++ b/tooling/agent/tracing_impl.h @@ -16,10 +16,11 @@ #ifndef ECMASCRIPT_TOOLING_AGENT_TRACING_IMPL_H #define ECMASCRIPT_TOOLING_AGENT_TRACING_IMPL_H +#include "base/pt_params.h" +#include "base/pt_returns.h" +#include "dispatcher.h" + #include "ecmascript/dfx/cpu_profiler/samples_record.h" -#include "ecmascript/tooling/base/pt_params.h" -#include "ecmascript/tooling/base/pt_returns.h" -#include "ecmascript/tooling/dispatcher.h" #include "libpandabase/macros.h" namespace panda::ecmascript::tooling { diff --git a/tooling/backend/debugger_api.cpp b/tooling/backend/debugger_api.cpp deleted file mode 100644 index 8c881e88..00000000 --- a/tooling/backend/debugger_api.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - * 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/backend/debugger_api.h" - -#include "ecmascript/base/number_helper.h" -#include "ecmascript/interpreter/frame_handler.h" -#include "ecmascript/interpreter/slow_runtime_stub.h" -#include "ecmascript/interpreter/fast_runtime_stub-inl.h" -#include "ecmascript/jspandafile/js_pandafile_executor.h" -#include "ecmascript/jspandafile/program_object.h" -#include "ecmascript/js_handle.h" -#include "ecmascript/jspandafile/js_pandafile_manager.h" -#include "ecmascript/method.h" -#include "ecmascript/napi/jsnapi_helper.h" -#include "ecmascript/tooling/backend/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; - -// FrameHandler -uint32_t DebuggerApi::GetStackDepth(const EcmaVM *ecmaVm) -{ - uint32_t count = 0; - FrameHandler frameHandler(ecmaVm->GetJSThread()); - for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) { - if (frameHandler.IsEntryFrame() || frameHandler.IsBuiltinFrame()) { - continue; - } - ++count; - } - return count; -} - -std::shared_ptr DebuggerApi::NewFrameHandler(const EcmaVM *ecmaVm) -{ - return std::make_shared(ecmaVm->GetJSThread()); -} - -bool DebuggerApi::StackWalker(const EcmaVM *ecmaVm, std::function func) -{ - FrameHandler frameHandler(ecmaVm->GetJSThread()); - for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) { - if (frameHandler.IsEntryFrame() || frameHandler.IsBuiltinFrame()) { - 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(); -} - -std::unique_ptr DebuggerApi::GetMethod(const EcmaVM *ecmaVm) -{ - FrameHandler frameHandler(ecmaVm->GetJSThread()); - Method* method = frameHandler.GetMethod(); - std::unique_ptr ptMethod = std::make_unique( - method->GetJSPandaFile(), method->GetMethodId(), method->IsNativeWithCallField()); - return ptMethod; -} - -void DebuggerApi::SetVRegValue(FrameHandler *frameHandler, size_t index, Local value) -{ - return frameHandler->SetVRegValue(index, JSNApiHelper::ToJSTaggedValue(*value)); -} - -uint32_t DebuggerApi::GetBytecodeOffset(const FrameHandler *frameHandler) -{ - return frameHandler->GetBytecodeOffset(); -} - -Method *DebuggerApi::GetMethod(const FrameHandler *frameHandler) -{ - return frameHandler->GetMethod(); -} - -bool DebuggerApi::IsNativeMethod(const EcmaVM *ecmaVm) -{ - FrameHandler frameHandler(ecmaVm->GetJSThread()); - return DebuggerApi::IsNativeMethod(&frameHandler); -} - -bool DebuggerApi::IsNativeMethod(const FrameHandler *frameHandler) -{ - Method* method = frameHandler->GetMethod(); - return method->IsNativeWithCallField(); -} - -JSPandaFile *DebuggerApi::GetJSPandaFile(const EcmaVM *ecmaVm) -{ - Method *method = FrameHandler(ecmaVm->GetJSThread()).GetMethod(); - return const_cast(method->GetJSPandaFile()); -} - -JSTaggedValue DebuggerApi::GetEnv(const FrameHandler *frameHandler) -{ - return frameHandler->GetEnv(); -} - -JSTaggedType *DebuggerApi::GetSp(const FrameHandler *frameHandler) -{ - return frameHandler->GetSp(); -} - -int32_t DebuggerApi::GetVregIndex(const FrameHandler *frameHandler, std::string_view name) -{ - Method *method = DebuggerApi::GetMethod(frameHandler); - if (method->IsNativeWithCallField()) { - LOG_DEBUGGER(ERROR) << "GetVregIndex: native frame not support"; - return -1; - } - JSPtExtractor *extractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile()); - if (extractor == nullptr) { - LOG_DEBUGGER(ERROR) << "GetVregIndex: extractor is null"; - return -1; - } - auto table = extractor->GetLocalVariableTable(method->GetMethodId()); - auto iter = table.find(name.data()); - if (iter == table.end()) { - return -1; - } - return iter->second; -} - -Local DebuggerApi::GetVRegValue(const EcmaVM *ecmaVm, - const FrameHandler *frameHandler, size_t index) -{ - auto value = frameHandler->GetVRegValue(index); - JSHandle handledValue(ecmaVm->GetJSThread(), value); - return JSNApiHelper::ToLocal(handledValue); -} - -// JSThread -Local DebuggerApi::GetAndClearException(const EcmaVM *ecmaVm) -{ - auto exception = ecmaVm->GetJSThread()->GetException(); - JSHandle handledException(ecmaVm->GetJSThread(), exception); - ecmaVm->GetJSThread()->ClearException(); - return JSNApiHelper::ToLocal(handledException); -} - -void DebuggerApi::SetException(const EcmaVM *ecmaVm, Local 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, - Local condFuncRef) -{ - return debugger->SetBreakpoint(location, condFuncRef); -} - -bool DebuggerApi::RemoveBreakpoint(JSDebugger *debugger, const JSPtLocation &location) -{ - return debugger->RemoveBreakpoint(location); -} - -// ScopeInfo -Local DebuggerApi::GetProperties(const EcmaVM *vm, const FrameHandler *frameHandler, - int32_t level, uint32_t slot) -{ - JSTaggedValue env = frameHandler->GetEnv(); - 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 handledValue(vm->GetJSThread(), value); - return JSNApiHelper::ToLocal(handledValue); -} - -void DebuggerApi::SetProperties(const EcmaVM *vm, const FrameHandler *frameHandler, - int32_t level, uint32_t slot, Local value) -{ - JSTaggedValue env = frameHandler->GetEnv(); - 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(vm->GetJSThread(), slot, target); -} - -std::pair DebuggerApi::GetLevelSlot(const FrameHandler *frameHandler, std::string_view name) -{ - int32_t level = 0; - uint32_t slot = 0; - JSTaggedValue curEnv = frameHandler->GetEnv(); - 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(result); - auto iter = scopeDebugInfo->scopeInfo.find(name.data()); - if (iter == scopeDebugInfo->scopeInfo.end()) { - continue; - } - slot = iter->second; - return std::make_pair(level, slot); - } - return std::make_pair(-1, 0); -} - -Local DebuggerApi::GetGlobalValue(const EcmaVM *vm, Local name) -{ - JSTaggedValue result; - JSTaggedValue globalObj = vm->GetGlobalEnv()->GetGlobalObject(); - JSThread *thread = vm->GetJSThread(); - - JSTaggedValue key = JSNApiHelper::ToJSTaggedValue(*name); - JSTaggedValue globalRec = SlowRuntimeStub::LdGlobalRecord(thread, key); - if (!globalRec.IsUndefined()) { - ASSERT(globalRec.IsPropertyBox()); - result = PropertyBox::Cast(globalRec.GetTaggedObject())->GetValue(); - return JSNApiHelper::ToLocal(JSHandle(thread, result)); - } - - JSTaggedValue globalVar = FastRuntimeStub::GetGlobalOwnProperty(thread, globalObj, key); - if (!globalVar.IsHole()) { - return JSNApiHelper::ToLocal(JSHandle(thread, globalVar)); - } else { - result = SlowRuntimeStub::TryLdGlobalByNameFromGlobalProto(thread, globalObj, key); - return JSNApiHelper::ToLocal(JSHandle(thread, result)); - } - - return Local(); -} - -bool DebuggerApi::SetGlobalValue(const EcmaVM *vm, Local name, Local value) -{ - JSTaggedValue result; - JSTaggedValue globalObj = vm->GetGlobalEnv()->GetGlobalObject(); - JSThread *thread = vm->GetJSThread(); - - JSTaggedValue key = JSNApiHelper::ToJSTaggedValue(*name); - JSTaggedValue newVal = JSNApiHelper::ToJSTaggedValue(*value); - JSTaggedValue globalRec = SlowRuntimeStub::LdGlobalRecord(thread, key); - if (!globalRec.IsUndefined()) { - result = SlowRuntimeStub::TryUpdateGlobalRecord(thread, key, newVal); - return !result.IsException(); - } - - JSTaggedValue globalVar = FastRuntimeStub::GetGlobalOwnProperty(thread, globalObj, key); - if (!globalVar.IsHole()) { - result = SlowRuntimeStub::StGlobalVar(thread, key, newVal); - return !result.IsException(); - } - - return false; -} - -void DebuggerApi::HandleUncaughtException(const EcmaVM *ecmaVm, std::string &message) -{ - JSThread *thread = ecmaVm->GetJSThread(); - [[maybe_unused]] EcmaHandleScope handleScope(thread); - const GlobalEnvConstants *globalConst = thread->GlobalConstants(); - - JSHandle exHandle(thread, thread->GetException()); - if (exHandle->IsJSError()) { - JSHandle nameKey = globalConst->GetHandledNameString(); - JSHandle name(JSObject::GetProperty(thread, exHandle, nameKey).GetValue()); - JSHandle msgKey = globalConst->GetHandledMessageString(); - JSHandle msg(JSObject::GetProperty(thread, exHandle, msgKey).GetValue()); - message = ConvertToString(*name) + ": " + ConvertToString(*msg); - } else { - JSHandle ecmaStr = JSTaggedValue::ToString(thread, exHandle); - message = ConvertToString(*ecmaStr); - } - thread->ClearException(); -} - -Local DebuggerApi::GenerateFuncFromBuffer(const EcmaVM *ecmaVm, const void *buffer, - size_t size, std::string_view entryPoint) -{ - JSPandaFileManager *mgr = JSPandaFileManager::GetInstance(); - const auto *jsPandaFile = mgr->LoadJSPandaFile(ecmaVm->GetJSThread(), "", entryPoint, buffer, size); - if (jsPandaFile == nullptr) { - return JSValueRef::Undefined(ecmaVm); - } - - JSHandle program = mgr->GenerateProgram(const_cast(ecmaVm), jsPandaFile, entryPoint); - JSTaggedValue func = program->GetMainFunction(); - return JSNApiHelper::ToLocal(JSHandle(ecmaVm->GetJSThread(), func)); -} - -Local DebuggerApi::EvaluateViaFuncCall(EcmaVM *ecmaVm, Local funcRef, - std::shared_ptr &frameHandler) -{ - JSNApi::EnableUserUncaughtErrorHandler(ecmaVm); - - JsDebuggerManager *mgr = ecmaVm->GetJsDebuggerManager(); - bool prevDebugMode = mgr->IsDebugMode(); - mgr->SetEvalFrameHandler(frameHandler); - mgr->SetDebugMode(false); // in order to catch exception - std::vector> args; - auto result = funcRef->Call(ecmaVm, JSValueRef::Undefined(ecmaVm), args.data(), args.size()); - mgr->SetDebugMode(prevDebugMode); - mgr->SetEvalFrameHandler(nullptr); - - return result; -} - -bool DebuggerApi::IsExceptionCaught(const EcmaVM *ecmaVm) -{ - FrameHandler frameHandler(ecmaVm->GetJSThread()); - for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) { - if (frameHandler.IsEntryFrame()) { - return false; - } - auto method = frameHandler.GetMethod(); - if (ecmaVm->FindCatchBlock(method, frameHandler.GetBytecodeOffset())) { - return true; - } - } - return false; -} -} // namespace panda::ecmascript::tooling diff --git a/tooling/backend/debugger_api.h b/tooling/backend/debugger_api.h deleted file mode 100644 index 5c1f1a26..00000000 --- a/tooling/backend/debugger_api.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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. - */ - -#ifndef ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_API_H -#define ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_API_H - -#include - -#include "ecmascript/common.h" -#include "ecmascript/jspandafile/scope_info_extractor.h" -#include "ecmascript/napi/include/jsnapi.h" -#include "ecmascript/lexical_env.h" -#include "ecmascript/tooling/backend/js_debugger_interface.h" -#include "ecmascript/tooling/base/pt_method.h" - -namespace panda { -namespace ecmascript { -class FrameHandler; -class EcmaVM; -class Method; -class JSThread; -namespace tooling { -class JSDebugger; -} -} // ecmascript -} // panda - -namespace panda::ecmascript::tooling { -enum StackState { - CONTINUE, - FAILED, - SUCCESS, -}; - -class PUBLIC_API DebuggerApi { -public: - // FrameHandler - static uint32_t GetStackDepth(const EcmaVM *ecmaVm); - static std::shared_ptr NewFrameHandler(const EcmaVM *ecmaVm); - static bool StackWalker(const EcmaVM *ecmaVm, std::function func); - - static uint32_t GetBytecodeOffset(const EcmaVM *ecmaVm); - static uint32_t GetBytecodeOffset(const FrameHandler *frameHandler); - static std::unique_ptr GetMethod(const EcmaVM *ecmaVm); - static Method *GetMethod(const FrameHandler *frameHandler); - static bool IsNativeMethod(const EcmaVM *ecmaVm); - static bool IsNativeMethod(const FrameHandler *frameHandler); - static JSPandaFile *GetJSPandaFile(const EcmaVM *ecmaVm); - - static JSTaggedValue GetEnv(const FrameHandler *frameHandler); - static JSTaggedType *GetSp(const FrameHandler *frameHandler); - static int32_t GetVregIndex(const FrameHandler *frameHandler, std::string_view name); - static Local GetVRegValue(const EcmaVM *ecmaVm, - const FrameHandler *frameHandler, size_t index); - static void SetVRegValue(FrameHandler *frameHandler, size_t index, Local value); - - static Local GetProperties(const EcmaVM *ecmaVm, const FrameHandler *frameHandler, - int32_t level, uint32_t slot); - static void SetProperties(const EcmaVM *vm, const FrameHandler *frameHandler, int32_t level, - uint32_t slot, Local value); - static std::pair GetLevelSlot(const FrameHandler *frameHandler, std::string_view name); - static Local GetGlobalValue(const EcmaVM *vm, Local name); - static bool SetGlobalValue(const EcmaVM *vm, Local name, Local value); - - // JSThread - static Local GetAndClearException(const EcmaVM *ecmaVm); - static void SetException(const EcmaVM *ecmaVm, Local exception); - static void ClearException(const EcmaVM *ecmaVm); - static bool IsExceptionCaught(const EcmaVM *ecmaVm); - - // NumberHelper - static double StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix); - - // JSDebugger - static JSDebugger *CreateJSDebugger(const EcmaVM *ecmaVm); - static void DestroyJSDebugger(JSDebugger *debugger); - static void RegisterHooks(JSDebugger *debugger, PtHooks *hooks); - static bool SetBreakpoint(JSDebugger *debugger, const JSPtLocation &location, - Local condFuncRef); - static bool RemoveBreakpoint(JSDebugger *debugger, const JSPtLocation &location); - static void HandleUncaughtException(const EcmaVM *ecmaVm, std::string &message); - static Local EvaluateViaFuncCall(EcmaVM *ecmaVm, Local funcRef, - std::shared_ptr &frameHandler); - static Local GenerateFuncFromBuffer(const EcmaVM *ecmaVm, const void *buffer, size_t size, - std::string_view entryPoint); -}; -} // namespace panda::ecmascript::tooling - -#endif // ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_API_H diff --git a/tooling/backend/debugger_executor.cpp b/tooling/backend/debugger_executor.cpp index cbf67754..02e85449 100644 --- a/tooling/backend/debugger_executor.cpp +++ b/tooling/backend/debugger_executor.cpp @@ -13,10 +13,10 @@ * limitations under the License. */ -#include "ecmascript/tooling/backend/debugger_executor.h" +#include "backend/debugger_executor.h" -#include "ecmascript/tooling/backend/debugger_api.h" -#include "ecmascript/tooling/interface/js_debugger_manager.h" +#include "ecmascript/debugger/debugger_api.h" +#include "ecmascript/debugger/js_debugger_manager.h" namespace panda::ecmascript::tooling { void DebuggerExecutor::Initialize(const EcmaVM *vm) diff --git a/tooling/backend/js_debugger.cpp b/tooling/backend/js_debugger.cpp deleted file mode 100644 index 069dd9bb..00000000 --- a/tooling/backend/js_debugger.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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/backend/js_debugger.h" - -#include "ecmascript/base/builtins_base.h" -#include "ecmascript/ecma_macros.h" -#include "ecmascript/global_env.h" -#include "ecmascript/interpreter/fast_runtime_stub-inl.h" -#include "ecmascript/interpreter/frame_handler.h" -#include "ecmascript/jspandafile/js_pandafile_manager.h" - -namespace panda::ecmascript::tooling { -using panda::ecmascript::base::BuiltinsBase; - -bool JSDebugger::SetBreakpoint(const JSPtLocation &location, Local condFuncRef) -{ - std::unique_ptr ptMethod = FindMethod(location); - if (ptMethod == nullptr) { - LOG_DEBUGGER(ERROR) << "SetBreakpoint: Cannot find MethodLiteral"; - return false; - } - - if (location.GetBytecodeOffset() >= ptMethod->GetCodeSize()) { - LOG_DEBUGGER(ERROR) << "SetBreakpoint: Invalid breakpoint location"; - return false; - } - - auto [_, success] = breakpoints_.emplace(ptMethod.release(), location.GetBytecodeOffset(), - Global(ecmaVm_, condFuncRef)); - if (!success) { - // also return true - LOG_DEBUGGER(WARN) << "SetBreakpoint: Breakpoint already exists"; - } - - return true; -} - -bool JSDebugger::RemoveBreakpoint(const JSPtLocation &location) -{ - std::unique_ptr ptMethod = FindMethod(location); - if (ptMethod == nullptr) { - LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: Cannot find MethodLiteral"; - return false; - } - - if (!RemoveBreakpoint(ptMethod, location.GetBytecodeOffset())) { - LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: Breakpoint not found"; - return false; - } - - return true; -} - -void JSDebugger::BytecodePcChanged(JSThread *thread, JSHandle method, uint32_t bcOffset) -{ - ASSERT(bcOffset < method->GetCodeSize() && "code size of current Method less then bcOffset"); - HandleExceptionThrowEvent(thread, method, bcOffset); - - // Step event is reported before breakpoint, according to the spec. - if (!HandleStep(method, bcOffset)) { - HandleBreakpoint(method, bcOffset); - } -} - -bool JSDebugger::HandleBreakpoint(JSHandle method, uint32_t bcOffset) -{ - auto breakpoint = FindBreakpoint(method, bcOffset); - if (hooks_ == nullptr || !breakpoint.has_value()) { - return false; - } - - JSThread *thread = ecmaVm_->GetJSThread(); - auto condFuncRef = breakpoint.value().GetConditionFunction(); - if (condFuncRef->IsFunction()) { - LOG_DEBUGGER(INFO) << "HandleBreakpoint: begin evaluate condition"; - auto handlerPtr = std::make_shared(ecmaVm_->GetJSThread()); - auto res = DebuggerApi::EvaluateViaFuncCall(const_cast(ecmaVm_), - condFuncRef.ToLocal(ecmaVm_), handlerPtr); - if (thread->HasPendingException()) { - LOG_DEBUGGER(ERROR) << "HandleBreakpoint: has pending exception"; - thread->ClearException(); - return false; - } - bool isMeet = res->ToBoolean(ecmaVm_)->Value(); - if (!isMeet) { - LOG_DEBUGGER(ERROR) << "HandleBreakpoint: condition not meet"; - return false; - } - } - - auto *pf = method->GetJSPandaFile(); - JSPtLocation location {pf->GetJSPandaFileDesc().c_str(), method->GetMethodId(), bcOffset}; - - hooks_->Breakpoint(location); - return true; -} - -void JSDebugger::HandleExceptionThrowEvent(const JSThread *thread, JSHandle method, uint32_t bcOffset) -{ - if (hooks_ == nullptr || !thread->HasPendingException()) { - return; - } - - auto *pf = method->GetJSPandaFile(); - JSPtLocation throwLocation {pf->GetJSPandaFileDesc().c_str(), method->GetMethodId(), bcOffset}; - - hooks_->Exception(throwLocation); -} - -bool JSDebugger::HandleStep(JSHandle method, uint32_t bcOffset) -{ - if (hooks_ == nullptr) { - return false; - } - - auto *pf = method->GetJSPandaFile(); - JSPtLocation location {pf->GetJSPandaFileDesc().c_str(), method->GetMethodId(), bcOffset}; - - return hooks_->SingleStep(location); -} - -std::optional JSDebugger::FindBreakpoint(JSHandle method, uint32_t bcOffset) const -{ - for (const auto &bp : breakpoints_) { - if ((bp.GetBytecodeOffset() == bcOffset) && - (bp.GetPtMethod()->GetJSPandaFile() == method->GetJSPandaFile()) && - (bp.GetPtMethod()->GetMethodId() == method->GetMethodId())) { - return bp; - } - } - return {}; -} - -bool JSDebugger::RemoveBreakpoint(const std::unique_ptr &ptMethod, uint32_t bcOffset) -{ - for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) { - const auto &bp = *it; - if ((bp.GetBytecodeOffset() == bcOffset) && - (bp.GetPtMethod()->GetJSPandaFile() == ptMethod->GetJSPandaFile()) && - (bp.GetPtMethod()->GetMethodId() == ptMethod->GetMethodId())) { - it = breakpoints_.erase(it); - return true; - } - } - - return false; -} - -std::unique_ptr JSDebugger::FindMethod(const JSPtLocation &location) const -{ - std::unique_ptr ptMethod {nullptr}; - ::panda::ecmascript::JSPandaFileManager::GetInstance()->EnumerateJSPandaFiles([&ptMethod, location]( - const panda::ecmascript::JSPandaFile *jsPandaFile) { - if (jsPandaFile->GetJSPandaFileDesc() == location.GetPandaFile()) { - MethodLiteral *methodsData = jsPandaFile->GetMethodLiterals(); - uint32_t numberMethods = jsPandaFile->GetNumMethods(); - for (uint32_t i = 0; i < numberMethods; ++i) { - if (methodsData[i].GetMethodId() == location.GetMethodId()) { - MethodLiteral *methodLiteral = methodsData + i; - ptMethod = std::make_unique(jsPandaFile, - methodLiteral->GetMethodId(), methodLiteral->IsNativeWithCallField()); - return false; - } - } - } - return true; - }); - return ptMethod; -} -} // namespace panda::tooling::ecmascript diff --git a/tooling/backend/js_debugger.h b/tooling/backend/js_debugger.h deleted file mode 100644 index 145670d1..00000000 --- a/tooling/backend/js_debugger.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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. - */ - -#ifndef ECMASCRIPT_TOOLING_BACKEND_JS_DEBUGGER_H -#define ECMASCRIPT_TOOLING_BACKEND_JS_DEBUGGER_H - -#include "ecmascript/ecma_vm.h" -#include "ecmascript/jspandafile/method_literal.h" -#include "ecmascript/tooling/backend/debugger_api.h" -#include "ecmascript/tooling/base/pt_method.h" -#include "ecmascript/tooling/interface/notification_manager.h" -#include "ecmascript/tooling/interface/js_debugger_manager.h" - -namespace panda::ecmascript::tooling { -class JSBreakpoint { -public: - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) - JSBreakpoint(PtMethod *ptMethod, uint32_t bcOffset, const Global &condFuncRef) - : ptMethod_(ptMethod), bcOffset_(bcOffset), condFuncRef_(condFuncRef) {} - ~JSBreakpoint() = default; - - PtMethod *GetPtMethod() const - { - return ptMethod_; - } - - uint32_t GetBytecodeOffset() const - { - return bcOffset_; - } - - bool operator==(const JSBreakpoint &bpoint) const - { - return ptMethod_->GetMethodId() == bpoint.GetPtMethod()->GetMethodId() && - ptMethod_->GetJSPandaFile() == bpoint.GetPtMethod()->GetJSPandaFile() && - GetBytecodeOffset() == bpoint.GetBytecodeOffset(); - } - - const Global &GetConditionFunction() - { - return condFuncRef_; - } - - DEFAULT_COPY_SEMANTIC(JSBreakpoint); - DEFAULT_MOVE_SEMANTIC(JSBreakpoint); - -private: - PtMethod *ptMethod_ {nullptr}; - uint32_t bcOffset_; - Global condFuncRef_; -}; - -class HashJSBreakpoint { -public: - size_t operator()(const JSBreakpoint &bpoint) const - { - return (std::hash()(bpoint.GetPtMethod())) ^ (std::hash()(bpoint.GetBytecodeOffset())); - } -}; - -class JSDebugger : public JSDebugInterface, RuntimeListener { -public: - explicit JSDebugger(const EcmaVM *vm) : ecmaVm_(vm) - { - notificationMgr_ = ecmaVm_->GetJsDebuggerManager()->GetNotificationManager(); - notificationMgr_->AddListener(this); - } - ~JSDebugger() override - { - notificationMgr_->RemoveListener(); - } - - void RegisterHooks(PtHooks *hooks) override - { - hooks_ = hooks; - // send vm start event after add hooks - notificationMgr_->VmStartEvent(); - } - void UnregisterHooks() override - { - // send vm death event before delete hooks - notificationMgr_->VmDeathEvent(); - hooks_ = nullptr; - } - - bool SetBreakpoint(const JSPtLocation &location, Local condFuncRef) override; - bool RemoveBreakpoint(const JSPtLocation &location) override; - void BytecodePcChanged(JSThread *thread, JSHandle method, uint32_t bcOffset) override; - void LoadModule(std::string_view filename, std::string_view entryPoint) override - { - if (hooks_ == nullptr) { - return; - } - hooks_->LoadModule(filename, entryPoint); - } - void VmStart() override - { - if (hooks_ == nullptr) { - return; - } - hooks_->VmStart(); - } - void VmDeath() override - { - if (hooks_ == nullptr) { - return; - } - hooks_->VmDeath(); - } - void PendingJobEntry() override - { - if (hooks_ == nullptr) { - return; - } - hooks_->PendingJobEntry(); - } - void NativeCalling(const void *nativeAddress) override - { - if (hooks_ == nullptr) { - return; - } - hooks_->NativeCalling(nativeAddress); - } -private: - std::unique_ptr FindMethod(const JSPtLocation &location) const; - std::optional FindBreakpoint(JSHandle method, uint32_t bcOffset) const; - bool RemoveBreakpoint(const std::unique_ptr &ptMethod, uint32_t bcOffset); - void HandleExceptionThrowEvent(const JSThread *thread, JSHandle method, uint32_t bcOffset); - bool HandleStep(JSHandle method, uint32_t bcOffset); - bool HandleBreakpoint(JSHandle method, uint32_t bcOffset); - - const EcmaVM *ecmaVm_; - PtHooks *hooks_ {nullptr}; - NotificationManager *notificationMgr_ {nullptr}; - - CUnorderedSet breakpoints_ {}; -}; -} // namespace panda::ecmascript::tooling - -#endif // ECMASCRIPT_TOOLING_BACKEND_JS_DEBUGGER_H diff --git a/tooling/backend/js_debugger_interface.h b/tooling/backend/js_debugger_interface.h deleted file mode 100644 index a8b02858..00000000 --- a/tooling/backend/js_debugger_interface.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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. - */ - -#ifndef ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUG_INTERFACE_H -#define ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUG_INTERFACE_H - -#include - -#include "ecmascript/napi/include/jsnapi.h" -#include "ecmascript/tooling/backend/js_pt_location.h" -#include "libpandafile/file.h" -#include "libpandabase/macros.h" -#include "libpandabase/utils/expected.h" - -namespace panda::ecmascript::tooling { -struct JSPtStepRange { - uint32_t startBcOffset {0}; - uint32_t endBcOffset {0}; -}; - -enum PauseReason { - AMBIGUOUS, - ASSERT, - DEBUGCOMMAND, - DOM, - EVENTLISTENER, - EXCEPTION, - INSTRUMENTATION, - OOM, - OTHER, - PROMISEREJECTION, - XHR, - BREAK_ON_START -}; - -class PtHooks { -public: - PtHooks() = default; - - /** - * \brief called by the ecmavm when breakpoint hits. Thread where breakpoint hits is stopped until - * continue or step event will be received - * @param thread Identifier of the thread where breakpoint hits. Now the callback is called in the same - * thread - * @param location Breakpoint location - */ - virtual void Breakpoint(const JSPtLocation &location) = 0; - - /** - * \brief called by the ecmavm when panda file is loaded - * @param pandaFileName Path to panda file that is loaded - */ - virtual void LoadModule(std::string_view pandaFileName, std::string_view entryPoint) = 0; - - /** - * \brief called before executing pending job - */ - virtual void PendingJobEntry() = 0; - - /** - * \brief called by the ecmavm when virtual machine start initialization - */ - virtual void VmStart() = 0; - - /** - * \brief called by the ecmavm when virtual machine death - */ - virtual void VmDeath() = 0; - - virtual void Exception(const JSPtLocation &location) = 0; - - virtual bool SingleStep(const JSPtLocation &location) = 0; - - virtual void NativeCalling(const void *nativeAddress) = 0; - - virtual ~PtHooks() = default; - - NO_COPY_SEMANTIC(PtHooks); - NO_MOVE_SEMANTIC(PtHooks); -}; - -class JSDebugInterface { -public: - JSDebugInterface() = default; - - /** - * \brief Register debug hooks in the ecmavm - * @param hooks Pointer to object that implements PtHooks interface - * @return Error if any errors occur - */ - virtual void RegisterHooks(PtHooks *hooks) = 0; - - /** - * \brief Unregister debug hooks in the ecmavm - * @return Error if any errors occur - */ - virtual void UnregisterHooks() = 0; - - /** - * \brief Set breakpoint to \param location with an optional \param condition - * @param location Breakpoint location - * @param condition Optional condition - * @return Error if any errors occur - */ - virtual bool SetBreakpoint(const JSPtLocation &location, Local condFuncRef) = 0; - - /** - * \brief Remove breakpoint from \param location - * @param location Breakpoint location - * @return Error if any errors occur - */ - virtual bool RemoveBreakpoint(const JSPtLocation &location) = 0; - - virtual ~JSDebugInterface() = default; - - NO_COPY_SEMANTIC(JSDebugInterface); - NO_MOVE_SEMANTIC(JSDebugInterface); -}; -} // namespace panda::ecmascript::tooling - -#endif // ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUG_INTERFACE_H diff --git a/tooling/backend/js_pt_extractor.h b/tooling/backend/js_pt_extractor.h deleted file mode 100644 index 41cf4a29..00000000 --- a/tooling/backend/js_pt_extractor.h +++ /dev/null @@ -1,129 +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_BACKEND_JS_PT_EXTRACTOR_H -#define ECMASCRIPT_TOOLING_BACKEND_JS_PT_EXTRACTOR_H - -#include "ecmascript/js_thread.h" -#include "ecmascript/jspandafile/debug_info_extractor.h" -#include "ecmascript/tooling/backend/js_debugger_interface.h" -#include "ecmascript/tooling/base/pt_method.h" -#include "libpandabase/macros.h" - -namespace panda::ecmascript::tooling { -using panda::panda_file::File; - -class JSPtExtractor : public DebugInfoExtractor { -public: - class SingleStepper { - public: - enum class Type { STEP_INTO, STEP_OVER, STEP_OUT }; - SingleStepper(const EcmaVM *ecmaVm, std::unique_ptr ptMethod, - std::list stepRanges, Type type) - : ecmaVm_(ecmaVm), - method_(std::move(ptMethod)), - stepRanges_(std::move(stepRanges)), - stackDepth_(GetStackDepth()), - type_(type) - { - } - virtual ~SingleStepper() = default; - NO_COPY_SEMANTIC(SingleStepper); - NO_MOVE_SEMANTIC(SingleStepper); - - bool StepComplete(uint32_t bcOffset) const; - Type GetStepperType() const - { - return type_; - } - private: - uint32_t GetStackDepth() const; - bool InStepRange(uint32_t pc) const; - - const EcmaVM *ecmaVm_; - std::unique_ptr method_; - std::list stepRanges_; - uint32_t stackDepth_; - Type type_; - }; - - explicit JSPtExtractor(const JSPandaFile *jsPandaFile) : DebugInfoExtractor(jsPandaFile) {} - virtual ~JSPtExtractor() = default; - - template - bool MatchWithLocation(const Callback &cb, int32_t line, int32_t column) - { - if (line == SPECIAL_LINE_MARK) { - return false; - } - - auto methods = GetMethodIdList(); - for (const auto &method : methods) { - const LineNumberTable &lineTable = GetLineNumberTable(method); - const ColumnNumberTable &columnTable = GetColumnNumberTable(method); - for (uint32_t i = 0; i < lineTable.size(); i++) { - if (lineTable[i].line != line) { - continue; - } - uint32_t currentOffset = lineTable[i].offset; - uint32_t nextOffset = ((i == lineTable.size() - 1) ? UINT32_MAX : lineTable[i + 1].offset); - for (const auto &pair : columnTable) { - if (pair.column == column && pair.offset >= currentOffset && pair.offset < nextOffset) { - return cb(method, pair.offset); - } - } - return cb(method, currentOffset); - } - } - return false; - } - - template - bool MatchLineWithOffset(const Callback &cb, File::EntityId methodId, uint32_t offset) - { - int32_t line = 0; - const LineNumberTable &lineTable = GetLineNumberTable(methodId); - auto iter = std::upper_bound(lineTable.begin(), lineTable.end(), LineTableEntry {offset, 0}); - if (iter != lineTable.begin()) { - line = (iter - 1)->line; - } - return cb(line); - } - - template - bool MatchColumnWithOffset(const Callback &cb, File::EntityId methodId, uint32_t offset) - { - int32_t column = 0; - const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId); - auto iter = std::upper_bound(columnTable.begin(), columnTable.end(), ColumnTableEntry {offset, 0}); - if (iter != columnTable.begin()) { - column = (iter - 1)->column; - } - return cb(column); - } - std::unique_ptr GetStepIntoStepper(const EcmaVM *ecmaVm); - std::unique_ptr GetStepOverStepper(const EcmaVM *ecmaVm); - std::unique_ptr GetStepOutStepper(const EcmaVM *ecmaVm); - - constexpr static int32_t SPECIAL_LINE_MARK = -1; - -private: - NO_COPY_SEMANTIC(JSPtExtractor); - NO_MOVE_SEMANTIC(JSPtExtractor); - std::list GetStepRanges(File::EntityId methodId, uint32_t offset); - std::unique_ptr GetStepper(const EcmaVM *ecmaVm, SingleStepper::Type type); -}; -} // namespace panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_BACKEND_JS_PT_EXTRACTOR_H diff --git a/tooling/backend/js_pt_hooks.cpp b/tooling/backend/js_pt_hooks.cpp index f47bc2a4..1e7ae288 100644 --- a/tooling/backend/js_pt_hooks.cpp +++ b/tooling/backend/js_pt_hooks.cpp @@ -13,9 +13,9 @@ * limitations under the License. */ -#include "ecmascript/tooling/backend/js_pt_hooks.h" +#include "backend/js_pt_hooks.h" -#include "ecmascript/tooling/agent/debugger_impl.h" +#include "agent/debugger_impl.h" namespace panda::ecmascript::tooling { void JSPtHooks::Breakpoint(const JSPtLocation &location) diff --git a/tooling/backend/js_pt_hooks.h b/tooling/backend/js_pt_hooks.h index 3b5ef631..2b1acd89 100644 --- a/tooling/backend/js_pt_hooks.h +++ b/tooling/backend/js_pt_hooks.h @@ -16,11 +16,10 @@ #ifndef ECMASCRIPT_TOOLING_BACKEND_JS_PT_HOOKS_H #define ECMASCRIPT_TOOLING_BACKEND_JS_PT_HOOKS_H -#include "ecmascript/tooling/backend/js_pt_extractor.h" -#include "ecmascript/tooling/base/pt_events.h" -#include "ecmascript/tooling/base/pt_script.h" -#include "ecmascript/tooling/backend/js_debugger_interface.h" +#include "base/pt_events.h" +#include "base/pt_script.h" +#include "ecmascript/debugger/js_debugger_interface.h" #include "libpandabase/macros.h" namespace panda::ecmascript::tooling { diff --git a/tooling/backend/js_pt_location.h b/tooling/backend/js_pt_location.h deleted file mode 100644 index 434372fa..00000000 --- a/tooling/backend/js_pt_location.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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. - */ - -#ifndef ECMASCRIPT_TOOLING_BACKEND_JS_PT_LOCATION_H -#define ECMASCRIPT_TOOLING_BACKEND_JS_PT_LOCATION_H - -#include - -#include "libpandafile/file.h" -#include "libpandabase/macros.h" - -namespace panda::ecmascript::tooling { -class JSPtLocation { -public: - using EntityId = panda_file::File::EntityId; - - explicit JSPtLocation(const char *pandaFile, EntityId methodId, uint32_t bytecodeOffset) - : pandaFile_(pandaFile), methodId_(methodId), bytecodeOffset_(bytecodeOffset) - { - } - - const char *GetPandaFile() const - { - return pandaFile_; - } - - EntityId GetMethodId() const - { - return methodId_; - } - - uint32_t GetBytecodeOffset() const - { - return bytecodeOffset_; - } - - bool operator==(const JSPtLocation &location) const - { - return methodId_ == location.methodId_ && bytecodeOffset_ == location.bytecodeOffset_ && - ::strcmp(pandaFile_, location.pandaFile_) == 0; - } - - ~JSPtLocation() = default; - - DEFAULT_COPY_SEMANTIC(JSPtLocation); - DEFAULT_MOVE_SEMANTIC(JSPtLocation); - -private: - const char *pandaFile_; - EntityId methodId_; - uint32_t bytecodeOffset_ {0}; -}; -} // namespace panda::ecmascript::tooling - -#endif // ECMASCRIPT_TOOLING_BACKEND_JS_PT_LOCATION_H diff --git a/tooling/backend/js_pt_extractor.cpp b/tooling/backend/js_single_stepper.cpp similarity index 66% rename from tooling/backend/js_pt_extractor.cpp rename to tooling/backend/js_single_stepper.cpp index aacf3334..ab65470a 100644 --- a/tooling/backend/js_pt_extractor.cpp +++ b/tooling/backend/js_single_stepper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 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 @@ -13,17 +13,19 @@ * limitations under the License. */ -#include "ecmascript/tooling/backend/js_pt_extractor.h" +#include "backend/js_single_stepper.h" -#include "ecmascript/tooling/backend/debugger_api.h" +#include "ecmascript/debugger/debugger_api.h" +#include "ecmascript/jspandafile/debug_info_extractor.h" +#include "ecmascript/jspandafile/js_pandafile_manager.h" namespace panda::ecmascript::tooling { -uint32_t JSPtExtractor::SingleStepper::GetStackDepth() const +uint32_t SingleStepper::GetStackDepth() const { return DebuggerApi::GetStackDepth(ecmaVm_); } -bool JSPtExtractor::SingleStepper::InStepRange(uint32_t pc) const +bool SingleStepper::InStepRange(uint32_t pc) const { for (const auto &range : stepRanges_) { if (pc >= range.startBcOffset && pc < range.endBcOffset) { @@ -33,7 +35,7 @@ bool JSPtExtractor::SingleStepper::InStepRange(uint32_t pc) const return false; } -bool JSPtExtractor::SingleStepper::StepComplete(uint32_t bcOffset) const +bool SingleStepper::StepComplete(uint32_t bcOffset) const { std::unique_ptr ptMethod = DebuggerApi::GetMethod(ecmaVm_); uint32_t stackDepth = GetStackDepth(); @@ -70,25 +72,26 @@ bool JSPtExtractor::SingleStepper::StepComplete(uint32_t bcOffset) const return true; } -std::unique_ptr JSPtExtractor::GetStepIntoStepper(const EcmaVM *ecmaVm) +std::unique_ptr SingleStepper::GetStepIntoStepper(const EcmaVM *ecmaVm) { return GetStepper(ecmaVm, SingleStepper::Type::STEP_INTO); } -std::unique_ptr JSPtExtractor::GetStepOverStepper(const EcmaVM *ecmaVm) +std::unique_ptr SingleStepper::GetStepOverStepper(const EcmaVM *ecmaVm) { return GetStepper(ecmaVm, SingleStepper::Type::STEP_OVER); } -std::unique_ptr JSPtExtractor::GetStepOutStepper(const EcmaVM *ecmaVm) +std::unique_ptr SingleStepper::GetStepOutStepper(const EcmaVM *ecmaVm) { return GetStepper(ecmaVm, SingleStepper::Type::STEP_OUT); } -std::list JSPtExtractor::GetStepRanges(File::EntityId methodId, uint32_t offset) +std::list SingleStepper::GetStepRanges(DebugInfoExtractor *extractor, + File::EntityId methodId, uint32_t offset) { std::list ranges {}; - const LineNumberTable &table = GetLineNumberTable(methodId); + const LineNumberTable &table = extractor->GetLineNumberTable(methodId); auto callbackFunc = [table, &ranges](int32_t line) -> bool { for (auto it = table.begin(); it != table.end(); ++it) { auto next = it + 1; @@ -99,20 +102,29 @@ std::list JSPtExtractor::GetStepRanges(File::EntityId methodId, u } return true; }; - MatchLineWithOffset(callbackFunc, methodId, offset); + extractor->MatchLineWithOffset(callbackFunc, methodId, offset); return ranges; } -std::unique_ptr JSPtExtractor::GetStepper(const EcmaVM *ecmaVm, SingleStepper::Type type) +std::unique_ptr SingleStepper::GetStepper(const EcmaVM *ecmaVm, + SingleStepper::Type type) { std::unique_ptr ptMethod = DebuggerApi::GetMethod(ecmaVm); ASSERT(ptMethod != nullptr); + DebugInfoExtractor *extractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor( + ptMethod->GetJSPandaFile()); + if (extractor == nullptr) { + LOG_DEBUGGER(ERROR) << "GetStepper: extractor is null"; + return nullptr; + } + if (type == SingleStepper::Type::STEP_OUT) { return std::make_unique(ecmaVm, std::move(ptMethod), std::list {}, type); } - std::list ranges = GetStepRanges(ptMethod->GetMethodId(), DebuggerApi::GetBytecodeOffset(ecmaVm)); + std::list ranges = GetStepRanges(extractor, ptMethod->GetMethodId(), + DebuggerApi::GetBytecodeOffset(ecmaVm)); return std::make_unique(ecmaVm, std::move(ptMethod), std::move(ranges), type); } } // namespace panda::ecmascript::tooling diff --git a/tooling/backend/js_single_stepper.h b/tooling/backend/js_single_stepper.h new file mode 100644 index 00000000..a7f380b4 --- /dev/null +++ b/tooling/backend/js_single_stepper.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ECMASCRIPT_TOOLING_BACKEND_JS_PT_EXTRACTOR_H +#define ECMASCRIPT_TOOLING_BACKEND_JS_PT_EXTRACTOR_H + +#include "ecmascript/debugger/js_debugger_interface.h" +#include "ecmascript/debugger/js_pt_method.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/jspandafile/debug_info_extractor.h" + +#include "libpandabase/macros.h" + +namespace panda::ecmascript::tooling { +using panda::panda_file::File; + +class SingleStepper { +public: + enum class Type { STEP_INTO, STEP_OVER, STEP_OUT }; + SingleStepper(const EcmaVM *ecmaVm, std::unique_ptr ptMethod, + std::list stepRanges, Type type) + : ecmaVm_(ecmaVm), + method_(std::move(ptMethod)), + stepRanges_(std::move(stepRanges)), + stackDepth_(GetStackDepth()), + type_(type) {} + + virtual ~SingleStepper() = default; + NO_COPY_SEMANTIC(SingleStepper); + NO_MOVE_SEMANTIC(SingleStepper); + + bool StepComplete(uint32_t bcOffset) const; + Type GetStepperType() const + { + return type_; + } + + static std::unique_ptr GetStepIntoStepper(const EcmaVM *ecmaVm); + static std::unique_ptr GetStepOverStepper(const EcmaVM *ecmaVm); + static std::unique_ptr GetStepOutStepper(const EcmaVM *ecmaVm); + +private: + uint32_t GetStackDepth() const; + bool InStepRange(uint32_t pc) const; + static std::list GetStepRanges(DebugInfoExtractor *extractor, + File::EntityId methodId, uint32_t offset); + static std::unique_ptr GetStepper(const EcmaVM *ecmaVm, SingleStepper::Type type); + + const EcmaVM *ecmaVm_; + std::unique_ptr method_; + std::list stepRanges_; + uint32_t stackDepth_; + Type type_; +}; +} // namespace panda::ecmascript::tooling +#endif // ECMASCRIPT_TOOLING_BACKEND_JS_PT_EXTRACTOR_H diff --git a/tooling/base/pt_base64.cpp b/tooling/base/pt_base64.cpp index 805717d2..66d6c8db 100644 --- a/tooling/base/pt_base64.cpp +++ b/tooling/base/pt_base64.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "ecmascript/tooling/base/pt_base64.h" +#include "base/pt_base64.h" namespace panda::ecmascript::tooling { static const uint8_t DECODE_STR_LEN = 3; diff --git a/tooling/base/pt_events.cpp b/tooling/base/pt_events.cpp index b0fabe16..42106dc2 100644 --- a/tooling/base/pt_events.cpp +++ b/tooling/base/pt_events.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "ecmascript/tooling/base/pt_events.h" +#include "base/pt_events.h" namespace panda::ecmascript::tooling { std::unique_ptr BreakpointResolved::ToJson() const diff --git a/tooling/base/pt_events.h b/tooling/base/pt_events.h index 3f7b86c5..98165ed9 100644 --- a/tooling/base/pt_events.h +++ b/tooling/base/pt_events.h @@ -19,9 +19,9 @@ #include #include -#include "ecmascript/tooling/base/pt_script.h" -#include "ecmascript/tooling/base/pt_types.h" -#include "ecmascript/tooling/dispatcher.h" +#include "base/pt_script.h" +#include "base/pt_types.h" +#include "dispatcher.h" #include "libpandabase/macros.h" diff --git a/tooling/base/pt_json.cpp b/tooling/base/pt_json.cpp index 2943e98b..87815b53 100644 --- a/tooling/base/pt_json.cpp +++ b/tooling/base/pt_json.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "ecmascript/tooling/base/pt_json.h" +#include "base/pt_json.h" #include "libpandabase/macros.h" diff --git a/tooling/base/pt_method.h b/tooling/base/pt_method.h deleted file mode 100644 index 945f8e08..00000000 --- a/tooling/base/pt_method.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 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. - */ - -#ifndef ECMASCRIPT_TOOLING_BASE_PT_METHOD_H -#define ECMASCRIPT_TOOLING_BASE_PT_METHOD_H - -#include "ecmascript/jspandafile/js_pandafile.h" - -#include "libpandafile/code_data_accessor-inl.h" -#include "libpandafile/method_data_accessor-inl.h" - -namespace panda::ecmascript::tooling { -class PtMethod { -public: - explicit PtMethod(const JSPandaFile *jsPandaFile, EntityId methodId, bool isNative) - : jsPandaFile_(jsPandaFile), methodId_(methodId), isNative_(isNative) - { - } - ~PtMethod() = default; - - const JSPandaFile *GetJSPandaFile() const - { - return jsPandaFile_; - } - - EntityId GetMethodId() const - { - return methodId_; - } - - bool IsNativeMethod() const - { - return isNative_; - } - - bool operator==(const PtMethod &method) const - { - return methodId_ == method.methodId_ && - jsPandaFile_ == method.jsPandaFile_; - } - - uint32_t GetCodeSize() const - { - if (jsPandaFile_ == nullptr) { - return 0; - } - panda_file::MethodDataAccessor mda(*(jsPandaFile_->GetPandaFile()), methodId_); - auto codeId = mda.GetCodeId().value(); - if (!codeId.IsValid()) { - return 0; - } - panda_file::CodeDataAccessor cda(*(jsPandaFile_->GetPandaFile()), codeId); - return cda.GetCodeSize(); - } - -private: - const JSPandaFile *jsPandaFile_ {nullptr}; - panda_file::File::EntityId methodId_ {0}; - bool isNative_ {false}; -}; -} // namespace panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_BASE_PT_METHOD_H diff --git a/tooling/base/pt_params.cpp b/tooling/base/pt_params.cpp index fc135d53..26983d94 100644 --- a/tooling/base/pt_params.cpp +++ b/tooling/base/pt_params.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "ecmascript/tooling/base/pt_params.h" +#include "base/pt_params.h" namespace panda::ecmascript::tooling { std::unique_ptr EnableParams::Create(const PtJson ¶ms) @@ -445,7 +445,7 @@ std::unique_ptr ReplyNativeCallingParams::Create(const std::string error; Result ret; - bool userCode= false; + bool userCode = false; ret = params.GetBool("userCode", &userCode); if (ret == Result::SUCCESS) { paramsObject->userCode_ = userCode; diff --git a/tooling/base/pt_params.h b/tooling/base/pt_params.h index a2d813c7..9503bd72 100644 --- a/tooling/base/pt_params.h +++ b/tooling/base/pt_params.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_BASE_PT_PARAMS_H #define ECMASCRIPT_TOOLING_BASE_PT_PARAMS_H -#include "ecmascript/tooling/base/pt_types.h" +#include "base/pt_types.h" namespace panda::ecmascript::tooling { class PtBaseParams : public PtBaseTypes { diff --git a/tooling/base/pt_returns.cpp b/tooling/base/pt_returns.cpp index 1d4e576c..423aeb58 100644 --- a/tooling/base/pt_returns.cpp +++ b/tooling/base/pt_returns.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "ecmascript/tooling/base/pt_returns.h" +#include "base/pt_returns.h" namespace panda::ecmascript::tooling { std::unique_ptr EnableReturns::ToJson() const diff --git a/tooling/base/pt_returns.h b/tooling/base/pt_returns.h index 3a10671d..c6417200 100644 --- a/tooling/base/pt_returns.h +++ b/tooling/base/pt_returns.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_BASE_PT_RETURNS_H #define ECMASCRIPT_TOOLING_BASE_PT_RETURNS_H -#include "ecmascript/tooling/base/pt_types.h" +#include "base/pt_types.h" namespace panda::ecmascript::tooling { class PtBaseReturns : public PtBaseTypes { diff --git a/tooling/base/pt_script.cpp b/tooling/base/pt_script.cpp index 120442d9..15c1e3a4 100644 --- a/tooling/base/pt_script.cpp +++ b/tooling/base/pt_script.cpp @@ -13,8 +13,9 @@ * limitations under the License. */ -#include "ecmascript/tooling/base/pt_script.h" -#include "ecmascript/tooling/backend/debugger_api.h" +#include "base/pt_script.h" + +#include "ecmascript/debugger/debugger_api.h" namespace panda::ecmascript::tooling { PtScript::PtScript(ScriptId scriptId, const std::string &fileName, const std::string &url, const std::string &source) diff --git a/tooling/base/pt_script.h b/tooling/base/pt_script.h index ab88b06a..5670ed8b 100644 --- a/tooling/base/pt_script.h +++ b/tooling/base/pt_script.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_BASE_PT_SCRIPT_H #define ECMASCRIPT_TOOLING_BASE_PT_SCRIPT_H -#include "ecmascript/tooling/base/pt_types.h" +#include "base/pt_types.h" #include "libpandabase/macros.h" diff --git a/tooling/base/pt_types.h b/tooling/base/pt_types.h index 22b51ee3..38e093e0 100644 --- a/tooling/base/pt_types.h +++ b/tooling/base/pt_types.h @@ -19,9 +19,10 @@ #include #include +#include "base/pt_json.h" + +#include "ecmascript/debugger/debugger_api.h" #include "ecmascript/dfx/cpu_profiler/samples_record.h" -#include "ecmascript/tooling/backend/debugger_api.h" -#include "ecmascript/tooling/base/pt_json.h" #include "libpandabase/macros.h" namespace panda::ecmascript::tooling { diff --git a/tooling/debugger_service.cpp b/tooling/debugger_service.cpp index da1c86d6..d3fdb106 100644 --- a/tooling/debugger_service.cpp +++ b/tooling/debugger_service.cpp @@ -13,11 +13,12 @@ * limitations under the License. */ -#include "ecmascript/tooling/debugger_service.h" +#include "debugger_service.h" +#include "protocol_handler.h" + +#include "ecmascript/debugger/js_debugger_manager.h" #include "ecmascript/ecma_vm.h" -#include "ecmascript/tooling/protocol_handler.h" -#include "ecmascript/tooling/interface/js_debugger_manager.h" namespace panda::ecmascript::tooling { void InitializeDebugger(::panda::ecmascript::EcmaVM *vm, diff --git a/tooling/dispatcher.cpp b/tooling/dispatcher.cpp index f1420f1f..62ad8a93 100644 --- a/tooling/dispatcher.cpp +++ b/tooling/dispatcher.cpp @@ -13,18 +13,18 @@ * limitations under the License. */ -#include "ecmascript/tooling/dispatcher.h" +#include "dispatcher.h" -#include "ecmascript/tooling/agent/debugger_impl.h" -#include "ecmascript/tooling/agent/runtime_impl.h" +#include "agent/debugger_impl.h" +#include "agent/runtime_impl.h" #ifdef ECMASCRIPT_SUPPORT_HEAPPROFILER -#include "ecmascript/tooling/agent/heapprofiler_impl.h" +#include "agent/heapprofiler_impl.h" #endif #ifdef ECMASCRIPT_SUPPORT_CPUPROFILER -#include "ecmascript/tooling/agent/profiler_impl.h" +#include "agent/profiler_impl.h" #endif -#include "ecmascript/tooling/agent/tracing_impl.h" -#include "ecmascript/tooling/protocol_channel.h" +#include "agent/tracing_impl.h" +#include "protocol_channel.h" namespace panda::ecmascript::tooling { DispatchRequest::DispatchRequest(const std::string &message) diff --git a/tooling/dispatcher.h b/tooling/dispatcher.h index b247ba8b..01f6fa19 100644 --- a/tooling/dispatcher.h +++ b/tooling/dispatcher.h @@ -19,9 +19,10 @@ #include #include +#include "base/pt_returns.h" + +#include "ecmascript/debugger/js_debugger_interface.h" #include "ecmascript/napi/include/jsnapi.h" -#include "ecmascript/tooling/backend/js_debugger_interface.h" -#include "ecmascript/tooling/base/pt_returns.h" #include "libpandabase/macros.h" namespace panda::ecmascript::tooling { diff --git a/tooling/interface/file_stream.cpp b/tooling/interface/file_stream.cpp deleted file mode 100644 index f374487a..00000000 --- a/tooling/interface/file_stream.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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/file_stream.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ecmascript/log_wrapper.h" - -namespace panda::ecmascript { -FileStream::FileStream(const std::string &fileName) -{ - Initialize(fileName); -} - -void FileStream::EndOfStream() -{ - if (Good()) { - fileStream_.close(); - } -} - -bool FileStream::Good() -{ - return fileStream_.good(); -} - -void FileStream::Initialize(const std::string &fileName) -{ - // check file name - std::pair realPath = FilePathValid(fileName); - if (!realPath.first) { - LOG_ECMA(ERROR) << "FileStream: check file path failed"; - fileStream_.close(); - return; - } - - fileStream_.open(realPath.second.c_str(), std::ios::out); - if (fileStream_.fail()) { - LOG_ECMA(ERROR) << "FileStream: open file failed"; - } -} - -std::pair FileStream::FilePathValid(const std::string &fileName) -{ - if (fileName.empty() || fileName.size() > PATH_MAX) { - return std::make_pair(false, ""); - } - char resolvedPath[PATH_MAX] = {0}; - auto result = realpath(fileName.c_str(), resolvedPath); - if (result == resolvedPath || errno == ENOENT) { - return std::make_pair(true, std::string(resolvedPath)); - } - return std::make_pair(false, ""); -} - -// Writes the chunk of data into the stream -bool FileStream::WriteChunk(char *data, int32_t size) -{ - if (fileStream_.fail()) { - return false; - } - - std::string str; - str.resize(size); - for (int32_t i = 0; i < size; ++i) { - str[i] = data[i]; - } - - fileStream_ << str; - - return true; -} - -void FileDescriptorStream::EndOfStream() -{ - if (Good()) { - close(fd_); - } -} - -bool FileDescriptorStream::Good() -{ - return fd_ > 0; -} - -// Writes the chunk of data into the stream -bool FileDescriptorStream::WriteChunk(char *data, int32_t size) -{ - if (fd_ < 0) { - return false; - } - - std::string str; - str.resize(size); - for (int32_t i = 0; i < size; ++i) { - str[i] = data[i]; - } - int ret = dprintf(fd_, "%s", str.c_str()); - if (ret < 0) { - LOG_ECMA(ERROR) << "Write FD print failed, ret" << ret; - return false; - } - ret = fsync(fd_); - if (ret < 0) { - LOG_ECMA(ERROR) << "Write FD file failed, ret" << ret; - return false; - } - return true; -} -} diff --git a/tooling/interface/file_stream.h b/tooling/interface/file_stream.h deleted file mode 100644 index 0d284e4b..00000000 --- a/tooling/interface/file_stream.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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. - */ - -#ifndef ECMASCRIPT_TOOLING_INTERFACE_FILE_STREAM_H -#define ECMASCRIPT_TOOLING_INTERFACE_FILE_STREAM_H - -#include -#include -#include -#include - -#include "ecmascript/tooling/interface/stream.h" -#include "ecmascript/mem/mem_common.h" - -namespace panda::ecmascript { -class FileStream : public Stream { -public: - FileStream(const std::string &fileName); - ~FileStream() override = default; - - void EndOfStream() override; - - // Get chunk's size - int GetSize() override - { - const static int fileChunkSize = static_cast(10_KB); - return fileChunkSize; - } - - // Writes the chunk of data into the stream - bool WriteChunk(char* data, int32_t size) override; - bool Good() override; - void UpdateHeapStats([[maybe_unused]] HeapStat* data, [[maybe_unused]] int32_t count) override - { - } - void UpdateLastSeenObjectId([[maybe_unused]] int32_t lastSeenObjectId) override - { - } - -private: - void Initialize(const std::string &fileName); - std::pair FilePathValid(const std::string &fileName); - - std::fstream fileStream_; -}; - -class FileDescriptorStream : public Stream { -public: - explicit FileDescriptorStream(int32_t fd): fd_(fd) {} - ~FileDescriptorStream() override = default; - - void EndOfStream() override; - - // Get chunk's size - int GetSize() override - { - const static int fileChunkSize = static_cast(10_KB); - return fileChunkSize; - } - - // Writes the chunk of data into the stream - bool WriteChunk(char *data, int32_t size) override; - bool Good() override; - void UpdateHeapStats([[maybe_unused]] HeapStat* data, [[maybe_unused]] int32_t count) override - { - } - void UpdateLastSeenObjectId([[maybe_unused]] int32_t lastSeenObjectId) override - { - } - -private: - int32_t fd_; -}; -} // namespace panda::ecmascript::tooling - -#endif // ECMASCRIPT_TOOLING_INTERFACE_FILE_STREAM_H diff --git a/tooling/interface/js_debugger_manager.h b/tooling/interface/js_debugger_manager.h deleted file mode 100644 index a3ee9c94..00000000 --- a/tooling/interface/js_debugger_manager.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 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. - */ - -#ifndef ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUGGER_MANAGER_H -#define ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUGGER_MANAGER_H - -#include "ecmascript/interpreter/frame_handler.h" -#include "ecmascript/js_thread.h" -#include "ecmascript/napi/include/jsnapi.h" -#include "ecmascript/tooling/interface/notification_manager.h" - -#include "libpandabase/os/library_loader.h" - -namespace panda::ecmascript::tooling { -class ProtocolHandler; -class JsDebuggerManager { -public: - using LibraryHandle = os::library_loader::LibraryHandle; - using ObjectUpdaterFunc = - std::function)>; - using SingleStepperFunc = std::function; - - JsDebuggerManager() = default; - ~JsDebuggerManager() - { - delete notificationManager_; - } - - NO_COPY_SEMANTIC(JsDebuggerManager); - NO_MOVE_SEMANTIC(JsDebuggerManager); - - void Initialize(const EcmaVM *vm) - { - notificationManager_ = new NotificationManager(); - jsThread_ = vm->GetJSThread(); - } - - NotificationManager *GetNotificationManager() const - { - return notificationManager_; - } - - void SetDebugMode(bool isDebugMode) - { - if (isDebugMode_ == isDebugMode) { - return; - } - - isDebugMode_ = isDebugMode; - if (jsThread_ != nullptr && jsThread_->IsAsmInterpreter()) { - jsThread_->CheckSwitchDebuggerBCStub(); - } - } - - bool IsDebugMode() const - { - return isDebugMode_; - } - - void SetMixedDebugEnabled(bool enabled) - { - isMixedDebugEnabled_ = enabled; - } - - bool IsMixedDebugEnabled() const - { - return isMixedDebugEnabled_; - } - - void SetDebuggerHandler(ProtocolHandler *debuggerHandler) - { - debuggerHandler_ = debuggerHandler; - } - - ProtocolHandler *GetDebuggerHandler() const - { - return debuggerHandler_; - } - - void SetDebugLibraryHandle(LibraryHandle handle) - { - debuggerLibraryHandle_ = std::move(handle); - } - - const LibraryHandle &GetDebugLibraryHandle() const - { - return debuggerLibraryHandle_; - } - - void SetEvalFrameHandler(std::shared_ptr frameHandler) - { - frameHandler_ = frameHandler; - } - - const std::shared_ptr &GetEvalFrameHandler() const - { - return frameHandler_; - } - - void SetLocalScopeUpdater(ObjectUpdaterFunc *updaterFunc) - { - updaterFunc_ = updaterFunc; - } - - void NotifyLocalScopeUpdated(std::string_view varName, Local value) - { - if (updaterFunc_ != nullptr) { - (*updaterFunc_)(frameHandler_.get(), varName, value); - } - } - - void SetStepperFunc(SingleStepperFunc *stepperFunc) - { - stepperFunc_ = stepperFunc; - } - - void ClearSingleStepper() - { - if (stepperFunc_ != nullptr) { - (*stepperFunc_)(); - } - } - -private: - bool isDebugMode_ {false}; - bool isMixedDebugEnabled_ { false }; - ProtocolHandler *debuggerHandler_ {nullptr}; - LibraryHandle debuggerLibraryHandle_ {nullptr}; - NotificationManager *notificationManager_ {nullptr}; - ObjectUpdaterFunc *updaterFunc_ {nullptr}; - SingleStepperFunc *stepperFunc_ {nullptr}; - JSThread *jsThread_ {nullptr}; - std::shared_ptr frameHandler_; -}; -} // panda::ecmascript::tooling - -#endif // ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUGGER_MANAGER_H \ No newline at end of file diff --git a/tooling/interface/notification_manager.h b/tooling/interface/notification_manager.h deleted file mode 100644 index cac5b092..00000000 --- a/tooling/interface/notification_manager.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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. - */ - -#ifndef ECMASCRIPT_TOOLING_INTERFACE_NOTIFICATION_MANAGER_H -#define ECMASCRIPT_TOOLING_INTERFACE_NOTIFICATION_MANAGER_H - -#include - -#include "ecmascript/js_thread.h" - -namespace panda::ecmascript::tooling { -class RuntimeListener { -public: - RuntimeListener() = default; - virtual ~RuntimeListener() = default; - DEFAULT_COPY_SEMANTIC(RuntimeListener); - DEFAULT_MOVE_SEMANTIC(RuntimeListener); - - virtual void LoadModule(std::string_view name, std::string_view) = 0; - - virtual void BytecodePcChanged(JSThread *thread, JSHandle method, - uint32_t bc_offset) = 0; - virtual void VmStart() = 0; - virtual void VmDeath() = 0; - virtual void PendingJobEntry() = 0; - virtual void NativeCalling(const void *nativeAddress) = 0; -}; - -class NotificationManager { -public: - NotificationManager() = default; - ~NotificationManager() = default; - NO_COPY_SEMANTIC(NotificationManager); - NO_MOVE_SEMANTIC(NotificationManager); - - void AddListener(RuntimeListener *listener) - { - listener_ = listener; - } - void RemoveListener() - { - listener_ = nullptr; - } - - void LoadModuleEvent(std::string_view name, std::string_view entryPoint) const - { - if (UNLIKELY(listener_ != nullptr)) { - listener_->LoadModule(name, entryPoint); - } - } - - void BytecodePcChangedEvent(JSThread *thread, Method *method, uint32_t bcOffset) const - { - if (UNLIKELY(listener_ != nullptr)) { - JSHandle methodHandle(thread, method); - listener_->BytecodePcChanged(thread, methodHandle, bcOffset); - } - } - - void NativeCallingEvent(const void *nativeAddress) const - { - if (UNLIKELY(listener_ != nullptr)) { - listener_->NativeCalling(nativeAddress); - } - } - - void PendingJobEntryEvent() const - { - if (UNLIKELY(listener_ != nullptr)) { - listener_->PendingJobEntry(); - } - } - - void VmStartEvent() const - { - if (UNLIKELY(listener_ != nullptr)) { - listener_->VmStart(); - } - } - void VmDeathEvent() const - { - if (UNLIKELY(listener_ != nullptr)) { - listener_->VmDeath(); - } - } - -private: - RuntimeListener *listener_ {nullptr}; -}; -} // panda::ecmascript::tooling - -#endif // ECMASCRIPT_TOOLING_INTERFACE_NOTIFICATION_MANAGER_H \ No newline at end of file diff --git a/tooling/protocol_channel.h b/tooling/protocol_channel.h index 3c2d7ba2..1efc8b43 100644 --- a/tooling/protocol_channel.h +++ b/tooling/protocol_channel.h @@ -16,9 +16,9 @@ #ifndef ECMASCRIPT_TOOLING_PROTOCOL_CHANNEL_H #define ECMASCRIPT_TOOLING_PROTOCOL_CHANNEL_H -#include "ecmascript/tooling/base/pt_events.h" -#include "ecmascript/tooling/base/pt_returns.h" -#include "ecmascript/tooling/dispatcher.h" +#include "base/pt_events.h" +#include "base/pt_returns.h" +#include "dispatcher.h" #include "libpandabase/macros.h" diff --git a/tooling/protocol_handler.cpp b/tooling/protocol_handler.cpp index edb70ff8..d685a4e7 100644 --- a/tooling/protocol_handler.cpp +++ b/tooling/protocol_handler.cpp @@ -13,9 +13,9 @@ * limitations under the License. */ -#include "ecmascript/tooling/protocol_handler.h" +#include "protocol_handler.h" -#include "ecmascript/tooling/agent/debugger_impl.h" +#include "agent/debugger_impl.h" namespace panda::ecmascript::tooling { void ProtocolHandler::WaitForDebugger() diff --git a/tooling/protocol_handler.h b/tooling/protocol_handler.h index 1a0cb129..ee7a046a 100644 --- a/tooling/protocol_handler.h +++ b/tooling/protocol_handler.h @@ -21,7 +21,7 @@ #include #include -#include "ecmascript/tooling/protocol_channel.h" +#include "protocol_channel.h" namespace panda::ecmascript::tooling { class ProtocolHandler final : public ProtocolChannel { diff --git a/tooling/test/BUILD.gn b/tooling/test/BUILD.gn index fd120a75..eb83066b 100644 --- a/tooling/test/BUILD.gn +++ b/tooling/test/BUILD.gn @@ -13,28 +13,28 @@ import("//arkcompiler/ets_frontend/ts2panda/ts2abc_config.gni") import("//arkcompiler/ets_runtime/js_runtime_config.gni") -import("//arkcompiler/ets_runtime/test/test_helper.gni") +import("//arkcompiler/toolchain/test/test_helper.gni") import("//build/ohos.gni") import("//build/test.gni") -module_output_path = "arkcompiler/ets_runtime" +module_output_path = "arkcompiler/toolchain" config("debug_api_test") { visibility = [ ":*" ] ldflags = [ "-Wl,-rpath=\$ORIGIN/" ] - configs = [ "//arkcompiler/ets_runtime:ecma_test_config" ] + configs = [ "//arkcompiler/toolchain:toolchain_test_config" ] include_dirs = [ "//arkcompiler/ets_runtime", - "//arkcompiler/ets_runtime/ecmascript/tooling/test", + "//arkcompiler/toolchain/tooling", ] } -test_js_path = "//arkcompiler/ets_runtime/ecmascript/tooling/test/testcases/js/" +test_js_path = "//arkcompiler/toolchain/tooling/test/testcases/js/" -# add new js test file here and modify test/resource/js_runtime/ohos_test.xml +# When new js test file added, pls modify ohos_test.xml in test/resource accordingly. test_js_files = [ "arrow_func", "async_func", @@ -50,7 +50,9 @@ foreach(file, test_js_files) { ts2abc_gen_abc("gen_${file}_abc") { test_js = "${test_js_path}${file}.js" test_abc = "$target_out_dir/${file}.abc" - extra_visibility = [ ":*" ] # Only targets in this file can depend on this. + + # Only targets in this file can depend on this. + extra_visibility = [ ":*" ] src_js = rebase_path(test_js) dst_file = rebase_path(test_abc) extra_args = [ "--debug" ] @@ -91,7 +93,7 @@ source_set("jsdebugtest_set") { public_configs = [ ":debug_api_test", - "//arkcompiler/ets_runtime/ecmascript/tooling:ark_ecma_debugger_config", + "//arkcompiler/toolchain/tooling:ark_ecma_debugger_config", ] test_abc_dir = "/data/test/" @@ -113,7 +115,7 @@ source_set("jsdebugtest_set") { deps = [ "//arkcompiler/ets_runtime:libark_jsruntime_test", - "//arkcompiler/ets_runtime/ecmascript/tooling:libark_ecma_debugger_test", + "//arkcompiler/toolchain/tooling:libark_ecma_debugger_test", ] } @@ -150,7 +152,7 @@ host_unittest_action("DebuggerEntryTest") { ":debugger_entry_resource", ":jsdebugtest", "//arkcompiler/ets_runtime:libark_jsruntime_test", - "//arkcompiler/ets_runtime/ecmascript/tooling:libark_ecma_debugger_test", + "//arkcompiler/toolchain/tooling:libark_ecma_debugger_test", ] } @@ -170,11 +172,11 @@ host_unittest_action("DebuggerTest") { "pt_json_test.cpp", ] - configs = [ "//arkcompiler/ets_runtime:ecma_test_config" ] + configs = [ "//arkcompiler/toolchain:toolchain_test_config" ] deps = [ "//arkcompiler/ets_runtime:libark_jsruntime_test", - "//arkcompiler/ets_runtime/ecmascript/tooling:libark_ecma_debugger_test", + "//arkcompiler/toolchain/tooling:libark_ecma_debugger_test", sdk_libc_secshared_dep, ] } diff --git a/tooling/test/debugger_commands_test.cpp b/tooling/test/debugger_commands_test.cpp index 43e04f30..c7f91e8c 100644 --- a/tooling/test/debugger_commands_test.cpp +++ b/tooling/test/debugger_commands_test.cpp @@ -16,12 +16,13 @@ #include "ecmascript/js_array.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/object_factory.h" +#include "ecmascript/debugger/js_debugger.h" #include "ecmascript/tests/test_helper.h" -#include "ecmascript/tooling/base/pt_params.h" -#include "ecmascript/tooling/base/pt_returns.h" -#include "ecmascript/tooling/debugger_service.h" -#include "ecmascript/tooling/dispatcher.h" -#include "ecmascript/tooling/backend/js_debugger.h" + +#include "base/pt_params.h" +#include "base/pt_returns.h" +#include "debugger_service.h" +#include "dispatcher.h" using namespace panda::ecmascript; using namespace panda::ecmascript::tooling; diff --git a/tooling/test/debugger_entry_test.cpp b/tooling/test/debugger_entry_test.cpp index 53373d8e..11d0b50e 100644 --- a/tooling/test/debugger_entry_test.cpp +++ b/tooling/test/debugger_entry_test.cpp @@ -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/utils/test_list.h" +#include "test/utils/test_list.h" namespace panda::ecmascript::tooling::test { using panda::test::TestHelper; diff --git a/tooling/test/debugger_events_test.cpp b/tooling/test/debugger_events_test.cpp index b78e20ce..7c4f385b 100644 --- a/tooling/test/debugger_events_test.cpp +++ b/tooling/test/debugger_events_test.cpp @@ -17,9 +17,9 @@ #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/object_factory.h" #include "ecmascript/tests/test_helper.h" -#include "ecmascript/tooling/base/pt_events.h" -#include "ecmascript/tooling/base/pt_types.h" -#include "ecmascript/tooling/dispatcher.h" +#include "base/pt_events.h" +#include "base/pt_types.h" +#include "dispatcher.h" using namespace panda::ecmascript; using namespace panda::ecmascript::tooling; diff --git a/tooling/test/debugger_params_test.cpp b/tooling/test/debugger_params_test.cpp index 6b2e83a0..a9d79faa 100644 --- a/tooling/test/debugger_params_test.cpp +++ b/tooling/test/debugger_params_test.cpp @@ -13,14 +13,15 @@ * limitations under the License. */ +#include "base/pt_types.h" +#include "base/pt_params.h" +#include "dispatcher.h" + #include "ecmascript/js_array.h" #include "ecmascript/js_object-inl.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/object_factory.h" #include "ecmascript/tests/test_helper.h" -#include "ecmascript/tooling/base/pt_types.h" -#include "ecmascript/tooling/base/pt_params.h" -#include "ecmascript/tooling/dispatcher.h" using namespace panda::ecmascript; using namespace panda::ecmascript::tooling; diff --git a/tooling/test/debugger_returns_test.cpp b/tooling/test/debugger_returns_test.cpp index 8ea78952..f5b88504 100644 --- a/tooling/test/debugger_returns_test.cpp +++ b/tooling/test/debugger_returns_test.cpp @@ -13,14 +13,15 @@ * limitations under the License. */ +#include "base/pt_types.h" +#include "base/pt_returns.h" +#include "dispatcher.h" + #include "ecmascript/js_array.h" #include "ecmascript/js_object-inl.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/object_factory.h" #include "ecmascript/tests/test_helper.h" -#include "ecmascript/tooling/base/pt_types.h" -#include "ecmascript/tooling/base/pt_returns.h" -#include "ecmascript/tooling/dispatcher.h" using namespace panda::ecmascript; using namespace panda::ecmascript::tooling; diff --git a/tooling/test/debugger_script_test.cpp b/tooling/test/debugger_script_test.cpp index cc8ab425..27ee966d 100644 --- a/tooling/test/debugger_script_test.cpp +++ b/tooling/test/debugger_script_test.cpp @@ -1,3 +1,4 @@ + /* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,16 +14,17 @@ * limitations under the License. */ +#include "base/pt_params.h" +#include "base/pt_returns.h" +#include "base/pt_script.h" +#include "debugger_service.h" +#include "dispatcher.h" + +#include "ecmascript/debugger/js_debugger.h" #include "ecmascript/js_array.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/object_factory.h" #include "ecmascript/tests/test_helper.h" -#include "ecmascript/tooling/base/pt_params.h" -#include "ecmascript/tooling/base/pt_returns.h" -#include "ecmascript/tooling/debugger_service.h" -#include "ecmascript/tooling/dispatcher.h" -#include "ecmascript/tooling/backend/js_debugger.h" -#include "ecmascript/tooling/base/pt_script.h" using namespace panda::ecmascript; using namespace panda::ecmascript::tooling; diff --git a/tooling/test/debugger_types_test.cpp b/tooling/test/debugger_types_test.cpp index 738891ea..9bc6b0a9 100644 --- a/tooling/test/debugger_types_test.cpp +++ b/tooling/test/debugger_types_test.cpp @@ -13,13 +13,14 @@ * limitations under the License. */ +#include "base/pt_types.h" +#include "dispatcher.h" + #include "ecmascript/js_array.h" #include "ecmascript/js_object-inl.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/object_factory.h" #include "ecmascript/tests/test_helper.h" -#include "ecmascript/tooling/base/pt_types.h" -#include "ecmascript/tooling/dispatcher.h" using namespace panda::ecmascript; using namespace panda::ecmascript::tooling; diff --git a/tooling/test/entry/test_debugger_entry.cpp b/tooling/test/entry/test_debugger_entry.cpp index e10f6501..8aa591c3 100644 --- a/tooling/test/entry/test_debugger_entry.cpp +++ b/tooling/test/entry/test_debugger_entry.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "ecmascript/tooling/test/utils/test_entry.h" +#include "test/utils/test_entry.h" namespace panda::ecmascript::tooling::test { extern "C" bool StartDebug(const std::string &name, EcmaVM *vm, bool isDebugMode) diff --git a/tooling/test/js_pt_hooks_test.cpp b/tooling/test/js_pt_hooks_test.cpp index 32b22c13..cbae5c64 100644 --- a/tooling/test/js_pt_hooks_test.cpp +++ b/tooling/test/js_pt_hooks_test.cpp @@ -13,16 +13,17 @@ * limitations under the License. */ +#include "agent/debugger_impl.h" +#include "backend/js_pt_hooks.h" +#include "base/pt_types.h" +#include "base/pt_events.h" +#include "dispatcher.h" + +#include "ecmascript/debugger/js_debugger.h" #include "ecmascript/js_array.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/object_factory.h" #include "ecmascript/tests/test_helper.h" -#include "ecmascript/tooling/agent/debugger_impl.h" -#include "ecmascript/tooling/backend/js_pt_hooks.h" -#include "ecmascript/tooling/backend/js_debugger.h" -#include "ecmascript/tooling/base/pt_types.h" -#include "ecmascript/tooling/base/pt_events.h" -#include "ecmascript/tooling/dispatcher.h" using namespace panda::ecmascript; using namespace panda::ecmascript::tooling; diff --git a/tooling/test/pt_base64_test.cpp b/tooling/test/pt_base64_test.cpp index fdd7e446..27e3c85e 100644 --- a/tooling/test/pt_base64_test.cpp +++ b/tooling/test/pt_base64_test.cpp @@ -14,7 +14,7 @@ */ #include "ecmascript/tests/test_helper.h" -#include "ecmascript/tooling/base/pt_base64.h" +#include "base/pt_base64.h" using namespace panda::ecmascript::tooling; diff --git a/tooling/test/pt_json_test.cpp b/tooling/test/pt_json_test.cpp index 05997474..640e8f2f 100644 --- a/tooling/test/pt_json_test.cpp +++ b/tooling/test/pt_json_test.cpp @@ -14,7 +14,7 @@ */ #include "ecmascript/tests/test_helper.h" -#include "ecmascript/tooling/base/pt_json.h" +#include "base/pt_json.h" using namespace panda::ecmascript::tooling; diff --git a/tooling/test/testcases/js_breakpoint_arrow_test.h b/tooling/test/testcases/js_breakpoint_arrow_test.h index baf68e7d..a8d24325 100644 --- a/tooling/test/testcases/js_breakpoint_arrow_test.h +++ b/tooling/test/testcases/js_breakpoint_arrow_test.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_ARROW_TEST_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_ARROW_TEST_H -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsBreakpointArrowTest : public TestEvents { diff --git a/tooling/test/testcases/js_breakpoint_async_test.h b/tooling/test/testcases/js_breakpoint_async_test.h index 17917e19..3a2f94d1 100644 --- a/tooling/test/testcases/js_breakpoint_async_test.h +++ b/tooling/test/testcases/js_breakpoint_async_test.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_ASYNC_TEST_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_ASYNC_TEST_H -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsBreakpointAsyncTest : public TestEvents { diff --git a/tooling/test/testcases/js_breakpoint_test.h b/tooling/test/testcases/js_breakpoint_test.h index 5bbf2db6..7c6c8e54 100644 --- a/tooling/test/testcases/js_breakpoint_test.h +++ b/tooling/test/testcases/js_breakpoint_test.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_TEST_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_BREAKPOINT_TEST_H -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsBreakpointTest : public TestEvents { diff --git a/tooling/test/testcases/js_exception_test.h b/tooling/test/testcases/js_exception_test.h index 860e76b2..cc5415c9 100644 --- a/tooling/test/testcases/js_exception_test.h +++ b/tooling/test/testcases/js_exception_test.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_EXCEPTION_TEST_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_EXCEPTION_TEST_H -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsExceptionTest : public TestEvents { diff --git a/tooling/test/testcases/js_range_error_test.h b/tooling/test/testcases/js_range_error_test.h index 260f7342..2bfc1d46 100644 --- a/tooling/test/testcases/js_range_error_test.h +++ b/tooling/test/testcases/js_range_error_test.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_RANGE_ERROR_TEST_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_RANGE_ERROR_TEST_H -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsRangeErrorTest : public TestEvents { diff --git a/tooling/test/testcases/js_single_step_test.h b/tooling/test/testcases/js_single_step_test.h index 33753f3f..5a64e9b0 100644 --- a/tooling/test/testcases/js_single_step_test.h +++ b/tooling/test/testcases/js_single_step_test.h @@ -16,7 +16,7 @@ #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" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsSingleStepTest : public TestEvents { diff --git a/tooling/test/testcases/js_step_into_test.h b/tooling/test/testcases/js_step_into_test.h index 7a581130..ba0c2dd7 100644 --- a/tooling/test/testcases/js_step_into_test.h +++ b/tooling/test/testcases/js_step_into_test.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_INTO_TEST_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_INTO_TEST_H -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsStepIntoTest : public TestEvents { diff --git a/tooling/test/testcases/js_step_out_test.h b/tooling/test/testcases/js_step_out_test.h index 408d5bb6..de1d65db 100644 --- a/tooling/test/testcases/js_step_out_test.h +++ b/tooling/test/testcases/js_step_out_test.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_OUT_TEST_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_OUT_TEST_H -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsStepOutTest : public TestEvents { diff --git a/tooling/test/testcases/js_step_over_test.h b/tooling/test/testcases/js_step_over_test.h index e6a97bf2..e2718db3 100644 --- a/tooling/test/testcases/js_step_over_test.h +++ b/tooling/test/testcases/js_step_over_test.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_OVER_TEST_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_STEP_OVER_TEST_H -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsStepOverTest : public TestEvents { diff --git a/tooling/test/testcases/js_syntax_exception_test.h b/tooling/test/testcases/js_syntax_exception_test.h index 6e2503d7..9eeee16d 100644 --- a/tooling/test/testcases/js_syntax_exception_test.h +++ b/tooling/test/testcases/js_syntax_exception_test.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_SYNTAX_EXCEPTION_TEST_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_SYNTAX_EXCEPTION_TEST_H -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsSyntaxExceptionTest : public TestEvents { diff --git a/tooling/test/testcases/js_throw_exception_test.h b/tooling/test/testcases/js_throw_exception_test.h index fafc398c..a290e0c1 100644 --- a/tooling/test/testcases/js_throw_exception_test.h +++ b/tooling/test/testcases/js_throw_exception_test.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_THROW_EXCEPTION_TEST_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_THROW_EXCEPTION_TEST_H -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class JsThrowExceptionTest : public TestEvents { diff --git a/tooling/test/utils/test_entry.cpp b/tooling/test/utils/test_entry.cpp index 4c9e1b07..894a7198 100644 --- a/tooling/test/utils/test_entry.cpp +++ b/tooling/test/utils/test_entry.cpp @@ -13,12 +13,12 @@ * limitations under the License. */ -#include "ecmascript/tooling/test/utils/test_entry.h" +#include "test/utils/test_entry.h" #include -#include "ecmascript/tooling/test/utils/test_hooks.h" -#include "ecmascript/tooling/test/utils/test_list.h" +#include "test/utils/test_hooks.h" +#include "test/utils/test_list.h" namespace panda::ecmascript::tooling::test { static std::thread g_debuggerThread; diff --git a/tooling/test/utils/test_events.h b/tooling/test/utils/test_events.h index f61bff21..e2eeeeba 100644 --- a/tooling/test/utils/test_events.h +++ b/tooling/test/utils/test_events.h @@ -18,7 +18,7 @@ #include -#include "ecmascript/tooling/backend/js_pt_hooks.h" +#include "backend/js_pt_hooks.h" namespace panda::ecmascript::tooling::test { using BreakpointCallback = std::function; diff --git a/tooling/test/utils/test_extractor.cpp b/tooling/test/utils/test_extractor.cpp index 381c0a4c..af683709 100644 --- a/tooling/test/utils/test_extractor.cpp +++ b/tooling/test/utils/test_extractor.cpp @@ -14,7 +14,7 @@ */ // Taken from panda-tools/panda-debugger/debugger -#include "ecmascript/tooling/test/utils/test_extractor.h" +#include "test/utils/test_extractor.h" #include "libpandabase/utils/leb128.h" #include "libpandabase/utils/utf.h" @@ -30,7 +30,7 @@ std::pair TestExtractor::GetBreakpointAddress(const SourceLo { EntityId retId = EntityId(); uint32_t retOffset = 0; - auto callbackFunc = [&retId, &retOffset](File::EntityId id, uint32_t offset) -> bool { + auto callbackFunc = [&retId, &retOffset](panda_file::File::EntityId id, uint32_t offset) -> bool { retId = id; retOffset = offset; return true; diff --git a/tooling/test/utils/test_extractor.h b/tooling/test/utils/test_extractor.h index 5cc9bedc..2080a2e4 100644 --- a/tooling/test/utils/test_extractor.h +++ b/tooling/test/utils/test_extractor.h @@ -16,7 +16,7 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EXTRACTOR_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_EXTRACTOR_H -#include "ecmascript/tooling/backend/js_pt_extractor.h" +#include "ecmascript/jspandafile/debug_info_extractor.h" namespace panda::ecmascript::tooling::test { using EntityId = panda_file::File::EntityId; @@ -38,9 +38,9 @@ struct SourceLocation { } }; -class TestExtractor : public JSPtExtractor { +class TestExtractor : public DebugInfoExtractor { public: - explicit TestExtractor(const JSPandaFile *pandaFile) : JSPtExtractor(pandaFile) {} + explicit TestExtractor(const JSPandaFile *pandaFile) : DebugInfoExtractor(pandaFile) {} ~TestExtractor() = default; std::pair GetBreakpointAddress(const SourceLocation &sourceLocation); diff --git a/tooling/test/utils/test_hooks.h b/tooling/test/utils/test_hooks.h index 9ee2ee4f..89165e19 100644 --- a/tooling/test/utils/test_hooks.h +++ b/tooling/test/utils/test_hooks.h @@ -16,9 +16,9 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TEST_HOOKS_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_HOOKS_H -#include "ecmascript/tooling/agent/debugger_impl.h" -#include "ecmascript/tooling/backend/js_pt_hooks.h" -#include "ecmascript/tooling/test/utils/test_util.h" +#include "agent/debugger_impl.h" +#include "backend/js_pt_hooks.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { class TestHooks : public PtHooks { diff --git a/tooling/test/utils/test_list.cpp b/tooling/test/utils/test_list.cpp index 50471a82..0a07229c 100644 --- a/tooling/test/utils/test_list.cpp +++ b/tooling/test/utils/test_list.cpp @@ -13,22 +13,22 @@ * limitations under the License. */ -#include "ecmascript/tooling/test/utils/test_list.h" +#include "test/utils/test_list.h" -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" // testcase list -#include "ecmascript/tooling/test/testcases/js_breakpoint_test.h" -#include "ecmascript/tooling/test/testcases/js_breakpoint_arrow_test.h" -#include "ecmascript/tooling/test/testcases/js_breakpoint_async_test.h" -#include "ecmascript/tooling/test/testcases/js_exception_test.h" -#include "ecmascript/tooling/test/testcases/js_range_error_test.h" -#include "ecmascript/tooling/test/testcases/js_single_step_test.h" -#include "ecmascript/tooling/test/testcases/js_step_into_test.h" -#include "ecmascript/tooling/test/testcases/js_step_over_test.h" -#include "ecmascript/tooling/test/testcases/js_step_out_test.h" -#include "ecmascript/tooling/test/testcases/js_syntax_exception_test.h" -#include "ecmascript/tooling/test/testcases/js_throw_exception_test.h" +#include "test/testcases/js_breakpoint_test.h" +#include "test/testcases/js_breakpoint_arrow_test.h" +#include "test/testcases/js_breakpoint_async_test.h" +#include "test/testcases/js_exception_test.h" +#include "test/testcases/js_range_error_test.h" +#include "test/testcases/js_single_step_test.h" +#include "test/testcases/js_step_into_test.h" +#include "test/testcases/js_step_over_test.h" +#include "test/testcases/js_step_out_test.h" +#include "test/testcases/js_syntax_exception_test.h" +#include "test/testcases/js_throw_exception_test.h" namespace panda::ecmascript::tooling::test { static std::string g_currentTestName = ""; diff --git a/tooling/test/utils/test_util.cpp b/tooling/test/utils/test_util.cpp index 2ca7534d..d993558d 100644 --- a/tooling/test/utils/test_util.cpp +++ b/tooling/test/utils/test_util.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "ecmascript/tooling/test/utils/test_util.h" +#include "test/utils/test_util.h" namespace panda::ecmascript::tooling::test { TestMap TestUtil::testMap_; diff --git a/tooling/test/utils/test_util.h b/tooling/test/utils/test_util.h index 22f3c017..5086601a 100644 --- a/tooling/test/utils/test_util.h +++ b/tooling/test/utils/test_util.h @@ -16,11 +16,13 @@ #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H #define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H +#include "test/utils/test_events.h" +#include "test/utils/test_extractor.h" + +#include "agent/debugger_impl.h" + #include "ecmascript/jspandafile/js_pandafile_manager.h" -#include "ecmascript/tooling/agent/debugger_impl.h" -#include "ecmascript/tooling/backend/js_debugger.h" -#include "ecmascript/tooling/test/utils/test_events.h" -#include "ecmascript/tooling/test/utils/test_extractor.h" +#include "ecmascript/debugger/js_debugger.h" #include "os/mutex.h" namespace panda::ecmascript::tooling::test { -- Gitee