diff --git a/runtime/builtins/builtins_global.cpp b/runtime/builtins/builtins_global.cpp index 295e30eb1f1799e1b9348845f2293230510ab01f..250194aa0b651613678f32266f2ccd9c75efb961 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(); } diff --git a/runtime/ecma_language_context.cpp b/runtime/ecma_language_context.cpp index 3ea79ddacbfdbe59354af26c7e1eec56a59b5366..21a9ba9d408f86178c5c6b4f82ded8a5cb00aadd 100644 --- a/runtime/ecma_language_context.cpp +++ b/runtime/ecma_language_context.cpp @@ -19,19 +19,31 @@ #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 "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" namespace panda { +using ecmascript::EcmaHandleScope; using ecmascript::EcmaVM; +using ecmascript::JSHandle; +using ecmascript::JSTaggedValue; using ecmascript::JSThread; +using ecmascript::ObjectWrapper; +using tooling::ecmascript::PtEcmaScriptExtension; +using tooling::inspector::ObjectRepository; std::pair EcmaLanguageContext::GetCatchMethodAndOffset(Method *method, ManagedThread *thread) const { @@ -53,6 +65,45 @@ 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 +{ + 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..d42886e4b546a13ea1250f1a42d68d929d1c0244 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,14 @@ 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; + 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 d9cfaf6f2e98594a3ce8e333ee1668e522e686a5..343c33de8f0f3373c211bbed5160b8272e36a9ad 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 eb977f08299afc58de68a93aee85d8da4919051b..fbc8249c0aa7f06fae4b05984daa3aeadd04a6b7 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/js_object.cpp b/runtime/js_object.cpp index 4527c4b34494f8e9e559e5b00a5677c8c6b146ce..176a91fd7182a5361ba53f430afe6e6cfae99b22 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 c71c283fd76f4d04169275cd8f6284e25644d86a..dfcdd6124d0cfc07f248c77427ee91ea83c1adda 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 6ae233dab23a27e16cf6b9fa0cc1d1974de1fe20..1020944a5b3f759e200f598219af3a32eac60944 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 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;