From d7623daafcaaa530ad36b4741a77dcfe592bb2ab Mon Sep 17 00:00:00 2001 From: Lyupa Anastasia Date: Thu, 22 May 2025 11:41:37 +0300 Subject: [PATCH] Support comparison of function references Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/IC9SE6 Change-Id: I54a0972dbd5adbb947fb0d911edadae770cede29 Signed-off-by: Lyupa Anastasia --- .../ets/runtime/ets_panda_file_items.h | 3 ++ static_core/plugins/ets/runtime/ets_stubs.cpp | 13 ++++++ .../plugins/ets/runtime/types/ets_class.cpp | 46 +++++++++++++++++++ .../plugins/ets/runtime/types/ets_class.h | 28 ++++++++++- .../plugins/ets/stdlib/std/core/Class.ets | 5 ++ .../functions/function_reference.ets | 20 ++++++++ .../functions/method_reference.ets | 22 +++++++++ 7 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 static_core/plugins/ets/tests/ets_test_suite/functions/function_reference.ets create mode 100644 static_core/plugins/ets/tests/ets_test_suite/functions/method_reference.ets 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 7568e9b505..5ec1084eec 100644 --- a/static_core/plugins/ets/runtime/ets_panda_file_items.h +++ b/static_core/plugins/ets/runtime/ets_panda_file_items.h @@ -209,6 +209,9 @@ static constexpr std::string_view ANI_UNSAFE_DIRECT = "Lstd/a // Module annotation class static constexpr std::string_view ANNOTATION_MODULE = "Lets/annotation/Module;"; +// Function reference annotation class +static constexpr std::string_view ANNOTATION_FUNCTION_REFERENCE = "Lets/annotation/FunctionReference;"; + // escompat static constexpr std::string_view DATE = "Lescompat/Date;"; static constexpr std::string_view ARRAY_ENTRIES_ITERATOR_T = "Lescompat/ArrayEntriesIterator_T;"; diff --git a/static_core/plugins/ets/runtime/ets_stubs.cpp b/static_core/plugins/ets/runtime/ets_stubs.cpp index 6dc61bff8c..f235051e22 100644 --- a/static_core/plugins/ets/runtime/ets_stubs.cpp +++ b/static_core/plugins/ets/runtime/ets_stubs.cpp @@ -122,6 +122,19 @@ bool EtsValueTypedEquals(EtsCoroutine *coro, EtsObject *obj1, EtsObject *obj2) } return EtsReferenceEquals(coro, value1, value2); } + if (cls1->IsFunctionReference()) { + LOG(WARNING, RUNTIME) << "IsFunctionReference"; + if (UNLIKELY(cls2->IsFunctionReference())) { + return false; + } + if (cls1->GetTypeMetadata() != cls2->GetTypeMetadata()) { + return false; + } + if (cls1->GetFieldsNumber() != 1 || cls2->GetFieldsNumber() != 1) { + return false; + } + return obj1->GetFieldObject(cls1->GetFieldByIndex(0)) == obj2->GetFieldObject(cls2->GetFieldByIndex(0)); + } UNREACHABLE(); } diff --git a/static_core/plugins/ets/runtime/types/ets_class.cpp b/static_core/plugins/ets/runtime/types/ets_class.cpp index ca06a88b58..2c0af166af 100644 --- a/static_core/plugins/ets/runtime/types/ets_class.cpp +++ b/static_core/plugins/ets/runtime/types/ets_class.cpp @@ -405,6 +405,11 @@ void EtsClass::SetBigInt() flags_ = flags_ | IS_BIGINT; ASSERT(IsBigInt()); } +void EtsClass::SetFunctionReference() +{ + flags_ = flags_ | IS_FUNCTION_REFERENCE; + ASSERT(IsFunctionReference()); +} static bool HasFunctionTypeInSuperClasses(EtsClass *cls) { @@ -455,6 +460,16 @@ void EtsClass::Initialize(EtsClass *superClass, uint16_t accessFlags, bool isPri flags |= IS_MODULE; return true; }); + + uint32_t methodOffset = 0; + cda.EnumerateAnnotation(panda_file_items::class_descriptors::ANNOTATION_FUNCTION_REFERENCE.data(), + [&flags, &methodOffset](panda_file::AnnotationDataAccessor &ada) { + flags |= (IS_FUNCTION_REFERENCE | IS_VALUE_TYPED); + const auto value = ada.GetElement(0).GetScalarValue(); + methodOffset = value.GetValue(); + return true; + }); + SetFunctionReferenceMetadata(methodOffset); } SetFlags(flags); @@ -641,4 +656,35 @@ EtsObject *EtsClass::CreateInstance() return objHandle.GetPtr(); } +void EtsClass::SetFunctionReferenceMetadata(uint32_t methodOffset) +{ + auto *pfile = GetRuntimeClass()->GetPandaFile(); + if (pfile == nullptr || !methodOffset) { + return; + } + + auto coro = EtsCoroutine::GetCurrent(); + EtsClassLinker *linker = coro->GetPandaVM()->GetClassLinker(); + Method *resolvedMethod = linker->GetMethod(*pfile, panda_file::File::EntityId(methodOffset), GetLoadContext()); + + if (UNLIKELY(coro->HasPendingException())) { + return; + } + + if (resolvedMethod == nullptr) { + GetRuntimeClass()->SetState(Class::State::ERRONEOUS); + LOG(WARNING, RUNTIME) << "Cannot resolve method by offset " << methodOffset; + return; + } + + // Verify that class has $this field only and field type and class are correct + if (GetFieldsNumber() != 1 || GetFieldByIndex(0)->GetEtsType() != EtsType::OBJECT || + !GetFieldByIndex(0)->GetType()->GetRuntimeClass()->IsSubClassOf(resolvedMethod->GetClass())) { + GetRuntimeClass()->SetState(Class::State::ERRONEOUS); + LOG(WARNING, RUNTIME) << "Verification of function reference class failed"; + } + + SetTypeMetadata(reinterpret_cast(resolvedMethod)); +} + } // 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 1f7f9a0a17..c4048c4ec8 100644 --- a/static_core/plugins/ets/runtime/types/ets_class.h +++ b/static_core/plugins/ets/runtime/types/ets_class.h @@ -422,6 +422,16 @@ public: return GetObjectHeader()->GetFieldPrimitive(GetFlagsOffset()); } + void SetTypeMetadata(long typeMetadata) + { + GetObjectHeader()->SetFieldPrimitive(GetTypeMetadataOffset(), typeMetadata); + } + + long GetTypeMetadata() const + { + return GetObjectHeader()->GetFieldPrimitive(GetTypeMetadataOffset()); + } + void SetWeakReference(); void SetFinalizeReference(); void SetValueTyped(); @@ -430,6 +440,7 @@ public: void SetFunction(); void SetEtsEnum(); void SetBigInt(); + void SetFunctionReference(); [[nodiscard]] bool IsWeakReference() const { @@ -482,6 +493,11 @@ public: return (GetFlags() & IS_MODULE) != 0; } + [[nodiscard]] bool IsFunctionReference() const + { + return (GetFlags() & IS_FUNCTION_REFERENCE) != 0; + } + EtsClass() = delete; ~EtsClass() = delete; NO_COPY_SEMANTIC(EtsClass); @@ -502,6 +518,11 @@ public: return MEMBER_OFFSET(EtsClass, flags_); } + static constexpr size_t GetTypeMetadataOffset() + { + return MEMBER_OFFSET(EtsClass, typeMetadata_); + } + static constexpr size_t GCRefFieldsOffset() { return GetHeaderOffset() + sizeof(header_); @@ -509,7 +530,7 @@ public: static constexpr size_t GCRefFieldsNum() { - return (GetFlagsOffset() - GCRefFieldsOffset()) / sizeof(ObjectPointerType); + return (GetTypeMetadataOffset() - GCRefFieldsOffset()) / sizeof(ObjectPointerType); } private: @@ -581,6 +602,8 @@ private: return &header_; } + void SetFunctionReferenceMetadata(uint32_t methodOffset); + constexpr static uint32_t ETS_ACC_PRIMITIVE = 1U << 16U; /// Class is a WeakReference or successor of this class constexpr static uint32_t IS_WEAK_REFERENCE = 1U << 17U; @@ -602,6 +625,8 @@ private: constexpr static uint32_t IS_MODULE = 1U << 24U; // Class is enum constexpr static uint32_t IS_ETS_ENUM = 1U << 25U; + // Class is Function Reference + constexpr static uint32_t IS_FUNCTION_REFERENCE = 1U << 26U; ark::ObjectHeader header_; // EtsObject @@ -609,6 +634,7 @@ private: FIELD_UNUSED ObjectPointer name_; // String FIELD_UNUSED ObjectPointer superClass_; // Class FIELD_UNUSED uint32_t flags_; + FIELD_UNUSED long typeMetadata_; // Used for function references comparison // ets.Class fields END ark::Class klass_; diff --git a/static_core/plugins/ets/stdlib/std/core/Class.ets b/static_core/plugins/ets/stdlib/std/core/Class.ets index e2ea8bea00..697523a32b 100644 --- a/static_core/plugins/ets/stdlib/std/core/Class.ets +++ b/static_core/plugins/ets/stdlib/std/core/Class.ets @@ -22,6 +22,7 @@ export final class Class { private name: string | undefined private superClass: Class | undefined private flags: int + private typeMetadata: long = 0 private constructor() { throw new Error("Class constructor called") } private native getNameInternal(): string; @@ -34,6 +35,10 @@ export final class Class { return this.superClass } + public getTypeMetadata(): long { + return this.typeMetadata + } + public native getLinker(): RuntimeLinker /** diff --git a/static_core/plugins/ets/tests/ets_test_suite/functions/function_reference.ets b/static_core/plugins/ets/tests/ets_test_suite/functions/function_reference.ets new file mode 100644 index 0000000000..5f8fff23fa --- /dev/null +++ b/static_core/plugins/ets/tests/ets_test_suite/functions/function_reference.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +function foo() {} + +function main() { + assertEQ(foo == foo, true) +} diff --git a/static_core/plugins/ets/tests/ets_test_suite/functions/method_reference.ets b/static_core/plugins/ets/tests/ets_test_suite/functions/method_reference.ets new file mode 100644 index 0000000000..ecea7017da --- /dev/null +++ b/static_core/plugins/ets/tests/ets_test_suite/functions/method_reference.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +class X { foo() {} } + +function main() { + let x = new X() + assertEQ(x.foo == x.foo, true) + assertEQ(new X().foo == new X().foo, false) +} -- Gitee