From 30e80636cb234c16e5d2bd86ccec91e73f3c3ea8 Mon Sep 17 00:00:00 2001 From: Dmitry Pimenov Date: Mon, 27 Nov 2023 11:07:09 +0300 Subject: [PATCH] Add the ECMA extension for the debugger inspector Change-Id: l4f56a147f32dd5990311be6f491d981b1996c172 Signed-off-by: Dmitry Pimenov --- runtime/CMakeLists.txt | 1 + runtime/ecma_language_context.cpp | 7 +- runtime/ecma_language_context.h | 2 + runtime/ecma_string.h | 2 +- runtime/ecma_vm.h | 2 +- runtime/js_function.cpp | 6 + runtime/js_function.h | 1 + runtime/js_object.cpp | 6 +- runtime/js_object.h | 2 +- runtime/mem/tagged_object.h | 4 + runtime/tooling/ecma_inspector_extension.cpp | 177 +++++++++++++++++++ runtime/tooling/ecma_inspector_extension.h | 44 +++++ 12 files changed, 247 insertions(+), 7 deletions(-) create mode 100644 runtime/tooling/ecma_inspector_extension.cpp create mode 100644 runtime/tooling/ecma_inspector_extension.h diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index e3f0a6ab3..9585cde3e 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -180,6 +180,7 @@ set(ECMASCRIPT_SOURCES ${BUILTIN_BRIDGE_SOURCE} ${ECMA_SRC_DIR}/tooling/pt_ecmascript_extension.cpp + ${ECMA_SRC_DIR}/tooling/ecma_inspector_extension.cpp ) add_dependencies(arkruntime_obj ecmastdlib_inline_h) diff --git a/runtime/ecma_language_context.cpp b/runtime/ecma_language_context.cpp index 8f1fe7006..723e936ca 100644 --- a/runtime/ecma_language_context.cpp +++ b/runtime/ecma_language_context.cpp @@ -14,7 +14,6 @@ */ #include "plugins/ecmascript/runtime/ecma_language_context-inl.h" -#include "plugins/ecmascript/runtime/ecma_language_context.h" #include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" #include "plugins/ecmascript/runtime/ecma_class_linker_extension.h" @@ -32,6 +31,7 @@ #include "include/method.h" #include "runtime/core/core_itable_builder.h" #include "runtime/core/core_vtable_builder.h" +#include "plugins/ecmascript/runtime/tooling/ecma_inspector_extension.h" namespace panda { using ecmascript::EcmaVM; @@ -71,6 +71,11 @@ std::unique_ptr EcmaLanguageContext::CreateClassLinkerExte return std::make_unique(); } +std::unique_ptr EcmaLanguageContext::CreateInspectorExtension() const +{ + return std::make_unique(); +} + PandaUniquePtr EcmaLanguageContext::CreatePtLangExt() const { return PandaUniquePtr(); diff --git a/runtime/ecma_language_context.h b/runtime/ecma_language_context.h index 3b0694045..fa18138b7 100644 --- a/runtime/ecma_language_context.h +++ b/runtime/ecma_language_context.h @@ -47,6 +47,8 @@ public: std::unique_ptr CreateClassLinkerExtension() const override; + std::unique_ptr CreateInspectorExtension() const override; + PandaUniquePtr CreatePtLangExt() const override; void ThrowException(ManagedThread *thread, const uint8_t *mutf8_name, const uint8_t *mutf8_msg) const override; diff --git a/runtime/ecma_string.h b/runtime/ecma_string.h index 071722ce2..5d284ec82 100644 --- a/runtime/ecma_string.h +++ b/runtime/ecma_string.h @@ -193,7 +193,7 @@ public: } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - inline std::unique_ptr GetCString() + inline std::unique_ptr GetCString() const { auto length = GetUtf8Length(); char *buf = new char[length](); diff --git a/runtime/ecma_vm.h b/runtime/ecma_vm.h index 199c6c8b5..3185e2a4b 100644 --- a/runtime/ecma_vm.h +++ b/runtime/ecma_vm.h @@ -85,7 +85,7 @@ using HostPromiseRejectionTracker = void (*)(const EcmaVM *vm, const JSHandle return JSTaggedValue::DefinePropertyOrThrow(thread, func_handle, length_key_handle, length_desc); } +JSHandle JSFunction::GetFunctionLength(JSThread *thread, const JSHandle &func) +{ + const auto key = thread->GlobalConstants()->GetHandledLengthString(); + return JSObject::GetProperty(thread, JSHandle(func), key).GetValue(); +} + // 9.4.1.2[[Construct]](argumentsList, new_target) JSTaggedValue JSBoundFunction::ConstructInternal(EcmaRuntimeCallInfo *info) { diff --git a/runtime/js_function.h b/runtime/js_function.h index f1c5afc86..666e09975 100644 --- a/runtime/js_function.h +++ b/runtime/js_function.h @@ -85,6 +85,7 @@ public: const JSHandle &proto, bool writable = true); static bool SetFunctionLength(JSThread *thread, const JSHandle &func, JSTaggedValue length, bool cfg = true); + static JSHandle GetFunctionLength(JSThread *thread, const JSHandle &func); static JSHandle NewJSFunctionPrototype(JSThread *thread, ObjectFactory *factory, const JSHandle &func); static DynClass *GetOrCreateInitialDynClass(JSThread *thread, const JSHandle &fun); diff --git a/runtime/js_object.cpp b/runtime/js_object.cpp index 3da751356..69849858f 100644 --- a/runtime/js_object.cpp +++ b/runtime/js_object.cpp @@ -438,11 +438,11 @@ bool JSObject::GlobalSetProperty(JSThread *thread, const JSHandle return SetProperty(&op, value, may_throw); } -uint32_t JSObject::GetNumberOfElements() +uint32_t JSObject::GetNumberOfElements() const { uint32_t num_of_elements = 0; - if (IsJSPrimitiveRef() && JSPrimitiveRef::Cast(this)->IsString()) { - num_of_elements = JSPrimitiveRef::Cast(this)->GetStringLength(); + if (IsJSPrimitiveRef() && JSPrimitiveRef::ConstCast(this)->IsString()) { + num_of_elements = JSPrimitiveRef::ConstCast(this)->GetStringLength(); } TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); diff --git a/runtime/js_object.h b/runtime/js_object.h index 92a11e620..581cf45fd 100644 --- a/runtime/js_object.h +++ b/runtime/js_object.h @@ -564,7 +564,7 @@ public: static void GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle &obj, std::vector &key_vector); uint32_t GetNumberOfKeys(); - uint32_t GetNumberOfElements(); + uint32_t GetNumberOfElements() const; static JSHandle GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, uint32_t num_of_elements, uint32_t *keys); diff --git a/runtime/mem/tagged_object.h b/runtime/mem/tagged_object.h index 20fa7ac11..c6e2cae68 100644 --- a/runtime/mem/tagged_object.h +++ b/runtime/mem/tagged_object.h @@ -30,6 +30,10 @@ public: { return static_cast(header); } + static const TaggedObject *Cast(const ObjectHeader *header) + { + return static_cast(header); + } void SetClassWithoutBarrier(JSHClass *hclass); void SetClass(JSHandle hclass); diff --git a/runtime/tooling/ecma_inspector_extension.cpp b/runtime/tooling/ecma_inspector_extension.cpp new file mode 100644 index 000000000..8c7aa608e --- /dev/null +++ b/runtime/tooling/ecma_inspector_extension.cpp @@ -0,0 +1,177 @@ +/** + * Copyright (c) 2023 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 "ecma_inspector_extension.h" + +#include "js_object.h" +#include "runtime/include/hclass.h" +#include "runtime/include/mem/panda_string.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/mem/tagged_object-inl.h" + +namespace panda::ecmascript::tooling { + +namespace { + +std::string ToStdString(const EcmaString &value) +{ + return std::string {reinterpret_cast(value.GetDataUtf8()), value.GetUtf8Length() - 1}; +} + +std::optional ToStdString(const TaggedObject *object) +{ + if (!object) { + return {}; + } + const auto jshClass = object->GetClass(); + if (jshClass->IsString()) { + auto ecmaStr = EcmaString::ConstCast(object); + return ToStdString(*ecmaStr); + } + if (jshClass->IsSymbol()) { + const auto sym = JSSymbol::ConstCast(object); + const auto ecmaStr = EcmaString::ConstCast(sym->GetDescription().GetTaggedObject()); + return ToStdString(*ecmaStr); + } + return {}; +} + +std::optional ToStdString(const JSTaggedValue &value) +{ + if (!value.IsHeapObject()) { + return {}; + } + return ToStdString(value.GetTaggedObject()); +} + +TypedValue ToTypedValue(const JSTaggedValue &value) +{ + if (value.IsException()) { + return TypedValue::Invalid(); + } + return TypedValue::Tagged(value); +} + +} // namespace + +std::string EcmaInspectorExtension::GetClassName([[maybe_unused]] const ObjectHeader *object) +{ + if (!object) { + return {}; + } + const JSTaggedValue value {object}; + if (value.IsJSFunctionBase()) { + return "Function"; + } + if (value.IsJSObject()) { + return "Object"; + } + return {}; +} + +std::optional EcmaInspectorExtension::GetAsString(const ObjectHeader *object) +{ + if (!object) { + return {}; + } + const JSTaggedValue value {object}; + if (!value.IsString()) { + return {}; + } + if (const auto ecmaStr = EcmaString::ConstCast(value.GetTaggedObject())) { + const auto result = ToStdString(*ecmaStr); + return std::move(result); + } + return {}; +} + +std::optional EcmaInspectorExtension::GetLengthIfArray(const ObjectHeader *object) +{ + if (!object) { + return {}; + } + const JSTaggedValue value {object}; + if (!value.IsJSArray()) { + return {}; + } + return JSObject::ConstCast(object)->GetNumberOfElements(); +} + +std::optional EcmaInspectorExtension::GetAsFunction(const ObjectHeader *object) +{ + if (!object) { + return {}; + } + if (!TaggedObject::Cast(object)->GetClass()->IsJSFunction()) { + return {}; + } + const auto thread = JSThread::GetCurrent(); + const JSHandle js_func {thread, JSFunction::ConstCast(object)}; + const auto js_name = JSFunctionBase::GetFunctionName(thread, JSHandle::Cast(js_func)); + if (!js_name->IsString()) { + return {}; + } + const auto name = ToStdString(js_name->GetTaggedObject()); + const auto length = JSFunction::GetFunctionLength(thread, js_func)->GetInt(); + return Function {"Function", name ? *name : "", size_t(length)}; +} + +void EcmaInspectorExtension::EnumerateProperties(const ObjectHeader *object, const PropertyHandler &handler) +{ + if (!object) { + return; + } + const auto thread = JSThread::GetCurrent(); + const EcmaHandleScope scope(thread); + const JSTaggedValue value {object}; + if (!value.IsJSObject()) { + return; + } + const JSHandle js_object {thread, value.GetTaggedObject()}; + const auto keys = JSObject::GetOwnPropertyKeys(thread, js_object); + const auto keys_count = keys->GetLength(); + for (uint32_t i = 0; i < keys_count; ++i) { + const auto key = keys->Get(i); + const std::optional name = ToStdString(key); + if (name) { + const JSHandle keyHandle {thread, key}; + const auto result = JSObject::GetProperty(thread, js_object, keyHandle).GetValue().GetTaggedValue(); + handler(*name, ToTypedValue(result), false, false, false); + } + } +} + +void EcmaInspectorExtension::EnumerateGlobals(const PropertyHandler &handler) +{ + auto globalEnv = JSThread::GetCurrent()->GetEcmaVM()->GetGlobalEnv(); + EnumerateProperties(globalEnv->GetJSGlobalObject()->GetTaggedObject(), handler); + return; +} + +std::optional EcmaInspectorExtension::FrameLocalFilterByName(const std::string &name) +{ + if (name == "=t") { + return {"this"}; + } else if (name == "=nt") { + return {}; + } else if (name == "=f") { + return {}; + } + return name; +} + +} // namespace panda::ecmascript::tooling diff --git a/runtime/tooling/ecma_inspector_extension.h b/runtime/tooling/ecma_inspector_extension.h new file mode 100644 index 000000000..c55b5fbd9 --- /dev/null +++ b/runtime/tooling/ecma_inspector_extension.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2023 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_RUNTIME_TOOLING_INSPECTOR_EXTENSION_H +#define ECMASCRIPT_RUNTIME_TOOLING_INSPECTOR_EXTENSION_H + +#include "runtime/include/tooling/inspector_extension.h" + +namespace panda::ecmascript::tooling { + +using ::panda::tooling::InspectorExtension; + +class EcmaInspectorExtension : public InspectorExtension { +public: + EcmaInspectorExtension() = default; + ~EcmaInspectorExtension() override = default; + + NO_COPY_SEMANTIC(EcmaInspectorExtension); + NO_MOVE_SEMANTIC(EcmaInspectorExtension); + + std::string GetClassName(const ObjectHeader *object) override; + std::optional GetAsString(const ObjectHeader *object) override; + std::optional GetLengthIfArray(const ObjectHeader *object) override; + std::optional GetAsFunction(const ObjectHeader *object) override; + void EnumerateProperties(const ObjectHeader *object, const PropertyHandler &handler) override; + void EnumerateGlobals(const PropertyHandler &handler) override; + std::optional FrameLocalFilterByName(const std::string &name) override; +}; + +} // namespace panda::ecmascript::tooling + +#endif // ECMASCRIPT_RUNTIME_TOOLING_INSPECTOR_EXTENSION_H -- Gitee