From c3a961265edd4953cd1434d2b0d26338bf3cc829 Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Mon, 5 Dec 2022 03:32:42 +0300 Subject: [PATCH 1/3] Define InspectorExtension specialization This adds implementation of the InspectorExtension interface to convert values to Inspector representations. Signed-off-by: Eugene Sharygin --- runtime/CMakeLists.txt | 1 + runtime/ecma_language_context.cpp | 7 +- runtime/ecma_language_context.h | 2 + runtime/js_function.cpp | 7 + runtime/js_function.h | 1 + .../inspector/ecma_inspector_extension.cpp | 181 ++++++++++++++++++ .../inspector/ecma_inspector_extension.h | 100 ++++++++++ subproject_sources.gn | 1 + 8 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 runtime/tooling/inspector/ecma_inspector_extension.cpp create mode 100644 runtime/tooling/inspector/ecma_inspector_extension.h diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 521da2c3b..4f7f9ab90 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -188,6 +188,7 @@ set(ECMASCRIPT_SOURCES ${ECMA_SRC_DIR}/compiler/ecmascript_runtime_interface.cpp ${BUILTIN_BRIDGE_SOURCE} + ${ECMA_SRC_DIR}/tooling/inspector/ecma_inspector_extension.cpp ${ECMA_SRC_DIR}/tooling/pt_ecmascript_extension.cpp ) diff --git a/runtime/ecma_language_context.cpp b/runtime/ecma_language_context.cpp index 3ea79ddac..32d3c2ed6 100644 --- a/runtime/ecma_language_context.cpp +++ b/runtime/ecma_language_context.cpp @@ -22,9 +22,9 @@ #include "plugins/ecmascript/runtime/base/error_type.h" #include "plugins/ecmascript/runtime/js_method.h" #include "plugins/ecmascript/runtime/js_object.h" -#include "plugins/ecmascript/runtime/js_tagged_value.h" #include "plugins/ecmascript/runtime/js_thread.h" #include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tooling/inspector/ecma_inspector_extension.h" #include "include/method.h" #include "runtime/core/core_itable_builder.h" #include "runtime/core/core_vtable_builder.h" @@ -67,6 +67,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 a07ca51a0..11d72b13a 100644 --- a/runtime/ecma_language_context.h +++ b/runtime/ecma_language_context.h @@ -41,6 +41,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/js_function.cpp b/runtime/js_function.cpp index d9cfaf6f2..343c33de8 100644 --- a/runtime/js_function.cpp +++ b/runtime/js_function.cpp @@ -404,6 +404,13 @@ bool JSFunctionBase::SetFunctionName(JSThread *thread, const JSHandle JSFunction::GetFunctionLength(JSThread *thread, const JSHandle &func) +{ + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + + return JSObject::GetProperty(thread, JSHandle(func), lengthKey).GetValue(); +} + bool JSFunction::SetFunctionLength(JSThread *thread, const JSHandle &func, JSTaggedValue length, bool cfg) { ASSERT_PRINT(func->IsExtensible(), "Function must be extensible"); diff --git a/runtime/js_function.h b/runtime/js_function.h index eb977f082..fbc8249c0 100644 --- a/runtime/js_function.h +++ b/runtime/js_function.h @@ -83,6 +83,7 @@ public: static bool AddRestrictedFunctionProperties(const JSHandle &func, const JSHandle &realm); static bool MakeConstructor(JSThread *thread, const JSHandle &func, const JSHandle &proto, bool writable = true); + static JSHandle GetFunctionLength(JSThread *thread, const JSHandle &func); static bool SetFunctionLength(JSThread *thread, const JSHandle &func, JSTaggedValue length, bool cfg = true); static JSHandle NewJSFunctionPrototype(JSThread *thread, ObjectFactory *factory, diff --git a/runtime/tooling/inspector/ecma_inspector_extension.cpp b/runtime/tooling/inspector/ecma_inspector_extension.cpp new file mode 100644 index 000000000..4848eb49b --- /dev/null +++ b/runtime/tooling/inspector/ecma_inspector_extension.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecma_inspector_extension.h" + +#include "ecma_string.h" +#include "global_env.h" +#include "include/tooling/inspector/object_repository.h" +#include "include/tooling/inspector/property_descriptor.h" +#include "include/tooling/inspector/remote_object.h" +#include "js_array.h" +#include "js_date.h" +#include "js_handle.h" +#include "js_function.h" +#include "js_object.h" +#include "js_symbol.h" +#include "js_tagged_value.h" +#include "js_thread.h" +#include "macros.h" +#include "object_wrapper.h" + +#include +#include +#include + +using panda::ecmascript::EcmaString; +using panda::ecmascript::JSArray; +using panda::ecmascript::JSDate; +using panda::ecmascript::JSFunction; +using panda::ecmascript::JSFunctionBase; +using panda::ecmascript::JSHandle; +using panda::ecmascript::JSObject; +using panda::ecmascript::JSSymbol; +using panda::ecmascript::JSTaggedValue; +using panda::ecmascript::JSThread; +using panda::ecmascript::ObjectWrapper; +using panda::ecmascript::TaggedObject; + +namespace panda::tooling::inspector::ecmascript { +EcmaInspectorExtension::ObjectConverter::ObjectConverter(ManagedThread *thread, ObjectRepository &object_repository) + : thread_(JSThread::Cast(thread)), + exception_(thread_->GetException()), + handle_scope_(thread_), + object_repository_(object_repository) +{ + thread_->ClearException(); +} + +EcmaInspectorExtension::ObjectConverter::~ObjectConverter() +{ + // Reset pending exception + if (UNLIKELY(!exception_.IsHole() && !thread_->HasPendingException())) { + thread_->SetException(exception_); + } +} + +static std::string GetSymbolDescription(JSSymbol *symbol) +{ + auto description = symbol->GetDescription(); + if (description.IsUndefined()) { + return "Symbol()"; + } + + auto string = EcmaString::Cast(description.GetTaggedObject()); + return "Symbol(" + std::string(string->GetCString().get()) + ")"; +} + +RemoteObject EcmaInspectorExtension::ObjectConverter::GetRemoteObject(TaggedObject *object) +{ + auto [result_iter, inserted] = cache_.try_emplace(object, RemoteObject::Object("Object", {}, "[Object]")); + auto &result = result_iter->second; + if (!inserted) { + return result; + } + + auto cls = object->GetClass(); + + if (cls->IsJSGlobalObject()) { + result = RemoteObject::Object("Object", {}, "[Global Object]"); + } else if (cls->IsJSObject()) { + JSHandle js_object(thread_, object); + auto object_id = object_repository_.CreateObjectId(); + result = GetRemoteObject(object_id, js_object); + AddProperties(object_id, js_object); + } else if (cls->IsObjectWrapper()) { + result = GetRemoteObject(ObjectWrapper::Cast(object)->GetValue()); + } else if (cls->IsString()) { + result = RemoteObject::String(EcmaString::Cast(object)->GetCString().get()); + } else if (cls->IsSymbol()) { + result = RemoteObject::Symbol(GetSymbolDescription(JSSymbol::Cast(object))); + } + + return result; +} + +RemoteObject EcmaInspectorExtension::ObjectConverter::GetRemoteObject(RemoteObjectId object_id, + const JSHandle &object) +{ + auto constructor = JSHandle::Cast( + JSObject::SpeciesConstructor(thread_, object, thread_->GetEcmaVM()->GetGlobalEnv()->GetObjectFunction())); + + std::string class_name; + if (auto constructor_name = JSFunctionBase::GetFunctionName(thread_, constructor); constructor_name->IsString()) { + class_name = EcmaString::Cast(constructor_name->GetTaggedObject())->GetCString().get(); + } + + if (object->IsDate()) { + auto date = JSDate::Cast(*object); + auto description = EcmaString::Cast(date->ToString(thread_).GetTaggedObject()); + return RemoteObject::Object(std::move(class_name), object_id, description->GetCString().get()); + } + if (object->IsJSArray()) { + auto array = JSHandle::Cast(object); + return RemoteObject::Array(std::move(class_name), array->GetArrayLength(), object_id); + } + if (object->IsJSFunction()) { + auto function_base = JSHandle::Cast(object); + auto function = JSHandle::Cast(object); + + std::string name; + if (auto str = JSFunctionBase::GetFunctionName(thread_, function_base); str->IsString()) { + name = EcmaString::Cast(str->GetTaggedObject())->GetCString().get(); + } + + auto length = JSTaggedValue::ToLength(thread_, JSFunction::GetFunctionLength(thread_, function)).ToUint32(); + return RemoteObject::Function(std::move(class_name), std::move(name), length, object_id); + } + return RemoteObject::Object(class_name, object_id); +} + +void EcmaInspectorExtension::ObjectConverter::AddProperties(RemoteObjectId object_id, const JSHandle &object) +{ + PropertyDescriptor prototype("[[Prototype]]", GetRemoteObject(object->GetPrototype(thread_))); + prototype.SetEnumerable(false); + object_repository_.AddProperty(object_id, std::move(prototype)); + + auto keys = JSObject::GetOwnPropertyKeys(thread_, object); + for (auto key_idx = 0U; key_idx < keys->GetLength(); ++key_idx) { + auto key = keys->Get(key_idx).GetTaggedObject(); + + panda::ecmascript::PropertyDescriptor js_property(thread_); + if (UNLIKELY(!JSObject::GetOwnProperty(thread_, object, JSHandle(thread_, key), js_property))) { + continue; + } + + auto name = key->GetClass()->IsString() ? std::string(EcmaString::Cast(key)->GetCString().get()) + : GetSymbolDescription(JSSymbol::Cast(key)); + + std::optional property; + if (js_property.HasGetter()) { + property = PropertyDescriptor::Accessor(std::move(name), + GetRemoteObject(js_property.GetGetter().GetTaggedValue())); + } else { + property.emplace(std::move(name), GetRemoteObject(js_property.GetValue().GetTaggedValue()), + key_idx < object->GetNumberOfElements()); + property->SetWritable(js_property.IsWritable()); + } + + property->SetConfigurable(js_property.IsConfigurable()); + property->SetEnumerable(js_property.IsEnumerable()); + + if (key->GetClass()->IsSymbol()) { + property->SetSymbol(GetRemoteObject(key)); + } + + object_repository_.AddProperty(object_id, *std::move(property)); + } +} +} // namespace panda::tooling::inspector::ecmascript diff --git a/runtime/tooling/inspector/ecma_inspector_extension.h b/runtime/tooling/inspector/ecma_inspector_extension.h new file mode 100644 index 000000000..a4334d7ea --- /dev/null +++ b/runtime/tooling/inspector/ecma_inspector_extension.h @@ -0,0 +1,100 @@ +/* + * 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 PANDA_TOOLING_INSPECTOR_ECMASCRIPT_ECMA_INSPECTOR_EXTENSION_H +#define PANDA_TOOLING_INSPECTOR_ECMASCRIPT_ECMA_INSPECTOR_EXTENSION_H + +#include "ecma_handle_scope.h" +#include "include/tooling/inspector/inspector_extension.h" +#include "include/tooling/inspector/property_descriptor.h" +#include "include/tooling/inspector/remote_object.h" +#include "include/tooling/inspector/remote_object_id.h" +#include "include/tooling/pt_ecmascript_extension.h" +#include "include/tooling/vreg_value.h" +#include "js_handle.h" +#include "js_tagged_value.h" + +#include + +namespace panda { +class ManagedThread; + +namespace ecmascript { +class JSObject; +class JSThread; +class TaggedObject; +} // namespace ecmascript + +namespace tooling::inspector { +class ObjectRepository; + +namespace ecmascript { +class EcmaInspectorExtension : public InspectorExtension { +public: + RemoteObject GetRemoteObject(ManagedThread *thread, ObjectRepository &object_repository, + ObjectHeader *object) override + { + return ObjectConverter(thread, object_repository) + .GetRemoteObject(panda::ecmascript::TaggedObject::Cast(object)); + } + + std::optional GetVariableDescriptor(ManagedThread *thread, ObjectRepository &object_repository, + const std::string &name, + [[maybe_unused]] const std::string &signature, + VRegValue value) override + { + if (name == "=f" || name == "=nt") { + return {}; + } + return PropertyDescriptor(name == "=t" ? "this" : name, + ObjectConverter(thread, object_repository).GetRemoteObject(value)); + } + +private: + class ObjectConverter { + public: + ObjectConverter(ManagedThread *thread, ObjectRepository &object_repository); + ~ObjectConverter(); + + RemoteObject GetRemoteObject(VRegValue value) + { + return GetRemoteObject(panda::tooling::ecmascript::PtEcmaScriptExtension::VRegValueToTaggedValue(value)); + } + + RemoteObject GetRemoteObject(panda::ecmascript::JSTaggedValue value) + { + return value.IsHeapObject() ? GetRemoteObject(value.GetTaggedObject()) : GetRemoteObjectForPrimitive(value); + } + + RemoteObject GetRemoteObject(panda::ecmascript::TaggedObject *object); + + private: + RemoteObject GetRemoteObject(RemoteObjectId object_id, + const panda::ecmascript::JSHandle &object); + + void AddProperties(RemoteObjectId object_id, + const panda::ecmascript::JSHandle &object); + + panda::ecmascript::JSThread *thread_; + panda::ecmascript::JSTaggedValue exception_; + [[maybe_unused]] panda::ecmascript::EcmaHandleScope handle_scope_; + ObjectRepository &object_repository_; + std::unordered_map cache_; + }; +}; +} // namespace ecmascript +} // namespace tooling::inspector +} // namespace panda + +#endif // PANDA_TOOLING_INSPECTOR_ECMASCRIPT_ECMA_INSPECTOR_EXTENSION_H diff --git a/subproject_sources.gn b/subproject_sources.gn index 5aa6f9978..33d3f5025 100644 --- a/subproject_sources.gn +++ b/subproject_sources.gn @@ -212,6 +212,7 @@ srcs_runtime = [ "runtime/weak_vector.cpp", "runtime/class_info_extractor.cpp", "runtime/compiler/ecmascript_runtime_interface.cpp", + "runtime/tooling/inspector/ecma_inspector_extension.cpp", "runtime/tooling/pt_ecmascript_extension.cpp", "runtime/init_icu.cpp", ] -- Gitee From 734480590cbfecd2da56e84fb13dc86a38d66698 Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Tue, 22 Nov 2022 22:58:49 +0300 Subject: [PATCH 2/3] Emit ConsoleCallEvent from print global function Signed-off-by: Eugene Sharygin --- runtime/builtins/builtins_global.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/runtime/builtins/builtins_global.cpp b/runtime/builtins/builtins_global.cpp index 52e72c8be..161848ede 100644 --- a/runtime/builtins/builtins_global.cpp +++ b/runtime/builtins/builtins_global.cpp @@ -27,9 +27,18 @@ #include "plugins/ecmascript/runtime/js_invoker.h" #include "plugins/ecmascript/runtime/js_eval.h" #include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" #include "plugins/ecmascript/runtime/tagged_array-inl.h" #include "plugins/ecmascript/runtime/ecma_global_storage-inl.h" +#include "include/console_call_type.h" +#include "include/coretypes/tagged_value.h" +#include "include/mem/panda_containers.h" +#include "include/runtime.h" +#include "include/runtime_notification.h" #include "include/thread_scopes.h" +#include "utils/time.h" + +using panda::coretypes::TaggedValue; namespace panda::ecmascript::builtins { using NumberHelper = ecmascript::base::NumberHelper; @@ -721,8 +730,13 @@ JSTaggedValue BuiltinsGlobal::PrintEntrypoint(EcmaRuntimeCallInfo *msg) BUILTINS_API_TRACE(thread, Global, PrintEntryPoint); uint32_t numArgs = msg->GetArgsNumber(); + PandaVector arguments(numArgs); + for (uint32_t i = 0; i < numArgs; i++) { - JSHandle stringContent = JSTaggedValue::ToString(thread, GetCallArg(msg, i)); + auto arg = GetCallArg(msg, i); + arguments[i] = arg.GetTaggedValue(); + + JSHandle stringContent = JSTaggedValue::ToString(thread, arg); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); PrintString(thread, *stringContent); @@ -731,6 +745,10 @@ JSTaggedValue BuiltinsGlobal::PrintEntrypoint(EcmaRuntimeCallInfo *msg) } } std::cout << std::endl; + + Runtime::GetCurrent()->GetNotificationManager()->ConsoleCallEvent(thread, ConsoleCallType::LOG, + time::GetCurrentTimeInMillis(), arguments); + return JSTaggedValue::Undefined(); } -- Gitee From 987328249c9ce73e1d40bf7c45e58af5c77f7d7f Mon Sep 17 00:00:00 2001 From: Vladislav Ivanishin Date: Mon, 19 Dec 2022 13:17:28 +0300 Subject: [PATCH 3/3] remove [[maybe_unused]] for private member Signed-off-by: Vladislav Ivanishin --- runtime/tooling/inspector/ecma_inspector_extension.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tooling/inspector/ecma_inspector_extension.h b/runtime/tooling/inspector/ecma_inspector_extension.h index a4334d7ea..6f5d82d45 100644 --- a/runtime/tooling/inspector/ecma_inspector_extension.h +++ b/runtime/tooling/inspector/ecma_inspector_extension.h @@ -88,7 +88,7 @@ private: panda::ecmascript::JSThread *thread_; panda::ecmascript::JSTaggedValue exception_; - [[maybe_unused]] panda::ecmascript::EcmaHandleScope handle_scope_; + panda::ecmascript::EcmaHandleScope handle_scope_; ObjectRepository &object_repository_; std::unordered_map cache_; }; -- Gitee