From 812aa5d2c8a3559df2bd5d116c68bb4a83fd68eb Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Fri, 26 Aug 2022 05:57:42 +0300 Subject: [PATCH 1/4] Implement GetInspectorPropertyDescriptor in EcmaLanguageContext This implements a conversion function from VRegValues associated with variables to the structured Inspector protocol representation, enabling EcmaScript variables to be exposed through Inspector debugger. Signed-off-by: Eugene Sharygin --- runtime/ecma_language_context.cpp | 205 ++++++++++++++++++++++++++++++ runtime/ecma_language_context.h | 6 + runtime/js_function.cpp | 7 + runtime/js_function.h | 1 + 4 files changed, 219 insertions(+) diff --git a/runtime/ecma_language_context.cpp b/runtime/ecma_language_context.cpp index 3ea79ddac..f42d50c6f 100644 --- a/runtime/ecma_language_context.cpp +++ b/runtime/ecma_language_context.cpp @@ -17,21 +17,53 @@ #include "plugins/ecmascript/runtime/ecma_language_context.h" #include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" +#include "plugins/ecmascript/runtime/global_env.h" #include "plugins/ecmascript/runtime/ecma_class_linker_extension.h" #include "plugins/ecmascript/runtime/ecma_exceptions.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_string.h" #include "plugins/ecmascript/runtime/base/error_type.h" +#include "plugins/ecmascript/runtime/include/tooling/pt_ecmascript_extension.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" #include "plugins/ecmascript/runtime/js_method.h" #include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_symbol.h" #include "plugins/ecmascript/runtime/js_tagged_value.h" #include "plugins/ecmascript/runtime/js_thread.h" #include "plugins/ecmascript/runtime/object_factory.h" #include "include/method.h" +#include "include/tooling/inspector/object_repository.h" +#include "include/tooling/inspector/property_descriptor.h" +#include "include/tooling/inspector/remote_object.h" +#include "include/tooling/inspector/remote_object_id.h" #include "runtime/core/core_itable_builder.h" #include "runtime/core/core_vtable_builder.h" +#include +#include +#include +#include +#include +#include + namespace panda { +using ecmascript::EcmaString; using ecmascript::EcmaVM; +using ecmascript::JSArray; +using ecmascript::JSDate; +using ecmascript::JSFunction; +using ecmascript::JSFunctionBase; +using ecmascript::JSHandle; +using ecmascript::JSObject; +using ecmascript::JSSymbol; +using ecmascript::JSTaggedValue; using ecmascript::JSThread; +using tooling::inspector::ObjectRepository; +using tooling::inspector::RemoteObject; +using tooling::inspector::RemoteObjectId; std::pair EcmaLanguageContext::GetCatchMethodAndOffset(Method *method, ManagedThread *thread) const { @@ -53,6 +85,179 @@ std::pair EcmaLanguageContext::GetCatchMethodAndOffset(Metho return std::make_pair(catchMethod, catchOffset); } +static std::string GetSymbolDescription(const JSSymbol *symbol) +{ + if (symbol->GetDescription().IsUndefined()) { + return "Symbol()"; + } + + auto description = EcmaString::Cast(symbol->GetDescription().GetTaggedObject()); + return "Symbol(" + std::string(description->GetCString().get()) + ")"; +} + +struct JSTaggedValueHash { +public: + size_t operator()(const JSTaggedValue &tagged_value) const + { + return tagged_value.GetRawData(); + } +}; + +using ObjectCache = std::unordered_map, JSTaggedValueHash>; + +static std::optional GetInspectorObject(JSThread *thread, ObjectRepository &object_repository, + ObjectCache &object_cache, JSHandle value); + +static std::optional GetInspectorPropertyDescriptor( + JSThread *thread, ObjectRepository &object_repository, ObjectCache &object_cache, JSHandle object, + JSHandle key, bool is_entry) +{ + ecmascript::PropertyDescriptor property(thread); + if (!JSObject::GetOwnProperty(thread, object, key, property)) { + return {}; + } + + std::string name; + if (key->IsString()) { + name = EcmaString::Cast(key->GetTaggedObject())->GetCString().get(); + } else if (key->IsSymbol()) { + name = GetSymbolDescription(JSSymbol::Cast(key->GetTaggedObject())); + } else { + return {}; + } + + std::optional desc; + if (property.HasGetter()) { + if (auto getter = GetInspectorObject(thread, object_repository, object_cache, property.GetGetter())) { + desc = tooling::inspector::PropertyDescriptor::Accessor(std::move(name), *std::move(getter)); + } + } else if (auto value = GetInspectorObject(thread, object_repository, object_cache, property.GetValue())) { + desc.emplace(std::move(name), *std::move(value), is_entry); + desc->SetWritable(property.IsWritable()); + } + + if (!desc) { + return {}; + } + + desc->SetConfigurable(property.IsConfigurable()); + desc->SetEnumerable(property.IsEnumerable()); + + if (key->IsSymbol()) { + if (auto symbol = GetInspectorObject(thread, object_repository, object_cache, key)) { + desc->SetSymbol(*std::move(symbol)); + } + } + + return desc; +} + +static RemoteObject GetInspectorObject(JSThread *thread, JSHandle object, RemoteObjectId object_id) +{ + 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()); + } else if (object->IsJSArray()) { + auto array = JSHandle::Cast(object); + return RemoteObject::Array(std::move(class_name), array->GetArrayLength(), object_id); + } else 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(std::move(class_name), object_id); +} + +static std::optional GetInspectorObject(JSThread *thread, ObjectRepository &object_repository, + ObjectCache &object_cache, JSHandle value) +{ + auto [result_iter, inserted] = object_cache.try_emplace(**value); + auto &result = result_iter->second; + if (!inserted) { + return result; + } + + if (value->IsUndefined()) { + return result = RemoteObject::Undefined(); + } else if (value->IsNull()) { + return result = RemoteObject::Null(); + } else if (value->IsBoolean()) { + return result = RemoteObject::Boolean(value->IsTrue()); + } else if (value->IsNumber()) { + return result = RemoteObject::Number(value->GetNumber()); + } else if (value->IsString()) { + return result = RemoteObject::String(EcmaString::Cast(value->GetTaggedObject())->GetCString().get()); + } else if (value->IsSymbol()) { + return result = RemoteObject::Symbol(GetSymbolDescription(JSSymbol::Cast(value->GetTaggedObject()))); + } else if (value->IsJSGlobalObject()) { + return result = RemoteObject::Object("Object", std::nullopt, "[Global Object]"); + } + + if (!value->IsJSObject()) { + return {}; + } + + auto object = JSHandle::Cast(value); + auto object_id = object_repository.CreateObjectId(); + result = GetInspectorObject(thread, object, object_id); + + if (auto prototype = GetInspectorObject(thread, object_repository, object_cache, + JSHandle(thread, object->GetPrototype(thread)))) { + tooling::inspector::PropertyDescriptor prototype_desc("[[Prototype]]", *std::move(prototype)); + prototype_desc.SetEnumerable(false); + + object_repository.AddProperty(object_id, std::move(prototype_desc)); + } + + auto keys = JSObject::GetOwnPropertyKeys(thread, object); + for (auto key_idx = 0U; key_idx < keys->GetLength(); ++key_idx) { + if (auto property = GetInspectorPropertyDescriptor(thread, object_repository, object_cache, object, + JSHandle(thread, keys->Get(key_idx)), + key_idx < object->GetNumberOfElements())) { + object_repository.AddProperty(object_id, *std::move(property)); + } + } + + return result; +} + +std::optional EcmaLanguageContext::GetInspectorPropertyDescriptor( + ManagedThread *thread, ObjectRepository &object_repository, const std::string &name, + [[maybe_unused]] const std::string &signature, tooling::VRegValue value) const +{ + if (name == "=f" || name == "=nt") { + return {}; + } + + JSHandle tagged_value(JSThread::Cast(thread), + tooling::ecmascript::PtEcmaScriptExtension::VRegValueToTaggedValue(value)); + ObjectCache object_cache; + + if (auto object = GetInspectorObject(JSThread::Cast(thread), object_repository, object_cache, tagged_value)) { + return tooling::inspector::PropertyDescriptor(name == "=t" ? "this" : name, *std::move(object)); + } else { + return {}; + } +} + PandaVM *EcmaLanguageContext::CreateVM(Runtime *runtime, const RuntimeOptions &options) const { auto ret = EcmaVM::Create(runtime, ecmascript::JSRuntimeOptions::Cast(options)); diff --git a/runtime/ecma_language_context.h b/runtime/ecma_language_context.h index a07ca51a0..dffa6671a 100644 --- a/runtime/ecma_language_context.h +++ b/runtime/ecma_language_context.h @@ -21,6 +21,8 @@ #include "plugins/ecmascript/runtime/js_hclass.h" namespace panda { +class ManagedThread; + class PUBLIC_API EcmaLanguageContext : public LanguageContextBase { public: EcmaLanguageContext() = default; @@ -37,6 +39,10 @@ public: std::pair GetCatchMethodAndOffset(Method *method, ManagedThread *thread) const override; + std::optional GetInspectorPropertyDescriptor( + ManagedThread *thread, tooling::inspector::ObjectRepository &object_repository, const std::string &name, + const std::string &signature, tooling::VRegValue value) const override; + PandaVM *CreateVM(Runtime *runtime, const RuntimeOptions &options) const override; std::unique_ptr CreateClassLinkerExtension() 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, -- Gitee From dc693d1f3e47cbad57d3bfbafabc800bd9df43ce Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Tue, 22 Nov 2022 00:37:51 +0300 Subject: [PATCH 2/4] Expose ToInspectorObject as a static member of JSTaggedValue and JSObject This commit refactors EcmaLanguageContext implementation by defining ToInspectorObject as a static member function on JSTaggedValue and JSObject classes. Signed-off-by: Eugene Sharygin --- runtime/ecma_language_context.cpp | 196 ++---------------------------- runtime/js_object.cpp | 130 ++++++++++++++++++++ runtime/js_object.h | 10 ++ runtime/js_tagged_value.cpp | 58 +++++++++ runtime/js_tagged_value.h | 12 ++ 5 files changed, 218 insertions(+), 188 deletions(-) diff --git a/runtime/ecma_language_context.cpp b/runtime/ecma_language_context.cpp index f42d50c6f..750b202d7 100644 --- a/runtime/ecma_language_context.cpp +++ b/runtime/ecma_language_context.cpp @@ -17,53 +17,30 @@ #include "plugins/ecmascript/runtime/ecma_language_context.h" #include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" -#include "plugins/ecmascript/runtime/global_env.h" #include "plugins/ecmascript/runtime/ecma_class_linker_extension.h" #include "plugins/ecmascript/runtime/ecma_exceptions.h" -#include "plugins/ecmascript/runtime/ecma_macros.h" -#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_handle_scope.h" #include "plugins/ecmascript/runtime/base/error_type.h" #include "plugins/ecmascript/runtime/include/tooling/pt_ecmascript_extension.h" -#include "plugins/ecmascript/runtime/js_array.h" -#include "plugins/ecmascript/runtime/js_date.h" -#include "plugins/ecmascript/runtime/js_function.h" #include "plugins/ecmascript/runtime/js_handle.h" #include "plugins/ecmascript/runtime/js_method.h" #include "plugins/ecmascript/runtime/js_object.h" -#include "plugins/ecmascript/runtime/js_symbol.h" #include "plugins/ecmascript/runtime/js_tagged_value.h" #include "plugins/ecmascript/runtime/js_thread.h" #include "plugins/ecmascript/runtime/object_factory.h" #include "include/method.h" -#include "include/tooling/inspector/object_repository.h" #include "include/tooling/inspector/property_descriptor.h" -#include "include/tooling/inspector/remote_object.h" -#include "include/tooling/inspector/remote_object_id.h" #include "runtime/core/core_itable_builder.h" #include "runtime/core/core_vtable_builder.h" -#include -#include -#include -#include -#include -#include - namespace panda { -using ecmascript::EcmaString; +using ecmascript::EcmaHandleScope; using ecmascript::EcmaVM; -using ecmascript::JSArray; -using ecmascript::JSDate; -using ecmascript::JSFunction; -using ecmascript::JSFunctionBase; using ecmascript::JSHandle; -using ecmascript::JSObject; -using ecmascript::JSSymbol; using ecmascript::JSTaggedValue; using ecmascript::JSThread; +using tooling::ecmascript::PtEcmaScriptExtension; using tooling::inspector::ObjectRepository; -using tooling::inspector::RemoteObject; -using tooling::inspector::RemoteObjectId; std::pair EcmaLanguageContext::GetCatchMethodAndOffset(Method *method, ManagedThread *thread) const { @@ -85,160 +62,6 @@ std::pair EcmaLanguageContext::GetCatchMethodAndOffset(Metho return std::make_pair(catchMethod, catchOffset); } -static std::string GetSymbolDescription(const JSSymbol *symbol) -{ - if (symbol->GetDescription().IsUndefined()) { - return "Symbol()"; - } - - auto description = EcmaString::Cast(symbol->GetDescription().GetTaggedObject()); - return "Symbol(" + std::string(description->GetCString().get()) + ")"; -} - -struct JSTaggedValueHash { -public: - size_t operator()(const JSTaggedValue &tagged_value) const - { - return tagged_value.GetRawData(); - } -}; - -using ObjectCache = std::unordered_map, JSTaggedValueHash>; - -static std::optional GetInspectorObject(JSThread *thread, ObjectRepository &object_repository, - ObjectCache &object_cache, JSHandle value); - -static std::optional GetInspectorPropertyDescriptor( - JSThread *thread, ObjectRepository &object_repository, ObjectCache &object_cache, JSHandle object, - JSHandle key, bool is_entry) -{ - ecmascript::PropertyDescriptor property(thread); - if (!JSObject::GetOwnProperty(thread, object, key, property)) { - return {}; - } - - std::string name; - if (key->IsString()) { - name = EcmaString::Cast(key->GetTaggedObject())->GetCString().get(); - } else if (key->IsSymbol()) { - name = GetSymbolDescription(JSSymbol::Cast(key->GetTaggedObject())); - } else { - return {}; - } - - std::optional desc; - if (property.HasGetter()) { - if (auto getter = GetInspectorObject(thread, object_repository, object_cache, property.GetGetter())) { - desc = tooling::inspector::PropertyDescriptor::Accessor(std::move(name), *std::move(getter)); - } - } else if (auto value = GetInspectorObject(thread, object_repository, object_cache, property.GetValue())) { - desc.emplace(std::move(name), *std::move(value), is_entry); - desc->SetWritable(property.IsWritable()); - } - - if (!desc) { - return {}; - } - - desc->SetConfigurable(property.IsConfigurable()); - desc->SetEnumerable(property.IsEnumerable()); - - if (key->IsSymbol()) { - if (auto symbol = GetInspectorObject(thread, object_repository, object_cache, key)) { - desc->SetSymbol(*std::move(symbol)); - } - } - - return desc; -} - -static RemoteObject GetInspectorObject(JSThread *thread, JSHandle object, RemoteObjectId object_id) -{ - 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()); - } else if (object->IsJSArray()) { - auto array = JSHandle::Cast(object); - return RemoteObject::Array(std::move(class_name), array->GetArrayLength(), object_id); - } else 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(std::move(class_name), object_id); -} - -static std::optional GetInspectorObject(JSThread *thread, ObjectRepository &object_repository, - ObjectCache &object_cache, JSHandle value) -{ - auto [result_iter, inserted] = object_cache.try_emplace(**value); - auto &result = result_iter->second; - if (!inserted) { - return result; - } - - if (value->IsUndefined()) { - return result = RemoteObject::Undefined(); - } else if (value->IsNull()) { - return result = RemoteObject::Null(); - } else if (value->IsBoolean()) { - return result = RemoteObject::Boolean(value->IsTrue()); - } else if (value->IsNumber()) { - return result = RemoteObject::Number(value->GetNumber()); - } else if (value->IsString()) { - return result = RemoteObject::String(EcmaString::Cast(value->GetTaggedObject())->GetCString().get()); - } else if (value->IsSymbol()) { - return result = RemoteObject::Symbol(GetSymbolDescription(JSSymbol::Cast(value->GetTaggedObject()))); - } else if (value->IsJSGlobalObject()) { - return result = RemoteObject::Object("Object", std::nullopt, "[Global Object]"); - } - - if (!value->IsJSObject()) { - return {}; - } - - auto object = JSHandle::Cast(value); - auto object_id = object_repository.CreateObjectId(); - result = GetInspectorObject(thread, object, object_id); - - if (auto prototype = GetInspectorObject(thread, object_repository, object_cache, - JSHandle(thread, object->GetPrototype(thread)))) { - tooling::inspector::PropertyDescriptor prototype_desc("[[Prototype]]", *std::move(prototype)); - prototype_desc.SetEnumerable(false); - - object_repository.AddProperty(object_id, std::move(prototype_desc)); - } - - auto keys = JSObject::GetOwnPropertyKeys(thread, object); - for (auto key_idx = 0U; key_idx < keys->GetLength(); ++key_idx) { - if (auto property = GetInspectorPropertyDescriptor(thread, object_repository, object_cache, object, - JSHandle(thread, keys->Get(key_idx)), - key_idx < object->GetNumberOfElements())) { - object_repository.AddProperty(object_id, *std::move(property)); - } - } - - return result; -} - std::optional EcmaLanguageContext::GetInspectorPropertyDescriptor( ManagedThread *thread, ObjectRepository &object_repository, const std::string &name, [[maybe_unused]] const std::string &signature, tooling::VRegValue value) const @@ -247,15 +70,12 @@ std::optional EcmaLanguageContext::GetIn return {}; } - JSHandle tagged_value(JSThread::Cast(thread), - tooling::ecmascript::PtEcmaScriptExtension::VRegValueToTaggedValue(value)); - ObjectCache object_cache; + [[maybe_unused]] EcmaHandleScope handle_scope(JSThread::Cast(thread)); + JSHandle tagged_value(JSThread::Cast(thread), PtEcmaScriptExtension::VRegValueToTaggedValue(value)); - if (auto object = GetInspectorObject(JSThread::Cast(thread), object_repository, object_cache, tagged_value)) { - return tooling::inspector::PropertyDescriptor(name == "=t" ? "this" : name, *std::move(object)); - } else { - return {}; - } + return tooling::inspector::PropertyDescriptor( + name == "=t" ? "this" : name, + JSTaggedValue::ToInspectorObject(JSThread::Cast(thread), object_repository, tagged_value)); } PandaVM *EcmaLanguageContext::CreateVM(Runtime *runtime, const RuntimeOptions &options) const diff --git a/runtime/js_object.cpp b/runtime/js_object.cpp index 4527c4b34..176a91fd7 100644 --- a/runtime/js_object.cpp +++ b/runtime/js_object.cpp @@ -13,24 +13,42 @@ * limitations under the License. */ +#include +#include +#include +#include + #include "accessor_data.h" #include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_string.h" #include "plugins/ecmascript/runtime/ecma_vm.h" #include "plugins/ecmascript/runtime/global_env.h" #include "plugins/ecmascript/runtime/internal_call_params.h" #include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" #include "plugins/ecmascript/runtime/js_primitive_ref.h" #include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/tagged_object.h" #include "global_dictionary-inl.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_for_in_iterator.h" +#include "js_function.h" +#include "js_handle.h" #include "js_hclass.h" #include "js_invoker.h" #include "js_iterator.h" +#include "js_symbol.h" +#include "js_tagged_value.h" #include "object_factory.h" #include "property_attributes.h" #include "tagged_array-inl.h" +using panda::tooling::inspector::ObjectRepository; +using panda::tooling::inspector::RemoteObject; + namespace panda::ecmascript { PropertyAttributes::PropertyAttributes(const PropertyDescriptor &desc) { @@ -1896,6 +1914,118 @@ bool JSObject::UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue return true; } +static std::optional GetInspectorPropertyDescriptor( + JSThread *thread, ObjectRepository &object_repository, const JSHandle &object, + const JSHandle &key, bool is_entry, std::unordered_map *cache) +{ + PropertyDescriptor property(thread); + if (!JSObject::GetOwnProperty(thread, object, key, property)) { + return {}; + } + + std::string name; + if (key->IsString()) { + name = EcmaString::Cast(key->GetTaggedObject())->GetCString().get(); + } else if (key->IsSymbol()) { + auto description = JSSymbol::Cast(key->GetTaggedObject())->GetDescription(); + + if (description.IsUndefined()) { + name = "Symbol()"; + } else { + std::string string(EcmaString::Cast(description.GetTaggedObject())->GetCString().get()); + name = "Symbol(" + string + ")"; + } + } else { + return {}; + } + + std::optional desc; + if (property.HasGetter()) { + auto getter = JSTaggedValue::ToInspectorObject(thread, object_repository, property.GetGetter(), cache); + desc = tooling::inspector::PropertyDescriptor::Accessor(std::move(name), std::move(getter)); + } else { + auto value = JSTaggedValue::ToInspectorObject(thread, object_repository, property.GetValue(), cache); + desc.emplace(std::move(name), std::move(value), is_entry); + desc->SetWritable(property.IsWritable()); + } + + desc->SetConfigurable(property.IsConfigurable()); + desc->SetEnumerable(property.IsEnumerable()); + + if (key->IsSymbol()) { + desc->SetSymbol(JSTaggedValue::ToInspectorObject(thread, object_repository, key, cache)); + } + + return desc; +} + +RemoteObject JSObject::ToInspectorObject(JSThread *thread, ObjectRepository &object_repository, + const JSHandle &object, + std::unordered_map *cache) +{ + if (cache != nullptr) { + if (auto iter = cache->find(*object); iter != cache->end()) { + return iter->second; + } + } + + auto constructor = JSHandle::Cast( + 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(); + } + + auto object_id = object_repository.CreateObjectId(); + auto value = RemoteObject::Object(class_name, object_id); + + if (object->IsDate()) { + auto date = JSDate::Cast(*object); + auto description = EcmaString::Cast(date->ToString(thread).GetTaggedObject()); + value = RemoteObject::Object(std::move(class_name), object_id, description->GetCString().get()); + } else if (object->IsJSArray()) { + auto array = JSHandle::Cast(object); + value = RemoteObject::Array(std::move(class_name), array->GetArrayLength(), object_id); + } else 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(); + value = RemoteObject::Function(std::move(class_name), std::move(name), length, object_id); + } + + std::unordered_map cache_map; + if (cache == nullptr) { + cache = &cache_map; + } + + cache->emplace(*object, value); + + auto prototype = JSTaggedValue::ToInspectorObject( + thread, object_repository, JSHandle(thread, object->GetPrototype(thread)), cache); + tooling::inspector::PropertyDescriptor prototype_desc("[[Prototype]]", std::move(prototype)); + prototype_desc.SetEnumerable(false); + object_repository.AddProperty(object_id, std::move(prototype_desc)); + + auto keys = GetOwnPropertyKeys(thread, object); + for (auto key_idx = 0U; key_idx < keys->GetLength(); ++key_idx) { + auto key = JSHandle(thread, keys->Get(key_idx)); + auto is_entry = key_idx < object->GetNumberOfElements(); + + if (auto property = GetInspectorPropertyDescriptor(thread, object_repository, object, key, is_entry, cache)) { + object_repository.AddProperty(object_id, *std::move(property)); + } + } + + return value; +} + void ECMAObject::SetHash(int32_t hash) { auto hashField = Barriers::GetDynValue(this, HASH_OFFSET); diff --git a/runtime/js_object.h b/runtime/js_object.h index c71c283fd..dfcdd6124 100644 --- a/runtime/js_object.h +++ b/runtime/js_object.h @@ -16,6 +16,7 @@ #ifndef ECMASCRIPT_JSOBJECT_H #define ECMASCRIPT_JSOBJECT_H +#include #include #include "plugins/ecmascript/runtime/ecma_macros.h" @@ -30,11 +31,16 @@ #include "plugins/ecmascript/runtime/object_operator.h" #include "plugins/ecmascript/runtime/property_attributes.h" #include "plugins/ecmascript/runtime/tagged_array.h" +#include "include/tooling/inspector/remote_object.h" namespace panda::test { class JSArrayTest; } // namespace panda::test +namespace panda::tooling::inspector { +class ObjectRepository; +} // namespace panda::tooling::inspector + namespace panda::ecmascript { class ObjectOperator; @@ -595,6 +601,10 @@ public: static JSHandle GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, uint32_t capacity); + static tooling::inspector::RemoteObject ToInspectorObject( + JSThread *thread, tooling::inspector::ObjectRepository &object_repository, const JSHandle &object, + std::unordered_map *cache = nullptr); + protected: static void ElementsToDictionary(const JSThread *thread, JSHandle obj); diff --git a/runtime/js_tagged_value.cpp b/runtime/js_tagged_value.cpp index 6ae233dab..1020944a5 100644 --- a/runtime/js_tagged_value.cpp +++ b/runtime/js_tagged_value.cpp @@ -15,21 +15,30 @@ #include "plugins/ecmascript/runtime/js_tagged_value.h" #include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_string.h" #include "plugins/ecmascript/runtime/ecma_vm.h" #include "plugins/ecmascript/runtime/global_env.h" #include "plugins/ecmascript/runtime/internal_call_params.h" #include "plugins/ecmascript/runtime/js_array.h" #include "plugins/ecmascript/runtime/js_arraylist.h" #include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_object.h" #include "plugins/ecmascript/runtime/js_primitive_ref.h" #include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/js_symbol.h" #include "plugins/ecmascript/runtime/js_tagged_value-inl.h" #include "plugins/ecmascript/runtime/js_thread.h" #include "plugins/ecmascript/runtime/js_typed_array.h" #include "plugins/ecmascript/runtime/tagged_array.h" +#include "include/tooling/inspector/remote_object.h" #include "js_object-inl.h" #include "object_factory.h" +#include + +using panda::tooling::inspector::ObjectRepository; +using panda::tooling::inspector::RemoteObject; + namespace panda::ecmascript { JSHandle GetTypeString(JSThread *thread, PreferredPrimitiveType type) { @@ -906,4 +915,53 @@ bool JSTaggedValue::GetContainerProperty(JSThread *thread, const JSHandle &tagged, + std::unordered_map *cache) +{ + if (tagged->IsUndefined() || tagged->IsHole()) { + return RemoteObject::Undefined(); + } else if (tagged->IsNull()) { + return RemoteObject::Null(); + } else if (tagged->IsBoolean()) { + return RemoteObject::Boolean(tagged->IsTrue()); + } else if (tagged->IsNumber()) { + return RemoteObject::Number(tagged->GetNumber()); + } else if (tagged->IsJSObject() && !tagged->IsJSGlobalObject()) { + return JSObject::ToInspectorObject(thread, object_repository, JSHandle::Cast(tagged), cache); + } + + auto object = tagged->GetTaggedObject(); + + if (cache != nullptr) { + if (auto iter = cache->find(object); iter != cache->end()) { + return iter->second; + } + } + + auto cls = object->GetClass(); + auto value = RemoteObject::Object("Object", {}, "[Object]"); + + if (cls->IsString()) { + value = RemoteObject::String(EcmaString::Cast(object)->GetCString().get()); + } else if (cls->IsSymbol()) { + auto description = JSSymbol::Cast(object)->GetDescription(); + + if (description.IsUndefined()) { + value = RemoteObject::Symbol("Symbol()"); + } else { + std::string string(EcmaString::Cast(description.GetTaggedObject())->GetCString().get()); + value = RemoteObject::Symbol("Symbol(" + string + ")"); + } + } else if (cls->IsJSGlobalObject()) { + value = RemoteObject::Object("Object", {}, "[Global Object]"); + } + + if (cache != nullptr) { + cache->emplace(object, value); + } + + return value; +} } // namespace panda::ecmascript diff --git a/runtime/js_tagged_value.h b/runtime/js_tagged_value.h index 970bd94fa..08b8d090b 100644 --- a/runtime/js_tagged_value.h +++ b/runtime/js_tagged_value.h @@ -19,6 +19,13 @@ #include "plugins/ecmascript/runtime/mem/ecma_string.h" #include "plugins/ecmascript/runtime/mem/tagged_object.h" #include "include/coretypes/tagged_value.h" +#include "include/tooling/inspector/remote_object.h" + +#include + +namespace panda::tooling::inspector { +class ObjectRepository; +} // namespace panda::tooling::inspector namespace panda::ecmascript { class JSObject; @@ -342,6 +349,11 @@ public: inline uint32_t GetHashCode(); static JSTaggedValue GetSuperBase(JSThread *thread, const JSHandle &obj); + static tooling::inspector::RemoteObject ToInspectorObject( + JSThread *thread, tooling::inspector::ObjectRepository &object_repository, + const JSHandle &tagged, + std::unordered_map *cache = nullptr); + void DumpTaggedValue(JSThread *thread, std::ostream &os) const DUMP_API_ATTR; void Dump(JSThread *thread, std::ostream &os) const DUMP_API_ATTR; void D() const DUMP_API_ATTR; -- Gitee From 25766a2500670bada14260a3b1ede336b25ce862 Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Tue, 22 Nov 2022 22:58:49 +0300 Subject: [PATCH 3/4] Forward print calls to Inspector Forward print calls by emitting InspectorConsole debug events and encoding arguments as RemoteObjects. Signed-off-by: Eugene Sharygin --- runtime/builtins/builtins_global.cpp | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/runtime/builtins/builtins_global.cpp b/runtime/builtins/builtins_global.cpp index 295e30eb1..250194aa0 100644 --- a/runtime/builtins/builtins_global.cpp +++ b/runtime/builtins/builtins_global.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "plugins/ecmascript/runtime/base/number_helper.h" #include "plugins/ecmascript/runtime/base/string_helper.h" @@ -27,8 +28,21 @@ #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/mem/tagged_object.h" #include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "include/runtime.h" #include "include/thread_scopes.h" +#include "include/tooling/inspector/console_call.h" +#include "include/tooling/inspector/object_repository.h" +#include "include/tooling/inspector/remote_object.h" +#include "include/tooling/pt_thread.h" +#include "utils/time.h" + +using panda::tooling::inspector::ConsoleCall; +using panda::tooling::inspector::ObjectRepository; +using panda::tooling::inspector::RemoteObject; +using panda::tooling::PtThread; namespace panda::ecmascript::builtins { using NumberHelper = ecmascript::base::NumberHelper; @@ -707,6 +721,22 @@ JSTaggedValue BuiltinsGlobal::PrintEntrypoint(EcmaRuntimeCallInfo *msg) } } std::cout << std::endl; + + if (auto debug_session = Runtime::GetCurrent()->GetDebugSession()) { + debug_session->GetDebugger().InspectorConsole(PtThread(thread), [&](ObjectRepository &object_repository) { + ConsoleCall console_call(PtThread(thread), ConsoleCall::Type::LOG, time::GetCurrentTimeInMillis()); + + std::unordered_map object_cache; + + for (uint32_t i = 0; i < numArgs; i++) { + console_call.AddArgument( + JSTaggedValue::ToInspectorObject(thread, object_repository, GetCallArg(msg, i), &object_cache)); + } + + return console_call; + }); + } + return JSTaggedValue::Undefined(); } -- Gitee From 50eb6f35b5c48a41c3f0e4b00b2d9d62a1e9ec80 Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Fri, 25 Nov 2022 14:07:56 +0300 Subject: [PATCH 4/4] Implement GetInspectorExceptionObject in EcmaLanguageContext This implements a LanguageContext function for converting from an exception object to RemoteObject representation of the Inspector protocol. Signed-off-by: Eugene Sharygin --- runtime/ecma_language_context.cpp | 26 ++++++++++++++++++++++++++ runtime/ecma_language_context.h | 4 ++++ 2 files changed, 30 insertions(+) diff --git a/runtime/ecma_language_context.cpp b/runtime/ecma_language_context.cpp index 750b202d7..21a9ba9d4 100644 --- a/runtime/ecma_language_context.cpp +++ b/runtime/ecma_language_context.cpp @@ -28,8 +28,10 @@ #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/object_wrapper.h" #include "include/method.h" #include "include/tooling/inspector/property_descriptor.h" +#include "macros.h" #include "runtime/core/core_itable_builder.h" #include "runtime/core/core_vtable_builder.h" @@ -39,6 +41,7 @@ using ecmascript::EcmaVM; using ecmascript::JSHandle; using ecmascript::JSTaggedValue; using ecmascript::JSThread; +using ecmascript::ObjectWrapper; using tooling::ecmascript::PtEcmaScriptExtension; using tooling::inspector::ObjectRepository; @@ -62,6 +65,29 @@ std::pair EcmaLanguageContext::GetCatchMethodAndOffset(Metho return std::make_pair(catchMethod, catchOffset); } +tooling::inspector::RemoteObject EcmaLanguageContext::GetInspectorExceptionObject( + ManagedThread *thread, tooling::inspector::ObjectRepository &object_repository, ObjectHeader *object) const +{ + auto js_thread = JSThread::Cast(thread); + + ASSERT(JSTaggedValue(object).IsObjectWrapper()); + auto object_wrapper = static_cast(object); + + [[maybe_unused]] EcmaHandleScope handle_scope(js_thread); + JSHandle value(js_thread, object_wrapper->GetValue()); + + // Save pending exception + auto exception = js_thread->GetException(); + js_thread->ClearException(); + + auto result = JSTaggedValue::ToInspectorObject(js_thread, object_repository, value); + + if (LIKELY(!js_thread->HasPendingException())) { + js_thread->SetException(exception); + } + return result; +} + std::optional EcmaLanguageContext::GetInspectorPropertyDescriptor( ManagedThread *thread, ObjectRepository &object_repository, const std::string &name, [[maybe_unused]] const std::string &signature, tooling::VRegValue value) const diff --git a/runtime/ecma_language_context.h b/runtime/ecma_language_context.h index dffa6671a..d42886e4b 100644 --- a/runtime/ecma_language_context.h +++ b/runtime/ecma_language_context.h @@ -39,6 +39,10 @@ public: std::pair GetCatchMethodAndOffset(Method *method, ManagedThread *thread) const override; + tooling::inspector::RemoteObject GetInspectorExceptionObject( + ManagedThread *thread, tooling::inspector::ObjectRepository &object_repository, + ObjectHeader *object) const override; + std::optional GetInspectorPropertyDescriptor( ManagedThread *thread, tooling::inspector::ObjectRepository &object_repository, const std::string &name, const std::string &signature, tooling::VRegValue value) const override; -- Gitee