From 6a8fc911286f70348685f422fe34cb366d42c149 Mon Sep 17 00:00:00 2001 From: yuesiyuan Date: Thu, 12 Jun 2025 20:50:12 +0800 Subject: [PATCH] Impl StaticObjectAccessor Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICET9G Signed-off-by: yuesiyuan --- common_interfaces/base/common.h | 44 +-- common_interfaces/objects/base_object.h | 12 +- .../objects/base_object_accessor.h | 23 -- .../objects/base_object_descriptor.h | 2 +- .../objects/base_object_dispatcher.h | 56 ++-- .../objects/base_object_operator.h | 1 + common_interfaces/objects/base_state_word.h | 38 ++- common_interfaces/objects/base_type.h | 4 +- common_interfaces/objects/field.h | 2 + common_interfaces/objects/ref_field.h | 43 +-- .../static_object_accessor_interface.h | 48 +++ common_interfaces/thread/mutator_base.h | 58 ++-- common_interfaces/thread/thread_holder.h | 12 +- common_interfaces/thread/thread_state.h | 3 + static_core/codecheck_ignore.json | 1 + static_core/plugins/ets/BUILD.gn | 5 +- .../plugins/ets/runtime/CMakeLists.txt | 2 + static_core/plugins/ets/runtime/ets_utils.cpp | 56 ++++ static_core/plugins/ets/runtime/ets_utils.h | 4 + .../ets/runtime/static_object_accessor.cpp | 191 ++++++++++++ .../ets/runtime/static_object_accessor.h | 44 +++ .../plugins/ets/runtime/types/ets_object.h | 2 +- static_core/plugins/ets/subproject_sources.gn | 1 + .../plugins/ets/tests/runtime/CMakeLists.txt | 12 + .../runtime/static_object_accessor_test.cpp | 286 ++++++++++++++++++ .../runtime/include/thread_proxy_static.h | 2 +- 26 files changed, 826 insertions(+), 126 deletions(-) create mode 100644 common_interfaces/objects/static_object_accessor_interface.h create mode 100644 static_core/plugins/ets/runtime/static_object_accessor.cpp create mode 100644 static_core/plugins/ets/runtime/static_object_accessor.h create mode 100644 static_core/plugins/ets/tests/runtime/static_object_accessor_test.cpp diff --git a/common_interfaces/base/common.h b/common_interfaces/base/common.h index a9a6aa7006..5ebfc55933 100755 --- a/common_interfaces/base/common.h +++ b/common_interfaces/base/common.h @@ -27,16 +27,19 @@ namespace panda { #ifndef PANDA_TARGET_WINDOWS -#define PUBLIC_API __attribute__((visibility ("default"))) +#define PUBLIC_API __attribute__((visibility("default"))) #else #define PUBLIC_API __declspec(dllexport) #endif #define NO_INLINE_CC __attribute__((noinline)) -#define LIKELY_CC(exp) (__builtin_expect((exp) != 0, true)) -#define UNLIKELY_CC(exp) (__builtin_expect((exp) != 0, false)) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define LIKELY_CC(exp) (__builtin_expect((exp) != 0, true)) // CC-OFF(G.PRE.02-CPP) design decision +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNLIKELY_CC(exp) (__builtin_expect((exp) != 0, false)) // CC-OFF(G.PRE.02-CPP) design decision +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREACHABLE_CC() \ do { \ std::cerr << "This line should be unreachable" << std::endl; \ @@ -44,13 +47,14 @@ namespace panda { __builtin_unreachable(); \ } while (0) -#define CHECK_CC(expr) \ - do { \ - if (UNLIKELY_CC(!(expr))) { \ - std::cerr << "CHECK FAILED: " << #expr; \ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHECK_CC(expr) \ + do { \ + if (UNLIKELY_CC(!(expr))) { \ + std::cerr << "CHECK FAILED: " << #expr; \ std::cerr << " IN: " << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << std::endl; \ - std::abort(); \ - } \ + std::abort(); \ + } \ } while (0) #if defined(NDEBUG) @@ -58,18 +62,17 @@ namespace panda { #define DCHECK_CC(x) #else #define ALWAYS_INLINE_CC -#define DCHECK_CC(x) CHECK_CC(x) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DCHECK_CC(x) CHECK_CC(x) // CC-OFF(G.PRE.02-CPP) #endif #if defined(__cplusplus) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define NO_COPY_CTOR_CC(TypeName) \ - TypeName(const TypeName&) = delete +#define NO_COPY_CTOR_CC(TypeName) TypeName(const TypeName &) = delete // CC-OFF(G.PRE.02-CPP) design decision // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define NO_COPY_OPERATOR_CC(TypeName) \ - void operator=(const TypeName&) = delete +#define NO_COPY_OPERATOR_CC(TypeName) void operator=(const TypeName &) = delete // CC-OFF(G.PRE.02-CPP) design decision // Disabling copy ctor and copy assignment operator. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -78,14 +81,14 @@ namespace panda { NO_COPY_OPERATOR_CC(TypeName) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define NO_MOVE_CTOR_CC(TypeName) \ +#define NO_MOVE_CTOR_CC(TypeName) \ /* NOLINTNEXTLINE(misc-macro-parentheses) */ \ - TypeName(TypeName&&) = delete + TypeName(TypeName &&) = delete // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define NO_MOVE_OPERATOR_CC(TypeName) \ +#define NO_MOVE_OPERATOR_CC(TypeName) \ /* NOLINTNEXTLINE(misc-macro-parentheses) */ \ - TypeName& operator=(TypeName&&) = delete + TypeName &operator=(TypeName &&) = delete // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define NO_MOVE_SEMANTIC_CC(TypeName) \ @@ -94,14 +97,15 @@ namespace panda { #endif // defined(__cplusplus) +// NOLINTNEXTLINE(readability-identifier-naming) enum class PUBLIC_API LOG_LEVEL : uint8_t { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3, FATAL = 4, - FOLLOW = 100, // if hilog enabled follow hilog, otherwise use INFO level + FOLLOW = 100, // if hilog enabled follow hilog, otherwise use INFO level }; -} // panda +} // namespace panda #endif // COMMON_INTERFACES_BASE_COMMON_H diff --git a/common_interfaces/objects/base_object.h b/common_interfaces/objects/base_object.h index be7dd8bda8..7f6f957b43 100644 --- a/common_interfaces/objects/base_object.h +++ b/common_interfaces/objects/base_object.h @@ -16,8 +16,8 @@ #ifndef COMMON_INTERFACES_OBJECTS_BASE_OBJECT_H #define COMMON_INTERFACES_OBJECTS_BASE_OBJECT_H -#include -#include +#include +#include #include "base/common.h" #include "objects/base_object_operator.h" @@ -39,7 +39,7 @@ public: { if (IsForwarded()) { return GetSizeForwarded(); - } else { + } else { // NOLINT(readability-else-after-return) return GetOperator()->GetSize(this); } } @@ -75,13 +75,13 @@ public: size_t objectHeaderSize = sizeof(BaseStateWord); DCHECK_CC(size >= objectHeaderSize + sizeof(size_t)); auto addr = reinterpret_cast(this) + objectHeaderSize; - *reinterpret_cast(addr) = size; + *reinterpret_cast(addr) = size; } inline size_t GetSizeForwarded() const { auto addr = reinterpret_cast(this) + sizeof(BaseStateWord); - return *reinterpret_cast(addr); + return *reinterpret_cast(addr); } inline bool IsForwarding() const @@ -185,7 +185,7 @@ protected: } static PUBLIC_API BaseObjectOperator operator_; - BaseStateWord state_; + BaseStateWord state_; // NOLINT(misc-non-private-member-variables-in-classes) }; } // namespace panda #endif // COMMON_INTERFACES_OBJECTS_BASE_OBJECT_H diff --git a/common_interfaces/objects/base_object_accessor.h b/common_interfaces/objects/base_object_accessor.h index 3b23482f6a..7457f14d70 100644 --- a/common_interfaces/objects/base_object_accessor.h +++ b/common_interfaces/objects/base_object_accessor.h @@ -42,28 +42,5 @@ public: // SetElementByIdx is used to set the value of an element in a dynamic object with the given index. virtual bool SetElementByIdx(ThreadHolder *thread, BaseObject *obj, const uint32_t index, JSTaggedValue value) = 0; }; - -// The interface will be implemented in the static runtime to provide the ability to access -// properties of static objects. -class StaticObjectAccessorInterface { -public: - // HasProperty is used to check if the static object has a property with the given name. - virtual bool HasProperty(ThreadHolder *thread, const BaseObject *obj, const char *name) = 0; - - // GetProperty is used to get the value of a property from a static object with the given name. - virtual BoxedValue GetProperty(ThreadHolder *thread, const BaseObject *obj, const char *name) = 0; - - // SetProperty is used to set the value of a property in a static object with the given name. - virtual bool SetProperty(ThreadHolder *thread, BaseObject *obj, const char *name, BoxedValue value) = 0; - - // HasElementByIdx is used to check if the static object has an element with the given index. - virtual bool HasElementByIdx(ThreadHolder *thread, const BaseObject *obj, const uint32_t index) = 0; - - // GetElementByIdx is used to get the value of an element from a static object with the given index. - virtual BoxedValue GetElementByIdx(ThreadHolder *thread, const BaseObject *obj, const uint32_t index) = 0; - - // SetElementByIdx is used to set the value of an element in a static object with the given index. - virtual bool SetElementByIdx(ThreadHolder *thread, BaseObject *obj, uint32_t index, const BoxedValue value) = 0; -}; } // namespace panda #endif // COMMON_INTERFACES_BASE_OBJECT_ACCESSOR_H \ No newline at end of file diff --git a/common_interfaces/objects/base_object_descriptor.h b/common_interfaces/objects/base_object_descriptor.h index 0fa68215c7..3e47425a86 100644 --- a/common_interfaces/objects/base_object_descriptor.h +++ b/common_interfaces/objects/base_object_descriptor.h @@ -22,7 +22,7 @@ namespace panda::ecmascript { class HandlerBase; -} +} // namespace panda::ecmascript namespace panda { // for IC diff --git a/common_interfaces/objects/base_object_dispatcher.h b/common_interfaces/objects/base_object_dispatcher.h index 247d4651cd..29bf73fb4c 100644 --- a/common_interfaces/objects/base_object_dispatcher.h +++ b/common_interfaces/objects/base_object_dispatcher.h @@ -21,14 +21,16 @@ #include "objects/base_object_accessor.h" #include "objects/base_object_descriptor.h" #include "objects/base_type_converter.h" +#include "objects/static_object_accessor_interface.h" #include "thread/thread_holder.h" namespace panda { class BaseObjectDispatcher { -enum class ObjectType : uint8_t { STATIC = 0x0, DYNAMIC, UNKNOWN }; + enum class ObjectType : uint8_t { STATIC = 0x0, DYNAMIC, UNKNOWN }; + public: // Singleton - static BaseObjectDispatcher& GetDispatcher() + static BaseObjectDispatcher &GetDispatcher() { static BaseObjectDispatcher instance; return instance; @@ -64,8 +66,9 @@ public: stcObjDescriptor_ = stcObjDescriptor; } + // NOLINTNEXTLINE(readability-identifier-naming) template - JSTaggedValue GetTaggedProperty(ThreadHolder *thread, const BaseObject *obj, const char* name) const + JSTaggedValue GetTaggedProperty(ThreadHolder *thread, const BaseObject *obj, const char *name) const { if constexpr (objType == ObjectType::DYNAMIC) { // fix(hewei): exceptions may occur, check here and return default value. @@ -78,7 +81,7 @@ public: if (obj->IsDynamic()) { // fix(hewei): exceptions may occur, check here and return default value. return dynObjAccessor_->GetProperty(thread, obj, name); - } else { + } else { // NOLINT(readability-else-after-return) BoxedValue value = stcObjAccessor_->GetProperty(thread, obj, name); // fix(hewei): exceptions may occur, check here and return. Also, check exceptions in Wrap functions. return dynTypeConverter_->WrapTagged(thread, stcTypeConverter_->UnwrapBoxed(value)); @@ -86,8 +89,9 @@ public: } } + // NOLINTNEXTLINE(readability-identifier-naming) template - BoxedValue GetBoxedProperty(ThreadHolder *thread, const BaseObject *obj, const char* name) const + BoxedValue GetBoxedProperty(ThreadHolder *thread, const BaseObject *obj, const char *name) const { if constexpr (objType == ObjectType::DYNAMIC) { JSTaggedValue value = dynObjAccessor_->GetProperty(thread, obj, name); @@ -101,13 +105,14 @@ public: JSTaggedValue value = dynObjAccessor_->GetProperty(thread, obj, name); // fix(hewei): exceptions may occur, check here and return. Also, check exceptions in Wrap functions. return stcTypeConverter_->WrapBoxed(dynTypeConverter_->UnwrapTagged(value)); - } else { + } else { // NOLINT(readability-else-after-return) // fix(hewei): exceptions may occur, check here and return default value. return stcObjAccessor_->GetProperty(thread, obj, name); } } } + // NOLINTNEXTLINE(readability-identifier-naming) template JSTaggedValue GetTaggedElementByIdx(ThreadHolder *thread, const BaseObject *obj, const uint32_t index) const { @@ -122,7 +127,7 @@ public: if (obj->IsDynamic()) { // fix(hewei): exceptions may occur, check here and return default value. return dynObjAccessor_->GetElementByIdx(thread, obj, index); - } else { + } else { // NOLINT(readability-else-after-return) BoxedValue value = stcObjAccessor_->GetElementByIdx(thread, obj, index); // fix(hewei): exceptions may occur, check here and return. Also, check exceptions in Wrap functions. return dynTypeConverter_->WrapTagged(thread, stcTypeConverter_->UnwrapBoxed(value)); @@ -130,6 +135,7 @@ public: } } + // NOLINTNEXTLINE(readability-identifier-naming) template BoxedValue GetBoxedElementByIdx(ThreadHolder *thread, const BaseObject *obj, const uint32_t index) const { @@ -145,15 +151,16 @@ public: JSTaggedValue value = dynObjAccessor_->GetElementByIdx(thread, obj, index); // fix(hewei): exceptions may occur, check here and return. Also, check exceptions in Wrap functions. return stcTypeConverter_->WrapBoxed(dynTypeConverter_->UnwrapTagged(value)); - } else { + } else { // NOLINT(readability-else-after-return) // fix(hewei): exceptions may occur, check here and return default value. return stcObjAccessor_->GetElementByIdx(thread, obj, index); } } } + // NOLINTNEXTLINE(readability-identifier-naming) template - bool SetTaggedProperty(ThreadHolder *thread, BaseObject *obj, const char* name, JSTaggedValue value) + bool SetTaggedProperty(ThreadHolder *thread, BaseObject *obj, const char *name, JSTaggedValue value) { if constexpr (objType == ObjectType::DYNAMIC) { // fix(hewei): exceptions may occur, check here and return default value. @@ -166,7 +173,7 @@ public: if (obj->IsDynamic()) { // fix(hewei): exceptions may occur, check here and return default value. return dynObjAccessor_->SetProperty(thread, obj, name, value); - } else { + } else { // NOLINT(readability-else-after-return) // fix(hewei): exceptions may occur, check here and return. Also, check exceptions in Wrap functions. return stcObjAccessor_->SetProperty( thread, obj, name, stcTypeConverter_->WrapBoxed(dynTypeConverter_->UnwrapTagged(value))); @@ -174,6 +181,7 @@ public: } } + // NOLINTNEXTLINE(readability-identifier-naming) template bool SetBoxedProperty(ThreadHolder *thread, BaseObject *obj, const char *name, BoxedValue value) { @@ -189,13 +197,14 @@ public: // fix(hewei): exceptions may occur, check here and return. Also, check exceptions in Wrap functions. auto wrapedValue = dynTypeConverter_->WrapTagged(thread, stcTypeConverter_->UnwrapBoxed(value)); return dynObjAccessor_->SetProperty(thread, obj, name, wrapedValue); - } else { + } else { // NOLINT(readability-else-after-return) // fix(hewei): exceptions may occur, check here and return default value. return stcObjAccessor_->SetProperty(thread, obj, name, value); } } } + // NOLINTNEXTLINE(readability-identifier-naming) template bool SetTaggedElementByIdx(ThreadHolder *thread, BaseObject *obj, const uint32_t index, JSTaggedValue value) { @@ -204,13 +213,13 @@ public: return dynObjAccessor_->SetElementByIdx(thread, obj, index, value); } else if constexpr (objType == ObjectType::STATIC) { // fix(hewei): exceptions may occur, check here and return. Also, check exceptions in Wrap functions. - return stcObjAccessor_->SetElementByIdx(thread, obj, index, - stcTypeConverter_->WrapBoxed(dynTypeConverter_->UnwrapTagged(value))); + return stcObjAccessor_->SetElementByIdx( + thread, obj, index, stcTypeConverter_->WrapBoxed(dynTypeConverter_->UnwrapTagged(value))); } else { if (obj->IsDynamic()) { // fix(hewei): exceptions may occur, check here and return default value. return dynObjAccessor_->SetElementByIdx(thread, obj, index, value); - } else { + } else { // NOLINT(readability-else-after-return) // fix(hewei): exceptions may occur, check here and return. Also, check exceptions in Wrap functions. return stcObjAccessor_->SetElementByIdx( thread, obj, index, stcTypeConverter_->WrapBoxed(dynTypeConverter_->UnwrapTagged(value))); @@ -218,30 +227,32 @@ public: } } + // NOLINTNEXTLINE(readability-identifier-naming) template bool SetBoxedElementByIdx(ThreadHolder *thread, BaseObject *obj, const uint32_t index, BoxedValue value) { if constexpr (objType == ObjectType::DYNAMIC) { // fix(hewei): exceptions may occur, check here and return. Also, check exceptions in Wrap functions. - return dynObjAccessor_->SetElementByIdx(thread, obj, index, - dynTypeConverter_->WrapTagged(thread, stcTypeConverter_->UnwrapBoxed(value))); + return dynObjAccessor_->SetElementByIdx( + thread, obj, index, dynTypeConverter_->WrapTagged(thread, stcTypeConverter_->UnwrapBoxed(value))); } else if constexpr (objType == ObjectType::STATIC) { // fix(hewei): exceptions may occur, check here and return default value. return stcObjAccessor_->SetElementByIdx(thread, obj, index, value); } else { if (obj->IsDynamic()) { // fix(hewei): exceptions may occur, check here and return. Also, check exceptions in Wrap functions. - return dynObjAccessor_->SetElementByIdx(thread, obj, index, - dynTypeConverter_->WrapTagged(thread, stcTypeConverter_->UnwrapBoxed(value))); - } else { + return dynObjAccessor_->SetElementByIdx( + thread, obj, index, dynTypeConverter_->WrapTagged(thread, stcTypeConverter_->UnwrapBoxed(value))); + } else { // NOLINT(readability-else-after-return) // fix(hewei): exceptions may occur, check here and return default value. return stcObjAccessor_->SetElementByIdx(thread, obj, index, value); } } } + // NOLINTNEXTLINE(readability-identifier-naming) template - bool HasProperty(ThreadHolder *thread, const BaseObject *obj, const char* name) const + bool HasProperty(ThreadHolder *thread, const BaseObject *obj, const char *name) const { if constexpr (objType == ObjectType::DYNAMIC) { // fix(hewei): exceptions may occur, check here and return default value. @@ -253,13 +264,14 @@ public: if (obj->IsDynamic()) { // fix(hewei): exceptions may occur, check here and return default value. return dynObjAccessor_->HasProperty(thread, obj, name); - } else { + } else { // NOLINT(readability-else-after-return) // fix(hewei): exceptions may occur, check here and return default value. return stcObjAccessor_->HasProperty(thread, obj, name); } } } + // NOLINTNEXTLINE(readability-identifier-naming) template bool HasElementByIdx(ThreadHolder *thread, const BaseObject *obj, const uint32_t index) const { @@ -273,7 +285,7 @@ public: if (obj->IsDynamic()) { // fix(hewei): exceptions may occur, check here and return default value. return dynObjAccessor_->HasElementByIdx(thread, obj, index); - } else { + } else { // NOLINT(readability-else-after-return) // fix(hewei): exceptions may occur, check here and return default value. return stcObjAccessor_->HasElementByIdx(thread, obj, index); } diff --git a/common_interfaces/objects/base_object_operator.h b/common_interfaces/objects/base_object_operator.h index 0e572eace7..7d97d019f0 100644 --- a/common_interfaces/objects/base_object_operator.h +++ b/common_interfaces/objects/base_object_operator.h @@ -27,6 +27,7 @@ namespace panda { class BaseObject; +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) class BaseObjectOperatorInterfaces { public: // Get Object size. diff --git a/common_interfaces/objects/base_state_word.h b/common_interfaces/objects/base_state_word.h index dcb0dfabc0..9294500c27 100644 --- a/common_interfaces/objects/base_state_word.h +++ b/common_interfaces/objects/base_state_word.h @@ -16,7 +16,7 @@ #ifndef COMMON_INTERFACES_OBJECTS_BASE_STATE_WORD_H #define COMMON_INTERFACES_OBJECTS_BASE_STATE_WORD_H -#include +#include #include namespace panda { @@ -36,27 +36,27 @@ public: static constexpr size_t LANGUAGE_WIDTH = 2; BaseStateWord() = default; + // CC-OFFNXT(WordsTool.95 Google) sensitive word conflict + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, google-explicit-constructor) BaseStateWord(MAddress header) : header_(header) {}; - enum class ForwardState : uint64_t { - NORMAL, - FORWARDING, - FORWARDED, - TO_VERSION - }; + enum class ForwardState : uint64_t { NORMAL, FORWARDING, FORWARDED, TO_VERSION }; inline void SetForwarding() { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) state_.forwardState_ = ForwardState::FORWARDING; } inline bool IsForwarding() const { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) return state_.forwardState_ == ForwardState::FORWARDING; } inline bool IsToVersion() const { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) return state_.forwardState_ == ForwardState::TO_VERSION; } @@ -65,6 +65,7 @@ public: if (current.IsForwarding()) { return false; } + // NOLINTNEXTLINE(modernize-use-auto) BaseStateWord newState = BaseStateWord(current.GetHeader()); newState.SetForwardState(ForwardState::FORWARDING); return CompareExchangeHeader(current.GetHeader(), newState.GetHeader()); @@ -74,6 +75,7 @@ public: { do { BaseStateWord current = AtomicGetBaseStateWord(); + // NOLINTNEXTLINE(modernize-use-auto) BaseStateWord newState = BaseStateWord(current.GetHeader()); newState.SetForwardState(forwardState); if (CompareExchangeHeader(current.GetHeader(), newState.GetHeader())) { @@ -84,19 +86,22 @@ public: void SetForwardState(ForwardState state) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) state_.forwardState_ = state; } ForwardState GetForwardState() const { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) return state_.forwardState_; } private: // Little endian + // NOLINTBEGIN(readability-identifier-naming) struct State { - StateWordType padding_ : PADDING_WIDTH; - Language language_ : LANGUAGE_WIDTH; + StateWordType padding_ : PADDING_WIDTH; + Language language_ : LANGUAGE_WIDTH; ForwardState forwardState_ : FORWARD_WIDTH; }; @@ -104,6 +109,7 @@ private: State state_; MAddress header_; }; + // NOLINTEND(readability-identifier-naming) BaseStateWord AtomicGetBaseStateWord() const { @@ -112,19 +118,26 @@ private: MAddress AtomicGetHeader() const { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) return __atomic_load_n(&header_, __ATOMIC_ACQUIRE); } - MAddress GetHeader() const { return header_; } + MAddress GetHeader() const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) + return header_; + } bool CompareExchangeHeader(MAddress expected, MAddress newState) { #if defined(__x86_64__) bool success = + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) __atomic_compare_exchange_n(&header_, &expected, newState, true, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE); #else // due to "Spurious Failure" of compare_exchange_weak, compare_exchange_strong is chosen. bool success = + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) __atomic_compare_exchange_n(&header_, &expected, newState, false, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE); #endif return success; @@ -132,26 +145,31 @@ private: inline void SetForwarded() { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) state_.forwardState_ = ForwardState::FORWARDED; } inline bool IsForwarded() const { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) return state_.forwardState_ == ForwardState::FORWARDED; } inline void SetLanguage(Language language) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) state_.language_ = language; } inline bool IsStatic() const { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) return state_.language_ == Language::STATIC; } inline bool IsDynamic() const { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) return state_.language_ == Language::DYNAMIC; } diff --git a/common_interfaces/objects/base_type.h b/common_interfaces/objects/base_type.h index 31532b7a29..b54d463231 100644 --- a/common_interfaces/objects/base_type.h +++ b/common_interfaces/objects/base_type.h @@ -24,7 +24,7 @@ namespace panda::ecmascript { class JSTaggedValue; -} +} // namespace panda::ecmascript using JSTaggedValue = panda::ecmascript::JSTaggedValue; namespace panda { @@ -34,7 +34,7 @@ struct BaseNull {}; // The common consensus type between static and dynamic using PandaType = std::variant; + double, int64_t, uint64_t, BaseUndefined, BaseNull, BaseObject *>; // base type for static vm using BoxedValue = BaseObject *; diff --git a/common_interfaces/objects/field.h b/common_interfaces/objects/field.h index ed513a245f..e37f2f4e86 100644 --- a/common_interfaces/objects/field.h +++ b/common_interfaces/objects/field.h @@ -23,6 +23,7 @@ class BaseObject; using MemoryOrder = std::memory_order; // T is primitive field +// NOLINTBEGIN(readability-identifier-naming, cppcoreguidelines-pro-type-vararg) template class Field { public: @@ -77,5 +78,6 @@ public: private: T value; }; +// NOLINTEND(readability-identifier-naming, cppcoreguidelines-pro-type-vararg) } // namespace panda #endif // COMMON_INTERFACES_OBJECTS_REF_FIELD_H diff --git a/common_interfaces/objects/ref_field.h b/common_interfaces/objects/ref_field.h index a7fac104c6..a4538c7aac 100644 --- a/common_interfaces/objects/ref_field.h +++ b/common_interfaces/objects/ref_field.h @@ -20,6 +20,8 @@ #include #include "objects/base_state_word.h" +// NOLINTBEGIN(readability-identifier-naming, readability-else-after-return,-warnings-as-errors, +// cppcoreguidelines-pro-type-union-access) namespace panda { class BaseObject; @@ -28,10 +30,18 @@ class RefField { public: static constexpr uint64_t TAG_WEAK = 0x01ULL; static constexpr MAddress REF_UNDEFINED = 0x02ULL; + + struct BitFieldLayout { + MAddress address : 48; + MAddress isTagged : 1; + MAddress tagID : 1; + MAddress padding : 14; + }; + // size in bytes static constexpr size_t GetSize() { - return sizeof(fieldVal); + return sizeof(RefFieldValue); } BaseObject *GetTargetObject(std::memory_order order = std::memory_order_relaxed) const @@ -75,7 +85,6 @@ public: if (isAtomic) { __atomic_store_n(&fieldVal, static_cast(newVal), order); - } else { fieldVal = static_cast(newVal); } } @@ -114,22 +123,22 @@ public: MAddress GetAddress() const { - return address; + return layout.address; } bool IsWeak() const { - return (address & TAG_WEAK); + return (layout.address & TAG_WEAK); } - // bool IsTagged() const { return isTagged == 1; } bool IsTagged() const { return false; } + uint16_t GetTagID() const { - return tagID; + return layout.tagID; } ~RefField() = default; @@ -137,16 +146,19 @@ public: RefField(const RefField &ref) : fieldVal(ref.fieldVal) {} explicit RefField(const BaseObject *obj) : fieldVal(0) { - address = reinterpret_cast(obj); + layout.address = reinterpret_cast(obj); } - RefField(const BaseObject* obj, bool forWeak) : fieldVal(0) + RefField(const BaseObject *obj, bool forWeak) : fieldVal(0) { MAddress tag = forWeak ? TAG_WEAK : 0; - address = reinterpret_cast(obj) | tag; + layout.address = reinterpret_cast(obj) | tag; } - RefField(const BaseObject *obj, uint16_t tagged, uint16_t tagid) - : address(reinterpret_cast(obj)), isTagged(tagged), tagID(tagid), padding(0) + RefField(const BaseObject *obj, uint16_t tagged, uint16_t tagid) : fieldVal(0) { + layout.address = reinterpret_cast(obj); + layout.isTagged = tagged; + layout.tagID = tagid; + layout.padding = 0; } RefField(RefField &&ref) : fieldVal(ref.fieldVal) {} @@ -158,14 +170,11 @@ private: using RefFieldValue = MAddress; union { - struct { - MAddress address : 48; - MAddress isTagged : 1; - MAddress tagID : 1; - MAddress padding : 14; - }; + BitFieldLayout layout; RefFieldValue fieldVal; }; }; } // namespace panda +// NOLINTEND(readability-identifier-naming, readability-else-after-return,-warnings-as-errors, +// cppcoreguidelines-pro-type-union-access) #endif // COMMON_INTERFACES_OBJECTS_REF_FIELD_H diff --git a/common_interfaces/objects/static_object_accessor_interface.h b/common_interfaces/objects/static_object_accessor_interface.h new file mode 100644 index 0000000000..ad540420d1 --- /dev/null +++ b/common_interfaces/objects/static_object_accessor_interface.h @@ -0,0 +1,48 @@ +/* + * 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 COMMON_INTERFACES_OBJECTS_STATIC_OBJECT_ACCESSOR_INTERFACE_H +#define COMMON_INTERFACES_OBJECTS_STATIC_OBJECT_ACCESSOR_INTERFACE_H + +#include "objects/base_type.h" +#include "objects/base_object.h" +#include "thread/thread_holder.h" + +namespace panda { +// NOTE (#26214): extracted `StaticObjectAccessor` here untill `JSTaggedValue` migrated from ets_runtime +class StaticObjectAccessorInterface { +public: + // HasProperty is used to check if the static object has a property with the given name. + virtual bool HasProperty(ThreadHolder *thread, const BaseObject *obj, const char *name) = 0; + + // GetProperty is used to get the value of a property from a static object with the given name. + virtual BoxedValue GetProperty(ThreadHolder *thread, const BaseObject *obj, const char *name) = 0; + + // SetProperty is used to set the value of a property in a static object with the given name. + virtual bool SetProperty(ThreadHolder *thread, BaseObject *obj, const char *name, BoxedValue value) = 0; + + // NOTE (#26214): remove untill ets_runtime changes + // HasElementByIdx is used to check if the static object has an element with the given index. + virtual bool HasElementByIdx(ThreadHolder *thread, const BaseObject *obj, const uint32_t index) = 0; + + // GetElementByIdx is used to get the value of an element from a static object with the given index. + virtual BoxedValue GetElementByIdx(ThreadHolder *thread, const BaseObject *obj, const uint32_t index) = 0; + + // SetElementByIdx is used to set the value of an element in a static object with the given index. + // NOLINTNEXTLINE(misc-misplaced-const) + virtual bool SetElementByIdx(ThreadHolder *thread, BaseObject *obj, uint32_t index, const BoxedValue value) = 0; +}; +} // namespace panda +#endif // COMMON_INTERFACES_OBJECTS_STATIC_OBJECT_ACCESSOR_INTERFACE_H \ No newline at end of file diff --git a/common_interfaces/thread/mutator_base.h b/common_interfaces/thread/mutator_base.h index 02b0af8bcf..9e2ff360c1 100644 --- a/common_interfaces/thread/mutator_base.h +++ b/common_interfaces/thread/mutator_base.h @@ -22,7 +22,7 @@ namespace panda { class ThreadHolder; -} +} // namespace panda namespace panda { class Mutator; @@ -100,14 +100,26 @@ public: return inSaferegion_.load(std::memory_order_seq_cst) != SAFE_REGION_FALSE; } - inline void IncObserver() { observerCnt_.fetch_add(1); } + inline void IncObserver() + { + observerCnt_.fetch_add(1); + } - inline void DecObserver() { observerCnt_.fetch_sub(1); } + inline void DecObserver() + { + observerCnt_.fetch_sub(1); + } // Return true indicate there are some observer is visitting this mutator - inline bool HasObserver() { return observerCnt_.load() != 0; } + inline bool HasObserver() + { + return observerCnt_.load() != 0; + } - inline size_t GetObserverCount() const { return observerCnt_.load(); } + inline size_t GetObserverCount() const + { + return observerCnt_.load(); + } // Force current mutator enter saferegion, internal use only. __attribute__((always_inline)) inline void DoEnterSaferegion(); @@ -189,6 +201,7 @@ public: __attribute__((always_inline)) inline bool HasCallbackRequest() const { + // NOLINTNEXTLINE(readability-implicit-bool-conversion) return (callbackRequest_.load(std::memory_order_acquire) != 0); } @@ -202,11 +215,14 @@ public: callbackRequest_.store(false, std::memory_order_relaxed); } - void SetSafepointStatePtr(uint64_t* slot) { safepointStatePtr_ = slot; } + void SetSafepointStatePtr(uint64_t* slot) + { + safepointStatePtr_ = slot; + } void SetSafepointActive(bool value) { - uint64_t* statePtr = safepointStatePtr_; + uint64_t *statePtr = safepointStatePtr_; if (statePtr == nullptr) { return; } @@ -260,37 +276,43 @@ public: return mutatorPhase_.load(std::memory_order_acquire); } - const void* GetSafepointPage() const + const void *GetSafepointPage() const { return safepointStatePtr_; } - void MutatorBaseLock() { mutatorBaseLock_.lock(); } + void MutatorBaseLock() + { + mutatorBaseLock_.lock(); + } - void MutatorBaseUnlock() { mutatorBaseLock_.unlock(); } + void MutatorBaseUnlock() + { + mutatorBaseLock_.unlock(); + } private: // Indicate the current mutator phase and use which barrier in concurrent gc // ATTENTION: THE LAYOUT FOR GCPHASE MUST NOT BE CHANGED! - std::atomic mutatorPhase_ = { GCPhase::GC_PHASE_UNDEF }; + std::atomic mutatorPhase_ = {GCPhase::GC_PHASE_UNDEF}; // in saferegion, it will not access any managed objects and can be visitted by observer - std::atomic inSaferegion_ = { SAFE_REGION_TRUE }; + std::atomic inSaferegion_ = {SAFE_REGION_TRUE}; // Protect observerCnt std::mutex observeCntMutex_; // Increase when this mutator is observed by some observer - std::atomic observerCnt_ = { 0 }; + std::atomic observerCnt_ = {0}; - uint64_t* safepointStatePtr_ = nullptr; // state: active or not + uint64_t *safepointStatePtr_ = nullptr; // state: active or not // If set implies this mutator should process suspension requests - std::atomic suspensionFlag_ = { 0 }; - std::atomic callbackRequest_ = { false }; + std::atomic suspensionFlag_ = {0}; + std::atomic callbackRequest_ = {false}; // Indicate the state of mutator's phase transition - std::atomic transitionState_ = { NO_TRANSITION }; + std::atomic transitionState_ = {NO_TRANSITION}; std::mutex mutatorBaseLock_; - std::atomic cpuProfileState_ = { NO_CPUPROFILE }; + std::atomic cpuProfileState_ = {NO_CPUPROFILE}; // This is stored for process `satbNode`, merge Mutator & MutatorBase & SatbNode void *mutator_ {nullptr}; diff --git a/common_interfaces/thread/thread_holder.h b/common_interfaces/thread/thread_holder.h index be338c155a..d02f362630 100644 --- a/common_interfaces/thread/thread_holder.h +++ b/common_interfaces/thread/thread_holder.h @@ -27,11 +27,11 @@ namespace panda::ecmascript { class JSThread; -} +} // namespace panda::ecmascript namespace ark { class Coroutine; -} +} // namespace ark namespace panda { class BaseThread; @@ -53,6 +53,8 @@ public: using Coroutine = ark::Coroutine; using MutatorBase = panda::MutatorBase; + // CC-OFFNXT(WordsTool.95 Google) sensitive word conflict + // NOLINTNEXTLINE(google-explicit-constructor) ThreadHolder(MutatorBase *mutatorBase) : mutatorBase_(mutatorBase) { SetCurrent(this); @@ -111,7 +113,7 @@ public: void UnregisterCoroutine(Coroutine *coroutine); void VisitAllThreads(CommonRootVisitor visitor); - JSThread* GetJSThread() const + JSThread *GetJSThread() const { return jsThread_; } @@ -127,9 +129,11 @@ public: } // Return if thread has already binded mutator. + // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) class TryBindMutatorScope { public: - TryBindMutatorScope(ThreadHolder *holder); + // CC-OFFNXT(WordsTool.95 Google) sensitive word conflict + TryBindMutatorScope(ThreadHolder *holder); // NOLINT(google-explicit-constructor) ~TryBindMutatorScope(); private: diff --git a/common_interfaces/thread/thread_state.h b/common_interfaces/thread/thread_state.h index e5214e822d..9c62362738 100644 --- a/common_interfaces/thread/thread_state.h +++ b/common_interfaces/thread/thread_state.h @@ -23,6 +23,7 @@ namespace panda { +// NOLINTBEGIN(hicpp-signed-bitwise) enum ThreadFlag : uint16_t { NO_FLAGS = 0 << 0, SUSPEND_REQUEST = 1 << 0, @@ -31,6 +32,7 @@ enum ThreadFlag : uint16_t { constexpr uint32_t THREAD_STATE_OFFSET = 16; constexpr uint32_t THREAD_FLAGS_MASK = (0x1 << THREAD_STATE_OFFSET) - 1; +// NOLINTEND(hicpp-signed-bitwise) enum class ThreadState : uint16_t { CREATED = 0, RUNNING = 1, @@ -40,6 +42,7 @@ enum class ThreadState : uint16_t { TERMINATED = 5, }; +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) union ThreadStateAndFlags { explicit ThreadStateAndFlags(uint32_t val = 0) : asInt(val) {} struct { diff --git a/static_core/codecheck_ignore.json b/static_core/codecheck_ignore.json index 1ea2fc9fc4..0d67f2d802 100644 --- a/static_core/codecheck_ignore.json +++ b/static_core/codecheck_ignore.json @@ -133,6 +133,7 @@ "plugins/ets/tests/runtime/CMakeLists.txt": "*", "plugins/ets/tests/runtime/ets_vm_init_prealloc_test.cpp": "*", "plugins/ets/tests/runtime/ets_vm_test.cpp": "*", + "plugins/ets/tests/runtime/static_object_accessor_test.cpp": "*", "plugins/ets/tests/runtime/tooling/sampler/managed/CMakeLists.txt": {"bc-40001": "*"}, "plugins/ets/tests/runtime/types/CMakeLists.txt": "*", "plugins/ets/tests/runtime/types/ets_array_test.cpp": "*", diff --git a/static_core/plugins/ets/BUILD.gn b/static_core/plugins/ets/BUILD.gn index 4926ebc641..0588cb58d3 100644 --- a/static_core/plugins/ets/BUILD.gn +++ b/static_core/plugins/ets/BUILD.gn @@ -218,7 +218,10 @@ ark_gen_file("ark_asm_ets_meta_gen_h") { } config("runtime") { - include_dirs = [ "$ark_root/plugins/ets/runtime" ] + include_dirs = [ + "$ark_root/plugins/ets/runtime", + "$ark_root/../common_interfaces", + ] configs = [ "$ark_es2panda_root:libes2panda_public_config", "$ark_root/assembler:arkassembler_public_config", diff --git a/static_core/plugins/ets/runtime/CMakeLists.txt b/static_core/plugins/ets/runtime/CMakeLists.txt index 5405186388..f1be7021f4 100644 --- a/static_core/plugins/ets/runtime/CMakeLists.txt +++ b/static_core/plugins/ets/runtime/CMakeLists.txt @@ -111,6 +111,7 @@ set(ETS_RUNTIME_SOURCES ${ETS_EXT_SOURCES}/ets_utils.cpp ${ETS_EXT_SOURCES}/ets_object_state_info.cpp ${ETS_EXT_SOURCES}/ets_object_state_table.cpp + ${ETS_EXT_SOURCES}/static_object_accessor.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/etsstdlib.cpp @@ -165,6 +166,7 @@ panda_target_include_directories(arkruntime_obj PUBLIC ${PANDA_BINARY_ROOT}/ # The line below needed only for stdlib. It must be removed after stdlib separation from runtime is implemented(#18135). ${PANDA_ETS_PLUGIN_SOURCE}/runtime/ani/ + ${PANDA_ROOT}/../common_interfaces ) add_dependencies(arkruntime_obj es2panda-public) panda_target_link_libraries(arkruntime_obj es2panda-public arkassembler) diff --git a/static_core/plugins/ets/runtime/ets_utils.cpp b/static_core/plugins/ets/runtime/ets_utils.cpp index ec626c0a3c..fcf69e25bc 100644 --- a/static_core/plugins/ets/runtime/ets_utils.cpp +++ b/static_core/plugins/ets/runtime/ets_utils.cpp @@ -14,6 +14,7 @@ */ #include "plugins/ets/runtime/ets_utils.h" +#include "plugins/ets/runtime/types/ets_box_primitive-inl.h" #include "plugins/ets/runtime/types/ets_class.h" #include "plugins/ets/runtime/types/ets_field.h" #include "plugins/ets/runtime/types/ets_method.h" @@ -39,6 +40,61 @@ bool IsEtsGlobalClassName(const std::string &descriptor) return etsGlobalClass || etsGlobalClassInPackage; } +EtsObject *GetBoxedValue(EtsCoroutine *coro, Value value, EtsType type) +{ + switch (type) { + case EtsType::BOOLEAN: + return EtsBoxPrimitive::Create(coro, value.GetAs()); + case EtsType::BYTE: + return EtsBoxPrimitive::Create(coro, value.GetAs()); + case EtsType::CHAR: + return EtsBoxPrimitive::Create(coro, value.GetAs()); + case EtsType::SHORT: + return EtsBoxPrimitive::Create(coro, value.GetAs()); + case EtsType::INT: + return EtsBoxPrimitive::Create(coro, value.GetAs()); + case EtsType::LONG: + return EtsBoxPrimitive::Create(coro, value.GetAs()); + case EtsType::FLOAT: + return EtsBoxPrimitive::Create(coro, value.GetAs()); + case EtsType::DOUBLE: + return EtsBoxPrimitive::Create(coro, value.GetAs()); + default: + UNREACHABLE(); + } +} + +Value GetUnboxedValue(EtsCoroutine *coro, EtsObject *obj) +{ + auto *cls = obj->GetClass(); + auto ptypes = PlatformTypes(coro); + if (cls == ptypes->coreBoolean) { + return Value((reinterpret_cast *>(obj))->GetValue()); + } + if (cls == ptypes->coreDouble) { + return Value((reinterpret_cast *>(obj))->GetValue()); + } + if (cls == ptypes->coreInt) { + return Value((reinterpret_cast *>(obj))->GetValue()); + } + if (cls == ptypes->coreByte) { + return Value((reinterpret_cast *>(obj))->GetValue()); + } + if (cls == ptypes->coreShort) { + return Value((reinterpret_cast *>(obj))->GetValue()); + } + if (cls == ptypes->coreLong) { + return Value((reinterpret_cast *>(obj))->GetValue()); + } + if (cls == ptypes->coreFloat) { + return Value((reinterpret_cast *>(obj))->GetValue()); + } + if (cls == ptypes->coreChar) { + return Value((reinterpret_cast *>(obj))->GetValue()); + } + return Value(reinterpret_cast(obj)); +} + void LambdaUtils::InvokeVoid(EtsCoroutine *coro, EtsObject *lambda) { EtsMethod *invoke = lambda->GetClass()->GetInstanceMethod(INVOKE_METHOD_NAME, nullptr); diff --git a/static_core/plugins/ets/runtime/ets_utils.h b/static_core/plugins/ets/runtime/ets_utils.h index e809ed75da..755519fa16 100644 --- a/static_core/plugins/ets/runtime/ets_utils.h +++ b/static_core/plugins/ets/runtime/ets_utils.h @@ -28,6 +28,10 @@ static constexpr char const ETSGLOBAL_CLASS_NAME[] = "ETSGLOBAL"; bool IsEtsGlobalClassName(const std::string &descriptor); +EtsObject *GetBoxedValue(EtsCoroutine *coro, Value value, EtsType type); + +Value GetUnboxedValue(EtsCoroutine *coro, EtsObject *obj); + class LambdaUtils { public: PANDA_PUBLIC_API static void InvokeVoid(EtsCoroutine *coro, EtsObject *lambda); diff --git a/static_core/plugins/ets/runtime/static_object_accessor.cpp b/static_core/plugins/ets/runtime/static_object_accessor.cpp new file mode 100644 index 0000000000..0569ba5398 --- /dev/null +++ b/static_core/plugins/ets/runtime/static_object_accessor.cpp @@ -0,0 +1,191 @@ +/* + * 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 "static_object_accessor.h" +#include "objects/base_object.h" +#include "libpandabase/utils/logger.h" +#include "plugins/ets/runtime/ets_coroutine.h" +#include "plugins/ets/runtime/ets_utils.h" +#include "plugins/ets/runtime/types/ets_method.h" +#include "plugins/ets/runtime/types/ets_object.h" +#include "plugins/ets/runtime/types/ets_box_primitive-inl.h" + +namespace ark::ets { + +StaticObjectAccessor StaticObjectAccessor::stcObjAccessor_; + +bool StaticObjectAccessor::HasProperty([[maybe_unused]] panda::ThreadHolder *thread, const panda::BaseObject *obj, + const char *name) +{ + const EtsObject *etsObj = reinterpret_cast(obj); // NOLINT(modernize-use-auto) + return etsObj->GetClass()->GetFieldIDByName(name) != nullptr; +} + +panda::BoxedValue GetPropertyValue(EtsCoroutine *coro, const EtsObject *etsObj, EtsField *field) +{ + EtsType type = field->GetEtsType(); + switch (type) { + case EtsType::BOOLEAN: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::BYTE: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::CHAR: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::SHORT: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::INT: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::LONG: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::FLOAT: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::DOUBLE: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::OBJECT: { + return reinterpret_cast(etsObj->GetFieldObject(field)); + } + default: + return nullptr; + } +} + +panda::BoxedValue StaticObjectAccessor::GetProperty([[maybe_unused]] panda::ThreadHolder *thread, + const panda::BaseObject *obj, const char *name) +{ + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + const EtsObject *etsObj = reinterpret_cast(obj); // NOLINT(modernize-use-auto) + if (etsObj == nullptr) { + return nullptr; + } + auto fieldIndex = etsObj->GetClass()->GetFieldIndexByName(name); + EtsField *field = etsObj->GetClass()->GetFieldByIndex(fieldIndex); + if (field == nullptr) { + return nullptr; + } + return GetPropertyValue(coro, etsObj, field); +} + +bool StaticObjectAccessor::SetProperty([[maybe_unused]] panda::ThreadHolder *thread, panda::BaseObject *obj, + const char *name, panda::BoxedValue value) +{ + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + auto *etsObj = reinterpret_cast(obj); + if (etsObj == nullptr) { + return false; + } + ark::Value etsVal = GetUnboxedValue(coro, reinterpret_cast(value)); + auto fieldIndex = etsObj->GetClass()->GetFieldIndexByName(name); + EtsField *field = etsObj->GetClass()->GetFieldByIndex(fieldIndex); + if (field == nullptr) { + return false; + } + EtsType type = field->GetEtsType(); + switch (type) { + case EtsType::BOOLEAN: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::BYTE: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::CHAR: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::SHORT: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::INT: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::LONG: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::FLOAT: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::DOUBLE: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::OBJECT: + etsObj->SetFieldObject(field, reinterpret_cast(value)); + break; + default: + return false; + } + return true; +} + +bool StaticObjectAccessor::HasElementByIdx([[maybe_unused]] panda::ThreadHolder *thread, + [[maybe_unused]] const panda::BaseObject *obj, + [[maybe_unused]] const uint32_t index) +{ + LOG(ERROR, RUNTIME) << "HasElementByIdx has no meaning for static object"; + return false; +} + +panda::BoxedValue StaticObjectAccessor::GetElementByIdx([[maybe_unused]] panda::ThreadHolder *thread, + const panda::BaseObject *obj, const uint32_t index) +{ + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + const EtsObject *etsObj = reinterpret_cast(obj); // NOLINT(modernize-use-auto) + if (etsObj == nullptr) { + return nullptr; + } + EtsObject *etsObject = const_cast(etsObj); // NOLINT(modernize-use-auto) + EtsMethod *method = etsObj->GetClass()->GetDirectMethod(GET_INDEX_METHOD, "I:Lstd/core/Object;"); + std::array args {ark::Value(reinterpret_cast(etsObject)), ark::Value(index)}; + ark::Value value = method->GetPandaMethod()->Invoke(coro, args.data()); + return reinterpret_cast(EtsObject::FromCoreType(value.GetAs())); +} + +bool StaticObjectAccessor::SetElementByIdx([[maybe_unused]] panda::ThreadHolder *thread, panda::BaseObject *obj, + uint32_t index, const panda::BoxedValue value) +{ + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + ASSERT(coro != nullptr); + auto *etsObj = reinterpret_cast(obj); + if (etsObj == nullptr) { + return false; + } + EtsMethod *method = etsObj->GetClass()->GetDirectMethod(SET_INDEX_METHOD, "ILstd/core/Object;:V"); + + std::array args {ark::Value(reinterpret_cast(etsObj)), ark::Value(index), + ark::Value(reinterpret_cast(value))}; + method->GetPandaMethod()->Invoke(coro, args.data()); + if (UNLIKELY(coro->HasPendingException())) { + return false; // NOLINT(readability-simplify-boolean-expr) + } + return true; +} +} // namespace ark::ets \ No newline at end of file diff --git a/static_core/plugins/ets/runtime/static_object_accessor.h b/static_core/plugins/ets/runtime/static_object_accessor.h new file mode 100644 index 0000000000..159f2da3ac --- /dev/null +++ b/static_core/plugins/ets/runtime/static_object_accessor.h @@ -0,0 +1,44 @@ +/* + * 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_STATIC_OBJECT_ACCESSOR_H +#define PANDA_PLUGINS_ETS_RUNTIME_STATIC_OBJECT_ACCESSOR_H + +#include "objects/base_object.h" +#include "objects/static_object_accessor_interface.h" + +namespace ark::ets { +class StaticObjectAccessor : public panda::StaticObjectAccessorInterface { +public: + bool HasProperty(panda::ThreadHolder *thread, const panda::BaseObject *obj, const char *name) override; + + panda::BoxedValue GetProperty(panda::ThreadHolder *thread, const panda::BaseObject *obj, const char *name) override; + + bool SetProperty(panda::ThreadHolder *thread, panda::BaseObject *obj, const char *name, + panda::BoxedValue value) override; + + bool HasElementByIdx(panda::ThreadHolder *thread, const panda::BaseObject *obj, const uint32_t index) override; + + panda::BoxedValue GetElementByIdx(panda::ThreadHolder *thread, const panda::BaseObject *obj, + const uint32_t index) override; + + bool SetElementByIdx(panda::ThreadHolder *thread, panda::BaseObject *obj, uint32_t index, + const panda::BoxedValue value) override; + +private: + static StaticObjectAccessor stcObjAccessor_; +}; +} // namespace ark::ets +#endif // PANDA_PLUGINS_ETS_RUNTIME_STATIC_OBJECT_ACCESSOR_H \ No newline at end of file diff --git a/static_core/plugins/ets/runtime/types/ets_object.h b/static_core/plugins/ets/runtime/types/ets_object.h index e3b15c4747..e358cc02b8 100644 --- a/static_core/plugins/ets/runtime/types/ets_object.h +++ b/static_core/plugins/ets/runtime/types/ets_object.h @@ -62,7 +62,7 @@ public: } template - T GetFieldPrimitive(EtsField *field) + T GetFieldPrimitive(EtsField *field) const { ASSERT(field->GetEtsType() == GetEtsTypeByPrimitive()); ASSERT(HasField(field)); diff --git a/static_core/plugins/ets/subproject_sources.gn b/static_core/plugins/ets/subproject_sources.gn index adb87a35e5..ab36122dd4 100644 --- a/static_core/plugins/ets/subproject_sources.gn +++ b/static_core/plugins/ets/subproject_sources.gn @@ -186,6 +186,7 @@ srcs_runtime = [ "runtime/types/ets_weak_reference.cpp", "runtime//types/ets_finalizable_weak_ref.cpp", "runtime/ets_utils.cpp", + "runtime/static_object_accessor.cpp", "stdlib/native/init_native_methods.cpp", "stdlib/native/escompat/Process.cpp", "stdlib/native/core/Intl.cpp", diff --git a/static_core/plugins/ets/tests/runtime/CMakeLists.txt b/static_core/plugins/ets/tests/runtime/CMakeLists.txt index 7774052a25..64639a17aa 100644 --- a/static_core/plugins/ets/tests/runtime/CMakeLists.txt +++ b/static_core/plugins/ets/tests/runtime/CMakeLists.txt @@ -25,6 +25,18 @@ panda_ets_add_gtest( TEST_GROUP ets_tests_runtime_gtests ) +panda_ets_add_gtest( + NO_CORES + NAME static_object_accessor_test + SOURCES static_object_accessor_test.cpp + LIBRARIES arkbase arkfile arkruntime + INCLUDE_DIRS ${PANDA_ETS_PLUGIN_SOURCE}/runtime + SANITIZERS ${PANDA_SANITIZERS_LIST} + PANDA_STD_LIB "${PANDA_BINARY_ROOT}/plugins/ets/etsstdlib.abc" + DEPS_TARGETS mock_stdlib + TEST_GROUP ets_tests_runtime_gtests +) + panda_add_gtest_with_ani(ets_vm_init_prealloc_test CPP_SOURCES ${PANDA_ETS_PLUGIN_SOURCE}/tests/runtime/ets_vm_init_prealloc_test.cpp ETS_SOURCES ${PANDA_ETS_PLUGIN_SOURCE}/tests/runtime/ets_vm_init_prealloc_test.ets diff --git a/static_core/plugins/ets/tests/runtime/static_object_accessor_test.cpp b/static_core/plugins/ets/tests/runtime/static_object_accessor_test.cpp new file mode 100644 index 0000000000..67b300cf0e --- /dev/null +++ b/static_core/plugins/ets/tests/runtime/static_object_accessor_test.cpp @@ -0,0 +1,286 @@ +/** + * 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 +#include + +#include "assembly-emitter.h" +#include "assembly-parser.h" +#include "plugins/ets/runtime/static_object_accessor.h" +#include "plugins/ets/runtime/ets_coroutine.h" +#include "plugins/ets/runtime/types/ets_box_primitive-inl.h" +#include "plugins/ets/runtime/types/ets_class.h" +#include "types/ets_array.h" +#include "types/ets_escompat_array.h" +#include "types/ets_object.h" +#include "ets_vm.h" +#include "ets_class_linker_extension.h" +#include "libpandabase/utils/utils.h" + +namespace ark::ets::test { + +class StaticObjectAccessorTest : public testing::Test { +public: + static constexpr EtsFloat VAL_FLOAT = 1.1F; + static constexpr EtsDouble VAL_DOUBLE = 2.2F; + static constexpr size_t ARRAY_LENGTH = 100U; + StaticObjectAccessorTest() + { + RuntimeOptions options; + options.SetShouldLoadBootPandaFiles(true); + options.SetShouldInitializeIntrinsics(true); + options.SetCompilerEnableJit(false); + options.SetGcType("epsilon"); + options.SetLoadRuntimes({"ets"}); + + Logger::InitializeStdLogging(Logger::Level::ERROR, 0); + + 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}); + + ark::Runtime::Create(options); + SetClassesPandasmSources(); + } + + ~StaticObjectAccessorTest() override + { + Runtime::Destroy(); + } + + NO_COPY_SEMANTIC(StaticObjectAccessorTest); + NO_MOVE_SEMANTIC(StaticObjectAccessorTest); + + void SetClassesPandasmSources(); + EtsClass *GetTestClass(std::string className); + + void SetUp() override + { + coroutine_ = EtsCoroutine::GetCurrent(); + coroutine_->ManagedCodeBegin(); + } + + void TearDown() override + { + coroutine_->ManagedCodeEnd(); + } + + template + void CheckSetAndGetProperty(EtsClass *klass, const char *property, T value) + { + EtsObject *obj = EtsObject::Create(klass); + StaticObjectAccessor staticObjectAccessor; + auto *coro = EtsCoroutine::GetCurrent(); + EtsObject *valueObject = EtsBoxPrimitive::Create(coro, value); + + staticObjectAccessor.SetProperty(nullptr, nullptr, property, + reinterpret_cast(valueObject)); + staticObjectAccessor.SetProperty(nullptr, reinterpret_cast(obj), "test", + reinterpret_cast(valueObject)); + staticObjectAccessor.SetProperty(nullptr, reinterpret_cast(obj), property, + reinterpret_cast(valueObject)); + + panda::BaseObject *baseObject = staticObjectAccessor.GetProperty(nullptr, nullptr, property); + ASSERT_EQ(baseObject, nullptr); + baseObject = staticObjectAccessor.GetProperty(nullptr, reinterpret_cast(obj), "test"); + ASSERT_EQ(baseObject, nullptr); + baseObject = staticObjectAccessor.GetProperty(nullptr, reinterpret_cast(obj), property); + ASSERT_EQ(reinterpret_cast(baseObject)->GetValue(), value); + ASSERT_EQ(staticObjectAccessor.HasProperty(nullptr, reinterpret_cast(obj), property), + true); + ASSERT_EQ(staticObjectAccessor.HasProperty(nullptr, reinterpret_cast(obj), "test"), false); + } + + template + void CheckSetAndGetElementByIdx(T val) + { + auto *array = EtsArrayObject::Create(ARRAY_LENGTH); + auto *baseObject = reinterpret_cast(array); + ASSERT_NE(baseObject, nullptr); + StaticObjectAccessor staticObjectAccessor; + auto *coro = EtsCoroutine::GetCurrent(); + EtsObject *valueObject = BoxType::Create(coro, val); + staticObjectAccessor.SetElementByIdx(nullptr, nullptr, 1, reinterpret_cast(valueObject)); + staticObjectAccessor.SetElementByIdx(nullptr, baseObject, 1, + reinterpret_cast(valueObject)); + panda::BaseObject *baseObject2 = staticObjectAccessor.GetElementByIdx(nullptr, nullptr, 1); + ASSERT_EQ(baseObject2, nullptr); + baseObject2 = staticObjectAccessor.GetElementByIdx(nullptr, baseObject, 1); + ASSERT_EQ(reinterpret_cast(valueObject), baseObject2); + } + +private: + RuntimeOptions options_; + EtsCoroutine *coroutine_ = nullptr; + std::unordered_map sources_; + +protected: + const std::unordered_map &GetSources() + { + return sources_; + } +}; + +void StaticObjectAccessorTest::SetClassesPandasmSources() +{ + const char *source = R"( + .language eTS + .record A { + u1 boolean + i8 byte + u16 char + i16 short + i32 int + i64 long + f32 float + f64 double + } + )"; + sources_["A"] = source; + + source = R"( + .language eTS + .record Triangle { + f64 firSide + i32 secSide + f32 thirdSide + i64 Color + } + )"; + sources_["Triangle"] = source; + + source = R"( + .language eTS + .record Foo { + i32 member + } + .record Bar { + Foo foo1 + Foo foo2 + } + )"; + sources_["Foo"] = source; + sources_["Bar"] = source; +} + +EtsClass *StaticObjectAccessorTest::GetTestClass(std::string className) +{ + std::unordered_map sources = GetSources(); + pandasm::Parser p; + + auto res = p.Parse(sources[className]); + auto pf = pandasm::AsmEmitter::Emit(res.Value()); + + auto classLinker = Runtime::GetCurrent()->GetClassLinker(); + classLinker->AddPandaFile(std::move(pf)); + + className.insert(0, 1, 'L'); + className.push_back(';'); + + EtsClass *klass = coroutine_->GetPandaVM()->GetClassLinker()->GetClass(className.c_str()); + ASSERT(klass); + return klass; +} + +TEST_F(StaticObjectAccessorTest, SetAndGetPropertyValue0) +{ + EtsClass *klass = GetTestClass("A"); + CheckSetAndGetProperty>(klass, "int", 1); + // NOLINTNEXTLINE(readability-implicit-bool-conversion) + CheckSetAndGetProperty>(klass, "boolean", true); + CheckSetAndGetProperty>(klass, "byte", 1); + CheckSetAndGetProperty>(klass, "char", 'a'); + CheckSetAndGetProperty>(klass, "short", 1); + CheckSetAndGetProperty>(klass, "long", 1); + CheckSetAndGetProperty>(klass, "float", VAL_FLOAT); + CheckSetAndGetProperty>(klass, "double", VAL_DOUBLE); +} + +TEST_F(StaticObjectAccessorTest, SetAndGetPropertyValue1) +{ + EtsClass *barKlass = GetTestClass("Bar"); + EtsClass *fooKlass = GetTestClass("Foo"); + EtsObject *barObj = EtsObject::Create(barKlass); + EtsObject *fooObj1 = EtsObject::Create(fooKlass); + StaticObjectAccessor staticObjectAccessor; + staticObjectAccessor.SetProperty(nullptr, reinterpret_cast(barObj), "foo1", + reinterpret_cast(fooObj1)); + panda::BaseObject *baseObject = + staticObjectAccessor.GetProperty(nullptr, reinterpret_cast(barObj), "foo1"); + ASSERT_EQ(reinterpret_cast(baseObject), fooObj1); + ASSERT_EQ(staticObjectAccessor.HasProperty(nullptr, reinterpret_cast(barObj), "foo1"), true); + ASSERT_EQ(staticObjectAccessor.HasProperty(nullptr, reinterpret_cast(barObj), "test"), false); +} + +TEST_F(StaticObjectAccessorTest, SetAndGetPropertyValue2) +{ + EtsClass *barKlass = GetTestClass("Bar"); + EtsClass *fooKlass = GetTestClass("Foo"); + EtsObject *barObj = EtsObject::Create(barKlass); + EtsObject *fooObj1 = EtsObject::Create(fooKlass); + auto *coro = EtsCoroutine::GetCurrent(); + EtsInt val = 1; + EtsObject *valueObject = EtsBoxPrimitive::Create(coro, val); + StaticObjectAccessor staticObjectAccessor; + staticObjectAccessor.SetProperty(nullptr, reinterpret_cast(fooObj1), "member", + reinterpret_cast(valueObject)); + staticObjectAccessor.SetProperty(nullptr, reinterpret_cast(barObj), "foo1", + reinterpret_cast(fooObj1)); + panda::BaseObject *baseObject = + staticObjectAccessor.GetProperty(nullptr, reinterpret_cast(barObj), "foo1"); + panda::BaseObject *reObject = staticObjectAccessor.GetProperty(nullptr, baseObject, "member"); + ASSERT_EQ(reinterpret_cast *>(reObject)->GetValue(), 1); +} + +TEST_F(StaticObjectAccessorTest, SetAndGetElementByIdx0) +{ + CheckSetAndGetElementByIdx>(1); + // NOLINTNEXTLINE(readability-implicit-bool-conversion) + CheckSetAndGetElementByIdx>(true); + CheckSetAndGetElementByIdx>(1); + CheckSetAndGetElementByIdx>('a'); + CheckSetAndGetElementByIdx>(1); + CheckSetAndGetElementByIdx>(1); + CheckSetAndGetElementByIdx>(VAL_FLOAT); + CheckSetAndGetElementByIdx>(VAL_DOUBLE); +} + +TEST_F(StaticObjectAccessorTest, SetAndGetElementByIdx1) +{ + EtsClass *klass = GetTestClass("Triangle"); + ASSERT_NE(klass, nullptr); + auto *array = EtsArrayObject::Create(ARRAY_LENGTH); + ASSERT_NE(array, nullptr); + auto obj = EtsObject::Create(klass); + auto *coro = EtsCoroutine::GetCurrent(); + EtsObject *valueObject = EtsBoxPrimitive::Create(coro, VAL_DOUBLE); + StaticObjectAccessor staticObjectAccessor; + staticObjectAccessor.SetProperty(nullptr, reinterpret_cast(obj), "firSide", + reinterpret_cast(valueObject)); + staticObjectAccessor.SetElementByIdx(nullptr, + reinterpret_cast(reinterpret_cast(array)), 1, + reinterpret_cast(obj)); + panda::BaseObject *baseObject = + staticObjectAccessor.GetElementByIdx(nullptr, reinterpret_cast(array), 1); + ASSERT_EQ(baseObject, reinterpret_cast(obj)); + panda::BaseObject *reObject = + staticObjectAccessor.GetProperty(nullptr, reinterpret_cast(baseObject), "firSide"); + ASSERT_EQ(reinterpret_cast *>(reObject)->GetValue(), VAL_DOUBLE); +} +} // namespace ark::ets::test \ No newline at end of file diff --git a/static_core/runtime/include/thread_proxy_static.h b/static_core/runtime/include/thread_proxy_static.h index 9d0aaa885b..4ebbf7da34 100644 --- a/static_core/runtime/include/thread_proxy_static.h +++ b/static_core/runtime/include/thread_proxy_static.h @@ -204,7 +204,7 @@ private: NO_MOVE_SEMANTIC(FlagsAndThreadStatus); }; - // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + // NOLINTBEGIN(misc-non-private-member-variables-in-classes) FlagsAndThreadStatus fts_ {}; PANDA_PUBLIC_API static ThreadFlag initialThreadFlag_; // NOLINTEND(misc-non-private-member-variables-in-classes) -- Gitee