From 3ffbdb434663cf676df6d0b47424733c70a9fd24 Mon Sep 17 00:00:00 2001 From: Lyupa Anastasia Date: Fri, 5 Sep 2025 15:19:36 +0300 Subject: [PATCH 1/6] [TypeAPI] Introduce new classes Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICW201 Introduce core classes for new TypeAPI, reflect classes for Method and Field. Implement std.core.reflect.Method, std.core.reflect.Field and mirror classes for them. Change-Id: Ic50e242fd96bbd65c045b445b084e53c97c8d983 Signed-off-by: Lyupa Anastasia Signed-off-by: kurnevichstanislav Signed-off-by: Anna Antipina Signed-off-by: groshevmaksim --- .../plugins/ets/runtime/CMakeLists.txt | 4 + .../ets/runtime/ets_libbase_runtime.yaml | 155 ++++++++ .../ets/runtime/ets_panda_file_items.h | 5 + .../ets/runtime/ets_platform_types.cpp | 6 + .../plugins/ets/runtime/ets_platform_types.h | 6 + .../plugins/ets/runtime/ets_stubs-inl.h | 10 +- static_core/plugins/ets/runtime/ets_stubs.cpp | 2 +- .../ets/runtime/intrinsics/std_core_Class.cpp | 330 +++++++++++++++++- .../intrinsics/std_core_reflect_Field.cpp | 28 ++ .../intrinsics/std_core_reflect_Method.cpp | 94 +++++ .../plugins/ets/runtime/types/ets_class.cpp | 58 +++ .../plugins/ets/runtime/types/ets_class.h | 22 +- .../plugins/ets/runtime/types/ets_method.cpp | 66 ++-- .../plugins/ets/runtime/types/ets_method.h | 9 +- .../ets/runtime/types/ets_reflect_field.cpp | 30 ++ .../ets/runtime/types/ets_reflect_field.h | 110 ++++++ .../ets/runtime/types/ets_reflect_method.cpp | 31 ++ .../ets/runtime/types/ets_reflect_method.h | 104 ++++++ .../plugins/ets/stdlib/std/core/Class.ets | 83 +++++ .../stdlib/std/core/ReflectConstructor.ets | 35 ++ .../ets/stdlib/std/core/ReflectField.ets | 65 ++++ .../stdlib/std/core/ReflectInstanceField.ets | 40 +++ .../stdlib/std/core/ReflectInstanceMethod.ets | 55 +++ .../ets/stdlib/std/core/ReflectMethod.ets | 79 +++++ .../stdlib/std/core/ReflectStaticField.ets | 40 +++ .../stdlib/std/core/ReflectStaticMethod.ets | 47 +++ .../ets/stdlib/std/testing/arktest.ets | 4 +- static_core/plugins/ets/subproject_sources.gn | 4 + .../std/core/ReflectFieldTest.ets | 109 ++++++ .../std/core/ReflectMethodTest.ets | 260 ++++++++++++++ .../std/core/ReflectTypeTest.ets | 205 +++++++++++ .../ets/tests/runtime/types/CMakeLists.txt | 1 + .../runtime/types/ets_class_file_test.cpp | 37 +- .../tests/runtime/types/ets_reflect_test.cpp | 112 ++++++ .../declgen-ets2ts-func-tests-ignored.txt | 1 + 35 files changed, 2202 insertions(+), 45 deletions(-) create mode 100644 static_core/plugins/ets/runtime/intrinsics/std_core_reflect_Field.cpp create mode 100644 static_core/plugins/ets/runtime/intrinsics/std_core_reflect_Method.cpp create mode 100644 static_core/plugins/ets/runtime/types/ets_reflect_field.cpp create mode 100644 static_core/plugins/ets/runtime/types/ets_reflect_field.h create mode 100644 static_core/plugins/ets/runtime/types/ets_reflect_method.cpp create mode 100644 static_core/plugins/ets/runtime/types/ets_reflect_method.h create mode 100644 static_core/plugins/ets/stdlib/std/core/ReflectConstructor.ets create mode 100644 static_core/plugins/ets/stdlib/std/core/ReflectField.ets create mode 100644 static_core/plugins/ets/stdlib/std/core/ReflectInstanceField.ets create mode 100644 static_core/plugins/ets/stdlib/std/core/ReflectInstanceMethod.ets create mode 100644 static_core/plugins/ets/stdlib/std/core/ReflectMethod.ets create mode 100644 static_core/plugins/ets/stdlib/std/core/ReflectStaticField.ets create mode 100644 static_core/plugins/ets/stdlib/std/core/ReflectStaticMethod.ets create mode 100644 static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectFieldTest.ets create mode 100644 static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectMethodTest.ets create mode 100644 static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectTypeTest.ets create mode 100644 static_core/plugins/ets/tests/runtime/types/ets_reflect_test.cpp diff --git a/static_core/plugins/ets/runtime/CMakeLists.txt b/static_core/plugins/ets/runtime/CMakeLists.txt index be56e23fe0..3318c1ab9f 100644 --- a/static_core/plugins/ets/runtime/CMakeLists.txt +++ b/static_core/plugins/ets/runtime/CMakeLists.txt @@ -83,6 +83,8 @@ set(ETS_RUNTIME_SOURCES ${ETS_EXT_SOURCES}/intrinsics/std_core_Type.cpp ${ETS_EXT_SOURCES}/intrinsics/std_core_TypeCreator.cpp ${ETS_EXT_SOURCES}/intrinsics/std_core_Method.cpp + ${ETS_EXT_SOURCES}/intrinsics/std_core_reflect_Method.cpp + ${ETS_EXT_SOURCES}/intrinsics/std_core_reflect_Field.cpp ${ETS_EXT_SOURCES}/intrinsics/std_core_Value.cpp ${ETS_EXT_SOURCES}/intrinsics/std_core_Arrays.cpp ${ETS_EXT_SOURCES}/intrinsics/std_core_Job.cpp @@ -113,7 +115,9 @@ set(ETS_RUNTIME_SOURCES ${ETS_EXT_SOURCES}/types/ets_string.cpp ${ETS_EXT_SOURCES}/types/ets_typeapi_create.cpp ${ETS_EXT_SOURCES}/types/ets_typeapi_field.cpp + ${ETS_EXT_SOURCES}/types/ets_reflect_field.cpp ${ETS_EXT_SOURCES}/types/ets_typeapi_method.cpp + ${ETS_EXT_SOURCES}/types/ets_reflect_method.cpp ${ETS_EXT_SOURCES}/types/ets_typeapi_parameter.cpp ${ETS_EXT_SOURCES}/types/ets_waiters_list.cpp ${ETS_EXT_SOURCES}/types/ets_finalizable_weak_ref.cpp diff --git a/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml b/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml index 4a281a2a66..90b4c6de61 100644 --- a/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml +++ b/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml @@ -42,15 +42,33 @@ coretypes: - managed_class: Array mirror_class: ark::ets::EtsCharArray +- managed_class: std.core.reflect.TypeInfo + mirror_class: ark::ets::ReflectTypeInfo + - managed_class: std.core.Type mirror_class: ark::ets::EtsTypeAPIType - managed_class: std.core.Field mirror_class: ark::ets::EtsTypeAPIField +- managed_class: std.core.reflect.InstanceField + mirror_class: ark::ets::EtsReflectField + +- managed_class: std.core.reflect.StaticField + mirror_class: ark::ets::EtsReflectField + - managed_class: std.core.Method mirror_class: ark::ets::EtsTypeAPIMethod +- managed_class: std.core.reflect.InstanceMethod + mirror_class: ark::ets::EtsReflectMethod + +- managed_class: std.core.reflect.StaticMethod + mirror_class: ark::ets::EtsReflectMethod + +- managed_class: std.core.reflect.Constructor + mirror_class: ark::ets::EtsReflectMethod + - managed_class: std.core.Parameter mirror_class: ark::ets::EtsTypeAPIParameter @@ -6909,6 +6927,143 @@ intrinsics: args: [ ] impl: ark::ets::intrinsics::StdCoreClassIsFixedArray + - name: StdCoreClassGetInstanceMethodsInternal + space: ets + class_name: std.core.Class + method_name: getInstanceMethodsInternal + static: false + signature: + ret: std.core.reflect.InstanceMethod[] + args: [ u1 ] + impl: ark::ets::intrinsics::StdCoreClassGetInstanceMethodsInternal + + - name: StdCoreClassGetInstanceMethodByNameInternal + space: ets + class_name: std.core.Class + method_name: getInstanceMethodByNameInternal + static: false + signature: + ret: std.core.reflect.InstanceMethod + args: [ std.core.String, u1 ] + impl: ark::ets::intrinsics::StdCoreClassGetInstanceMethodByNameInternal + + - name: StdCoreClassGetInstanceFieldByNameInternal + space: ets + class_name: std.core.Class + method_name: getInstanceFieldByNameInternal + static: false + signature: + ret: std.core.reflect.InstanceField + args: [ std.core.String, u1 ] + impl: ark::ets::intrinsics::StdCoreClassGetInstanceFieldByNameInternal + + - name: StdCoreClassGetStaticMethodsInternal + space: ets + class_name: std.core.Class + method_name: getStaticMethodsInternal + static: false + signature: + ret: std.core.reflect.StaticMethod[] + args: [ u1 ] + impl: ark::ets::intrinsics::StdCoreClassGetStaticMethodsInternal + + - name: StdCoreClassGetConstructorsInternal + space: ets + class_name: std.core.Class + method_name: getConstructorsInternal + static: false + signature: + ret: std.core.reflect.Constructor[] + args: [ u1 ] + impl: ark::ets::intrinsics::StdCoreClassGetConstructorsInternal + + - name: StdCoreClassGetInstanceFieldsInternal + space: ets + class_name: std.core.Class + method_name: getInstanceFieldsInternal + static: false + signature: + ret: std.core.reflect.InstanceField[] + args: [ u1 ] + impl: ark::ets::intrinsics::StdCoreClassGetInstanceFieldsInternal + + - name: StdCoreClassGetStaticFieldsInternal + space: ets + class_name: std.core.Class + method_name: getStaticFieldsInternal + static: false + signature: + ret: std.core.reflect.StaticField[] + args: [ u1 ] + impl: ark::ets::intrinsics::StdCoreClassGetStaticFieldsInternal + +########################### +# std.core.reflect.Method # +########################### + - name: StdCoreReflectMethodGetReturnTypeImpl + space: ets + class_name: std.core.reflect.Method + method_name: getReturnTypeImpl + static: true + signature: + ret: std.core.Class + args: [ i64 ] + impl: ark::ets::intrinsics::ReflectMethodGetReturnTypeImpl + + - name: StdCoreReflectMethodGetParameterTypeByIdxImpl + space: ets + class_name: std.core.reflect.Method + method_name: getParameterTypeByIdxImpl + static: true + signature: + ret: std.core.Class + args: [ i64, i32 ] + impl: ark::ets::intrinsics::ReflectMethodGetParameterTypeByIdxImpl + + - name: StdCoreReflectMethodGetParametersTypesImpl + space: ets + class_name: std.core.reflect.Method + method_name: getParametersTypesImpl + static: true + signature: + ret: std.core.Class[] + args: [ i64 ] + impl: ark::ets::intrinsics::ReflectMethodGetParametersTypesImpl + + - name: StdCoreReflectMethodGetParametersNumImpl + space: ets + class_name: std.core.reflect.Method + method_name: getParametersNumImpl + static: true + signature: + ret: i32 + args: [ i64 ] + impl: ark::ets::intrinsics::ReflectMethodGetParametersNumImpl + + - name: StdCoreReflectMethodGetNameInternal + space: ets + class_name: std.core.reflect.Method + method_name: getNameInternal + static: true + signature: + ret: std.core.String + args: [ i64 ] + impl: ark::ets::intrinsics::ReflectMethodGetNameInternal + +########################## +# std.core.reflect.Field # +########################## + + - name: StdCoreReflectFieldGetNameInternal + space: ets + class_name: std.core.reflect.Field + method_name: getNameInternal + static: true + signature: + ret: std.core.String + args: [ i64 ] + impl: ark::ets::intrinsics::ReflectFieldGetNameInternal + ########################## # std.core.RuntimeLinker # ########################## diff --git a/static_core/plugins/ets/runtime/ets_panda_file_items.h b/static_core/plugins/ets/runtime/ets_panda_file_items.h index abbda9808c..776a702c69 100644 --- a/static_core/plugins/ets/runtime/ets_panda_file_items.h +++ b/static_core/plugins/ets/runtime/ets_panda_file_items.h @@ -54,6 +54,11 @@ static constexpr std::string_view STRING_BUILDER = "Lstd/c // TypeAPI classes static constexpr std::string_view CLASS_TYPE = "Lstd/core/ClassType;"; +static constexpr std::string_view REFLECT_INSTANCE_FIELD = "Lstd/core/reflect/InstanceField;"; +static constexpr std::string_view REFLECT_INSTANCE_METHOD = "Lstd/core/reflect/InstanceMethod;"; +static constexpr std::string_view REFLECT_STATIC_FIELD = "Lstd/core/reflect/StaticField;"; +static constexpr std::string_view REFLECT_STATIC_METHOD = "Lstd/core/reflect/StaticMethod;"; +static constexpr std::string_view REFLECT_CONSTRUCTOR = "Lstd/core/reflect/Constructor;"; // Runtime classes static constexpr std::string_view STACK_TRACE_ELEMENT = "Lstd/core/StackTraceElement;"; diff --git a/static_core/plugins/ets/runtime/ets_platform_types.cpp b/static_core/plugins/ets/runtime/ets_platform_types.cpp index a2733854e7..8f2fe33d6b 100644 --- a/static_core/plugins/ets/runtime/ets_platform_types.cpp +++ b/static_core/plugins/ets/runtime/ets_platform_types.cpp @@ -238,6 +238,12 @@ EtsPlatformTypes::EtsPlatformTypes([[maybe_unused]] EtsCoroutine *coro) findType(&EtsPlatformTypes::coreParameter, PARAMETER); findType(&EtsPlatformTypes::coreClassType, CLASS_TYPE); + findType(&EtsPlatformTypes::reflectInstanceField, REFLECT_INSTANCE_FIELD); + findType(&EtsPlatformTypes::reflectInstanceMethod, REFLECT_INSTANCE_METHOD); + findType(&EtsPlatformTypes::reflectStaticField, REFLECT_STATIC_FIELD); + findType(&EtsPlatformTypes::reflectStaticMethod, REFLECT_STATIC_METHOD); + findType(&EtsPlatformTypes::reflectConstructor, REFLECT_CONSTRUCTOR); + findType(&EtsPlatformTypes::escompatProcess, PROCESS); findMethod(&EtsPlatformTypes::escompatProcessListUnhandledJobs, escompatProcess, "listUnhandledJobs", "Lescompat/Array;:V", true); diff --git a/static_core/plugins/ets/runtime/ets_platform_types.h b/static_core/plugins/ets/runtime/ets_platform_types.h index ff54afd0c9..9da6cff6dc 100644 --- a/static_core/plugins/ets/runtime/ets_platform_types.h +++ b/static_core/plugins/ets/runtime/ets_platform_types.h @@ -125,6 +125,12 @@ public: EtsClass *coreParameter {}; EtsClass *coreClassType {}; + EtsClass *reflectInstanceField {}; + EtsClass *reflectInstanceMethod {}; + EtsClass *reflectStaticField {}; + EtsClass *reflectStaticMethod {}; + EtsClass *reflectConstructor {}; + /* escompat.Process */ EtsClass *escompatProcess {}; EtsMethod *escompatProcessListUnhandledJobs {}; diff --git a/static_core/plugins/ets/runtime/ets_stubs-inl.h b/static_core/plugins/ets/runtime/ets_stubs-inl.h index 08973e8df5..09f00a293e 100644 --- a/static_core/plugins/ets/runtime/ets_stubs-inl.h +++ b/static_core/plugins/ets/runtime/ets_stubs-inl.h @@ -33,12 +33,6 @@ ALWAYS_INLINE inline bool EtsReferenceNullish(EtsCoroutine *coro, EtsObject *ref return ref == nullptr || ref == EtsObject::FromCoreType(coro->GetNullValue()); } -ALWAYS_INLINE inline bool IsReferenceNullish(EtsCoroutine *coro, EtsObject *ref) -{ - ASSERT(coro != nullptr); - return ref == nullptr || ref == EtsObject::FromCoreType(coro->GetNullValue()); -} - template ALWAYS_INLINE inline bool EtsReferenceEquals(EtsCoroutine *coro, EtsObject *ref1, EtsObject *ref2) { @@ -46,11 +40,11 @@ ALWAYS_INLINE inline bool EtsReferenceEquals(EtsCoroutine *coro, EtsObject *ref1 return true; } - if (IsReferenceNullish(coro, ref1) || IsReferenceNullish(coro, ref2)) { + if (EtsReferenceNullish(coro, ref1) || EtsReferenceNullish(coro, ref2)) { if constexpr (IS_STRICT) { return false; } else { - return IsReferenceNullish(coro, ref1) && IsReferenceNullish(coro, ref2); + return EtsReferenceNullish(coro, ref1) && EtsReferenceNullish(coro, ref2); } } diff --git a/static_core/plugins/ets/runtime/ets_stubs.cpp b/static_core/plugins/ets/runtime/ets_stubs.cpp index b2f1f6f6ef..a6389fb254 100644 --- a/static_core/plugins/ets/runtime/ets_stubs.cpp +++ b/static_core/plugins/ets/runtime/ets_stubs.cpp @@ -300,7 +300,7 @@ EtsString *EtsGetTypeof(EtsCoroutine *coro, EtsObject *obj) bool EtsGetIstrue(EtsCoroutine *coro, EtsObject *obj) { - if (IsReferenceNullish(coro, obj)) { + if (EtsReferenceNullish(coro, obj)) { return false; } EtsClass *cls = obj->GetClass(); diff --git a/static_core/plugins/ets/runtime/intrinsics/std_core_Class.cpp b/static_core/plugins/ets/runtime/intrinsics/std_core_Class.cpp index 0c59560841..216ada25ab 100644 --- a/static_core/plugins/ets/runtime/intrinsics/std_core_Class.cpp +++ b/static_core/plugins/ets/runtime/intrinsics/std_core_Class.cpp @@ -26,7 +26,14 @@ #include "plugins/ets/runtime/types/ets_primitives.h" #include "plugins/ets/runtime/types/ets_runtime_linker.h" #include "plugins/ets/runtime/types/ets_string.h" +#include "plugins/ets/runtime/types/ets_class.h" +#include "plugins/ets/runtime/types/ets_reflect_field.h" +#include "plugins/ets/runtime/types/ets_reflect_method.h" +#include "plugins/ets/runtime/types/ets_typeapi.h" +#include "plugins/ets/runtime/ets_annotation.h" +#include "plugins/ets/runtime/ets_utils.h" #include "runtime/handle_scope-inl.h" +#include "utils/utf.h" #ifdef PANDA_ETS_INTEROP_JS #include "plugins/ets/runtime/interop_js/interop_context.h" @@ -47,7 +54,8 @@ EtsRuntimeLinker *StdCoreClassGetLinker(EtsClass *cls) EtsClass *StdCoreClassOf(EtsObject *obj) { ASSERT(obj != nullptr); - return obj->GetClass(); + auto *cls = obj->GetClass(); + return cls->ResolvePublicClass(); } EtsClass *StdCoreClassCurrent() @@ -189,4 +197,324 @@ EtsBoolean StdCoreClassIsFixedArray(EtsClass *cls) return cls->IsArrayClass(); } +static EtsReflectMethod *CreateEtsReflectMethodUnderHandleScope(EtsCoroutine *coro, EtsMethod *method) +{ + ASSERT(coro != nullptr); + ASSERT(method != nullptr); + + auto reflectMethod = EtsHandle(coro, EtsReflectMethod::Create(coro)); + if (UNLIKELY(reflectMethod.GetPtr() == nullptr)) { + ASSERT(coro->HasPendingException()); + return nullptr; + } + ASSERT(reflectMethod.GetPtr() != nullptr); + + auto *ownerType = method->GetClass(); + reflectMethod.GetPtr()->SetOwnerType(ownerType); + reflectMethod.GetPtr()->SetEtsMethod(reinterpret_cast(method)); + + // Set specific attributes + uint32_t attr = 0; + attr |= (method->IsStatic()) ? static_cast(EtsTypeAPIAttributes::STATIC) : 0U; + attr |= (method->IsConstructor()) ? static_cast(EtsTypeAPIAttributes::CONSTRUCTOR) : 0U; + attr |= (method->IsAbstract()) ? static_cast(EtsTypeAPIAttributes::ABSTRACT) : 0U; + attr |= (method->IsGetter()) ? static_cast(EtsTypeAPIAttributes::GETTER) : 0U; + attr |= (method->IsSetter()) ? static_cast(EtsTypeAPIAttributes::SETTER) : 0U; + attr |= (method->IsFinal()) ? static_cast(EtsTypeAPIAttributes::FINAL) : 0U; + panda_file::File::EntityId asyncAnnId = EtsAnnotation::FindAsyncAnnotation(method->GetPandaMethod()); + attr |= (method->IsNative() && !asyncAnnId.IsValid()) ? static_cast(EtsTypeAPIAttributes::NATIVE) : 0U; + attr |= (asyncAnnId.IsValid()) ? static_cast(EtsTypeAPIAttributes::ASYNC) : 0U; + + reflectMethod.GetPtr()->SetAttributes(attr); + + int8_t accessMod = method->IsPublic() + ? static_cast(EtsTypeAPIAccessModifier::PUBLIC) + : (method->IsProtected() ? static_cast(EtsTypeAPIAccessModifier::PROTECTED) + : static_cast(EtsTypeAPIAccessModifier::PRIVATE)); + + reflectMethod.GetPtr()->SetAccessMod(accessMod); + + return reflectMethod.GetPtr(); +} + +static PandaVector GetInstanceMethods(EtsClass *cls, bool onlyPublic) +{ + PandaVector instanceMethods; + instanceMethods.reserve(cls->GetRuntimeClass()->GetVTableSize()); + + cls->EnumerateVtable([&instanceMethods, onlyPublic](Method *method) { + if (!method->IsConstructor() && ((onlyPublic && method->IsPublic()) || !onlyPublic)) { + instanceMethods.push_back(EtsMethod::FromRuntimeMethod(method)); + } + return false; + }); + + return instanceMethods; +} + +static ObjectHeader *CreateEtsReflectMethodArray(EtsCoroutine *coro, const PandaVector &methods, + bool isStatic = false, bool isConstructor = false) +{ + [[maybe_unused]] EtsHandleScope scope(coro); + EtsClass *klass = isConstructor ? PlatformTypes(coro)->reflectConstructor + : (isStatic ? PlatformTypes(coro)->reflectStaticMethod + : PlatformTypes(coro)->reflectInstanceMethod); + + EtsHandle arrayH(coro, EtsObjectArray::Create(klass, methods.size())); + if (UNLIKELY(arrayH.GetPtr() == nullptr)) { + ASSERT(coro->HasPendingException()); + return nullptr; + } + ASSERT(arrayH.GetPtr() != nullptr); + + for (size_t idx = 0; idx < methods.size(); ++idx) { + auto *reflectMethod = CreateEtsReflectMethodUnderHandleScope(coro, methods[idx]); + arrayH.GetPtr()->Set(idx, reflectMethod->AsObject()); + } + + return arrayH.GetPtr()->AsObject()->GetCoreType(); +} + +ObjectHeader *StdCoreClassGetInstanceMethodsInternal(EtsClass *cls, EtsBoolean publicOnly) +{ + ASSERT(cls != nullptr); + + auto *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + + auto instanceMethods = GetInstanceMethods(cls, publicOnly != 0U); + + return CreateEtsReflectMethodArray(coro, instanceMethods); +} + +EtsReflectMethod *StdCoreClassGetInstanceMethodByNameInternal(EtsClass *cls, EtsString *name, EtsBoolean publicOnly) +{ + ASSERT(cls != nullptr); + ASSERT(name != nullptr); + + auto *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + + [[maybe_unused]] EtsHandleScope scope(coro); + + // Hadle case with overloads in case of spec issue#362 + EtsMethod *method = cls->GetInstanceMethod(name->GetUtf8().c_str(), nullptr); + if (method == nullptr) { + return nullptr; + } + if ((publicOnly != 0U) && !method->IsPublic()) { + return nullptr; + } + + return CreateEtsReflectMethodUnderHandleScope(coro, method); +} + +static EtsReflectField *CreateEtsReflectFieldUnderHandleScope(EtsCoroutine *coro, EtsField *field) +{ + ASSERT(field != nullptr); + + auto reflectField = EtsHandle(coro, EtsReflectField::Create(coro, field->IsStatic())); + if (UNLIKELY(reflectField.GetPtr() == nullptr)) { + ASSERT(coro->HasPendingException()); + return nullptr; + } + ASSERT(reflectField.GetPtr() != nullptr); + + // do not expose primitive types and string types except std.sore.String + auto *resolvedType = field->GetType()->ResolvePublicClass(); + reflectField.GetPtr()->SetFieldType(resolvedType); + reflectField.GetPtr()->SetEtsField(reinterpret_cast(field)); + reflectField.GetPtr()->SetOwnerType(field->GetDeclaringClass()); + + uint32_t attr = 0; + attr |= (field->IsStatic()) ? static_cast(EtsTypeAPIAttributes::STATIC) : 0U; + attr |= (field->IsReadonly()) ? static_cast(EtsTypeAPIAttributes::READONLY) : 0U; + + reflectField.GetPtr()->SetAttributes(attr); + + int8_t accessMod = field->IsPublic() + ? static_cast(EtsTypeAPIAccessModifier::PUBLIC) + : (field->IsProtected() ? static_cast(EtsTypeAPIAccessModifier::PROTECTED) + : static_cast(EtsTypeAPIAccessModifier::PRIVATE)); + reflectField.GetPtr()->SetAccessMod(accessMod); + + return reflectField.GetPtr(); +} + +EtsReflectField *StdCoreClassGetInstanceFieldByNameInternal(EtsClass *cls, EtsString *name, EtsBoolean publicOnly) +{ + ASSERT(cls != nullptr); + ASSERT(name != nullptr); + + auto *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + + [[maybe_unused]] EtsHandleScope scope(coro); + + EtsField *field = cls->GetFieldIDByName(name->GetUtf8().c_str(), nullptr); + if (field == nullptr) { + return nullptr; + } + if ((publicOnly != 0U) && !field->IsPublic()) { + return nullptr; + } + return CreateEtsReflectFieldUnderHandleScope(coro, field); +} + +static PandaVector GetConstructors(EtsClass *cls, bool onlyPublic) +{ + PandaVector constructors; + + cls->EnumerateDirectMethods([&constructors, onlyPublic](EtsMethod *method) { + if (method->IsConstructor()) { + if ((onlyPublic && method->IsPublic()) || !onlyPublic) { + constructors.push_back(method); + } + } + return false; + }); + + return constructors; +} + +ObjectHeader *StdCoreClassGetConstructorsInternal(EtsClass *cls, EtsBoolean publicOnly) +{ + ASSERT(cls != nullptr); + + auto *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + + auto constructors = GetConstructors(cls, publicOnly != 0U); + + return CreateEtsReflectMethodArray(coro, constructors, false, true); +} + +static PandaVector GetStaticMethods(EtsClass *cls, bool onlyPublic) +{ + PandaVector staticMethods; + PandaUnorderedSet uniqNames; + + cls->EnumerateBaseClasses([&](EtsClass *c) { + auto methods = c->GetRuntimeClass()->GetStaticMethods(); + auto mnum = methods.Size(); + for (uint32_t i = 0; i < mnum; i++) { + if (onlyPublic && !methods[i].IsPublic()) { + continue; + } + PandaString methodName = methods[i].GetFullName(true); + if (!uniqNames.count(methodName)) { + staticMethods.push_back(EtsMethod::FromRuntimeMethod(&methods[i])); + uniqNames.emplace(methodName); + } + } + return false; + }); + return staticMethods; +} + +ObjectHeader *StdCoreClassGetStaticMethodsInternal(EtsClass *cls, EtsBoolean publicOnly) +{ + ASSERT(cls != nullptr); + + auto *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + + auto staticMethods = GetStaticMethods(cls, publicOnly != 0U); + + return CreateEtsReflectMethodArray(coro, staticMethods, true, false); +} + +static PandaVector GetInstanceFields(EtsClass *cls, bool onlyPublic) +{ + PandaVector instanceFields; + PandaUnorderedSet uniqNames; + + cls->EnumerateBaseClasses([&](EtsClass *c) { + auto fields = c->GetRuntimeClass()->GetInstanceFields(); + auto fnum = fields.Size(); + for (uint32_t i = 0; i < fnum; i++) { + if (onlyPublic && !fields[i].IsPublic()) { + continue; + } + PandaString fieldName = utf::Mutf8AsCString(fields[i].GetName().data); + if (!uniqNames.count(fieldName)) { + instanceFields.push_back(EtsField::FromRuntimeField(&fields[i])); + uniqNames.emplace(fieldName); + } + } + return false; + }); + return instanceFields; +} + +static PandaVector GetStaticFields(EtsClass *cls, bool onlyPublic) +{ + PandaVector staticFields; + PandaUnorderedSet uniqNames; + + cls->EnumerateBaseClasses([&](EtsClass *c) { + auto fields = c->GetRuntimeClass()->GetStaticFields(); + auto fnum = fields.Size(); + for (uint32_t i = 0; i < fnum; i++) { + if (onlyPublic && !fields[i].IsPublic()) { + continue; + } + PandaString fieldName = utf::Mutf8AsCString(fields[i].GetName().data); + if (!uniqNames.count(fieldName)) { + staticFields.push_back(EtsField::FromRuntimeField(&fields[i])); + uniqNames.emplace(fieldName); + } + } + return false; + }); + return staticFields; +} + +static ObjectHeader *CreateEtsReflectFieldArray(EtsCoroutine *coro, const PandaVector &fields, + bool isStatic = false) +{ + [[maybe_unused]] EtsHandleScope scope(coro); + EtsClass *klass = isStatic ? PlatformTypes(coro)->reflectStaticField : PlatformTypes(coro)->reflectInstanceField; + + EtsHandle arrayH(coro, EtsObjectArray::Create(klass, fields.size())); + if (UNLIKELY(arrayH.GetPtr() == nullptr)) { + ASSERT(coro->HasPendingException()); + return nullptr; + } + ASSERT(arrayH.GetPtr() != nullptr); + + for (size_t idx = 0; idx < fields.size(); ++idx) { + auto *reflectField = CreateEtsReflectFieldUnderHandleScope(coro, fields[idx]); + arrayH.GetPtr()->Set(idx, reflectField->AsObject()); + } + + return arrayH.GetPtr()->AsObject()->GetCoreType(); +} + +ObjectHeader *StdCoreClassGetInstanceFieldsInternal(EtsClass *cls, EtsBoolean publicOnly) +{ + ASSERT(cls != nullptr); + ASSERT(!(cls->IsInterface())); + + auto *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + + auto instanceFields = GetInstanceFields(cls, publicOnly != 0U); + + return CreateEtsReflectFieldArray(coro, instanceFields); +} + +ObjectHeader *StdCoreClassGetStaticFieldsInternal(EtsClass *cls, EtsBoolean publicOnly) +{ + ASSERT(cls != nullptr); + ASSERT(!(cls->IsInterface())); + + auto *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + + auto staticFields = GetStaticFields(cls, publicOnly != 0U); + + return CreateEtsReflectFieldArray(coro, staticFields, true); +} + } // namespace ark::ets::intrinsics diff --git a/static_core/plugins/ets/runtime/intrinsics/std_core_reflect_Field.cpp b/static_core/plugins/ets/runtime/intrinsics/std_core_reflect_Field.cpp new file mode 100644 index 0000000000..6b1aeb0a89 --- /dev/null +++ b/static_core/plugins/ets/runtime/intrinsics/std_core_reflect_Field.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 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 +#include "intrinsics.h" +#include "plugins/ets/runtime/ets_utils.h" +#include "plugins/ets/runtime/types/ets_reflect_field.h" + +namespace ark::ets::intrinsics { + +extern "C" EtsString *ReflectFieldGetNameInternal(EtsLong etsFieldPtr) +{ + return ManglingUtils::GetDisplayNameStringFromField(reinterpret_cast(etsFieldPtr)); +} + +} // namespace ark::ets::intrinsics diff --git a/static_core/plugins/ets/runtime/intrinsics/std_core_reflect_Method.cpp b/static_core/plugins/ets/runtime/intrinsics/std_core_reflect_Method.cpp new file mode 100644 index 0000000000..89a29f3f74 --- /dev/null +++ b/static_core/plugins/ets/runtime/intrinsics/std_core_reflect_Method.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2025 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 +#include "intrinsics.h" +#include "types/ets_method.h" +#include "plugins/ets/runtime/ets_utils.h" +#include "plugins/ets/runtime/types/ets_typeapi.h" + +namespace ark::ets::intrinsics { + +extern "C" EtsClass *ReflectMethodGetReturnTypeImpl(EtsLong etsMethodPtr) +{ + auto *retCls = reinterpret_cast(etsMethodPtr)->ResolveReturnType(); + return retCls->ResolvePublicClass(); +} + +extern "C" EtsClass *ReflectMethodGetParameterTypeByIdxImpl(EtsLong etsFunctionPtr, EtsInt i) +{ + auto *function = reinterpret_cast(etsFunctionPtr); + ASSERT(function != nullptr); + ASSERT(i >= 0 && static_cast(i) < function->GetNumArgs()); + // 0 is recevier type + i = function->IsStatic() ? i : i + 1; + auto *resolvedType = function->ResolveArgType(i)->ResolvePublicClass(); + + return resolvedType; +} + +extern "C" EtsInt ReflectMethodGetParametersNumImpl(EtsLong etsFunctionPtr) +{ + auto *function = reinterpret_cast(etsFunctionPtr); + ASSERT(function != nullptr); + return function->GetParametersNum(); +} + +extern "C" ObjectHeader *ReflectMethodGetParametersTypesImpl(EtsLong etsFunctionPtr) +{ + auto *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + + auto *function = reinterpret_cast(etsFunctionPtr); + ASSERT(function != nullptr); + auto numParams = function->GetParametersNum(); + [[maybe_unused]] EtsHandleScope scope(coro); + EtsHandle arrayH(coro, EtsObjectArray::Create(PlatformTypes(coro)->coreClass, numParams)); + if (UNLIKELY(arrayH.GetPtr() == nullptr)) { + ASSERT(coro->HasPendingException()); + return nullptr; + } + ASSERT(arrayH.GetPtr() != nullptr); + + for (uint32_t idx = 0; idx < numParams; ++idx) { + // 0 is recevier type + auto i = function->IsStatic() ? idx : idx + 1; + auto *resolvedParameterType = function->ResolveArgType(i)->ResolvePublicClass(); + arrayH.GetPtr()->Set(idx, resolvedParameterType->AsObject()); + } + + return arrayH.GetPtr()->AsObject()->GetCoreType(); +} + +extern "C" EtsString *ReflectMethodGetNameInternal(EtsLong etsMethodPtr) +{ + auto *method = reinterpret_cast(etsMethodPtr); + EtsString *name; + if (method->IsInstanceConstructor()) { + name = EtsString::CreateFromMUtf8(CONSTRUCTOR_NAME); + } else { + name = method->GetNameString(); + } + if (UNLIKELY(name == nullptr)) { + [[maybe_unused]] auto *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + ASSERT(coro->HasPendingException()); + return nullptr; + } + ASSERT(name != nullptr); + return name; +} + +} // namespace ark::ets::intrinsics diff --git a/static_core/plugins/ets/runtime/types/ets_class.cpp b/static_core/plugins/ets/runtime/types/ets_class.cpp index ba928ab9fe..b636faf6a4 100644 --- a/static_core/plugins/ets/runtime/types/ets_class.cpp +++ b/static_core/plugins/ets/runtime/types/ets_class.cpp @@ -20,6 +20,7 @@ #include "macros.h" #include "plugins/ets/runtime/ets_class_linker_extension.h" #include "plugins/ets/runtime/ets_exceptions.h" +#include "plugins/ets/runtime/ets_utils.h" #include "plugins/ets/runtime/types/ets_array.h" #include "plugins/ets/runtime/types/ets_object.h" #include "plugins/ets/runtime/types/ets_field.h" @@ -979,4 +980,61 @@ EtsObject *EtsClass::CreateInstance() return objHandle.GetPtr(); } +EtsClass *EtsClass::ResolveStringClass() +{ + ASSERT(IsStringClass()); + auto *coreClass = GetRuntimeClass(); + if (coreClass->IsLineStringClass() || coreClass->IsSlicedStringClass() || coreClass->IsTreeStringClass()) { + EtsCoroutine *coroutine = EtsCoroutine::GetCurrent(); + ASSERT(coroutine != nullptr); + const EtsPlatformTypes *ptypes = PlatformTypes(coroutine); + return ptypes->coreString; + } + return this; +} + +static EtsClass *ResolvePrimitivePublicClass(panda_file::Type type) +{ + EtsCoroutine *coroutine = EtsCoroutine::GetCurrent(); + ASSERT(coroutine != nullptr); + const EtsPlatformTypes *ptypes = PlatformTypes(coroutine); + switch (type.GetId()) { + case panda_file::Type::TypeId::U1: + return ptypes->coreBoolean; + case panda_file::Type::TypeId::I8: + return ptypes->coreByte; + case panda_file::Type::TypeId::I16: + return ptypes->coreShort; + case panda_file::Type::TypeId::U16: + return ptypes->coreChar; + case panda_file::Type::TypeId::I32: + return ptypes->coreInt; + case panda_file::Type::TypeId::I64: + return ptypes->coreLong; + case panda_file::Type::TypeId::F32: + return ptypes->coreFloat; + case panda_file::Type::TypeId::F64: + return ptypes->coreDouble; + case panda_file::Type::TypeId::VOID: + return coroutine->GetPandaVM()->GetClassLinker()->GetClassRoot(EtsClassRoot::VOID); + default: + LOG(FATAL, RUNTIME) << "ResolveArgType: not a valid ets type for " << type; + return nullptr; + }; +} + +EtsClass *EtsClass::ResolvePublicClass() +{ + if (!IsStringClass() && !IsPrimitive()) { + return this; + } + if (IsStringClass()) { + return ResolveStringClass(); + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto type = panda_file::Type::GetTypeIdBySignature(GetDescriptor()[0]); + return ResolvePrimitivePublicClass(type); +} + } // namespace ark::ets diff --git a/static_core/plugins/ets/runtime/types/ets_class.h b/static_core/plugins/ets/runtime/types/ets_class.h index 78083df593..9f225681ee 100644 --- a/static_core/plugins/ets/runtime/types/ets_class.h +++ b/static_core/plugins/ets/runtime/types/ets_class.h @@ -46,6 +46,8 @@ class EtsErrorOptions; class EtsTypeAPIField; class EtsTypeAPIMethod; class EtsTypeAPIParameter; +class EtsReflectMethod; +class EtsReflectField; enum class EtsType; @@ -330,8 +332,12 @@ public: EtsMethod *ResolveVirtualMethod(const EtsMethod *method) const; + EtsClass *ResolvePublicClass(); + + PANDA_PUBLIC_API EtsClass *ResolveStringClass(); + template - void EnumerateMethods(const Callback &callback) + void EnumerateDirectMethods(const Callback &callback) const { for (auto &method : GetRuntimeClass()->GetMethods()) { bool finished = callback(reinterpret_cast(&method)); @@ -342,7 +348,19 @@ public: } template - void EnumerateDirectInterfaces(const Callback &callback) + void EnumerateVtable(Callback cb) + { + auto vtable = GetRuntimeClass()->GetVTable(); + for (auto *method : vtable) { + bool res = cb(method); + if (res) { + break; + } + } + } + + template + void EnumerateDirectInterfaces(const Callback &callback) const { for (Class *runtimeInterface : GetRuntimeClass()->GetInterfaces()) { EtsClass *interface = EtsClass::FromRuntimeClass(runtimeInterface); diff --git a/static_core/plugins/ets/runtime/types/ets_method.cpp b/static_core/plugins/ets/runtime/types/ets_method.cpp index 57937265f6..a063e1e524 100644 --- a/static_core/plugins/ets/runtime/types/ets_method.cpp +++ b/static_core/plugins/ets/runtime/types/ets_method.cpp @@ -25,6 +25,7 @@ #include "plugins/ets/runtime/ets_panda_file_items.h" #include "plugins/ets/runtime/types/ets_primitives.h" #include "plugins/ets/runtime/types/ets_type.h" +#include "plugins/ets/runtime/ets_utils.h" namespace ark::ets { @@ -168,6 +169,33 @@ uint32_t EtsMethod::GetNumMandatoryArgs() return numMandatoryArgs; } +static EtsClass *ResolveTypePrimitive(panda_file::Type type, EtsClassLinker *classLinker) +{ + switch (type.GetId()) { + case panda_file::Type::TypeId::U1: + return classLinker->GetClassRoot(EtsClassRoot::BOOLEAN); + case panda_file::Type::TypeId::I8: + return classLinker->GetClassRoot(EtsClassRoot::BYTE); + case panda_file::Type::TypeId::I16: + return classLinker->GetClassRoot(EtsClassRoot::SHORT); + case panda_file::Type::TypeId::U16: + return classLinker->GetClassRoot(EtsClassRoot::CHAR); + case panda_file::Type::TypeId::I32: + return classLinker->GetClassRoot(EtsClassRoot::INT); + case panda_file::Type::TypeId::I64: + return classLinker->GetClassRoot(EtsClassRoot::LONG); + case panda_file::Type::TypeId::F32: + return classLinker->GetClassRoot(EtsClassRoot::FLOAT); + case panda_file::Type::TypeId::F64: + return classLinker->GetClassRoot(EtsClassRoot::DOUBLE); + case panda_file::Type::TypeId::VOID: + return classLinker->GetClassRoot(EtsClassRoot::VOID); + default: + LOG(FATAL, RUNTIME) << "ResolveArgType: not a valid ets type for " << type; + return nullptr; + }; +} + EtsClass *EtsMethod::ResolveArgType(uint32_t idx) { if (!IsStatic()) { @@ -192,31 +220,25 @@ EtsClass *EtsMethod::ResolveArgType(uint32_t idx) } } ASSERT(refIdx <= proto.GetRefTypes().size()); - return classLinker->GetClass(proto.GetRefTypes()[refIdx].data(), false, GetClass()->GetLoadContext()); + auto *resCls = classLinker->GetClass(proto.GetRefTypes()[refIdx].data(), false, GetClass()->GetLoadContext()); + return resCls->ResolvePublicClass(); } // get primitive type - switch (type.GetId()) { - case panda_file::Type::TypeId::U1: - return classLinker->GetClassRoot(EtsClassRoot::BOOLEAN); - case panda_file::Type::TypeId::I8: - return classLinker->GetClassRoot(EtsClassRoot::BYTE); - case panda_file::Type::TypeId::I16: - return classLinker->GetClassRoot(EtsClassRoot::SHORT); - case panda_file::Type::TypeId::U16: - return classLinker->GetClassRoot(EtsClassRoot::CHAR); - case panda_file::Type::TypeId::I32: - return classLinker->GetClassRoot(EtsClassRoot::INT); - case panda_file::Type::TypeId::I64: - return classLinker->GetClassRoot(EtsClassRoot::LONG); - case panda_file::Type::TypeId::F32: - return classLinker->GetClassRoot(EtsClassRoot::FLOAT); - case panda_file::Type::TypeId::F64: - return classLinker->GetClassRoot(EtsClassRoot::DOUBLE); - default: - LOG(FATAL, RUNTIME) << "ResolveArgType: not a valid ets type for " << type; - return nullptr; - }; + return ResolveTypePrimitive(type, classLinker); +} + +EtsClass *EtsMethod::ResolveReturnType() +{ + panda_file::Type retType = GetPandaMethod()->GetReturnType(); + auto *classLinker = PandaEtsVM::GetCurrent()->GetClassLinker(); + if (!retType.IsPrimitive()) { + auto proto = GetPandaMethod()->GetProto(); + const char *descriptor = proto.GetReturnTypeDescriptor().data(); + auto *resCls = classLinker->GetClass(descriptor, false, GetClass()->GetLoadContext()); + return resCls->ResolvePublicClass(); + } + return ResolveTypePrimitive(retType, classLinker); } PandaString EtsMethod::GetMethodSignature(bool includeReturnType) const diff --git a/static_core/plugins/ets/runtime/types/ets_method.h b/static_core/plugins/ets/runtime/types/ets_method.h index 42103ff387..dd8b4e9c84 100644 --- a/static_core/plugins/ets/runtime/types/ets_method.h +++ b/static_core/plugins/ets/runtime/types/ets_method.h @@ -138,14 +138,7 @@ public: PANDA_PUBLIC_API EtsClass *ResolveArgType(uint32_t idx); - EtsClass *ResolveReturnType() - { - Method::Proto proto = GetPandaMethod()->GetProto(); - const char *descriptor = proto.GetReturnTypeDescriptor().data(); - Runtime::GetCurrent()->GetClassLinker(); - return EtsClass::FromRuntimeClass(Runtime::GetCurrent()->GetClassLinker()->GetClass( - utf::CStringAsMutf8(descriptor), false, GetClass()->GetLoadContext())); - } + EtsClass *ResolveReturnType(); size_t GetVTableID() const { diff --git a/static_core/plugins/ets/runtime/types/ets_reflect_field.cpp b/static_core/plugins/ets/runtime/types/ets_reflect_field.cpp new file mode 100644 index 0000000000..09c218de8f --- /dev/null +++ b/static_core/plugins/ets/runtime/types/ets_reflect_field.cpp @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2025 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 "plugins/ets/runtime/types/ets_reflect_field.h" +#include "plugins/ets/runtime/ets_coroutine.h" +#include "plugins/ets/runtime/ets_platform_types.h" + +namespace ark::ets { + +EtsReflectField *EtsReflectField::Create(EtsCoroutine *etsCoroutine, bool isStatic) +{ + EtsClass *klass = + isStatic ? PlatformTypes(etsCoroutine)->reflectStaticField : PlatformTypes(etsCoroutine)->reflectInstanceField; + EtsObject *etsObject = EtsObject::Create(etsCoroutine, klass); + return EtsReflectField::FromEtsObject(etsObject); +} + +} // namespace ark::ets diff --git a/static_core/plugins/ets/runtime/types/ets_reflect_field.h b/static_core/plugins/ets/runtime/types/ets_reflect_field.h new file mode 100644 index 0000000000..0c14f31083 --- /dev/null +++ b/static_core/plugins/ets/runtime/types/ets_reflect_field.h @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2025 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_PLUGINS_ETS_RUNTIME_TYPES_ETS_REFLECT_FIELD_H +#define PANDA_PLUGINS_ETS_RUNTIME_TYPES_ETS_REFLECT_FIELD_H + +#include "plugins/ets/runtime/types/ets_object.h" +#include "plugins/ets/runtime/types/ets_string.h" +#include "plugins/ets/runtime/types/ets_primitives.h" + +namespace ark::ets { + +namespace test { +class EtsReflectTest; +} // namespace test + +class EtsCoroutine; + +class EtsReflectField : public ObjectHeader { +public: + EtsReflectField() = delete; + ~EtsReflectField() = delete; + + NO_COPY_SEMANTIC(EtsReflectField); + NO_MOVE_SEMANTIC(EtsReflectField); + + static EtsReflectField *Create(EtsCoroutine *etsCoroutine, bool isStatic = false); + + EtsObject *AsObject() + { + return EtsObject::FromCoreType(this); + } + + const EtsObject *AsObject() const + { + return EtsObject::FromCoreType(this); + } + + static EtsReflectField *FromEtsObject(EtsObject *field) + { + return reinterpret_cast(field); + } + + void SetFieldType(EtsClass *fieldType) + { + ASSERT(fieldType != nullptr); + ObjectAccessor::SetObject(this, MEMBER_OFFSET(EtsReflectField, fieldType_), + fieldType->AsObject()->GetCoreType()); + } + + void SetOwnerType(EtsClass *ownerType) + { + ASSERT(ownerType != nullptr); + ObjectAccessor::SetObject(this, MEMBER_OFFSET(EtsReflectField, ownerType_), + ownerType->AsObject()->GetCoreType()); + } + + void SetEtsField(EtsLong field) + { + [[maybe_unused]] auto *etsField = reinterpret_cast(field); + ASSERT(etsField != nullptr); + ObjectAccessor::SetPrimitive(this, MEMBER_OFFSET(EtsReflectField, etsField_), field); + } + + void SetName(EtsString *name) + { + ObjectAccessor::SetObject(this, MEMBER_OFFSET(EtsReflectField, name_), name->AsObject()->GetCoreType()); + } + + void SetAttributes(EtsInt attr) + { + ObjectAccessor::SetPrimitive(this, MEMBER_OFFSET(EtsReflectField, attr_), attr); + } + + void SetAccessMod(EtsByte accessMod) + { + ObjectAccessor::SetPrimitive(this, MEMBER_OFFSET(EtsReflectField, accessMod_), accessMod); + } + + EtsField *GetEtsField() + { + return reinterpret_cast(etsField_); + } + +private: + ObjectPointer name_; + ObjectPointer fieldType_; + ObjectPointer ownerType_; + EtsInt attr_; // note alignment + EtsLong etsField_; + EtsByte accessMod_; + + friend class test::EtsReflectTest; +}; + +} // namespace ark::ets + +#endif // PANDA_PLUGINS_ETS_RUNTIME_TYPES_ETS_REFLECT_FIELD_H diff --git a/static_core/plugins/ets/runtime/types/ets_reflect_method.cpp b/static_core/plugins/ets/runtime/types/ets_reflect_method.cpp new file mode 100644 index 0000000000..d4c37906a4 --- /dev/null +++ b/static_core/plugins/ets/runtime/types/ets_reflect_method.cpp @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2025 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 "plugins/ets/runtime/types/ets_reflect_method.h" +#include "plugins/ets/runtime/ets_coroutine.h" +#include "plugins/ets/runtime/ets_platform_types.h" + +namespace ark::ets { + +EtsReflectMethod *EtsReflectMethod::Create(EtsCoroutine *etsCoroutine, bool isStatic, bool isConstructor) +{ + EtsClass *klass = isConstructor ? PlatformTypes(etsCoroutine)->reflectConstructor + : (isStatic ? PlatformTypes(etsCoroutine)->reflectStaticMethod + : PlatformTypes(etsCoroutine)->reflectInstanceMethod); + EtsObject *etsObject = EtsObject::Create(etsCoroutine, klass); + return EtsReflectMethod::FromEtsObject(etsObject); +} + +} // namespace ark::ets diff --git a/static_core/plugins/ets/runtime/types/ets_reflect_method.h b/static_core/plugins/ets/runtime/types/ets_reflect_method.h new file mode 100644 index 0000000000..27ff9339b7 --- /dev/null +++ b/static_core/plugins/ets/runtime/types/ets_reflect_method.h @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2025 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_PLUGINS_ETS_RUNTIME_TYPES_ETS_REFLECT_METHOD_H +#define PANDA_PLUGINS_ETS_RUNTIME_TYPES_ETS_REFLECT_METHOD_H + +#include "mem/object_pointer.h" +#include "plugins/ets/runtime/types/ets_object.h" +#include "plugins/ets/runtime/types/ets_primitives.h" +#include "plugins/ets/runtime/types/ets_string.h" + +namespace ark::ets { + +namespace test { +class EtsReflectTest; +} // namespace test + +class EtsCoroutine; + +class EtsReflectMethod : public ObjectHeader { +public: + EtsReflectMethod() = delete; + ~EtsReflectMethod() = delete; + + NO_COPY_SEMANTIC(EtsReflectMethod); + NO_MOVE_SEMANTIC(EtsReflectMethod); + + static EtsReflectMethod *Create(EtsCoroutine *etsCoroutine, bool isStatic = false, bool isConstructor = false); + + EtsObject *AsObject() + { + return EtsObject::FromCoreType(this); + } + + const EtsObject *AsObject() const + { + return EtsObject::FromCoreType(this); + } + + static EtsReflectMethod *FromEtsObject(EtsObject *method) + { + return reinterpret_cast(method); + } + + void SetName(EtsString *name) + { + ASSERT(name != nullptr); + ObjectAccessor::SetObject(this, MEMBER_OFFSET(EtsReflectMethod, name_), name->AsObject()->GetCoreType()); + } + + void SetOwnerType(EtsClass *ownerType) + { + ASSERT(ownerType != nullptr); + ObjectAccessor::SetObject(this, MEMBER_OFFSET(EtsReflectMethod, ownerType_), + ownerType->AsObject()->GetCoreType()); + } + + void SetEtsMethod(EtsLong method) + { + [[maybe_unused]] auto *etsMethod = reinterpret_cast(method); + ASSERT(etsMethod != nullptr); + ObjectAccessor::SetPrimitive(this, MEMBER_OFFSET(EtsReflectMethod, etsMethod_), method); + } + + void SetAttributes(EtsInt attr) + { + ObjectAccessor::SetPrimitive(this, MEMBER_OFFSET(EtsReflectMethod, attr_), attr); + } + + void SetAccessMod(EtsByte accessMod) + { + ObjectAccessor::SetPrimitive(this, MEMBER_OFFSET(EtsReflectMethod, accessMod_), accessMod); + } + + EtsMethod *GetEtsMethod() + { + return reinterpret_cast(etsMethod_); + } + +private: + ObjectPointer name_; + ObjectPointer ownerType_; + EtsLong etsMethod_; + EtsInt attr_; // note alignment + EtsByte accessMod_; + + friend class test::EtsReflectTest; +}; + +} // namespace ark::ets + +#endif // PANDA_PLUGINS_ETS_RUNTIME_TYPES_ETS_REFLECT_METHOD_H diff --git a/static_core/plugins/ets/stdlib/std/core/Class.ets b/static_core/plugins/ets/stdlib/std/core/Class.ets index eafb797034..1991158195 100644 --- a/static_core/plugins/ets/stdlib/std/core/Class.ets +++ b/static_core/plugins/ets/stdlib/std/core/Class.ets @@ -36,6 +36,15 @@ export final class Class { return this.superClass } + /** + * Gets the base class of a class + * + * @returns The superclass of a class, or `undefined` if this is the Object class + */ + public getBase(): Class | undefined { + return this.superClass + } + public native getLinker(): RuntimeLinker public native getInterfaces(): FixedArray @@ -71,4 +80,78 @@ export final class Class { public native isFixedArray(): boolean + /** + * Gets only public instance methods of a class + * + * @returns Immutable array of instance {@link reflect.InstanceMethod} objects + */ + public getInstanceMethods(): FixedArray { + return this.getInstanceMethodsInternal(true) + } + + /** + * Looks up an public instance method by name + * + * @param name - The method name to search for + * + * @returns The {@link reflect.InstanceMethod} if found, `undefined` otherwise + */ + public getInstanceMethodByName(name: string): reflect.InstanceMethod | undefined { + return this.getInstanceMethodByNameInternal(name, true) + } + + /** + * Looks up an public instance field by name + * + * @param name - The field name to search for + * + * @returns The {@link reflect.InstanceField} if found, `undefined` otherwise + */ + public getInstanceFieldByName(name: string): reflect.InstanceField | undefined { + return this.getInstanceFieldByNameInternal(name, true) + } + + /** + * Gets only public instance fields of a class + * + * @returns Immutable array of instance {@link reflect.InstanceField} objects + */ + public getInstanceFields(): FixedArray { + return this.getInstanceFieldsInternal(true) + } + + /** + * Gets only public static methods of a class + * + * @returns Immutable array of static {@link reflect.StaticMethod} objects + */ + public getStaticMethods(): FixedArray { + return this.getStaticMethodsInternal(true) + } + + /** + * Gets only public static fields of a class + * + * @returns Immutable array of static {@link reflect.StaticField} objects + */ + public getStaticFields(): FixedArray { + return this.getStaticFieldsInternal(true) + } + + /** + * Gets all public constructors of a class + * + * @returns Immutable array of constructor {@link reflect.Constructor} objects + */ + public getConstructors(): FixedArray { + return this.getConstructorsInternal(true) + } + + private native getInstanceMethodsInternal(publicOnly: boolean): FixedArray + private native getInstanceMethodByNameInternal(name: string, publicOnly: boolean): reflect.InstanceMethod | undefined + private native getInstanceFieldByNameInternal(name: string, publicOnly: boolean): reflect.InstanceField | undefined + private native getInstanceFieldsInternal(publicOnly: boolean): FixedArray + private native getStaticMethodsInternal(publicOnly: boolean): FixedArray + private native getStaticFieldsInternal(publicOnly: boolean): FixedArray + private native getConstructorsInternal(publicOnly: boolean): FixedArray } diff --git a/static_core/plugins/ets/stdlib/std/core/ReflectConstructor.ets b/static_core/plugins/ets/stdlib/std/core/ReflectConstructor.ets new file mode 100644 index 0000000000..6d45b998fb --- /dev/null +++ b/static_core/plugins/ets/stdlib/std/core/ReflectConstructor.ets @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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. + */ + +package std.core; + +export namespace reflect { + +/** + * Represents constructor of class + */ +export final class Constructor extends Method { + private constructor() { throw new Error("Constructor constructor called") } + + public equals(other: Constructor): boolean { + throw new Error("Not implemeted yet") + } + + public invoke(args?: FixedArray): Any { + throw new Error("Not implemeted yet") + } +} + +} // namespace reflect diff --git a/static_core/plugins/ets/stdlib/std/core/ReflectField.ets b/static_core/plugins/ets/stdlib/std/core/ReflectField.ets new file mode 100644 index 0000000000..40812575c4 --- /dev/null +++ b/static_core/plugins/ets/stdlib/std/core/ReflectField.ets @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 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. + */ + +package std.core; + +export namespace reflect { + +/** + * Represents field of class or interface + * + */ +class Field { + protected name: string | undefined + protected fieldType: Class + protected ownerType: Class + protected attributes: int + protected fieldPtr: long + protected accessMod: byte + + private native constructor() + + public isReadonly(): boolean { + return (this.attributes & Attributes.READONLY) != 0 + } + + public isPublic(): boolean { + return (this.accessMod == AccessModifier.PUBLIC) + } + + public isPrivate(): boolean { + return (this.accessMod == AccessModifier.PRIVATE) + } + + public isProtected(): boolean { + return (this.accessMod == AccessModifier.PROTECTED) + } + + public getType(): Class { + return this.fieldType + } + + public getOwner(): Class { + return this.ownerType + } + + public getName(): string { + return this.name ?? Field.getNameInternal(this.fieldPtr) + } + + private static native getNameInternal(fieldPtr: long): string +} + +} // namespace reflect diff --git a/static_core/plugins/ets/stdlib/std/core/ReflectInstanceField.ets b/static_core/plugins/ets/stdlib/std/core/ReflectInstanceField.ets new file mode 100644 index 0000000000..378ad129dc --- /dev/null +++ b/static_core/plugins/ets/stdlib/std/core/ReflectInstanceField.ets @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 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. + */ + +package std.core; + +export namespace reflect { + +/** + * Represents instance field of class or interface + * + */ +export final class InstanceField extends Field { + private constructor() { throw new Error("InstanceField constructor called") } + + public equals(other: InstanceField): boolean { + throw new Error("Not implemeted yet") + } + + public getValue(ownerInstance: Object): Any { + throw new Error("Not implemeted yet") + } + + public setValue(ownerInstance: Object, v: Any) { + throw new Error("Not implemeted yet") + } +} + +} // namespace reflect diff --git a/static_core/plugins/ets/stdlib/std/core/ReflectInstanceMethod.ets b/static_core/plugins/ets/stdlib/std/core/ReflectInstanceMethod.ets new file mode 100644 index 0000000000..6aece8fd7f --- /dev/null +++ b/static_core/plugins/ets/stdlib/std/core/ReflectInstanceMethod.ets @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 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. + */ + +package std.core; + +export namespace reflect { + +/** + * Represents instance method of class or interface + */ +export final class InstanceMethod extends Method { + private constructor() { throw new Error("InstanceMethod constructor called") } + + public isFinal(): boolean { + return (this.attributes & Attributes.FINAL) != 0 + } + + public isAsync(): boolean { + return (this.attributes & Attributes.ASYNC) != 0 + } + + public isAbstract(): boolean { + return (this.attributes & Attributes.ABSTRACT) != 0 + } + + public isGetter(): boolean { + return (this.attributes & Attributes.GETTER) != 0 + } + + public isSetter(): boolean { + return (this.attributes & Attributes.SETTER) != 0 + } + + public equals(other: InstanceMethod): boolean { + throw new Error("Not implemeted yet") + } + + public invoke(thisObj: Object, args?: FixedArray): Any { + throw new Error("Not implemeted yet") + } +} + +} // namespace reflect diff --git a/static_core/plugins/ets/stdlib/std/core/ReflectMethod.ets b/static_core/plugins/ets/stdlib/std/core/ReflectMethod.ets new file mode 100644 index 0000000000..e147b043b7 --- /dev/null +++ b/static_core/plugins/ets/stdlib/std/core/ReflectMethod.ets @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2025 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. + */ + +package std.core; + +export namespace reflect { + +/** + * Represents method of class or interface + */ +class Method { + protected name: string | undefined + protected ownerType: Class + protected methodPtr: long + protected attributes: int + protected accessMod: byte + + private native constructor() + + public isNative(): boolean { + return (this.attributes & Attributes.NATIVE) != 0 + } + + public isPublic(): boolean { + return (this.accessMod == AccessModifier.PUBLIC) + } + + public isPrivate(): boolean { + return (this.accessMod == AccessModifier.PRIVATE) + } + + public isProtected(): boolean { + return (this.accessMod == AccessModifier.PROTECTED) + } + + public getName(): string { + return this.name ?? Method.getNameInternal(this.methodPtr) + } + + public getOwner(): Class { + return this.ownerType + } + + public getReturnType(): Class { + return Method.getReturnTypeImpl(this.methodPtr)! + } + + public getParametersTypes(): FixedArray { + return Method.getParametersTypesImpl(this.methodPtr) + } + + public getParameterType(idx: int): Class | undefined { + return Method.getParameterTypeByIdxImpl(this.methodPtr, idx) + } + + public getParametersNum(): int { + return Method.getParametersNumImpl(this.methodPtr) + } + + private static native getNameInternal(methodPtr: long): string + private static native getReturnTypeImpl(methodPtr: long): Class | undefined + private static native getParametersTypesImpl(methodPtr: long): FixedArray + private static native getParameterTypeByIdxImpl(methodPtr: long, idx: int): Class | undefined + private static native getParametersNumImpl(methodPtr: long): int +} + +} // namespace reflect diff --git a/static_core/plugins/ets/stdlib/std/core/ReflectStaticField.ets b/static_core/plugins/ets/stdlib/std/core/ReflectStaticField.ets new file mode 100644 index 0000000000..79c23dabc5 --- /dev/null +++ b/static_core/plugins/ets/stdlib/std/core/ReflectStaticField.ets @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 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. + */ + +package std.core; + +export namespace reflect { + +/** + * Represents static field of class + * + */ +export final class StaticField extends Field { + private constructor() { throw new Error("StaticField constructor called") } + + public equals(other: StaticField): boolean { + throw new Error("Not implemeted yet") + } + + public getValue(): Any { + throw new Error("Not implemeted yet") + } + + public setValue(v: Any) { + throw new Error("Not implemeted yet") + } +} + +} // namespace reflect diff --git a/static_core/plugins/ets/stdlib/std/core/ReflectStaticMethod.ets b/static_core/plugins/ets/stdlib/std/core/ReflectStaticMethod.ets new file mode 100644 index 0000000000..9bc486f45f --- /dev/null +++ b/static_core/plugins/ets/stdlib/std/core/ReflectStaticMethod.ets @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 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. + */ + +package std.core; + +export namespace reflect { + +/** + * Represents static method of class + */ +export final class StaticMethod extends Method { + private constructor() { throw new Error("StaticMethod constructor called") } + + public isAsync(): boolean { + return (this.attributes & Attributes.ASYNC) != 0 + } + + public isGetter(): boolean { + return (this.attributes & Attributes.GETTER) != 0 + } + + public isSetter(): boolean { + return (this.attributes & Attributes.SETTER) != 0 + } + + public equals(other: InstanceMethod): boolean { + throw new Error("Not implemeted yet") + } + + public invoke(args?: FixedArray): Any { + throw new Error("Not implemeted yet") + } +} + +} // namespace reflect diff --git a/static_core/plugins/ets/stdlib/std/testing/arktest.ets b/static_core/plugins/ets/stdlib/std/testing/arktest.ets index edd7c6009e..bab834cac6 100644 --- a/static_core/plugins/ets/stdlib/std/testing/arktest.ets +++ b/static_core/plugins/ets/stdlib/std/testing/arktest.ets @@ -241,7 +241,7 @@ export namespace arktest { cb() } catch (e) { e = (e instanceof Error) ? e as Error - : failingAssertion("An arbitrary object instance was caught!") + : failingAssertion("An arbitrary object instance was caught!") let res = test?.(e) if (res != undefined && res != true) { failingAssertion((typeof res == "string") ? res as string : ("Unexpected value thrown: " + e)) @@ -391,7 +391,7 @@ export namespace arktest { /** * Create a testsuite instance - * @param name name of testsuite + * @param name name of testsuite */ constructor(name: string) { this.name = name; diff --git a/static_core/plugins/ets/subproject_sources.gn b/static_core/plugins/ets/subproject_sources.gn index 3030854f15..a7a0335342 100644 --- a/static_core/plugins/ets/subproject_sources.gn +++ b/static_core/plugins/ets/subproject_sources.gn @@ -170,6 +170,8 @@ srcs_runtime = [ "runtime/intrinsics/std_core_Type.cpp", "runtime/intrinsics/std_core_TypeCreator.cpp", "runtime/intrinsics/std_core_Method.cpp", + "runtime/intrinsics/std_core_reflect_Method.cpp", + "runtime/intrinsics/std_core_reflect_Field.cpp", "runtime/intrinsics/std_core_Value.cpp", "runtime/intrinsics/std_core_gc.cpp", "runtime/intrinsics/std_core_finalization_registry.cpp", @@ -195,7 +197,9 @@ srcs_runtime = [ "runtime/types/ets_string.cpp", "runtime/types/ets_typeapi_parameter.cpp", "runtime/types/ets_typeapi_method.cpp", + "runtime/types/ets_reflect_method.cpp", "runtime/types/ets_typeapi_field.cpp", + "runtime/types/ets_reflect_field.cpp", "runtime/types/ets_typeapi_create.cpp", "runtime/types/ets_waiters_list.cpp", "runtime/types/ets_finalizable_weak_ref.cpp", diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectFieldTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectFieldTest.ets new file mode 100644 index 0000000000..5b563af7f7 --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectFieldTest.ets @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025 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. + */ + +interface Serializable { + serialize(): String +} + +class Point implements Serializable { + protected xP: double + x: double + y: double + + override serialize(): String { + return "{" + this.x + "," + this.y + "}" + } + + add(oth: Point): Point { + let res = new Point() + res.x = this.x + oth.x + res.y = this.y + oth.y + return res + } +} + +class Point3D extends Point { + z: double + y: double = 3.0 + private stateP: int + state: int + static ZERO: double = 0.0 + readonly r: int = 1 +} + +function testField(): void { + let typePoint3D = Class.of(new Point3D()) + + arktest.assertEQ(typePoint3D.getInstanceFieldByName("xP"), undefined) // protected + let xf = typePoint3D.getInstanceFieldByName("x")! + let yf = typePoint3D.getInstanceFieldByName("y")! + arktest.assertEQ(typePoint3D.getInstanceFieldByName("stateP"), undefined) // private + let sf = typePoint3D.getInstanceFieldByName("state")! + let rf = typePoint3D.getInstanceFieldByName("r")! + + let isInherited = (f: reflect.InstanceField, type: Class): boolean => { + return f.getOwner().getName() != type.getName() + } + let isOverrided = (f: reflect.InstanceField, type: Class): boolean => { + if (f.getOwner().getName() != type.getName()) { + return false + } + let baseType = type.getBase()! + return baseType.getInstanceFieldByName(f.getName()) != undefined + } + + arktest.assertEQ(xf.getName(), "x") + arktest.assertEQ(xf.getOwner(), Class.of(new Point())) + arktest.assertEQ(xf.getType(), Class.of(1.0)) + arktest.assertFalse(xf.isReadonly()) + arktest.assertTrue(xf.isPublic()) + arktest.assertFalse(xf.isProtected()) + arktest.assertFalse(xf.isPrivate()) + arktest.assertTrue(isInherited(xf, typePoint3D)) + arktest.assertFalse(isOverrided(xf, typePoint3D)) + arktest.assertEQ(yf.getName(), "y") + arktest.assertEQ(yf.getOwner(), typePoint3D) + arktest.assertEQ(yf.getType(), Class.of(1.0)) + arktest.assertFalse(yf.isReadonly()) + arktest.assertTrue(yf.isPublic()) + arktest.assertFalse(yf.isProtected()) + arktest.assertFalse(yf.isPrivate()) + arktest.assertFalse(isInherited(yf, typePoint3D)) + arktest.assertTrue(isOverrided(yf, typePoint3D)) + arktest.assertEQ(sf.getName(), "state") + arktest.assertEQ(sf.getOwner(), typePoint3D) + arktest.assertEQ(sf.getType(), Class.of(1)) + arktest.assertFalse(sf.isReadonly()) + arktest.assertTrue(sf.isPublic()) + arktest.assertFalse(sf.isProtected()) + arktest.assertFalse(sf.isPrivate()) + arktest.assertFalse(isInherited(sf, typePoint3D)) + arktest.assertFalse(isOverrided(sf, typePoint3D)) + arktest.assertEQ(rf.getName(), "r") + arktest.assertEQ(rf.getOwner(), typePoint3D) + arktest.assertEQ(rf.getType(), Class.of(1)) + arktest.assertTrue(rf.isReadonly()) + arktest.assertTrue(rf.isPublic()) + arktest.assertFalse(rf.isProtected()) + arktest.assertFalse(rf.isPrivate()) + arktest.assertFalse(isInherited(rf, typePoint3D)) + arktest.assertFalse(isOverrided(rf, typePoint3D)) +} + +function main() { + const suite = new arktest.ArkTestsuite('Reflect Field tests') + suite.addTest('Test Reflect Field APIs', testField) + return suite.run() +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectMethodTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectMethodTest.ets new file mode 100644 index 0000000000..f0bf4b4b21 --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectMethodTest.ets @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2025 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. + */ + +interface Serializable { + serialize(): String +} + +class Point implements Serializable { + protected x: double + y: double + + override serialize(): String { + return "{" + this.x + "," + this.y + "}" + } + + add(oth: Point): Point { + let res = new Point() + res.x = this.x + oth.x + res.y = this.y + oth.y + return res + } + + testUnion(a: Int | Int[]): String | Int { + if (a instanceof Int) { + return "1" + } + return 2 + } + + optionalParams(x: int, opt1: int = 1, opt2: int = 2) {} + restParams(x: int[], ...rest: int[]) {} + optReadonlyRestParams(x: readonly int[] = [4], y: int = 4, z: readonly [number, string] = [1, "123"], ...rest: int[]) {} +} + +abstract class Writer { + abstract write(): Int + close() {} +} + +class HTMLWriter extends Writer { + private state_: int + + get state(): int { + return this.state_ + } + + set state(state: int) { + this.state_ = state + } + + static createFileExt(file: String): String { + return file + ".html" + } + + override write(): Int { + return new Int(2) + } + + constructor(state: int) { + this.state_ = state + } + + final finalMethod() {} + + native nativeMethod(): boolean + + async asyncMethod() {} +} + +function testReflectMethod(): void { + let typePoint = Class.of(new Point()) + let typeHWriter = Class.of(new HTMLWriter(10)) + let typeWriter = typeHWriter.getBase()! + + let serializeMethod = typePoint.getInstanceMethodByName("serialize")! + let addMethod = typePoint.getInstanceMethodByName("add")! + let unionMethod = typePoint.getInstanceMethodByName("testUnion")! + let getter = typeHWriter.getInstanceMethodByName("state")! + let setter = typeHWriter.getInstanceMethodByName("state")! + + arktest.assertEQ(serializeMethod.getReturnType(), Class.of("")) + arktest.assertEQ(serializeMethod.getOwner(), typePoint) + arktest.assertEQ(serializeMethod.getParametersNum(), 0) + arktest.assertEQ(addMethod.getParametersNum(), 1) + arktest.assertEQ(addMethod.getParameterType(0)!, typePoint) + arktest.assertEQ(addMethod.getReturnType(), typePoint) + arktest.assertEQ(addMethod.getOwner(), typePoint) + arktest.assertEQ(getter.getParametersNum(), 0) + arktest.assertEQ(getter.getReturnType(), Class.of(1)) + arktest.assertEQ(getter.getOwner(), typeHWriter) + arktest.assertEQ(setter.getParametersNum(), 1) + arktest.assertEQ(setter.getParameterType(0)!, Class.of(1)) + arktest.assertEQ(setter.getReturnType().getName(), "void") + arktest.assertEQ(setter.getOwner(), typeHWriter) +} + +function testMethodAttributes(): void { + let typePoint = Class.of(new Point()) + let typeHWriter = Class.of(new HTMLWriter(10)) + let typeWriter = typeHWriter.getBase()! + + let getterMethod = typeHWriter.getInstanceMethodByName("state")! + let setterMethod = typeHWriter.getInstanceMethodByName("state")! + let abstractWriteMethod = typeWriter.getInstanceMethodByName("write")! + let overrideWriteMethod = typeHWriter.getInstanceMethodByName("write")! + let closeMethod = typeHWriter.getInstanceMethodByName("close")! + let finalMethod = typeHWriter.getInstanceMethodByName("finalMethod")! + let nativeMethod = typeHWriter.getInstanceMethodByName("nativeMethod")! + let asyncMethod = typeHWriter.getInstanceMethodByName("asyncMethod")! + let constructors = typeHWriter.getConstructors() + let constructorOfClass = constructors[0] + + let isInherited = (m: reflect.InstanceMethod, type: Class): boolean => { + return m.getOwner().getName() != type.getName() + } + let isOverrided = (m: reflect.InstanceMethod, type: Class): boolean => { + if (m.getOwner().getName() != type.getName()) { + return false + } + let baseType = Class.of(type.getBase()!) + return baseType.getInstanceMethodByName(m.getName()) != undefined + } + + arktest.assertEQ(getterMethod.getName(), "state") + arktest.assertFalse(isInherited(getterMethod, typeHWriter)) + arktest.assertFalse(isOverrided(getterMethod, typeHWriter)) + arktest.assertFalse(getterMethod.isAbstract()) + arktest.assertTrue(getterMethod.isGetter()) + arktest.assertFalse(getterMethod.isSetter()) + arktest.assertFalse(getterMethod.isFinal()) + arktest.assertFalse(getterMethod.isNative()) + arktest.assertFalse(getterMethod.isAsync()) + arktest.assertTrue(getterMethod.isPublic()) + arktest.assertFalse(getterMethod.isProtected()) + arktest.assertFalse(getterMethod.isPrivate()) + arktest.assertEQ(setterMethod.getName(), "state") + arktest.assertFalse(isInherited(setterMethod, typeHWriter)) + arktest.assertFalse(isOverrided(setterMethod, typeHWriter)) + arktest.assertFalse(setterMethod.isAbstract()) + arktest.assertFalse(setterMethod.isGetter()) + arktest.assertTrue(setterMethod.isSetter()) + arktest.assertFalse(setterMethod.isFinal()) + arktest.assertFalse(setterMethod.isNative()) + arktest.assertFalse(setterMethod.isAsync()) + arktest.assertTrue(setterMethod.isPublic()) + arktest.assertFalse(setterMethod.isProtected()) + arktest.assertFalse(setterMethod.isPrivate()) + arktest.assertEQ(overrideWriteMethod.getName(), "write") + arktest.assertFalse(isInherited(overrideWriteMethod, typeHWriter)) + arktest.assertTrue(isOverrided(overrideWriteMethod, typeHWriter)) + arktest.assertFalse(overrideWriteMethod.isAbstract()) + arktest.assertFalse(overrideWriteMethod.isGetter()) + arktest.assertFalse(overrideWriteMethod.isSetter()) + arktest.assertFalse(overrideWriteMethod.isFinal()) + arktest.assertFalse(overrideWriteMethod.isNative()) + arktest.assertFalse(overrideWriteMethod.isAsync()) + arktest.assertTrue(overrideWriteMethod.isPublic()) + arktest.assertFalse(overrideWriteMethod.isProtected()) + arktest.assertFalse(overrideWriteMethod.isPrivate()) + arktest.assertEQ(abstractWriteMethod.getName(), "write") + arktest.assertFalse(isInherited(abstractWriteMethod, typeWriter)) + arktest.assertFalse(isOverrided(abstractWriteMethod, typeWriter)) + arktest.assertTrue(abstractWriteMethod.isAbstract()) + arktest.assertFalse(abstractWriteMethod.isGetter()) + arktest.assertFalse(abstractWriteMethod.isSetter()) + arktest.assertFalse(abstractWriteMethod.isFinal()) + arktest.assertFalse(abstractWriteMethod.isNative()) + arktest.assertFalse(abstractWriteMethod.isAsync()) + arktest.assertTrue(abstractWriteMethod.isPublic()) + arktest.assertFalse(abstractWriteMethod.isProtected()) + arktest.assertFalse(abstractWriteMethod.isPrivate()) + arktest.assertEQ(closeMethod.getName(), "close") + arktest.assertTrue(isInherited(closeMethod, typeHWriter)) + arktest.assertFalse(isOverrided(closeMethod, typeHWriter)) + arktest.assertFalse(closeMethod.isAbstract()) + arktest.assertFalse(closeMethod.isGetter()) + arktest.assertFalse(closeMethod.isSetter()) + arktest.assertFalse(closeMethod.isFinal()) + arktest.assertFalse(closeMethod.isNative()) + arktest.assertFalse(closeMethod.isAsync()) + arktest.assertTrue(closeMethod.isPublic()) + arktest.assertFalse(closeMethod.isProtected()) + arktest.assertFalse(closeMethod.isPrivate()) + arktest.assertEQ(finalMethod.getName(), "finalMethod") + arktest.assertFalse(isInherited(finalMethod, typeHWriter)) + arktest.assertFalse(isOverrided(finalMethod, typeHWriter)) + arktest.assertFalse(finalMethod.isAbstract()) + arktest.assertFalse(finalMethod.isGetter()) + arktest.assertFalse(finalMethod.isSetter()) + arktest.assertTrue(finalMethod.isFinal()) + arktest.assertFalse(finalMethod.isNative()) + arktest.assertFalse(finalMethod.isAsync()) + arktest.assertTrue(finalMethod.isPublic()) + arktest.assertFalse(finalMethod.isProtected()) + arktest.assertFalse(finalMethod.isPrivate()) + arktest.assertEQ(nativeMethod.getName(), "nativeMethod") + arktest.assertFalse(isInherited(nativeMethod, typeHWriter)) + arktest.assertFalse(isOverrided(nativeMethod, typeHWriter)) + arktest.assertFalse(nativeMethod.isAbstract()) + arktest.assertFalse(nativeMethod.isGetter()) + arktest.assertFalse(nativeMethod.isSetter()) + arktest.assertFalse(nativeMethod.isFinal()) + arktest.assertTrue(nativeMethod.isNative()) + arktest.assertFalse(nativeMethod.isAsync()) + arktest.assertTrue(nativeMethod.isPublic()) + arktest.assertFalse(nativeMethod.isProtected()) + arktest.assertFalse(nativeMethod.isPrivate()) + arktest.assertEQ(asyncMethod.getName(), "asyncMethod") + arktest.assertFalse(isInherited(asyncMethod, typeHWriter)) + arktest.assertFalse(isOverrided(asyncMethod, typeHWriter)) + arktest.assertFalse(asyncMethod.isAbstract()) + arktest.assertFalse(asyncMethod.isGetter()) + arktest.assertFalse(asyncMethod.isSetter()) + arktest.assertFalse(asyncMethod.isFinal()) + arktest.assertFalse(asyncMethod.isNative()) + arktest.assertTrue(asyncMethod.isAsync()) + arktest.assertTrue(asyncMethod.isPublic()) + arktest.assertFalse(asyncMethod.isProtected()) + arktest.assertFalse(asyncMethod.isPrivate()) + arktest.assertEQ(constructorOfClass.getName(), "constructor") + arktest.assertFalse(constructorOfClass.isNative()) + arktest.assertTrue(constructorOfClass.isPublic()) + arktest.assertFalse(constructorOfClass.isProtected()) + arktest.assertFalse(constructorOfClass.isPrivate()) +} + +function testParameters() { + let typePoint = Class.of(new Point()) + + let optionalParamsMethod = typePoint.getInstanceMethodByName("optionalParams")! + let restParamsMethod = typePoint.getInstanceMethodByName("restParams")! + let optReadonlyRestParamsMethod = typePoint.getInstanceMethodByName("optReadonlyRestParams")! + + let res = 0 + let paramsOpt = optionalParamsMethod.getParametersTypes() + let paramsRest = restParamsMethod.getParametersTypes() + arktest.assertEQ(optionalParamsMethod.getParametersNum(), 3) + arktest.assertEQ(restParamsMethod.getParametersNum(), 2) + arktest.assertEQ(optReadonlyRestParamsMethod.getParametersNum(), 4) +} + +function main() { + const suite = new arktest.ArkTestsuite('Reflect Method tests') + suite.addTest('Test Reflect Method base APIs', testReflectMethod) + suite.addTest('Test Reflect Method attributes APIs', testReflectMethod) + suite.addTest('Test Reflect Method Parameters APIs', testParameters) + return suite.run() +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectTypeTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectTypeTest.ets new file mode 100644 index 0000000000..8c31ef276d --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/core/ReflectTypeTest.ets @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2025 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. + */ + +interface Iface { + someDefaultMethod(): string { + return "111" + } +} + +class A implements Iface { + public static stfooFld1: Number = 1 + public static stfooFld2: Number = 1 + + public fooFld1: Number = 1 + public fooFld2: Number = 1 + + public foo1(): void {} + public foo2(): void {} + + public static stfoo1(): void {} + public static stfoo2(): void {} + + private prfoo1(): void {} + private prfoo2(): void {} +} + +final class B extends A { + public static stbooFld1: Number = 2 + public static stbooFld2: Number = 2 + + public booFld1: Number = 2 + public booFld2: Number = 2 + + public boo1(): string { + return "" + } + + public boo2(): string { + return "222" + } + + public static stboo1(): string { + return "" + } + + public static stboo2(): string { + return "222" + } +} + +class CtorsTest { + foo: Number + bla: String + static numOfCtors: Int = 2 + + constructor(x: Number) { + this.foo = x; + this.bla = "bla" + } + + constructor(x: String) { + this.bla = x; + this.foo = 2; + } +} + +function testGetName(type: Class, expected: string) { + arktest.assertEQ(type.getName(), expected) +} + +function testGetInstanceMethods(classType: Class, expectedMethodsArr: string[]) { + let expectedMtdsSet: Set = new Set(expectedMethodsArr) + + let methods: FixedArray = classType.getInstanceMethods() + arktest.assertEQ(methods.length, expectedMethodsArr.length) + + for (let i = 0; i < methods.length; ++i) { + arktest.assertTrue(expectedMtdsSet.has(methods[i].getName())) + } +} + +function testGetInstanceMethodByName(classType: Class, methodsToFind: string[]) { + for (let i = 0; i < methodsToFind.length; ++i) { + let obtainedMethod = classType.getInstanceMethodByName(methodsToFind[i]) + + if (obtainedMethod == undefined) { + throw new Error(`Can't find given method '${methodsToFind[i]}' in testGetInstanceMethodByName`) + } + + arktest.assertEQ(obtainedMethod.getName(), methodsToFind[i]) + } +} + +function tryGetPrivateInstanceMethodByName(classType: Class, methods: string[]) { + for (let i = 0; i < methods.length; ++i) { + let obtainedMethod = classType.getInstanceMethodByName(methods[i]) + if (obtainedMethod != undefined) { + throw new Error(`Given method '${methods[i]}' in tryGetPrivateInstanceMethodByName should be undefined`) + } + } +} + +function testGetConstructors() { + let ctorTestType = Class.of(new CtorsTest(1)) + let ctors: FixedArray = ctorTestType.getConstructors() + arktest.assertEQ(ctors.length, CtorsTest.numOfCtors) + + for (let i = 0; i < ctors.length; ++i) { + arktest.assertEQ(ctors[i].getName(), "constructor") + } +} + +function testGetStaticMethods() { + let info = Class.of(new B()) + let expectedMethodsArr = ([ "keys", "keys", "assign", "hasOwn", + "hasOwn", "values", "values", "entries", + "entries", "fromEntries", "getOwnPropertyNames", + "getOwnPropertyNames", "stboo1", "stboo2", + "stfoo1", "stfoo2" ]) + let expectedMtdsSet: Set = new Set(expectedMethodsArr) + + let methods: FixedArray = info.getStaticMethods() + arktest.assertEQ(methods.length, expectedMethodsArr.length) + for (let i = 0; i < methods.length; ++i) { + arktest.assertTrue(expectedMtdsSet.has(methods[i].getName())) + } +} + +function testGetStaticFields() { + let info = Class.of(new B()) + let expectedFldSet: Set = new Set([ "stfooFld1", "stfooFld2", "stbooFld1", "stbooFld2"]); + + let fields: FixedArray = info.getStaticFields() + arktest.assertEQ(fields.length, expectedFldSet.size) + for (let i = 0; i < fields.length; ++i) { + arktest.assertTrue(expectedFldSet.has(fields[i].getName())) + } +} + +function testGetInstanceFields() { + let info = Class.of(new B()) + let expectedFldSet: Set = new Set([ "fooFld1", "fooFld2", "booFld1", "booFld2"]); + + let fields: FixedArray = info.getInstanceFields() + arktest.assertEQ(fields.length, expectedFldSet.size) + for (let i = 0; i < fields.length; ++i) { + arktest.assertTrue(expectedFldSet.has(fields[i].getName())) + } +} + +function main(): int { + let typeBaseObj = Class.of(new Object()) + + testGetName(typeBaseObj, "std.core.Object") + + let typeA = Class.of(new A()) + + testGetName(typeA, "ReflectTypeTest.A") + + let typeB = Class.of(new B()) + + testGetName(typeB, "ReflectTypeTest.B") + + // Test getBase() + arktest.assertEQ(typeBaseObj.getBase(), undefined) + arktest.assertEQ(typeA.getBase(), typeBaseObj) + arktest.assertEQ(typeB.getBase(), typeA) + + // Test methods getters + let baseObjInstanceMethodsExpected = + [ "toString", "toLocaleString", "$_hashCode", "hasOwnProperty", "hasOwnProperty" ] + + let baseObjStaticMethodsExpected = + [ "keys", "keys", "assign", "hasOwn", "hasOwn", "values", "values", "entries", + "entries", "fromEntries", "getOwnPropertyNames", "getOwnPropertyNames" ] + + testGetInstanceMethods(typeBaseObj, baseObjInstanceMethodsExpected) + testGetInstanceMethods(typeA, baseObjInstanceMethodsExpected.concat(["someDefaultMethod", "foo1", "foo2"])) + testGetInstanceMethods(typeB, baseObjInstanceMethodsExpected.concat(["someDefaultMethod", "foo1", "foo2", "boo1", "boo2"])) + + testGetInstanceMethodByName(typeBaseObj, baseObjInstanceMethodsExpected) + testGetInstanceMethodByName(typeA, baseObjInstanceMethodsExpected.concat(["someDefaultMethod", "foo1", "foo2"])) + testGetInstanceMethodByName(typeB, baseObjInstanceMethodsExpected.concat(["someDefaultMethod", "foo1", "foo2", "boo1", "boo2"])) + + tryGetPrivateInstanceMethodByName(typeA, [ "prfoo1", "prfoo2" ]) + + testGetConstructors() + testGetStaticMethods() + testGetStaticFields() + testGetInstanceFields() + + return 0 +} diff --git a/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt b/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt index 541ddb9c20..75f0074a94 100644 --- a/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt +++ b/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt @@ -55,6 +55,7 @@ panda_ets_add_gtest( ets_runtime_linker_test.cpp ets_abc_runtime_linker_test.cpp ets_typeapi_test.cpp + ets_reflect_test.cpp typed_arrays_test.cpp ets_base_enum_test.cpp ets_error_test.cpp diff --git a/static_core/plugins/ets/tests/runtime/types/ets_class_file_test.cpp b/static_core/plugins/ets/tests/runtime/types/ets_class_file_test.cpp index 1c772a35da..8d94111d0d 100644 --- a/static_core/plugins/ets/tests/runtime/types/ets_class_file_test.cpp +++ b/static_core/plugins/ets/tests/runtime/types/ets_class_file_test.cpp @@ -888,7 +888,7 @@ TEST_F(EtsClassTest, EnumerateMethods) methods.push_back(klass->GetInstanceMethod("foo4", nullptr)); methods.push_back(klass->GetInstanceMethod("foo5", nullptr)); - klass->EnumerateMethods([&enumerateMethods, &methodsVectorSize](EtsMethod *method) { + klass->EnumerateDirectMethods([&enumerateMethods, &methodsVectorSize](EtsMethod *method) { enumerateMethods.push_back(method); return enumerateMethods.size() == methodsVectorSize; }); @@ -933,6 +933,41 @@ TEST_F(EtsClassTest, EnumerateInterfaces) } } +TEST_F(EtsClassTest, EnumerateVTable) +{ + const char *source = R"( + .language eTS + .record Test {} + .function void Test.foo1() {} + .function void Test.foo2() {} + .function void Test.foo3() {} + .function void Test.foo4() {} + .function void Test.foo5() {} + )"; + + EtsClass *klass = GetTestClass(source, "LTest;"); + ASSERT_NE(klass, nullptr); + + std::size_t methodsVectorSize = 5; + std::vector methods(methodsVectorSize); + std::vector enumerateMethods(methodsVectorSize); + + methods.push_back(klass->GetInstanceMethod("foo1", nullptr)); + methods.push_back(klass->GetInstanceMethod("foo2", nullptr)); + methods.push_back(klass->GetInstanceMethod("foo3", nullptr)); + methods.push_back(klass->GetInstanceMethod("foo4", nullptr)); + methods.push_back(klass->GetInstanceMethod("foo5", nullptr)); + + klass->EnumerateVtable([&enumerateMethods, &methodsVectorSize](Method *method) { + enumerateMethods.push_back(EtsMethod::FromRuntimeMethod(method)); + return enumerateMethods.size() == methodsVectorSize; + }); + + for (std::size_t i = 0; i < methodsVectorSize; ++i) { + ASSERT_EQ(methods[i], enumerateMethods[i]); + } +} + } // namespace ark::ets::test // NOLINTEND(readability-magic-numbers) diff --git a/static_core/plugins/ets/tests/runtime/types/ets_reflect_test.cpp b/static_core/plugins/ets/tests/runtime/types/ets_reflect_test.cpp new file mode 100644 index 0000000000..f4ec7dcab6 --- /dev/null +++ b/static_core/plugins/ets/tests/runtime/types/ets_reflect_test.cpp @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2025 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 + +#include "ets_coroutine.h" +#include "ets_vm.h" + +#include "plugins/ets/runtime/types/ets_reflect_field.h" +#include "plugins/ets/runtime/types/ets_reflect_method.h" +#include "tests/runtime/types/ets_test_mirror_classes.h" + +namespace ark::ets::test { + +class EtsReflectTest : public testing::Test { +public: + EtsReflectTest() + { + options_.SetShouldLoadBootPandaFiles(true); + options_.SetShouldInitializeIntrinsics(false); + options_.SetCompilerEnableJit(false); + options_.SetGcType("g1-gc"); + options_.SetLoadRuntimes({"ets"}); + + auto stdlib = std::getenv("PANDA_STD_LIB"); + if (stdlib == nullptr) { + std::cerr << "PANDA_STD_LIB env variable should be set and point to mock_stdlib.abc" << std::endl; + std::abort(); + } + options_.SetBootPandaFiles({stdlib}); + + Runtime::Create(options_); + } + + ~EtsReflectTest() override + { + Runtime::Destroy(); + } + + NO_COPY_SEMANTIC(EtsReflectTest); + NO_MOVE_SEMANTIC(EtsReflectTest); + + void SetUp() override + { + coroutine_ = EtsCoroutine::GetCurrent(); + vm_ = coroutine_->GetPandaVM(); + coroutine_->ManagedCodeBegin(); + } + + void TearDown() override + { + coroutine_->ManagedCodeEnd(); + } + + static std::vector GetReflectMethodClassMembers() + { + return std::vector {MIRROR_FIELD_INFO(EtsReflectMethod, name_, "name"), + MIRROR_FIELD_INFO(EtsReflectMethod, ownerType_, "ownerType"), + MIRROR_FIELD_INFO(EtsReflectMethod, attr_, "attributes"), + MIRROR_FIELD_INFO(EtsReflectMethod, etsMethod_, "methodPtr"), + MIRROR_FIELD_INFO(EtsReflectMethod, accessMod_, "accessMod")}; + } + + static std::vector GetReflectFieldClassMembers() + { + return std::vector {MIRROR_FIELD_INFO(EtsReflectField, name_, "name"), + MIRROR_FIELD_INFO(EtsReflectField, fieldType_, "fieldType"), + MIRROR_FIELD_INFO(EtsReflectField, ownerType_, "ownerType"), + MIRROR_FIELD_INFO(EtsReflectField, attr_, "attributes"), + MIRROR_FIELD_INFO(EtsReflectField, etsField_, "fieldPtr"), + MIRROR_FIELD_INFO(EtsReflectField, accessMod_, "accessMod")}; + } + +protected: + PandaEtsVM *vm_ = nullptr; // NOLINT(misc-non-private-member-variables-in-classes) + +private: + RuntimeOptions options_; + EtsCoroutine *coroutine_ = nullptr; +}; + +TEST_F(EtsReflectTest, ReflectFieldMemoryLayout) +{ + auto *reflectInstanceFieldClass = PlatformTypes(vm_)->reflectInstanceField; + auto *reflectStaticFieldClass = PlatformTypes(vm_)->reflectStaticField; + MirrorFieldInfo::CompareMemberOffsets(reflectInstanceFieldClass, GetReflectFieldClassMembers(), false); + MirrorFieldInfo::CompareMemberOffsets(reflectStaticFieldClass, GetReflectFieldClassMembers(), false); +} + +TEST_F(EtsReflectTest, ReflectMethodMemoryLayout) +{ + auto *reflectInstanceMethodClass = PlatformTypes(vm_)->reflectInstanceMethod; + auto *reflectStaticMethodClass = PlatformTypes(vm_)->reflectStaticMethod; + auto *reflectConstructorClass = PlatformTypes(vm_)->reflectConstructor; + MirrorFieldInfo::CompareMemberOffsets(reflectInstanceMethodClass, GetReflectMethodClassMembers(), false); + MirrorFieldInfo::CompareMemberOffsets(reflectStaticMethodClass, GetReflectMethodClassMembers(), false); + MirrorFieldInfo::CompareMemberOffsets(reflectConstructorClass, GetReflectMethodClassMembers(), false); +} + +} // namespace ark::ets::test diff --git a/static_core/plugins/ets/tests/test-lists/declgenets2ts/ets-func-tests/declgen-ets2ts-func-tests-ignored.txt b/static_core/plugins/ets/tests/test-lists/declgenets2ts/ets-func-tests/declgen-ets2ts-func-tests-ignored.txt index 79f4173a59..cc72eca94d 100644 --- a/static_core/plugins/ets/tests/test-lists/declgenets2ts/ets-func-tests/declgen-ets2ts-func-tests-ignored.txt +++ b/static_core/plugins/ets/tests/test-lists/declgenets2ts/ets-func-tests/declgen-ets2ts-func-tests-ignored.txt @@ -236,3 +236,4 @@ std/core/std_core_string_String_lastIndexOf_String_int.ets std/core/std_core_string_String_lastIndexOf_char_int.ets std/core/std_core_string_String_search_String.ets std/containers/BlockingQueue/AddAndPollStress.ets +std/core/ReflectTypeTest.ets -- Gitee From 37972d055161bc50ef54eb5cd3eaf10436b90d4d Mon Sep 17 00:00:00 2001 From: kurnevichstanislav Date: Wed, 10 Sep 2025 19:48:21 +0300 Subject: [PATCH 2/6] Add skeleton of new Proxy Signed-off-by: kurnevichstanislav Change-Id: I4a2d0e2b05819113a10854fb090bbf8afd452ad9 --- .../plugins/ets/runtime/CMakeLists.txt | 1 + .../ets/runtime/ets_libbase_runtime.yaml | 17 +++ .../ets/runtime/ets_panda_file_items.h | 3 + .../ets/runtime/ets_platform_types.cpp | 2 + .../plugins/ets/runtime/ets_platform_types.h | 3 + .../ets/runtime/intrinsics/reflect_Proxy.cpp | 79 ++++++++++++ .../intrinsics/std_core_TypeCreator.cpp | 2 +- .../plugins/ets/stdlib/std/core/ProxyNew.ets | 119 ++++++++++++++++++ .../plugins/ets/stdlib/std/core/Type.ets | 4 + static_core/plugins/ets/subproject_sources.gn | 1 + .../tests/ets_func_tests/std/core/Proxy.ets | 33 +++++ 11 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp create mode 100644 static_core/plugins/ets/stdlib/std/core/ProxyNew.ets create mode 100644 static_core/plugins/ets/tests/ets_func_tests/std/core/Proxy.ets diff --git a/static_core/plugins/ets/runtime/CMakeLists.txt b/static_core/plugins/ets/runtime/CMakeLists.txt index 3318c1ab9f..bf620bc7a3 100644 --- a/static_core/plugins/ets/runtime/CMakeLists.txt +++ b/static_core/plugins/ets/runtime/CMakeLists.txt @@ -82,6 +82,7 @@ set(ETS_RUNTIME_SOURCES ${ETS_EXT_SOURCES}/intrinsics/std_core_StackTrace.cpp ${ETS_EXT_SOURCES}/intrinsics/std_core_Type.cpp ${ETS_EXT_SOURCES}/intrinsics/std_core_TypeCreator.cpp + ${ETS_EXT_SOURCES}/intrinsics/reflect_Proxy.cpp ${ETS_EXT_SOURCES}/intrinsics/std_core_Method.cpp ${ETS_EXT_SOURCES}/intrinsics/std_core_reflect_Method.cpp ${ETS_EXT_SOURCES}/intrinsics/std_core_reflect_Field.cpp diff --git a/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml b/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml index 90b4c6de61..70f7e447aa 100644 --- a/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml +++ b/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml @@ -9674,3 +9674,20 @@ intrinsics: codegen_arch: [arm64, amd64] codegen_func: CreateMapDelete llvm_codegen_func: EmitMapDelete + +##################### +# std.reflect.Proxy # +##################### + - name: ReflectProxyGenerateProxy + space: ets + class_name: std.core.reflect.Proxy + method_name: generateProxy + static: true + signature: + ret: std.core.Class + args: + - std.core.RuntimeLinker + - std.core.String + - std.core.Class[] + - escompat.Array + impl: ark::ets::intrinsics::ReflectProxyGenerateProxy diff --git a/static_core/plugins/ets/runtime/ets_panda_file_items.h b/static_core/plugins/ets/runtime/ets_panda_file_items.h index 776a702c69..7ea29aca99 100644 --- a/static_core/plugins/ets/runtime/ets_panda_file_items.h +++ b/static_core/plugins/ets/runtime/ets_panda_file_items.h @@ -60,6 +60,9 @@ static constexpr std::string_view REFLECT_STATIC_FIELD = "Lstd/c static constexpr std::string_view REFLECT_STATIC_METHOD = "Lstd/core/reflect/StaticMethod;"; static constexpr std::string_view REFLECT_CONSTRUCTOR = "Lstd/core/reflect/Constructor;"; +// Proxy +static constexpr std::string_view PROXY_CLASS = "Lstd/core/reflect/Proxy;"; + // Runtime classes static constexpr std::string_view STACK_TRACE_ELEMENT = "Lstd/core/StackTraceElement;"; static constexpr std::string_view ABC_FILE = "Lstd/core/AbcFile;"; diff --git a/static_core/plugins/ets/runtime/ets_platform_types.cpp b/static_core/plugins/ets/runtime/ets_platform_types.cpp index 8f2fe33d6b..9e9684db39 100644 --- a/static_core/plugins/ets/runtime/ets_platform_types.cpp +++ b/static_core/plugins/ets/runtime/ets_platform_types.cpp @@ -244,6 +244,8 @@ EtsPlatformTypes::EtsPlatformTypes([[maybe_unused]] EtsCoroutine *coro) findType(&EtsPlatformTypes::reflectStaticMethod, REFLECT_STATIC_METHOD); findType(&EtsPlatformTypes::reflectConstructor, REFLECT_CONSTRUCTOR); + findType(&EtsPlatformTypes::proxy, PROXY_CLASS); + findType(&EtsPlatformTypes::escompatProcess, PROCESS); findMethod(&EtsPlatformTypes::escompatProcessListUnhandledJobs, escompatProcess, "listUnhandledJobs", "Lescompat/Array;:V", true); diff --git a/static_core/plugins/ets/runtime/ets_platform_types.h b/static_core/plugins/ets/runtime/ets_platform_types.h index 9da6cff6dc..eb2752d1f0 100644 --- a/static_core/plugins/ets/runtime/ets_platform_types.h +++ b/static_core/plugins/ets/runtime/ets_platform_types.h @@ -131,6 +131,9 @@ public: EtsClass *reflectStaticMethod {}; EtsClass *reflectConstructor {}; + /* Proxy */ + EtsClass *proxy {}; + /* escompat.Process */ EtsClass *escompatProcess {}; EtsMethod *escompatProcessListUnhandledJobs {}; diff --git a/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp b/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp new file mode 100644 index 0000000000..2a999445b0 --- /dev/null +++ b/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2025 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 "intrinsics.h" + +namespace ark::ets::intrinsics { + +static void CreateProxyConstructor(EtsClass *klass, EtsClass *proxyKlass, EtsMethod *out) +{ + ASSERT(klass != nullptr); + + EtsMethod *proxyClassConstructor = nullptr; + for (auto &method : proxyKlass->GetRuntimeClass()->GetMethods()) { + if (method.IsInstanceConstructor() && !method.IsPrivate()) { + proxyClassConstructor = EtsMethod::FromRuntimeMethod(&method); + ASSERT(proxyClassConstructor->IsProtected()); + break; // Proxy has 2 constructors: default private and protected. + } + } + + new (out) Method(proxyClassConstructor->GetPandaMethod()); + out->GetPandaMethod()->SetClass(klass->GetRuntimeClass()); + // out->GetPandaMethod()->SetAccessFlags() + +} + +extern "C" EtsClass *ReflectProxyGenerateProxy(EtsRuntimeLinker *linker, EtsString *name, ObjectHeader *interfaces, EtsEscompatArray *methods) +{ + ASSERT(linker != nullptr); + ASSERT(name != nullptr); + ASSERT(interfaces != nullptr); + ASSERT(methods != nullptr); + + PandaString descriptor; + ClassHelper::GetDescriptor(utf::CStringAsMutf8(name->GetMutf8().data()), &descriptor); + + auto *coroutine = EtsCoroutine::GetCurrent(); + ASSERT(coroutine != nullptr); + + auto *linkerCtx = linker->GetClassLinkerContext(); + ASSERT(linkerCtx != nullptr); + + auto *classLinker = coroutine->GetPandaVM()->GetClassLinker(); + auto *classLinkerExt = classLinker->GetEtsClassLinkerExtension(); + + auto *tempKlass = classLinkerExt->CreateClass(utf::CStringAsMutf8(descriptor.c_str()), 0, 0, sizeof(EtsClass)); + if (tempKlass == nullptr) { + ASSERT(coroutine->HasPendingException()); + return nullptr; + } + tempKlass->SetLoadContext(linkerCtx); + + auto *proxyKlass = classLinkerExt->GetPlatformTypes()->proxy; + ASSERT(proxyKlass != nullptr); + + size_t idx = 0; + + constexpr size_t NUM_OF_CONSTRUCTORS = 1U; + size_t numMethods = methods->GetActualLength() + NUM_OF_CONSTRUCTORS; + Span proxyMethods (coroutine->GetPandaVM()->GetHeapManager()->GetInternalAllocator()->AllocArray(numMethods), numMethods); + + CreateProxyConstructor(EtsClass::FromRuntimeClass(tempKlass), proxyKlass, &proxyMethods[idx++]); + + return nullptr; +} + +} // namespace ark::ets::intrinsics diff --git a/static_core/plugins/ets/runtime/intrinsics/std_core_TypeCreator.cpp b/static_core/plugins/ets/runtime/intrinsics/std_core_TypeCreator.cpp index e71215ae96..220d2677f6 100644 --- a/static_core/plugins/ets/runtime/intrinsics/std_core_TypeCreator.cpp +++ b/static_core/plugins/ets/runtime/intrinsics/std_core_TypeCreator.cpp @@ -181,7 +181,7 @@ EtsString *TypeAPITypeCreatorCtxCommit(EtsLong ctxPtr, EtsArray *objects, EtsRun } // debug - // panda_file::FileWriter {"/tmp/test.abc"}.WriteBytes(writer.GetData()); + panda_file::FileWriter {"/tmp/test.abc"}.WriteBytes(writer.GetData()); auto *classLinkerContext = targetLinker->GetClassLinkerContext(); ASSERT(!classLinkerContext->IsBootContext()); diff --git a/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets b/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets new file mode 100644 index 0000000000..f5cc8ab4cb --- /dev/null +++ b/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2025 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. + */ + + +package std.core; + +export namespace reflect { + +// Must be renamed to Proxy handler after old proxy is deleted. +export interface InvokationHandler { + get(proxy: Object, method: Method): Any + + set(proxy: Object, method: Method, value: Any): boolean + + invoke(proxy: Object, method: Method, args: FixedArray): Any +} + +// Each new proxy class extends Proxy. +export class Proxy { + protected handler: InvokationHandler + + private static PROXY_PREFIX: string = "$Proxy$" + + private static uniqueNameCounter: long = 0 + + private constructor() { + } + + protected constructor(handler: InvokationHandler) { + this.handler = handler + } + + public getHandler(): InvokationHandler { + return this.handler + } + + public static createClass(linker: RuntimeLinker, interfaces: FixedArray): Class { + if (interfaces.length < 1) { + throw new Error(`At least one interface expected`) + } + + let methods: Array = Proxy.getMethods(interfaces) + let proxyName: string = Proxy.createUniqueName(interfaces) + return Proxy.generateProxy(linker, proxyName, interfaces, methods) + } + + public static newInstance(proxyClass: Class, handler: InvokationHandler): Object { + // Each proxy class has one public constructor, + // so make will create and initialize instance with handler through public constructor. + let t: Type = Type.of(proxyClass) + if (!(t instanceof ClassType)) { + throw new Error(`Expected class type of first argument`) + } + return (t as ClassType).make([handler]) + } + + public static newInstance(linker: RuntimeLinker, interfaces: FixedArray, handler: InvokationHandler): Object { + let proxyClass = Proxy.createClass(linker, interfaces) + return Proxy.newInstance(proxyClass, handler) + } + + private static createUniqueName(interfaces: FixedArray): string { + return Proxy.PROXY_PREFIX.concat(interfaces[0].getName()).concat("$").concat((++Proxy.uniqueNameCounter).toString()) + } + + private static native generateProxy( + linker: RuntimeLinker, + proxyName: string, + interfaces: FixedArray, + methods: Array + ): Class + + private static getMethods(ifaces: FixedArray): Array { + let result = Array() + + for(let idx = 0; idx < ifaces.length; ++idx) { + let type: Type = Type.of(ifaces[idx]) + if (type instanceof InterfaceType) { + throw new Error(`Expected array of classes interfaces, entity of index ${idx} has no interface type`) + } + Proxy.getMethodsRecursive(type as InterfaceType, result) + Proxy.collectMethods(type as InterfaceType, result) + } + + return result + } + + // NOTE(kurnevichstanislav): should be replaced by call of getInterfaces from new typAPI instead of collecting interfaces one-by-one. + private static getMethodsRecursive(iface: InterfaceType, result: Array): void { + for(let idx = 0; idx < iface.getInterfacesNum(); ++idx) { + let intf = iface.getInterface(idx) + Proxy.getMethodsRecursive(intf, result) + Proxy.collectMethods(intf, result) + } + } + + // NOTE(kurnevichstanislav): should be replaced by call of getMethods from new typeAPI instead of collecting methods one-by-one. + private static collectMethods(iface: InterfaceType, result: Array): void { + const methodsCount = iface.getMethodsNum() + for(let idx = 0; idx < methodsCount; ++idx) { + const method = iface.getMethod(idx) + result.pushOne(method) + } + } +} // export class Proxy + +} // export namespace reflect diff --git a/static_core/plugins/ets/stdlib/std/core/Type.ets b/static_core/plugins/ets/stdlib/std/core/Type.ets index 702d45a177..db2e641267 100644 --- a/static_core/plugins/ets/stdlib/std/core/Type.ets +++ b/static_core/plugins/ets/stdlib/std/core/Type.ets @@ -2004,6 +2004,10 @@ export final class InterfaceType extends Type { this.cls = TypeAPI.getClass(td, contextLinker)! } + public getClass(): Class { + return this.cls + } + /** * Checks whether type is primitive or composite * diff --git a/static_core/plugins/ets/subproject_sources.gn b/static_core/plugins/ets/subproject_sources.gn index a7a0335342..d09a2e4163 100644 --- a/static_core/plugins/ets/subproject_sources.gn +++ b/static_core/plugins/ets/subproject_sources.gn @@ -169,6 +169,7 @@ srcs_runtime = [ "runtime/intrinsics/std_core_StringBuilder.cpp", "runtime/intrinsics/std_core_Type.cpp", "runtime/intrinsics/std_core_TypeCreator.cpp", + "runtime/intrinsics/reflect_Proxy.cpp", "runtime/intrinsics/std_core_Method.cpp", "runtime/intrinsics/std_core_reflect_Method.cpp", "runtime/intrinsics/std_core_reflect_Field.cpp", diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/core/Proxy.ets b/static_core/plugins/ets/tests/ets_func_tests/std/core/Proxy.ets new file mode 100644 index 0000000000..0f59b09460 --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/core/Proxy.ets @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 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. + */ + +interface AIface { + foo() +} + +class A { + foo() { + } +} + +function createClass() { + let obj = new A() + let linker = Class.of(obj).getLinker() + let proxyClass: Class = reflect.Proxy.createClass(linker, Type.of) +} + +function main() { + createClass() +} -- Gitee From 3e1c0373a2b168097a8a852800ba204dbfa46a0b Mon Sep 17 00:00:00 2001 From: kurnevichstanislav Date: Wed, 10 Sep 2025 13:13:07 +0300 Subject: [PATCH 3/6] Introduce amd64 proxy bridge Signed-off-by: kurnevichstanislav Change-Id: I0a287da836a10f70cbb7b60138066f3590eef537 --- .../plugins/ets/runtime/CMakeLists.txt | 5 +- .../arch/amd64/proxy_entry_point_amd64.S | 180 ++++++++++++++++++ .../ets/runtime/entrypoints/entrypoints.cpp | 62 ++++++ .../ets/runtime/entrypoints/entrypoints.h | 25 +++ .../ets/runtime/intrinsics/reflect_Proxy.cpp | 1 - static_core/plugins/ets/subproject_sources.gn | 2 + 6 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 static_core/plugins/ets/runtime/entrypoints/arch/amd64/proxy_entry_point_amd64.S create mode 100644 static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp create mode 100644 static_core/plugins/ets/runtime/entrypoints/entrypoints.h diff --git a/static_core/plugins/ets/runtime/CMakeLists.txt b/static_core/plugins/ets/runtime/CMakeLists.txt index bf620bc7a3..fc0c3b0b9c 100644 --- a/static_core/plugins/ets/runtime/CMakeLists.txt +++ b/static_core/plugins/ets/runtime/CMakeLists.txt @@ -130,6 +130,7 @@ set(ETS_RUNTIME_SOURCES ${ETS_EXT_SOURCES}/ets_object_state_table.cpp ${ETS_EXT_SOURCES}/static_object_accessor.cpp ${ETS_EXT_SOURCES}/static_type_converter.cpp + ${ETS_EXT_SOURCES}/entrypoints/entrypoints.cpp ${PANDA_ETS_PLUGIN_SOURCE}/stdlib/native/init_native_methods.cpp ${PANDA_ETS_PLUGIN_SOURCE}/stdlib/native/escompat/Process.cpp ${PANDA_ETS_PLUGIN_SOURCE}/stdlib/native/escompat/RegExp.cpp @@ -174,7 +175,9 @@ elseif(PANDA_TARGET_ARM64) elseif (PANDA_TARGET_AMD64) list (APPEND ETS_RUNTIME_SOURCES ${ETS_EXT_SOURCES}/ani/arch/amd64/ets_async_entry_point_amd64.S - ${ETS_EXT_SOURCES}/ani/arch/amd64/ets_napi_entry_point_amd64.S) + ${ETS_EXT_SOURCES}/ani/arch/amd64/ets_napi_entry_point_amd64.S + ${ETS_EXT_SOURCES}/entrypoints/arch/amd64/proxy_entry_point_amd64.S + ) endif() if (PANDA_TARGET_OHOS) diff --git a/static_core/plugins/ets/runtime/entrypoints/arch/amd64/proxy_entry_point_amd64.S b/static_core/plugins/ets/runtime/entrypoints/arch/amd64/proxy_entry_point_amd64.S new file mode 100644 index 0000000000..64f3981316 --- /dev/null +++ b/static_core/plugins/ets/runtime/entrypoints/arch/amd64/proxy_entry_point_amd64.S @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2025 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 "arch/asm_support.h" +#include "arch/amd64/helpers_amd64.S" +#include "arch/amd64/shorty.S" +#include "shorty_values.h" + +#define C2I_FP_OFFSET 2 +// int64_t EtsProxyMethodInvoke(Method *method, uint8_t *args, uint8_t *in_stack_args) +.extern EtsProxyMethodInvoke + +.macro PROXY_ENTRYPOINT name, entry +.global \name +.type \name, %function +\name: + CFI_STARTPROC + CFI_DEF_CFA(rsp, 8) + + // %rsp % 16 == 8 + pushq $COMPILED_CODE_TO_INTERPRETER_BRIDGE + CFI_ADJUST_CFA_OFFSET(8) + + pushq %rbp + CFI_ADJUST_CFA_OFFSET(8) + CFI_REL_OFFSET(rbp, 0) + movq %rsp, %rbp // frame pointer + CFI_DEF_CFA_REGISTER(rbp) + + subq $8, %rsp + + // save all the callee saved registers to the stack + // stack walker will read them during stack unwinding + pushq %r15 + CFI_REL_OFFSET(r15, -(2 * 8)) + pushq %r14 + CFI_REL_OFFSET(r14, -(3 * 8)) + pushq %r13 + CFI_REL_OFFSET(r13, -(4 * 8)) + pushq %r12 + CFI_REL_OFFSET(r12, -(5 * 8)) + pushq %rbx + CFI_REL_OFFSET(rbx, -(6 * 8)) + + subq $8, %rsp + // %rsp % 16 == 0 + + // If caller is the compiled method, we need to set C2I boundary frame in the TLS + movb MANAGED_THREAD_FRAME_KIND_OFFSET(%THREAD_REG), %r12b + testb %r12b, %r12b + jz 0f + + movq %rbp, MANAGED_THREAD_FRAME_OFFSET(%THREAD_REG) + +0: + // save arguments to the stack + PUSH_FP_REGS + + PUSH_GENERAL_REGS + // %rsp % 16 == 0 + + // Since PUSH_GENERAL_REGS has been called, then rsi will points to array of pushed arguments. + // rsi is the second argument of calling function. + movq %rsp, %rsi + + // rbp + 24 points to the stack arguments that has been pushed to the stack by i2c bridge before calling proxy bridge. + // rdx is the third argument of calling function. + leaq 24(%rbp), %rdx + + callq \entry@plt + + popq %rdi + + // handle the result + movq METHOD_SHORTY_OFFSET(%rdi), %rcx + movl (%rcx), %ecx + andl $0xF, %ecx + cmpl $SHORTY_VOID, %ecx + jne 1f + + // void method + jmp 3f + +1: subl $SHORTY_FIRST_FLOAT, %ecx + cmpl $(SHORTY_NUM_FLOAT_TYPES - 1), %ecx + jbe 2f + + // the return value is integer + jmp 3f + +2: + // it is a float or double + movq %rax, %xmm0 + +3: + movb %r12b, %r10b + + // Restore callee registers, since GC may change its values while moving objects. + movq -((CFRAME_HEADER_SIZE - C2I_FP_OFFSET + 0) * 8)(%rbp), %r15 + CFI_RESTORE(r15) + movq -((CFRAME_HEADER_SIZE - C2I_FP_OFFSET + 1) * 8)(%rbp), %r14 + CFI_RESTORE(r14) + movq -((CFRAME_HEADER_SIZE - C2I_FP_OFFSET + 2) * 8)(%rbp), %r13 + CFI_RESTORE(r13) + movq -((CFRAME_HEADER_SIZE - C2I_FP_OFFSET + 3) * 8)(%rbp), %r12 + CFI_RESTORE(r12) + movq -((CFRAME_HEADER_SIZE - C2I_FP_OFFSET + 4) * 8)(%rbp), %rbx + CFI_RESTORE(rbx) + + movq %rbp, %rsp + popq %rbp + CFI_RESTORE(rbp) + CFI_DEF_CFA(rsp, (2 * 8)) + addq $8, %rsp + CFI_ADJUST_CFA_OFFSET(-(1 * 8)) + + // check native exception + movq MANAGED_THREAD_EXCEPTION_OFFSET(%THREAD_REG), %r11 + testq %r11, %r11 + jz 4f + + // check frame is compiled + testb %r10b, %r10b + jz 4f + + // check prev frame is true CFRAME and not BYPASS + cmpq $BYPASS_BRIDGE, (SLOT_SIZE * COMP_METHOD_OFFSET)(%rbp) + je 4f + + movq (-CALLER_REG0_OFFSET + 0)(%rbp), %rax + movq (-CALLER_REG0_OFFSET + 8)(%rbp), %rcx + movq (-CALLER_REG0_OFFSET + 16)(%rbp), %rdx + movq (-CALLER_REG0_OFFSET + 24)(%rbp), %r11 + movq (-CALLER_REG0_OFFSET + 32)(%rbp), %r10 + movq (-CALLER_REG0_OFFSET + 40)(%rbp), %r9 + movq (-CALLER_REG0_OFFSET + 48)(%rbp), %rsi + movq (-CALLER_REG0_OFFSET + 56)(%rbp), %rdi + movq (-CALLER_REG0_OFFSET + 64)(%rbp), %r8 + + testq $CFRAME_HAS_FLOAT_REGS_FLAG_MASK, (-CFRAME_FLAGS_SLOT * SLOT_SIZE)(%rbp) + jz 1f + + movsd (-CALLER_VREG0_OFFSET + 0)(%rbp), %xmm0 + movsd (-CALLER_VREG0_OFFSET + 8)(%rbp), %xmm1 + movsd (-CALLER_VREG0_OFFSET + 16)(%rbp), %xmm2 + movsd (-CALLER_VREG0_OFFSET + 24)(%rbp), %xmm3 + movsd (-CALLER_VREG0_OFFSET + 32)(%rbp), %xmm4 + movsd (-CALLER_VREG0_OFFSET + 40)(%rbp), %xmm5 + movsd (-CALLER_VREG0_OFFSET + 48)(%rbp), %xmm6 + movsd (-CALLER_VREG0_OFFSET + 56)(%rbp), %xmm7 + movsd (-CALLER_VREG0_OFFSET + 64)(%rbp), %xmm8 + movsd (-CALLER_VREG0_OFFSET + 72)(%rbp), %xmm9 + movsd (-CALLER_VREG0_OFFSET + 80)(%rbp), %xmm10 + movsd (-CALLER_VREG0_OFFSET + 88)(%rbp), %xmm11 + movsd (-CALLER_VREG0_OFFSET + 96)(%rbp), %xmm12 + movsd (-CALLER_VREG0_OFFSET + 104)(%rbp), %xmm13 + movsd (-CALLER_VREG0_OFFSET + 112)(%rbp), %xmm14 + movsd (-CALLER_VREG0_OFFSET + 120)(%rbp), %xmm15 + +1: + jmp ThrowNativeExceptionBridge@plt + +4: + retq + CFI_ENDPROC +.endm + +PROXY_ENTRYPOINT EtsProxyEntryPoint EtsProxyMethodInvoke diff --git a/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp b/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp new file mode 100644 index 0000000000..881c3abce9 --- /dev/null +++ b/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2025 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 "plugins/ets/runtime/types/ets_method.h" + +namespace ark::ets::entrypoints { + +extern "C" void EtsProxyEntryPoint(); + +const void *GetEtsProxyEntryPoint() +{ + return reinterpret_cast(EtsProxyEntryPoint); +} + +/* In case of i2c(interpreter to compiled code) the following flow take place: + + interpreter --> HandleCall --> i2c bridge --> call entrypoint(proxy bridge) --> call EtsProxyMethodInvoke + + AMD64 STACK: + + | | top of the stack | + a | | | + d | ---------------------------------------------------- <-- call of EtsProxyMethodInvoke(%rdi, %rsi, %rdx) + d | | Arguments that pushed in registers by i2c (%rsi) | + r | ---------------------------------------------------- <-- call of proxy bridge + e | | return address | + s | ---------------------------------------------------| + s | | GPR arguments (%rdx) | + e | ---------------------------------------------------- + s | | FPR arguments | + V ---------------------------------------------------- + | Method* (%rdi) | + ---------------------------------------------------- + | ................................................ | + <-- call of i2c bridge by HandleCall + +*/ +extern "C" int64_t EtsProxyMethodInvoke(EtsMethod *method, uint8_t *args, uint8_t *inStackArgs) +{ + ASSERT(method != nullptr); + ASSERT(args != nullptr) + ASSERT(inStackArgs != nullptr); + + auto *coroutine = EtsCoroutine::GetCurrent(); + ASSERT(coroutine != nullptr); + + return 0; +} + +} // namespace ark::ets::entrypoints diff --git a/static_core/plugins/ets/runtime/entrypoints/entrypoints.h b/static_core/plugins/ets/runtime/entrypoints/entrypoints.h new file mode 100644 index 0000000000..081bfa4aa4 --- /dev/null +++ b/static_core/plugins/ets/runtime/entrypoints/entrypoints.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2025 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 PLUGINS_ETS_RUNTIME_ENTRYPOINTS_ENTRYPOINTS_H +#define PLUGINS_ETS_RUNTIME_ENTRYPOINTS_ENTRYPOINTS_H + +namespace ark::ets::entrypoints { + +const void *GetEtsProxyEntryPoint(); + +} // namespace ark::ets::entrypoints + +#endif // PLUGINS_ETS_RUNTIME_ENTRYPOINTS_ENTRYPOINTS_H diff --git a/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp b/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp index 2a999445b0..4d86423b31 100644 --- a/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp +++ b/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp @@ -33,7 +33,6 @@ static void CreateProxyConstructor(EtsClass *klass, EtsClass *proxyKlass, EtsMet new (out) Method(proxyClassConstructor->GetPandaMethod()); out->GetPandaMethod()->SetClass(klass->GetRuntimeClass()); // out->GetPandaMethod()->SetAccessFlags() - } extern "C" EtsClass *ReflectProxyGenerateProxy(EtsRuntimeLinker *linker, EtsString *name, ObjectHeader *interfaces, EtsEscompatArray *methods) diff --git a/static_core/plugins/ets/subproject_sources.gn b/static_core/plugins/ets/subproject_sources.gn index d09a2e4163..d57b9957b6 100644 --- a/static_core/plugins/ets/subproject_sources.gn +++ b/static_core/plugins/ets/subproject_sources.gn @@ -208,6 +208,7 @@ srcs_runtime = [ "runtime/ets_utils.cpp", "runtime/static_object_accessor.cpp", "runtime/static_type_converter.cpp", + "runtime/entrypoints/entrypoints.cpp", "stdlib/native/init_native_methods.cpp", "stdlib/native/escompat/Process.cpp", "stdlib/native/escompat/RegExp.cpp", @@ -317,6 +318,7 @@ if (current_cpu == "amd64" || current_cpu == "x64" || current_cpu == "x86_64") { srcs_runtime += [ "runtime/ani/arch/amd64/ets_napi_entry_point_amd64.S", "runtime/ani/arch/amd64/ets_async_entry_point_amd64.S", + "runtime/entrypoints/arch/amd64/proxy_entry_point_amd64.S" ] } else if (current_cpu == "arm64") { srcs_runtime += [ -- Gitee From ea0408dc41dac56d7863fce2ecf4a6cf6067267e Mon Sep 17 00:00:00 2001 From: kurnevichstanislav Date: Wed, 10 Sep 2025 19:36:51 +0300 Subject: [PATCH 4/6] Implement call of bridge prologue in ReflectProxyGenerateProxy Signed-off-by: kurnevichstanislav Change-Id: I9b993cbe8e7b2c7079115a59ba3d0c767af299f5 --- .../ets/runtime/entrypoints/entrypoints.cpp | 61 +++++++---- .../ets/runtime/entrypoints/entrypoints.h | 4 +- .../ets/runtime/intrinsics/reflect_Proxy.cpp | 101 +++++++++++++++--- .../plugins/ets/runtime/types/ets_method.h | 18 ++++ .../ets/runtime/types/ets_typeapi_method.cpp | 9 +- .../ets/runtime/types/ets_typeapi_method.h | 8 ++ 6 files changed, 160 insertions(+), 41 deletions(-) diff --git a/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp b/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp index 881c3abce9..801ddb7453 100644 --- a/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp +++ b/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp @@ -24,28 +24,39 @@ const void *GetEtsProxyEntryPoint() return reinterpret_cast(EtsProxyEntryPoint); } -/* In case of i2c(interpreter to compiled code) the following flow take place: - - interpreter --> HandleCall --> i2c bridge --> call entrypoint(proxy bridge) --> call EtsProxyMethodInvoke - - AMD64 STACK: - - | | top of the stack | - a | | | - d | ---------------------------------------------------- <-- call of EtsProxyMethodInvoke(%rdi, %rsi, %rdx) - d | | Arguments that pushed in registers by i2c (%rsi) | - r | ---------------------------------------------------- <-- call of proxy bridge - e | | return address | - s | ---------------------------------------------------| - s | | GPR arguments (%rdx) | - e | ---------------------------------------------------- - s | | FPR arguments | - V ---------------------------------------------------- - | Method* (%rdi) | - ---------------------------------------------------- - | ................................................ | - <-- call of i2c bridge by HandleCall +/* + In case of i2c(interpreter to compiled code) the following flow take place: + interpreter --> HandleCall --> i2c bridge --> call entrypoint(proxy bridge) --> call EtsProxyMethodInvoke + + When some method genereted by proxy called with N arguments, then runtime calls i2c bridge + and some of arguments saved in caller-saved registers and some on the stack according to the target calling +convention. Then i2c bridge calls proxy bridge (that was set as entrypoint to the Method *). Proxy bridge pushed on the +stack all incoming argument registers with purpose of passing it as array `args` to the EtsProxyMethodInvoke + `inStackArgs` is array of arguments from the stack that was formed by i2c bridge. + + So, proxy bridge does 2 main things: + 1) maps signature of given managed interface function to the signature of `EtsProxyMethodInvoke` + 2) forms arguments to have opportunity of passing N arguments as one array argument + +0x0000 AMD64 STACK: + + | | top of the stack | + a | | | + d | ---------------------------------------------------- <-- call of EtsProxyMethodInvoke(%rdi, %rsi, %rdx) + d | | Arguments that pushed in registers by i2c (%rsi) | + r | | GPR arguments | + e | | FPR arguments | + s | ---------------------------------------------------- <-- call of proxy bridge + s | | return address | + e | ---------------------------------------------------- + s | | in stack arguments (%rdx) | + V ---------------------------------------------------- + | Method* (%rdi) | + ---------------------------------------------------- + | ................................................ | + <-- call of i2c bridge by HandleCall +0xFFFF */ extern "C" int64_t EtsProxyMethodInvoke(EtsMethod *method, uint8_t *args, uint8_t *inStackArgs) { @@ -56,7 +67,13 @@ extern "C" int64_t EtsProxyMethodInvoke(EtsMethod *method, uint8_t *args, uint8_ auto *coroutine = EtsCoroutine::GetCurrent(); ASSERT(coroutine != nullptr); + Span gprArgs(args, arch::ExtArchTraits::GP_ARG_NUM_BYTES); + Span fprArgs(gprArgs.end(), arch::ExtArchTraits::FP_ARG_NUM_BYTES); + arch::ArgReader argReader(gprArgs, fprArgs, inStackArgs); + + (void)argReader; + return 0; } -} // namespace ark::ets::entrypoints +} // namespace ark::ets::entrypoints diff --git a/static_core/plugins/ets/runtime/entrypoints/entrypoints.h b/static_core/plugins/ets/runtime/entrypoints/entrypoints.h index 081bfa4aa4..a05260f6e6 100644 --- a/static_core/plugins/ets/runtime/entrypoints/entrypoints.h +++ b/static_core/plugins/ets/runtime/entrypoints/entrypoints.h @@ -20,6 +20,6 @@ namespace ark::ets::entrypoints { const void *GetEtsProxyEntryPoint(); -} // namespace ark::ets::entrypoints +} // namespace ark::ets::entrypoints -#endif // PLUGINS_ETS_RUNTIME_ENTRYPOINTS_ENTRYPOINTS_H +#endif // PLUGINS_ETS_RUNTIME_ENTRYPOINTS_ENTRYPOINTS_H diff --git a/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp b/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp index 4d86423b31..280eb05edd 100644 --- a/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp +++ b/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp @@ -15,39 +15,111 @@ #include "intrinsics.h" +#include "plugins/ets/runtime/types/ets_typeapi_method.h" +#include "plugins/ets/runtime/entrypoints/entrypoints.h" + namespace ark::ets::intrinsics { static void CreateProxyConstructor(EtsClass *klass, EtsClass *proxyKlass, EtsMethod *out) { ASSERT(klass != nullptr); + ASSERT(proxyKlass != nullptr); + ASSERT(out != nullptr); EtsMethod *proxyClassConstructor = nullptr; for (auto &method : proxyKlass->GetRuntimeClass()->GetMethods()) { if (method.IsInstanceConstructor() && !method.IsPrivate()) { proxyClassConstructor = EtsMethod::FromRuntimeMethod(&method); ASSERT(proxyClassConstructor->IsProtected()); - break; // Proxy has 2 constructors: default private and protected. + break; // Proxy has 2 constructors: default private and protected. } } new (out) Method(proxyClassConstructor->GetPandaMethod()); - out->GetPandaMethod()->SetClass(klass->GetRuntimeClass()); - // out->GetPandaMethod()->SetAccessFlags() + + auto *outMethod = out->GetPandaMethod(); + outMethod->SetClass(klass->GetRuntimeClass()); + + // NOTE(kurnevichstanislav): may be we should explicitly forbid JIT to add hotness counter for this method. + outMethod->SetAccessFlags((outMethod->GetAccessFlags() & ~ACC_PROTECTED) | ACC_PUBLIC); + + // NOTE(kurnevichstanislav): the following code may be needed in the future. + // out->SetProxyPointer(reinterpret_cast(proxyClassConstructor)); +} + +static void CreateProxyMethod(EtsClass *klass, EtsMethod *method, EtsMethod *out) +{ + ASSERT(klass != nullptr); + ASSERT(method != nullptr); + ASSERT(out != nullptr); + + constexpr uint32_t FLAGS_TO_REMOVE = ACC_ABSTRACT | ACC_DEFAULT_INTERFACE_METHOD; + + auto *outMethod = out->GetPandaMethod(); + ASSERT(outMethod->IsPublic()); + + outMethod->SetAccessFlags((outMethod->GetAccessFlags() & FLAGS_TO_REMOVE)); + outMethod->SetClass(klass->GetRuntimeClass()); + outMethod->SetCompiledEntryPoint(entrypoints::GetEtsProxyEntryPoint()); + + // Set original method of interface for which we create proxy method. + // This pointer will be passed to the proxy handler invoke/set/get. + method->SetProxyPointer(reinterpret_cast(method)); +} + +static Span GenerateMethods(EtsEscompatArray *methods, EtsClass *klass, + EtsClassLinkerExtension *classLinkerExt) +{ + ASSERT(methods != nullptr); + ASSERT(klass != nullptr) + ASSERT(classLinkerExt != nullptr); + + auto *coroutine = EtsCoroutine::GetCurrent(); + ASSERT(coroutine != nullptr); + + auto *proxyKlass = classLinkerExt->GetPlatformTypes()->proxy; + ASSERT(proxyKlass != nullptr); + + size_t idx = 0; + + constexpr size_t NUM_OF_CONSTRUCTORS = 1U; + size_t numMethods = methods->GetActualLength() + NUM_OF_CONSTRUCTORS; + Span proxyMethods( + coroutine->GetPandaVM()->GetHeapManager()->GetInternalAllocator()->AllocArray(numMethods), + numMethods); + + CreateProxyConstructor(klass, proxyKlass, &proxyMethods[idx++]); + + for (size_t i = 0; i < methods->GetActualLength(); ++i) { + EtsObject *methodObject = nullptr; + [[maybe_unused]] bool getRefSucc = methods->GetRef(i, &methodObject); + ASSERT(getRefSucc == true); + + auto typeApiMethod = EtsTypeAPIMethod::FromEtsObject(methodObject); + auto *method = typeApiMethod->GetEtsMethod(); + CreateProxyMethod(klass, method, &proxyMethods[idx++]); + } + + return proxyMethods; } -extern "C" EtsClass *ReflectProxyGenerateProxy(EtsRuntimeLinker *linker, EtsString *name, ObjectHeader *interfaces, EtsEscompatArray *methods) +extern "C" EtsClass *ReflectProxyGenerateProxy(EtsRuntimeLinker *linker, EtsString *name, ObjectHeader *interfaces, + EtsEscompatArray *methods) { ASSERT(linker != nullptr); ASSERT(name != nullptr); ASSERT(interfaces != nullptr); ASSERT(methods != nullptr); - PandaString descriptor; - ClassHelper::GetDescriptor(utf::CStringAsMutf8(name->GetMutf8().data()), &descriptor); - auto *coroutine = EtsCoroutine::GetCurrent(); ASSERT(coroutine != nullptr); + [[maybe_unused]] EtsHandleScope scope(coroutine); + EtsHandle interfacesH(coroutine, EtsObject::FromCoreType(interfaces)); + + PandaString descriptor; + ClassHelper::GetDescriptor(utf::CStringAsMutf8(name->GetMutf8().data()), &descriptor); + auto *linkerCtx = linker->GetClassLinkerContext(); ASSERT(linkerCtx != nullptr); @@ -61,18 +133,15 @@ extern "C" EtsClass *ReflectProxyGenerateProxy(EtsRuntimeLinker *linker, EtsStri } tempKlass->SetLoadContext(linkerCtx); - auto *proxyKlass = classLinkerExt->GetPlatformTypes()->proxy; - ASSERT(proxyKlass != nullptr); - - size_t idx = 0; + constexpr uint32_t PROXY_CLASS_ACCESS_FLAGS = ACC_PROXY | ACC_FINAL; + tempKlass->SetAccessFlags(PROXY_CLASS_ACCESS_FLAGS); - constexpr size_t NUM_OF_CONSTRUCTORS = 1U; - size_t numMethods = methods->GetActualLength() + NUM_OF_CONSTRUCTORS; - Span proxyMethods (coroutine->GetPandaVM()->GetHeapManager()->GetInternalAllocator()->AllocArray(numMethods), numMethods); + Span generatedProxyMethods = + GenerateMethods(methods, EtsClass::FromRuntimeClass(tempKlass), classLinkerExt); - CreateProxyConstructor(EtsClass::FromRuntimeClass(tempKlass), proxyKlass, &proxyMethods[idx++]); + (void)generatedProxyMethods; return nullptr; } -} // namespace ark::ets::intrinsics +} // namespace ark::ets::intrinsics diff --git a/static_core/plugins/ets/runtime/types/ets_method.h b/static_core/plugins/ets/runtime/types/ets_method.h index dd8b4e9c84..3043a84c4c 100644 --- a/static_core/plugins/ets/runtime/types/ets_method.h +++ b/static_core/plugins/ets/runtime/types/ets_method.h @@ -165,6 +165,24 @@ public: return GetPandaMethod()->IsNative(); } + bool IsProxy() const + { + return GetPandaMethod()->IsProxy(); + } + + void SetProxyPointer(void *proxyPointer) + { + ASSERT(IsProxy()); + // Proxy method is never native, so we have free field. + return GetPandaMethod()->SetNativePointer(proxyPointer); + } + + void *GetProxyPointer() const + { + ASSERT(IsProxy()); + return GetPandaMethod()->GetNativePointer(); + } + bool HasRestParam() const { return (this->GetAccessFlags() & ACC_VARARGS) != 0; diff --git a/static_core/plugins/ets/runtime/types/ets_typeapi_method.cpp b/static_core/plugins/ets/runtime/types/ets_typeapi_method.cpp index dc449488cd..daf9bbc67f 100644 --- a/static_core/plugins/ets/runtime/types/ets_typeapi_method.cpp +++ b/static_core/plugins/ets/runtime/types/ets_typeapi_method.cpp @@ -17,6 +17,7 @@ #include "plugins/ets/runtime/ets_coroutine.h" #include "plugins/ets/runtime/ets_platform_types.h" #include "plugins/ets/runtime/ets_vm.h" +#include "plugins/ets/runtime/types/ets_method.h" namespace ark::ets { @@ -27,4 +28,10 @@ EtsTypeAPIMethod *EtsTypeAPIMethod::Create(EtsCoroutine *etsCoroutine) return reinterpret_cast(etsObject); } -} // namespace ark::ets \ No newline at end of file +EtsMethod *EtsTypeAPIMethod::GetEtsMethod() +{ + return EtsMethod::FromTypeDescriptor(methodType_->GetRuntimeTypeDescriptor()->GetMutf8(), + methodType_->GetContextLinker()); +} + +} // namespace ark::ets diff --git a/static_core/plugins/ets/runtime/types/ets_typeapi_method.h b/static_core/plugins/ets/runtime/types/ets_typeapi_method.h index c0f4812ad7..23c50f8e9e 100644 --- a/static_core/plugins/ets/runtime/types/ets_typeapi_method.h +++ b/static_core/plugins/ets/runtime/types/ets_typeapi_method.h @@ -85,6 +85,14 @@ public: ObjectAccessor::SetPrimitive(this, MEMBER_OFFSET(EtsTypeAPIMethod, attr_), attr); } + EtsObject *GetMethodType(EtsCoroutine *coro) const + { + return EtsObject::FromCoreType( + ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsTypeAPIMethod, methodType_))); + } + + EtsMethod *GetEtsMethod(); + private: ObjectPointer methodType_; ObjectPointer ownerType_; -- Gitee From b521af455ac2853536688a4411f5d6e0058c56be Mon Sep 17 00:00:00 2001 From: kurnevichstanislav Date: Thu, 11 Sep 2025 12:25:31 +0300 Subject: [PATCH 5/6] temp Signed-off-by: kurnevichstanislav Change-Id: I6a9cde0b4f7d3f9e090bf7d24a9771c0de53768b --- .../plugins/ets/runtime/intrinsics/reflect_Proxy.cpp | 2 ++ .../ets/runtime/intrinsics/std_core_TypeCreator.cpp | 2 +- static_core/plugins/ets/stdlib/std/core/ProxyNew.ets | 8 ++++---- .../plugins/ets/tests/ets_func_tests/std/core/Proxy.ets | 5 ++++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp b/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp index 280eb05edd..46384dbace 100644 --- a/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp +++ b/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp @@ -82,6 +82,8 @@ static Span GenerateMethods(EtsEscompatArray *methods, EtsClass *klas size_t idx = 0; + std::cerr << "======================================" << std::endl; + constexpr size_t NUM_OF_CONSTRUCTORS = 1U; size_t numMethods = methods->GetActualLength() + NUM_OF_CONSTRUCTORS; Span proxyMethods( diff --git a/static_core/plugins/ets/runtime/intrinsics/std_core_TypeCreator.cpp b/static_core/plugins/ets/runtime/intrinsics/std_core_TypeCreator.cpp index 220d2677f6..e71215ae96 100644 --- a/static_core/plugins/ets/runtime/intrinsics/std_core_TypeCreator.cpp +++ b/static_core/plugins/ets/runtime/intrinsics/std_core_TypeCreator.cpp @@ -181,7 +181,7 @@ EtsString *TypeAPITypeCreatorCtxCommit(EtsLong ctxPtr, EtsArray *objects, EtsRun } // debug - panda_file::FileWriter {"/tmp/test.abc"}.WriteBytes(writer.GetData()); + // panda_file::FileWriter {"/tmp/test.abc"}.WriteBytes(writer.GetData()); auto *classLinkerContext = targetLinker->GetClassLinkerContext(); ASSERT(!classLinkerContext->IsBootContext()); diff --git a/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets b/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets index f5cc8ab4cb..237428979d 100644 --- a/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets +++ b/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets @@ -87,18 +87,18 @@ export class Proxy { for(let idx = 0; idx < ifaces.length; ++idx) { let type: Type = Type.of(ifaces[idx]) - if (type instanceof InterfaceType) { + if (!(type instanceof ClassType)) { throw new Error(`Expected array of classes interfaces, entity of index ${idx} has no interface type`) } - Proxy.getMethodsRecursive(type as InterfaceType, result) - Proxy.collectMethods(type as InterfaceType, result) + Proxy.getMethodsRecursive(type as ClassType, result) + Proxy.collectMethods(type as ClassType, result) } return result } // NOTE(kurnevichstanislav): should be replaced by call of getInterfaces from new typAPI instead of collecting interfaces one-by-one. - private static getMethodsRecursive(iface: InterfaceType, result: Array): void { + private static getMethodsRecursive(iface: ClassType, result: Array): void { for(let idx = 0; idx < iface.getInterfacesNum(); ++idx) { let intf = iface.getInterface(idx) Proxy.getMethodsRecursive(intf, result) diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/core/Proxy.ets b/static_core/plugins/ets/tests/ets_func_tests/std/core/Proxy.ets index 0f59b09460..e88b684fea 100644 --- a/static_core/plugins/ets/tests/ets_func_tests/std/core/Proxy.ets +++ b/static_core/plugins/ets/tests/ets_func_tests/std/core/Proxy.ets @@ -25,7 +25,10 @@ class A { function createClass() { let obj = new A() let linker = Class.of(obj).getLinker() - let proxyClass: Class = reflect.Proxy.createClass(linker, Type.of) + let interfaces: FixedArray = [ (Type.from() as InterfaceType).getClass() ] + let proxyClass: Class = reflect.Proxy.createClass(linker, interfaces) + console.log("=======================TEST PASS, DEBUG ASSERT========================") + arktest.assertEQ(true, false) } function main() { -- Gitee From d73afc718824838957adacd6d24c1225050f0de1 Mon Sep 17 00:00:00 2001 From: kurnevichstanislav Date: Fri, 12 Sep 2025 16:17:01 +0300 Subject: [PATCH 6/6] temp2 Signed-off-by: kurnevichstanislav Change-Id: Ib75b3e808a81e7202fe9be45aae56f58dbbd3f14 --- .../ets/runtime/entrypoints/entrypoints.cpp | 8 +- .../ets/runtime/intrinsics/reflect_Proxy.cpp | 130 ++++++++++++------ .../runtime/intrinsics/std_core_Arrays.cpp | 12 +- .../plugins/ets/runtime/types/ets_class.cpp | 4 + .../plugins/ets/stdlib/std/core/ProxyNew.ets | 75 +++++----- .../plugins/ets/stdlib/std/core/Type.ets | 6 +- 6 files changed, 155 insertions(+), 80 deletions(-) diff --git a/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp b/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp index 801ddb7453..cce4efaf34 100644 --- a/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp +++ b/static_core/plugins/ets/runtime/entrypoints/entrypoints.cpp @@ -31,9 +31,10 @@ const void *GetEtsProxyEntryPoint() When some method genereted by proxy called with N arguments, then runtime calls i2c bridge and some of arguments saved in caller-saved registers and some on the stack according to the target calling -convention. Then i2c bridge calls proxy bridge (that was set as entrypoint to the Method *). Proxy bridge pushed on the -stack all incoming argument registers with purpose of passing it as array `args` to the EtsProxyMethodInvoke - `inStackArgs` is array of arguments from the stack that was formed by i2c bridge. + convention. Then i2c bridge calls proxy bridge (that was set as entrypoint to the Method *). + Proxy bridge pushed on the stack all incoming argument registers with purpose of passing + it as array `args` to the EtsProxyMethodInvoke `inStackArgs` is array of arguments from the stack that was formed by +i2c bridge. So, proxy bridge does 2 main things: 1) maps signature of given managed interface function to the signature of `EtsProxyMethodInvoke` @@ -60,6 +61,7 @@ stack all incoming argument registers with purpose of passing it as array `args` */ extern "C" int64_t EtsProxyMethodInvoke(EtsMethod *method, uint8_t *args, uint8_t *inStackArgs) { + std::cerr << "-----------------EtsProxyMethodInvoke Etry---------------" << std::endl; ASSERT(method != nullptr); ASSERT(args != nullptr) ASSERT(inStackArgs != nullptr); diff --git a/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp b/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp index 46384dbace..e0e7f2b4df 100644 --- a/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp +++ b/static_core/plugins/ets/runtime/intrinsics/reflect_Proxy.cpp @@ -17,61 +17,63 @@ #include "plugins/ets/runtime/types/ets_typeapi_method.h" #include "plugins/ets/runtime/entrypoints/entrypoints.h" +#include "plugins/ets/runtime/types/ets_reflect_method.h" namespace ark::ets::intrinsics { -static void CreateProxyConstructor(EtsClass *klass, EtsClass *proxyKlass, EtsMethod *out) +static void CreateProxyConstructor(/*EtsClass *klass, */EtsClass *proxyKlass, Method *out) { - ASSERT(klass != nullptr); + // ASSERT(klass != nullptr); ASSERT(proxyKlass != nullptr); ASSERT(out != nullptr); - EtsMethod *proxyClassConstructor = nullptr; + Method *proxyClassConstructor = nullptr; for (auto &method : proxyKlass->GetRuntimeClass()->GetMethods()) { if (method.IsInstanceConstructor() && !method.IsPrivate()) { - proxyClassConstructor = EtsMethod::FromRuntimeMethod(&method); + std::cerr << "methodName ========" << method.GetFullName() << std::endl; + std::cerr << "method.GetNumArgs() = " << method.GetNumArgs() << std::endl; + std::cerr << "method.GetProto().GetSignature() = " << method.GetProto().GetSignature() << std::endl; + proxyClassConstructor = &method; ASSERT(proxyClassConstructor->IsProtected()); break; // Proxy has 2 constructors: default private and protected. } } - new (out) Method(proxyClassConstructor->GetPandaMethod()); + new (out) Method(proxyClassConstructor); - auto *outMethod = out->GetPandaMethod(); - outMethod->SetClass(klass->GetRuntimeClass()); + // out->SetClass(klass->GetRuntimeClass()); // NOTE(kurnevichstanislav): may be we should explicitly forbid JIT to add hotness counter for this method. - outMethod->SetAccessFlags((outMethod->GetAccessFlags() & ~ACC_PROTECTED) | ACC_PUBLIC); + out->SetAccessFlags((out->GetAccessFlags() & ~ACC_PROTECTED) | ACC_PUBLIC); // NOTE(kurnevichstanislav): the following code may be needed in the future. - // out->SetProxyPointer(reinterpret_cast(proxyClassConstructor)); + // EtsMethod::FromRuntimeMethod(out)->SetProxyPointer(reinterpret_cast(proxyClassConstructor)); } -static void CreateProxyMethod(EtsClass *klass, EtsMethod *method, EtsMethod *out) +static void CreateProxyMethod(/*EtsClass *klass, */EtsMethod *method, Method *out) { - ASSERT(klass != nullptr); + // ASSERT(klass != nullptr); ASSERT(method != nullptr); ASSERT(out != nullptr); constexpr uint32_t FLAGS_TO_REMOVE = ACC_ABSTRACT | ACC_DEFAULT_INTERFACE_METHOD; - auto *outMethod = out->GetPandaMethod(); - ASSERT(outMethod->IsPublic()); + ASSERT(out->IsPublic()); - outMethod->SetAccessFlags((outMethod->GetAccessFlags() & FLAGS_TO_REMOVE)); - outMethod->SetClass(klass->GetRuntimeClass()); - outMethod->SetCompiledEntryPoint(entrypoints::GetEtsProxyEntryPoint()); + out->SetAccessFlags((out->GetAccessFlags() & FLAGS_TO_REMOVE)); + // out->SetClass(klass->GetRuntimeClass()); + out->SetCompiledEntryPoint(entrypoints::GetEtsProxyEntryPoint()); // Set original method of interface for which we create proxy method. // This pointer will be passed to the proxy handler invoke/set/get. - method->SetProxyPointer(reinterpret_cast(method)); + EtsMethod::FromRuntimeMethod(out)->SetProxyPointer(reinterpret_cast(method)); } -static Span GenerateMethods(EtsEscompatArray *methods, EtsClass *klass, - EtsClassLinkerExtension *classLinkerExt) +static Span GenerateMethods(EtsEscompatArray *methods, /*EtsClass *klass,*/ + EtsClassLinkerExtension *classLinkerExt, InternalAllocatorPtr allocator) { ASSERT(methods != nullptr); - ASSERT(klass != nullptr) + // ASSERT(klass != nullptr) ASSERT(classLinkerExt != nullptr); auto *coroutine = EtsCoroutine::GetCurrent(); @@ -82,24 +84,23 @@ static Span GenerateMethods(EtsEscompatArray *methods, EtsClass *klas size_t idx = 0; - std::cerr << "======================================" << std::endl; - constexpr size_t NUM_OF_CONSTRUCTORS = 1U; size_t numMethods = methods->GetActualLength() + NUM_OF_CONSTRUCTORS; - Span proxyMethods( - coroutine->GetPandaVM()->GetHeapManager()->GetInternalAllocator()->AllocArray(numMethods), + Span proxyMethods( + allocator->AllocArray(numMethods), numMethods); - CreateProxyConstructor(klass, proxyKlass, &proxyMethods[idx++]); + CreateProxyConstructor(proxyKlass, &proxyMethods[idx++]); for (size_t i = 0; i < methods->GetActualLength(); ++i) { EtsObject *methodObject = nullptr; [[maybe_unused]] bool getRefSucc = methods->GetRef(i, &methodObject); ASSERT(getRefSucc == true); - auto typeApiMethod = EtsTypeAPIMethod::FromEtsObject(methodObject); - auto *method = typeApiMethod->GetEtsMethod(); - CreateProxyMethod(klass, method, &proxyMethods[idx++]); + auto reflectMethod = EtsReflectMethod::FromEtsObject(methodObject); + auto *method = reflectMethod->GetEtsMethod(); + std::cerr << "Method to create _____= " << method->GetPandaMethod()->GetProto().GetSignature() << std::endl; + CreateProxyMethod(method, &proxyMethods[idx++]); } return proxyMethods; @@ -108,6 +109,7 @@ static Span GenerateMethods(EtsEscompatArray *methods, EtsClass *klas extern "C" EtsClass *ReflectProxyGenerateProxy(EtsRuntimeLinker *linker, EtsString *name, ObjectHeader *interfaces, EtsEscompatArray *methods) { + std::cerr << "===================ReflectProxyGenerateProxy ENTRY===================" << std::endl; ASSERT(linker != nullptr); ASSERT(name != nullptr); ASSERT(interfaces != nullptr); @@ -117,10 +119,11 @@ extern "C" EtsClass *ReflectProxyGenerateProxy(EtsRuntimeLinker *linker, EtsStri ASSERT(coroutine != nullptr); [[maybe_unused]] EtsHandleScope scope(coroutine); - EtsHandle interfacesH(coroutine, EtsObject::FromCoreType(interfaces)); + EtsHandle interfacesH(coroutine, EtsObjectArray::FromCoreType(interfaces)); PandaString descriptor; ClassHelper::GetDescriptor(utf::CStringAsMutf8(name->GetMutf8().data()), &descriptor); + const uint8_t *descriptorMutf8 = utf::CStringAsMutf8(descriptor.c_str()); auto *linkerCtx = linker->GetClassLinkerContext(); ASSERT(linkerCtx != nullptr); @@ -128,22 +131,71 @@ extern "C" EtsClass *ReflectProxyGenerateProxy(EtsRuntimeLinker *linker, EtsStri auto *classLinker = coroutine->GetPandaVM()->GetClassLinker(); auto *classLinkerExt = classLinker->GetEtsClassLinkerExtension(); - auto *tempKlass = classLinkerExt->CreateClass(utf::CStringAsMutf8(descriptor.c_str()), 0, 0, sizeof(EtsClass)); - if (tempKlass == nullptr) { - ASSERT(coroutine->HasPendingException()); + // Create temporaroly class with the same descriptor as the needed proxy class. + // Link all methods to this class. + // auto *tempKlass = classLinkerExt->CreateClass(descriptorMutf8, 0, 0, sizeof(EtsClass)); + // if (tempKlass == nullptr) { + // ASSERT(coroutine->HasPendingException()); + // return nullptr; + // } + // tempKlass->SetLoadContext(linkerCtx); + + constexpr uint32_t PROXY_CLASS_ACCESS_FLAGS = ACC_PROXY | ACC_FINAL; + // tempKlass->SetAccessFlags(PROXY_CLASS_ACCESS_FLAGS); + + auto internalAllocator = coroutine->GetPandaVM()->GetHeapManager()->GetInternalAllocator(); + + Span generatedProxyMethods = + GenerateMethods(methods, classLinkerExt, internalAllocator); + + Span proxyInterfaces; + + uint32_t intfNum = interfacesH->GetLength(); + if (intfNum > 0) { + proxyInterfaces = Span(internalAllocator->AllocArray(intfNum), intfNum); + for (uint32_t idx = 0; idx < intfNum; ++idx) { + EtsObject *iface = interfacesH->Get(idx); + ASSERT(iface != nullptr); + proxyInterfaces[idx] = EtsClass::FromEtsClassObject(iface)->GetRuntimeClass(); + } + } + + Class *baseProxyClass = classLinkerExt->GetPlatformTypes()->proxy->GetRuntimeClass(); +std::cerr << "DESCRIPTOR =========== " << descriptorMutf8 << std::endl; + Class *proxyClass = Runtime::GetCurrent()->GetClassLinker()->BuildClass( + descriptorMutf8, true, PROXY_CLASS_ACCESS_FLAGS, generatedProxyMethods, Span(), + baseProxyClass, proxyInterfaces, linkerCtx, false); + if (proxyClass == nullptr) { + LOG(ERROR, ETS) << "Can't build proxy class " << name->GetMutf8(); + internalAllocator->Free(generatedProxyMethods.data()); + internalAllocator->Free(proxyInterfaces.data()); + // classLinkerExt->FreeClass(tempKlass); return nullptr; } - tempKlass->SetLoadContext(linkerCtx); - constexpr uint32_t PROXY_CLASS_ACCESS_FLAGS = ACC_PROXY | ACC_FINAL; - tempKlass->SetAccessFlags(PROXY_CLASS_ACCESS_FLAGS); + proxyClass->SetFieldIndex(baseProxyClass->GetFieldIndex()); + proxyClass->SetMethodIndex(baseProxyClass->GetMethodIndex()); + proxyClass->SetClassIndex(baseProxyClass->GetClassIndex()); + + proxyClass->SetState(Class::State::INITIALIZING); + proxyClass->SetState(Class::State::INITIALIZED); + // classLinkerExt->FreeClass(tempKlass); - Span generatedProxyMethods = - GenerateMethods(methods, EtsClass::FromRuntimeClass(tempKlass), classLinkerExt); + auto methods_ = proxyClass->GetMethods(); + + std::cerr << "----------------------------------DEBUG:" << proxyClass->GetName() << std::endl; + // NOTE(kirill-mitkin): cache in ets_class field + for (auto &method : methods_) { + // Skip constructors + std::cerr << "NOT PUSH " << method.GetProto().GetSignature() << std::endl; + if (method.IsInstanceConstructor()) { + std::cerr << "PUSH THE CONSTRUCTORRRRRRRR " << method.GetProto().GetSignature() << std::endl; + } + } - (void)generatedProxyMethods; + std::cerr << "===================ReflectProxyGenerateProxy EXIT===================" << std::endl; - return nullptr; + return EtsClass::FromRuntimeClass(proxyClass); } } // namespace ark::ets::intrinsics diff --git a/static_core/plugins/ets/runtime/intrinsics/std_core_Arrays.cpp b/static_core/plugins/ets/runtime/intrinsics/std_core_Arrays.cpp index 78dcb6f532..aa7e9e3600 100644 --- a/static_core/plugins/ets/runtime/intrinsics/std_core_Arrays.cpp +++ b/static_core/plugins/ets/runtime/intrinsics/std_core_Arrays.cpp @@ -253,15 +253,15 @@ static void RefCopy(ManagedThread *thread, ObjectArrayHandle srcArray, Object } copy(srcPtr, dstPtr); }; - auto putSafepoint = [&usePreBarrier, barrierSet, srcArray, dstArray, thread](T *&src, T *&dst, size_t start, + auto putSafepoint = [&usePreBarrier, barrierSet, srcArray, dstArray, thread](T *&src1, T *&dst1, size_t start, size_t count) mutable { PostWrite(thread, dstArray, start, count); ark::interpreter::RuntimeInterface::Safepoint(thread); usePreBarrier = barrierSet->IsPreBarrierEnabled(); // If GC suspends worker during RefCopy execution, it may move the arrays pointed by *srcPtr and *dstPtr // to different memory locations; therefore, the new array addresses should be re-read. - src = srcArray.GetStartPtr(); - dst = dstArray.GetStartPtr(); + src1 = srcArray.GetStartPtr(); + dst1 = dstArray.GetStartPtr(); }; if (backwards) { CopyBackward(src, dst, length, copyWithBarriers, putSafepoint); @@ -269,13 +269,13 @@ static void RefCopy(ManagedThread *thread, ObjectArrayHandle srcArray, Object CopyForward(src, dst, length, copyWithBarriers, putSafepoint); } } else { - auto putSafepoint = [srcArray, dstArray, thread](T *&src, T *&dst, size_t start, size_t count) mutable { + auto putSafepoint = [srcArray, dstArray, thread](T *&src2, T *&dst2, size_t start, size_t count) mutable { PostWrite(thread, dstArray, start, count); ark::interpreter::RuntimeInterface::Safepoint(thread); // If GC suspends worker during RefCopy execution, it may move the arrays pointed by *srcPtr and *dstPtr // to different memory locations; therefore, the new array addresses should be re-read. - src = srcArray.GetStartPtr(); - dst = dstArray.GetStartPtr(); + src2 = srcArray.GetStartPtr(); + dst2 = dstArray.GetStartPtr(); }; if (backwards) { CopyBackward(src, dst, length, copy, putSafepoint); diff --git a/static_core/plugins/ets/runtime/types/ets_class.cpp b/static_core/plugins/ets/runtime/types/ets_class.cpp index b636faf6a4..a9381abe34 100644 --- a/static_core/plugins/ets/runtime/types/ets_class.cpp +++ b/static_core/plugins/ets/runtime/types/ets_class.cpp @@ -490,13 +490,17 @@ PandaVector EtsClass::GetConstructors() { auto constructors = PandaVector(); auto methods = GetRuntimeClass()->GetMethods(); + // std::cerr << "----------------------------------" << GetRuntimeClass()->GetName() << std::endl; // NOTE(kirill-mitkin): cache in ets_class field for (auto &method : methods) { // Skip constructors + // std::cerr << "NOT PUSH " << method.GetProto().GetSignature() << std::endl; if (method.IsInstanceConstructor()) { + // std::cerr << "PUSH THE CONSTRUCTORRRRRRRR " << method.GetProto().GetSignature() << std::endl; constructors.emplace_back(EtsMethod::FromRuntimeMethod(&method)); } } + // std::cerr << "+++++++++++++++++++++++++" << std::endl; return constructors; } diff --git a/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets b/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets index 237428979d..d6885581d4 100644 --- a/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets +++ b/static_core/plugins/ets/stdlib/std/core/ProxyNew.ets @@ -20,11 +20,11 @@ export namespace reflect { // Must be renamed to Proxy handler after old proxy is deleted. export interface InvokationHandler { - get(proxy: Object, method: Method): Any + gett(proxy: Object, method: InstanceMethod): Any - set(proxy: Object, method: Method, value: Any): boolean + sett(proxy: Object, method: InstanceMethod, value: Any): boolean - invoke(proxy: Object, method: Method, args: FixedArray): Any + invoke(proxy: Object, method: InstanceMethod, args: FixedArray): Any } // Each new proxy class extends Proxy. @@ -51,9 +51,15 @@ export class Proxy { throw new Error(`At least one interface expected`) } - let methods: Array = Proxy.getMethods(interfaces) + let methods: Array = Proxy.getMethods(interfaces) + console.log("PRINT MANAGED METHOD START") + for (let i = 0; i < methods.length; ++i) { + console.log(methods[i].getName()) + } + console.log("PRINT MANAGED METHOD END") let proxyName: string = Proxy.createUniqueName(interfaces) - return Proxy.generateProxy(linker, proxyName, interfaces, methods) + let pr = Proxy.generateProxy(linker, proxyName, interfaces, methods) + return pr } public static newInstance(proxyClass: Class, handler: InvokationHandler): Object { @@ -79,41 +85,48 @@ export class Proxy { linker: RuntimeLinker, proxyName: string, interfaces: FixedArray, - methods: Array + methods: Array ): Class - private static getMethods(ifaces: FixedArray): Array { - let result = Array() + private static getMethods(ifaces: FixedArray): Array { + // Currently, interface can't has static methods. + let instanceMethods = Array() for(let idx = 0; idx < ifaces.length; ++idx) { - let type: Type = Type.of(ifaces[idx]) - if (!(type instanceof ClassType)) { - throw new Error(`Expected array of classes interfaces, entity of index ${idx} has no interface type`) + let arr: FixedArray = ifaces[idx].getInstanceMethods() + + for (let i = 0; i < arr.length; ++i) { + console.log(`PUSH METHOD ${arr[i]}`) + instanceMethods.pushOne(arr[i]) } - Proxy.getMethodsRecursive(type as ClassType, result) - Proxy.collectMethods(type as ClassType, result) + // let type: Type = Type.of(ifaces[idx]) + // if (!(type instanceof ClassType)) { + // throw new Error(`Expected array of classes interfaces, entity of index ${idx} has no interface type`) + // } + // Proxy.getMethodsRecursive(type as ClassType, result) + // Proxy.collectMethods(type as ClassType, result) } - return result + return instanceMethods } - // NOTE(kurnevichstanislav): should be replaced by call of getInterfaces from new typAPI instead of collecting interfaces one-by-one. - private static getMethodsRecursive(iface: ClassType, result: Array): void { - for(let idx = 0; idx < iface.getInterfacesNum(); ++idx) { - let intf = iface.getInterface(idx) - Proxy.getMethodsRecursive(intf, result) - Proxy.collectMethods(intf, result) - } - } - - // NOTE(kurnevichstanislav): should be replaced by call of getMethods from new typeAPI instead of collecting methods one-by-one. - private static collectMethods(iface: InterfaceType, result: Array): void { - const methodsCount = iface.getMethodsNum() - for(let idx = 0; idx < methodsCount; ++idx) { - const method = iface.getMethod(idx) - result.pushOne(method) - } - } + // // NOTE(kurnevichstanislav): should be replaced by call of getInterfaces from new typAPI instead of collecting interfaces one-by-one. + // private static getMethodsRecursive(iface: ClassType, result: Array): void { + // for(let idx = 0; idx < iface.getInterfacesNum(); ++idx) { + // let intf = iface.getInterface(idx) + // Proxy.getMethodsRecursive(intf, result) + // Proxy.collectMethods(intf, result) + // } + // } + + // // NOTE(kurnevichstanislav): should be replaced by call of getMethods from new typeAPI instead of collecting methods one-by-one. + // private static collectMethods(iface: InterfaceType, result: Array): void { + // const methodsCount = iface.getMethodsNum() + // for(let idx = 0; idx < methodsCount; ++idx) { + // const method = iface.getMethod(idx) + // result.pushOne(method) + // } + // } } // export class Proxy } // export namespace reflect diff --git a/static_core/plugins/ets/stdlib/std/core/Type.ets b/static_core/plugins/ets/stdlib/std/core/Type.ets index db2e641267..ea016648dc 100644 --- a/static_core/plugins/ets/stdlib/std/core/Type.ets +++ b/static_core/plugins/ets/stdlib/std/core/Type.ets @@ -1907,9 +1907,12 @@ export final class ClassType extends Type { for (let i = 0; i < ctorNum; i++) { const c = this.getConstructor(i) const ct = c.getType() + console.log(`-----ct.getParametersNum() = ${ct.getParametersNum()}; args.length = ${args.length}`) if (ct.getParametersNum() == args.length) { + console.log("im here 11111") let ok = true for (let arg = 0; arg < args.length; arg++) { + console.log(`im here 2222 --> ${args.length}`) if (!ct.getParameter(arg).getType().assignableFrom(argTypes[arg])) { ok = false break @@ -1920,6 +1923,7 @@ export final class ClassType extends Type { } } } + console.log(`____________________Constructors num = ${ctors.length}`) // inspect if constructor has a more specific one (O(n^2)) for (let inspect = 0; inspect < ctors.length; inspect++) { const toRem = ctors.at(inspect)!.getType() @@ -1946,7 +1950,7 @@ export final class ClassType extends Type { } } if (ctors.length != 1) { - throw new Error("can't select consturctor: " + ctors.length + " left") + throw new Error("can't select constructor: " + ctors.length + " left") } return ctors.at(0)!.invoke(undefined, args) as Object } -- Gitee