diff --git a/BUILD.gn b/BUILD.gn
new file mode 100644
index 0000000000000000000000000000000000000000..1ab08af34533f264fc08c5da9180dc57e81c0cfb
--- /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 1373083473503be67b5a607b090bb9ab895fd61e..e735a971559318b2c03d533791ad15d08bed0d5a 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 0000000000000000000000000000000000000000..0746cd736e26c1040c0f6fd03d8ee52054e14ec4
--- /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 0000000000000000000000000000000000000000..a1a6254e8f4caba17e7474f438c340720e71df00
--- /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 0000000000000000000000000000000000000000..b9a20c8eb6a74b442cec3b244a433adbfa9414a2
--- /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 0000000000000000000000000000000000000000..bff19b9ec5878568b72aac0a886a57db8926d81a
--- /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/test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.h b/test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.h
new file mode 100644
index 0000000000000000000000000000000000000000..002d0073c374dafb397fc9b38fd56ce13d380743
--- /dev/null
+++ b/test/fuzztest/dispatchprotocolmessage_fuzzer/dispatchprotocolmessage_fuzzer.h
@@ -0,0 +1,21 @@
+/*
+ * 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 0000000000000000000000000000000000000000..f7de9064465d473bb8e10fc88f0764eaad2d6112
--- /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 0000000000000000000000000000000000000000..cfde7f605c40dbf091696740bb7d5bb62095ef49
--- /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 0000000000000000000000000000000000000000..b9a20c8eb6a74b442cec3b244a433adbfa9414a2
--- /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 0000000000000000000000000000000000000000..ab64a4efc8af15091e156376a192bd30acffd3a3
--- /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/test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.h b/test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.h
new file mode 100644
index 0000000000000000000000000000000000000000..2c2299cf1afd468b2ec879682eb3994500202455
--- /dev/null
+++ b/test/fuzztest/initializedebugger_fuzzer/initializedebugger_fuzzer.h
@@ -0,0 +1,21 @@
+/*
+ * 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 0000000000000000000000000000000000000000..1020c7b430f25037af4c1531f98400dad761bd28
--- /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 0000000000000000000000000000000000000000..5a3cdfc9a13baa28328969603d32a5772cb527aa
--- /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 0000000000000000000000000000000000000000..85064946944b67bf9e56af099a6378e9627bd434
--- /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 991c3d65ee16e51d5c2a6a2d5e1ee4912ac13aad..7cd13cfb7108e7c6ebce8c6cd173212de1cd2232 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
new file mode 100644
index 0000000000000000000000000000000000000000..8031de1f4c98984c4fe4f5c71454ce93a3568068
--- /dev/null
+++ b/tooling/BUILD.gn
@@ -0,0 +1,159 @@
+# 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/cJSON",
+ ]
+}
+
+debugger_sources = [
+ "agent/debugger_impl.cpp",
+ "agent/runtime_impl.cpp",
+ "agent/tracing_impl.cpp",
+ "backend/debugger_executor.cpp",
+ "backend/js_pt_hooks.cpp",
+ "backend/js_single_stepper.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 = "toolchain"
+ 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 0000000000000000000000000000000000000000..07bdf97212ee5fa27f318d71bb5a1454a1a2e902
--- /dev/null
+++ b/tooling/agent/debugger_impl.cpp
@@ -0,0 +1,1189 @@
+/*
+ * 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 "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"
+namespace panda::ecmascript::tooling {
+using namespace std::placeholders;
+
+using ObjectType = RemoteObject::TypeName;
+using ObjectSubType = RemoteObject::SubTypeName;
+using ObjectClassName = RemoteObject::ClassName;
+using StepperType = 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;
+ }
+
+ DebugInfoExtractor *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)
+{
+ DebugInfoExtractor *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 == DebugInfoExtractor::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;
+ DebugInfoExtractor *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.");
+ }
+ DebugInfoExtractor *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");
+ }
+ DebugInfoExtractor *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 {};
+
+ DebugInfoExtractor *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)
+{
+ singleStepper_ = SingleStepper::GetStepIntoStepper(vm_);
+ if (singleStepper_ == nullptr) {
+ LOG_DEBUGGER(ERROR) << "StepInto: singleStepper is null";
+ return DispatchResponse::Fail("Failed to StepInto");
+ }
+ frontend_.Resumed(vm_);
+ return DispatchResponse::Ok();
+}
+
+DispatchResponse DebuggerImpl::StepOut()
+{
+ singleStepper_ = SingleStepper::GetStepOutStepper(vm_);
+ if (singleStepper_ == nullptr) {
+ LOG_DEBUGGER(ERROR) << "StepOut: singleStepper is null";
+ return DispatchResponse::Fail("Failed to StepOut");
+ }
+ frontend_.Resumed(vm_);
+ return DispatchResponse::Ok();
+}
+
+DispatchResponse DebuggerImpl::StepOver([[maybe_unused]] const StepOverParams ¶ms)
+{
+ singleStepper_ = SingleStepper::GetStepOverStepper(vm_);
+ if (singleStepper_ == nullptr) {
+ LOG_DEBUGGER(ERROR) << "StepOver: singleStepper is null";
+ return DispatchResponse::Fail("Failed to StepOver");
+ }
+ 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;
+}
+
+DebugInfoExtractor *DebuggerImpl::GetExtractor(const JSPandaFile *jsPandaFile)
+{
+ return JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
+}
+
+DebugInfoExtractor *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();
+ DebugInfoExtractor *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();
+ DebugInfoExtractor *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.";
+ }
+ DebugInfoExtractor *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 0000000000000000000000000000000000000000..8877f0df11b2642226f92506c6e6869f6bd50185
--- /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 "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 {
+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);
+ 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);
+ 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 0000000000000000000000000000000000000000..b87babf2d020b8e949a1dba81d961fd75a78a1ba
--- /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 "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 0000000000000000000000000000000000000000..80eca16ff8faf09b1d5b96b3b20e221e316879d8
--- /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 "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
+
+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 0000000000000000000000000000000000000000..0cfca7199cb30213d99ac6a4717fefb191233f50
--- /dev/null
+++ b/tooling/agent/profiler_impl.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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 "agent/profiler_impl.h"
+
+#include "base/pt_events.h"
+#include "protocol_channel.h"
+
+#include "ecmascript/napi/include/dfx_jsnapi.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 0000000000000000000000000000000000000000..ec165dee3b171885b21d9069456ce0ccbe5a12fe
--- /dev/null
+++ b/tooling/agent/profiler_impl.h
@@ -0,0 +1,96 @@
+/*
+ * 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 "base/pt_params.h"
+#include "base/pt_returns.h"
+#include "dispatcher.h"
+
+#include "ecmascript/dfx/cpu_profiler/samples_record.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 0000000000000000000000000000000000000000..d0c383423a55235ab6a6d73861a7e643d809755d
--- /dev/null
+++ b/tooling/agent/runtime_impl.cpp
@@ -0,0 +1,622 @@
+/*
+ * 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 "agent/runtime_impl.h"
+
+#include
+
+#include "base/pt_returns.h"
+#include "protocol_channel.h"
+
+#include "ecmascript/napi/include/dfx_jsnapi.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