diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index be90d0c3c14d69c643a7f3dc6f4d74049ec3866d..0a15ed17a2323d70cf99500002c68ac32e434ca5 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -181,6 +181,7 @@ set(ECMASCRIPT_SOURCES ${ECMA_SRC_DIR}/compiler/ecmascript_runtime_interface.cpp ${BUILTIN_BRIDGE_SOURCE} + ${ECMA_SRC_DIR}/tooling/ecma_inspector_extension.cpp ${ECMA_SRC_DIR}/tooling/pt_ecmascript_extension.cpp ) diff --git a/runtime/builtins/builtins_global.cpp b/runtime/builtins/builtins_global.cpp index b94c77632aa19ac1f19d2bfa66d8e0ad04f9633c..d0d3aa588f7ab903399ac394648804a79649f367 100644 --- a/runtime/builtins/builtins_global.cpp +++ b/runtime/builtins/builtins_global.cpp @@ -27,9 +27,16 @@ #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/mem/panda_containers.h" +#include "include/runtime.h" +#include "include/runtime_notification.h" #include "include/thread_scopes.h" +#include "include/typed_value.h" +#include "utils/time.h" namespace panda::ecmascript::builtins { using NumberHelper = ecmascript::base::NumberHelper; @@ -720,8 +727,13 @@ JSTaggedValue BuiltinsGlobal::PrintEntrypoint(EcmaRuntimeCallInfo *msg) BUILTINS_API_TRACE(thread, Global, PrintEntryPoint); uint32_t num_args = msg->GetArgsNumber(); + PandaVector arguments; + for (uint32_t i = 0; i < num_args; i++) { - JSHandle string_content = JSTaggedValue::ToString(thread, GetCallArg(msg, i)); + auto arg = GetCallArg(msg, i); + arguments.push_back(TypedValue::Tagged(arg.GetTaggedValue())); + + JSHandle string_content = JSTaggedValue::ToString(thread, arg); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); PrintString(thread, *string_content); @@ -730,6 +742,10 @@ JSTaggedValue BuiltinsGlobal::PrintEntrypoint(EcmaRuntimeCallInfo *msg) } } std::cout << std::endl; + + Runtime::GetCurrent()->GetNotificationManager()->ConsoleCallEvent(thread, ConsoleCallType::LOG, + time::GetCurrentTimeInMillis(), arguments); + return JSTaggedValue::Undefined(); } diff --git a/runtime/ecma_language_context.cpp b/runtime/ecma_language_context.cpp index 3cc6d5a606df4d6f6a48773bd0fb51c6e55b6922..c31734209bd1cf27e543d129c37632a866f54d24 100644 --- a/runtime/ecma_language_context.cpp +++ b/runtime/ecma_language_context.cpp @@ -29,10 +29,13 @@ #include "plugins/ecmascript/runtime/object_factory.h" #include "plugins/ecmascript/runtime/lexical_env.h" #include "plugins/ecmascript/runtime/interpreter/js_frame.h" +#include "plugins/ecmascript/runtime/tooling/ecma_inspector_extension.h" #include "include/method.h" #include "runtime/core/core_itable_builder.h" #include "runtime/core/core_vtable_builder.h" +#include + namespace panda { using ecmascript::EcmaVM; using ecmascript::JSThread; @@ -71,6 +74,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 79013806bddf210a01a124a91df083565b255adc..51629e1aa2b9cadb94661c887e054e6fca554657 100644 --- a/runtime/ecma_language_context.h +++ b/runtime/ecma_language_context.h @@ -47,6 +47,8 @@ public: std::unique_ptr CreateClassLinkerExtension() const override; + std::unique_ptr CreateInspectorExtension() const override; + PandaUniquePtr CreatePtLangExt() const override; void ThrowException(ManagedThread *thread, const uint8_t *mutf8_name, const uint8_t *mutf8_msg) const override; diff --git a/runtime/js_function.cpp b/runtime/js_function.cpp index cf79533d192c568c941d096c52b823588cd60215..589a1a38e506eeb557b6a9c5d4aedea32466dc3f 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/tooling/ecma_inspector_extension.cpp b/runtime/tooling/ecma_inspector_extension.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0347a0693fb6f027396807753024ab995a04504c --- /dev/null +++ b/runtime/tooling/ecma_inspector_extension.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecma_inspector_extension.h" + +#include "ecma_handle_scope.h" +#include "ecma_string.h" +#include "global_env.h" +#include "include/tooling/inspector_extension.h" +#include "include/typed_value.h" +#include "js_date.h" +#include "js_function.h" +#include "js_handle.h" +#include "js_hclass.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 "mem/tagged_object.h" +#include "tagged_array.h" + +#include +#include +#include + +using panda::ecmascript::EcmaHandleScope; +using panda::ecmascript::EcmaString; +using panda::ecmascript::PropertyDescriptor; +using panda::ecmascript::JSDate; +using panda::ecmascript::JSFunction; +using panda::ecmascript::JSFunctionBase; +using panda::ecmascript::JSHandle; +using panda::ecmascript::JSHClass; +using panda::ecmascript::JSObject; +using panda::ecmascript::JSSymbol; +using panda::ecmascript::JSTaggedValue; +using panda::ecmascript::JSThread; +using panda::ecmascript::TaggedArray; +using panda::ecmascript::TaggedObject; + +namespace panda::tooling::ecmascript { +static JSHClass *GetClass(const ObjectHeader *object) +{ + return static_cast(object)->GetClass(); +} + +std::string EcmaInspectorExtension::GetClassName(const ObjectHeader *object) +{ + if (!GetClass(object)->IsJSObject()) { + return ""; + } + + auto js_thread = JSThread::GetCurrent(); + EcmaHandleScope scope(js_thread); + JSHandle js_object(js_thread, object); + + auto constructor = + JSObject::SpeciesConstructor(js_thread, js_object, js_thread->GetEcmaVM()->GetGlobalEnv()->GetObjectFunction()); + + return *GetFunctionName(constructor->GetTaggedObject()); +} + +std::optional EcmaInspectorExtension::GetStringValue(const ObjectHeader *object) +{ + if (!GetClass(object)->IsString()) { + return {}; + } + + auto ecma_string = EcmaString::Cast(const_cast(object)); + return ecma_string->GetCString().get(); +} + +bool EcmaInspectorExtension::IsSymbol(const ObjectHeader *object) +{ + return GetClass(object)->IsSymbol(); +} + +std::optional EcmaInspectorExtension::GetDescription(const ObjectHeader *object) +{ + if (GetClass(object)->IsSymbol()) { + auto js_symbol = static_cast(object); + + std::string description; + if (auto desc = js_symbol->GetDescription(); !desc.IsUndefined()) { + description = EcmaString::Cast(desc.GetTaggedObject())->GetCString().get(); + } + + return "Symbol(" + description + ")"; + } + + if (GetClass(object)->IsDate()) { + auto js_date = static_cast(object); + auto description = EcmaString::Cast(js_date->ToString(JSThread::GetCurrent()).GetTaggedObject()); + return description->GetCString().get(); + } + + return {}; +} + +template +static void EnumerateProperties(JSThread *js_thread, JSHandle js_object, const JSHandle &keys, + const Handler &handler) +{ + for (auto key_idx = 0U; key_idx < keys->GetLength(); ++key_idx) { + auto key = keys->Get(key_idx).GetTaggedObject(); + + PropertyDescriptor js_property(js_thread); + if (UNLIKELY(!JSObject::GetOwnProperty(js_thread, js_object, JSHandle(js_thread, key), + js_property))) { + continue; + } + + auto value = js_property.HasGetter() + ? PtValueDescriptor::Accessor(TypedValue::Tagged(js_property.GetGetter().GetTaggedValue()), + TypedValue::Tagged(js_property.GetSetter().GetTaggedValue())) + : PtValueDescriptor::Data(TypedValue::Tagged(js_property.GetValue().GetTaggedValue())); + + unsigned access_flags = (js_property.IsConfigurable() ? PtPropertyDescriptor::CONFIGURABLE : 0) | + (js_property.IsEnumerable() ? PtPropertyDescriptor::ENUMERABLE : 0) | + (js_property.IsWritable() ? PtPropertyDescriptor::WRITABLE : 0); + + handler(key, std::move(value), access_flags); + } +} + +std::vector EcmaInspectorExtension::GetProperties(const ObjectHeader *object) +{ + if (!GetClass(object)->IsJSObject()) { + return {}; + } + + auto js_thread = JSThread::GetCurrent(); + EcmaHandleScope scope(js_thread); + JSHandle js_object(js_thread, object); + + std::vector properties { + {"[[Prototype]]", PtValueDescriptor::Data(TypedValue::Tagged(js_object->GetPrototype(js_thread))), 0}}; + + EnumerateProperties(js_thread, js_object, JSObject::GetOwnPropertyKeys(js_thread, js_object), + [&](auto key, auto value, auto access_flags) { + std::string name; + if (key->GetClass()->IsString()) { + name = EcmaString::Cast(key)->GetCString().get(); + } else { + ASSERT(key->GetClass()->IsSymbol()); + + auto description = GetDescription(key); + ASSERT(description); + + name = *description; + } + + properties.emplace_back(std::move(name), std::move(value), access_flags, + key->GetClass()->IsSymbol() ? key : nullptr); + }); + + return properties; +} + +std::optional EcmaInspectorExtension::GetArrayLength(const ObjectHeader *object) +{ + if (!GetClass(object)->IsJSArray()) { + return {}; + } + + auto js_object = JSObject::Cast(const_cast(object)); + return js_object->GetNumberOfElements(); +} + +std::optional> EcmaInspectorExtension::GetArrayElements( + const ObjectHeader *object) +{ + if (!GetClass(object)->IsJSArray()) { + return {}; + } + + auto js_thread = JSThread::GetCurrent(); + EcmaHandleScope scope(js_thread); + JSHandle js_object(js_thread, object); + + auto keys = js_thread->GetEcmaVM()->GetFactory()->NewTaggedArray(js_object->GetNumberOfElements()); + JSObject::GetAllElementKeys(js_thread, js_object, 0, keys); + + std::vector elements; + + EnumerateProperties(js_thread, js_object, keys, [&](auto, auto value, auto access_flags) { + elements.emplace_back(elements.size(), std::move(value), access_flags); + }); + + return elements; +} + +std::optional EcmaInspectorExtension::GetFunctionName(const ObjectHeader *object) +{ + if (!GetClass(object)->IsJSFunctionBase()) { + return {}; + } + + auto js_thread = JSThread::GetCurrent(); + EcmaHandleScope scope(js_thread); + JSHandle js_function(js_thread, object); + + auto function_name = JSFunctionBase::GetFunctionName(js_thread, js_function); + if (!function_name->IsString()) { + return ""; + } + + return EcmaString::Cast(function_name->GetTaggedObject())->GetCString().get(); +} + +std::optional EcmaInspectorExtension::GetFunctionNumArgs(const ObjectHeader *object) +{ + if (!GetClass(object)->IsJSFunction()) { + return {}; + } + + auto js_thread = JSThread::GetCurrent(); + EcmaHandleScope scope(js_thread); + JSHandle js_function(js_thread, object); + + return JSTaggedValue::ToLength(js_thread, JSFunction::GetFunctionLength(js_thread, js_function)).ToUint32(); +} + +std::optional EcmaInspectorExtension::GetVariableName(std::string_view name) +{ + if (name == "=f" || name == "=nt") { + return {}; + } + if (name == "=t") { + return "this"; + } + return std::string(name); +} +} // namespace panda::tooling::inspector::ecmascript diff --git a/runtime/tooling/ecma_inspector_extension.h b/runtime/tooling/ecma_inspector_extension.h new file mode 100644 index 0000000000000000000000000000000000000000..643b7a3a36423b29a025c6f5c9d59ebbbc764e83 --- /dev/null +++ b/runtime/tooling/ecma_inspector_extension.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef PANDA_TOOLING_ECMASCRIPT_ECMA_INSPECTOR_EXTENSION_H +#define PANDA_TOOLING_ECMASCRIPT_ECMA_INSPECTOR_EXTENSION_H + +#include "include/tooling/inspector_extension.h" + +namespace panda::tooling::ecmascript { +class EcmaInspectorExtension : public InspectorExtension { +public: + std::string GetClassName(const ObjectHeader *object) override; + std::optional GetStringValue(const ObjectHeader *object) override; + bool IsSymbol(const ObjectHeader *object) override; + std::optional GetDescription(const ObjectHeader *object) override; + std::vector GetProperties(const ObjectHeader *object) override; + std::optional GetArrayLength(const ObjectHeader *object) override; + std::optional> GetArrayElements(const ObjectHeader *object) override; + std::optional GetFunctionName(const ObjectHeader *object) override; + std::optional GetFunctionNumArgs(const ObjectHeader *object) override; + std::optional GetVariableName(std::string_view name) override; +}; +} // namespace panda::tooling::ecmascript + +#endif // PANDA_TOOLING_ECMASCRIPT_ECMA_INSPECTOR_EXTENSION_H diff --git a/subproject_sources.gn b/subproject_sources.gn index 28616162e568b30f646a8e0ea37ba2dac71ff9d3..eb6c4ebe4c9be5547ab7cacb6b9c14343aaa4036 100644 --- a/subproject_sources.gn +++ b/subproject_sources.gn @@ -207,6 +207,7 @@ srcs_runtime = [ "runtime/weak_vector.cpp", "runtime/class_info_extractor.cpp", "runtime/compiler/ecmascript_runtime_interface.cpp", + "runtime/tooling/ecma_inspector_extension.cpp", "runtime/tooling/pt_ecmascript_extension.cpp", ]