diff --git a/runtime/builtins/builtins_global.cpp b/runtime/builtins/builtins_global.cpp index 1f6e15062fc6d26f23e03286311bc2d90033b5e8..6a0cba809bb1c8edce79c6a8f9352324a7a321c0 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(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(); } diff --git a/runtime/ecma_language_context.cpp b/runtime/ecma_language_context.cpp index 3ea79ddacbfdbe59354af26c7e1eec56a59b5366..750b202d77e9e2d3f244caa0510b9ce81279cabe 100644 --- a/runtime/ecma_language_context.cpp +++ b/runtime/ecma_language_context.cpp @@ -19,19 +19,28 @@ #include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" #include "plugins/ecmascript/runtime/ecma_class_linker_extension.h" #include "plugins/ecmascript/runtime/ecma_exceptions.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_handle.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 "include/method.h" +#include "include/tooling/inspector/property_descriptor.h" #include "runtime/core/core_itable_builder.h" #include "runtime/core/core_vtable_builder.h" namespace panda { +using ecmascript::EcmaHandleScope; using ecmascript::EcmaVM; +using ecmascript::JSHandle; +using ecmascript::JSTaggedValue; using ecmascript::JSThread; +using tooling::ecmascript::PtEcmaScriptExtension; +using tooling::inspector::ObjectRepository; std::pair EcmaLanguageContext::GetCatchMethodAndOffset(Method *method, ManagedThread *thread) const { @@ -53,6 +62,22 @@ std::pair EcmaLanguageContext::GetCatchMethodAndOffset(Metho return std::make_pair(catchMethod, catchOffset); } +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 {}; + } + + [[maybe_unused]] EcmaHandleScope handle_scope(JSThread::Cast(thread)); + JSHandle tagged_value(JSThread::Cast(thread), PtEcmaScriptExtension::VRegValueToTaggedValue(value)); + + 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 { auto ret = EcmaVM::Create(runtime, ecmascript::JSRuntimeOptions::Cast(options)); diff --git a/runtime/ecma_language_context.h b/runtime/ecma_language_context.h index a07ca51a0ffa1b5544cb2e6d60554a240e9a6c04..dffa6671a498e8ae003e07298825e526b5f27667 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 2d6b0c125f94346bfbf8cc1794a0f24ce74f42e7..9261df35aa06fb4e193c448c178775101d5df6fb 100644 --- a/runtime/js_function.cpp +++ b/runtime/js_function.cpp @@ -454,6 +454,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 2ad1d11c3600e4b3cab99a1758e0d9c8426f8f71..fd2914d175eadaa3895557059b6d675c75b3b103 100644 --- a/runtime/js_function.h +++ b/runtime/js_function.h @@ -101,6 +101,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/js_object.cpp b/runtime/js_object.cpp index 38964067eed5f6acf00e22f4800afb09b455d7d4..55e045b35af92adf1d1be055b8a63f88971c2eae 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) { @@ -1893,6 +1911,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 72e7d63ccf71fb4456012217745781db46d56355..b8676e1979219cafca23384b1fe9bdf0ae793c4a 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; @@ -596,6 +602,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 42f4d168e42a72003fb7ef7d3b985408a9a289b5..2f8fbee92e5429947553838590a1c520ffe4eddf 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) { @@ -904,4 +913,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 970bd94faf4cb1c51b3e7651ff25bdf1324703bc..08b8d090bc4db3c7ca4f031c2d538d2613a37aa4 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;