From 1ce9d5c2561d107770e091062730128478949610 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Wed, 5 Jul 2023 18:27:50 +0300 Subject: [PATCH] interop_js: Implement allocator for objects wrappers Signed-off-by: Vyacheslav Cherkashin --- libpandabase/mem/mem.h | 5 + .../ets_proxy/ets_class_wrapper.cpp | 20 +- .../ets_proxy/ets_object_wrapper.cpp | 6 + .../interop_js/ets_proxy/ets_object_wrapper.h | 13 +- .../ets_proxy/ets_object_wrappers_storage.cpp | 78 ++-- .../ets_proxy/ets_object_wrappers_storage.h | 32 +- .../interop_js/ets_proxy/js_convert_object.h | 7 +- .../interop_js/ets_proxy/mem/items_pool.h | 125 ++++++ .../ets/runtime/interop_js/interop_context.h | 8 +- .../ets/runtime/interop_js/js_job_queue.cpp | 1 + .../interop_js/tests/ets_proxy/CMakeLists.txt | 1 + .../tests/ets_proxy/mem/CMakeLists.txt | 16 + .../CMakeLists.txt | 18 + .../test_ets_object_wrappers_storage_1.cpp | 152 +++++++ .../test_ets_object_wrappers_storage_1.ets | 1 + .../CMakeLists.txt | 17 + .../test_ets_object_wrappers_storage_2.ets | 25 ++ .../test_ets_object_wrappers_storage_2.js | 44 ++ .../ets_proxy/mem/items_pool/CMakeLists.txt | 21 + .../mem/items_pool/test_items_pool.cpp | 414 ++++++++++++++++++ runtime/mem/runslots.h | 4 - 21 files changed, 934 insertions(+), 74 deletions(-) create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/mem/items_pool.h create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/mem/CMakeLists.txt create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/CMakeLists.txt create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.cpp create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.ets create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/CMakeLists.txt create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.ets create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/mem/items_pool/CMakeLists.txt create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/mem/items_pool/test_items_pool.cpp diff --git a/libpandabase/mem/mem.h b/libpandabase/mem/mem.h index f8318b01f..5f2f96e8a 100644 --- a/libpandabase/mem/mem.h +++ b/libpandabase/mem/mem.h @@ -292,4 +292,9 @@ inline ObjectStatus GCKillEmAllVisitor([[maybe_unused]] const ObjectHeader *mem) } // namespace panda +// If the OS has this macro, do not redefine it. +#ifndef PAGE_SIZE +static constexpr size_t PAGE_SIZE = panda::SIZE_1K * 4; +#endif + #endif // LIBPANDABASE_MEM_H diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp index 8b59455b3..782ff40e9 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp @@ -176,11 +176,12 @@ napi_value EtsClassWrapper::CtorCallback(napi_env env, napi_callback_info cinfo) ScopedManagedCodeThreadSwitch managed_scope(coro); EtsObject *ets_object = EtsObject::FromCoreType(ctx->Refstor()->Get(ets_ref.get())); if (!storage->HasEtsObjectWrapper(ets_object)) { - if (UNLIKELY(storage->CreateObjectEtsWrapper(env, ets_object, js_this, std::move(ets_ref), - ets_class_wrapper) == nullptr)) { + EtsObjectWrapper *mem = storage->CreateObjectEtsWrapper(ets_object); + if (UNLIKELY(mem == nullptr)) { InteropCtx::ThrowJSError(env, "Cannot create ObjectEtsWrapper"); return nullptr; } + EtsObjectWrapper::CreateInplace(mem, env, js_this, std::move(ets_ref), ets_class_wrapper); } } return js_this; @@ -213,13 +214,18 @@ UniqueEtsObjectReference EtsClassWrapper::CreateEtsObject(napi_env env, napi_cal EtsObjectWrappersStorage *storage = ctx->GetEtsObjectWrappersStorage(); if (!storage->HasEtsObjectWrapper(ets_object.GetPtr())) { // Create New EtsObjectWrapper - UniqueEtsObjectReference ets_ref( - ctx->Refstor()->Add(ets_object->GetCoreType(), mem::Reference::ObjectType::GLOBAL)); - if (UNLIKELY(storage->CreateObjectEtsWrapper(env, ets_object.GetPtr(), js_this, std::move(ets_ref), this) == - nullptr)) { - InteropCtx::ThrowJSError(env, "Cannot create ObjectEtsWrapper"); + mem::Reference *ref = ctx->Refstor()->Add(ets_object->GetCoreType(), mem::Reference::ObjectType::GLOBAL); + if (UNLIKELY(ref == nullptr)) { + InteropCtx::ThrowJSError(env, "Cannot create EtsObjectWrapper, ref=nullptr"); return nullptr; } + UniqueEtsObjectReference ets_ref(ref); + EtsObjectWrapper *mem = storage->CreateObjectEtsWrapper(ets_object.GetPtr()); + if (UNLIKELY(mem == nullptr)) { + InteropCtx::ThrowJSError(env, "Cannot create EtsObjectWrapper, mem=nullptr"); + return nullptr; + } + EtsObjectWrapper::CreateInplace(mem, env, js_this, std::move(ets_ref), this); } Method *method = ets_ctor_wrapper->GetEtsMethod()->GetPandaMethod(); diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp index 7083433e8..5c5403e8b 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp @@ -67,4 +67,10 @@ void EtsObjectWrapper::FinalizeCB([[maybe_unused]] napi_env env, void *data, [[m ctx->GetEtsObjectWrappersStorage()->RemoveEtsObjectWrapper(mem_this); } +EtsObject *EtsObjectWrapper::GetEtsObject(InteropCtx *ctx) const +{ + ASSERT_MANAGED_CODE(); + return EtsObject::FromCoreType(ctx->Refstor()->Get(ets_ref_.get())); +} + } // namespace panda::ets::interop::js::ets_proxy diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h index 52c7d2188..a4e25807b 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h @@ -18,7 +18,6 @@ #include "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h" #include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h" -#include "plugins/ets/runtime/interop_js/interop_context.h" #include @@ -26,6 +25,10 @@ namespace panda::mem { class Reference; } // namespace panda::mem +namespace panda::ets::interop::js { +class InteropCtx; +} // namespace panda::ets::interop::js + namespace panda::ets::interop::js::ets_proxy { class EtsClassWrapper; @@ -40,11 +43,7 @@ public: return ets_ref_.get(); } - EtsObject *GetEtsObject(InteropCtx *ctx) const - { - ASSERT_MANAGED_CODE(); - return EtsObject::FromCoreType(ctx->Refstor()->Get(ets_ref_.get())); - } + EtsObject *GetEtsObject(InteropCtx *ctx) const; napi_value GetJsValue(napi_env env) const { @@ -57,6 +56,8 @@ private: static void FinalizeCB(napi_env env, void *data, void *hint); UniqueEtsObjectReference ets_ref_ {}; + // NOTE: + // Don't move js_ref_ field, it are using in Validator::IsZeroedObject() napi_ref js_ref_ {}; }; diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp index 60ec40c46..5005c7b23 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp @@ -15,19 +15,43 @@ #include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h" -#include "libpandabase/macros.h" -#include "libpandabase/utils/logger.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h" -#include "plugins/ets/runtime/types/ets_object.h" +#include "plugins/ets/runtime/interop_js/interop_context.h" namespace panda::ets::interop::js::ets_proxy { -constexpr size_t NUMBER_OF_WRAPPERS = 512; +// The Validator class helps to check whether ets_object_wrapper is allocated +class Validator { +public: + static inline bool IsZeroedObject(EtsObjectWrapper *ets_object_wrapper) + { + auto *validator = reinterpret_cast(ets_object_wrapper); + return validator->v0_ == 0; + } + + static inline void ZeroingObject(EtsObjectWrapper *ets_object_wrapper) + { + auto *validator = reinterpret_cast(ets_object_wrapper); + validator->v0_ = 0; + } + +private: + uintptr_t free_list_space_ FIELD_UNUSED; // Skip a memory space because it can store the link to next free item + uintptr_t v0_; +}; +static_assert(sizeof(Validator) == sizeof(EtsObjectWrapper)); +static_assert(alignof(Validator) == alignof(EtsObjectWrapper)); -EtsObjectWrappersStorage::EtsObjectWrappersStorage() +/*static*/ +std::unique_ptr EtsObjectWrappersStorage::Create() { - data_ = new EtsObjectWrapper[NUMBER_OF_WRAPPERS]; - data_end_ = data_ + NUMBER_OF_WRAPPERS; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + size_t real_size = EtsObjectWrappersPool::MAX_POOL_SIZE; + + void *data = os::mem::MapRWAnonymousRaw(real_size); + if (data == nullptr) { + INTEROP_LOG(FATAL) << "Cannot allocate MemPool"; + return nullptr; + } + return std::unique_ptr(new EtsObjectWrappersStorage(data, real_size)); } EtsObjectWrapper *EtsObjectWrappersStorage::GetEtsObjectWrapper(EtsObject *ets_object) @@ -35,53 +59,43 @@ EtsObjectWrapper *EtsObjectWrappersStorage::GetEtsObjectWrapper(EtsObject *ets_o ASSERT(ets_object->IsHashed() == true); uint32_t idx = ets_object->GetHash(); - ASSERT(idx < NUMBER_OF_WRAPPERS); - return GetWrapperByIndex(idx); + return GetItemByIndex(idx); } EtsObjectWrapper *EtsObjectWrappersStorage::GetEtsObjectWrapperOrNull(void *data) { - if (UNLIKELY(!IsAligned(uintptr_t(data)))) { + auto *ets_object_wrapper = reinterpret_cast(data); + if (UNLIKELY(!IsValidItem(ets_object_wrapper))) { + // Incorrect data pointer return nullptr; } - - auto *ets_object_wrapper = reinterpret_cast(data); - if (UNLIKELY(ets_object_wrapper >= data_end_)) { + if (UNLIKELY(Validator::IsZeroedObject(ets_object_wrapper))) { + // ets_object_wrapper won't be allocated return nullptr; } - return ets_object_wrapper; } -EtsObjectWrapper *EtsObjectWrappersStorage::CreateObjectEtsWrapper(napi_env env, EtsObject *ets_object, - napi_value js_object, - UniqueEtsObjectReference ets_ref, - EtsClassWrapper *ets_class_wrapper) +EtsObjectWrapper *EtsObjectWrappersStorage::CreateObjectEtsWrapper(EtsObject *ets_object) { ASSERT(ets_object->IsHashed() == false); - uint32_t new_idx = ++next_; - if (new_idx >= NUMBER_OF_WRAPPERS) { - // Out of memory to create EtsObjectWrapper + EtsObjectWrapper *mem = AllocItem(); + if (UNLIKELY(mem == nullptr)) { return nullptr; } - + uint32_t new_idx = GetIndexByItem(mem); ets_object->SetHash(new_idx); - EtsObjectWrapper *mem = GetWrapperByIndex(new_idx); - - EtsObjectWrapper::CreateInplace(mem, env, js_object, std::move(ets_ref), ets_class_wrapper); return mem; } void EtsObjectWrappersStorage::RemoveEtsObjectWrapper(EtsObjectWrapper *ets_object_wrapper) { - (void)ets_object_wrapper; - INTEROP_LOG(ERROR) << "EtsObjectWrappersStorage::RemoveEtsObjectWrapper: Not implemented"; -} + ASSERT(!Validator::IsZeroedObject(ets_object_wrapper)); -inline EtsObjectWrapper *EtsObjectWrappersStorage::GetWrapperByIndex(uint32_t idx) -{ - return &data_[idx]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + // Zeroing memory before free to correctly works IsZeroedObject() + Validator::ZeroingObject(ets_object_wrapper); + FreeItem(ets_object_wrapper); } } // namespace panda::ets::interop::js::ets_proxy diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h index 7c0c521d3..c8aa2c301 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h @@ -17,25 +17,19 @@ #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_ETS_OBJECT_WRAPPERS_STORAGE_H_ #include "libpandabase/macros.h" -#include "libpandabase/utils/logger.h" - +#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/mem/items_pool.h" #include "plugins/ets/runtime/types/ets_object.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h" - -#include - -namespace panda::ets { -class EtsObject; -} // namespace panda::ets +#include "runtime/mark_word.h" namespace panda::ets::interop::js::ets_proxy { -class EtsClassWrapper; -class EtsObjectWrapper; +using EtsObjectWrappersPool = ItemsPool; -class EtsObjectWrappersStorage { +class EtsObjectWrappersStorage : private EtsObjectWrappersPool { public: - EtsObjectWrappersStorage(); + static std::unique_ptr Create(); + ~EtsObjectWrappersStorage() = default; EtsObjectWrapper *GetEtsObjectWrapper(EtsObject *ets_object); @@ -45,17 +39,13 @@ public: } EtsObjectWrapper *GetEtsObjectWrapperOrNull(void *data); - EtsObjectWrapper *CreateObjectEtsWrapper(napi_env env, EtsObject *ets_object, napi_value js_object, - UniqueEtsObjectReference ets_ref, EtsClassWrapper *ets_class_wrapper); - + EtsObjectWrapper *CreateObjectEtsWrapper(EtsObject *ets_object); void RemoveEtsObjectWrapper(EtsObjectWrapper *ets_object_wrapper); private: - inline EtsObjectWrapper *GetWrapperByIndex(uint32_t idx); - - EtsObjectWrapper *data_; - EtsObjectWrapper *data_end_; - uint32_t next_ {0}; + EtsObjectWrappersStorage(void *data, size_t size) : EtsObjectWrappersPool(data, size) {} + NO_COPY_SEMANTIC(EtsObjectWrappersStorage); + NO_MOVE_SEMANTIC(EtsObjectWrappersStorage); }; } // namespace panda::ets::interop::js::ets_proxy diff --git a/plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h b/plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h index 8cf5b6595..aaa076d43 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h @@ -52,7 +52,12 @@ struct JSConvertOBJECT { // We use napi_new_instance() to create the EtsObjectWrapper for the EtsObject // and associated it with new napi_value ObjectHeader *obj = ets_value->GetCoreType(); - UniqueEtsObjectReference ets_ref(ctx->Refstor()->Add(obj, mem::Reference::ObjectType::GLOBAL)); + mem::Reference *ref = ctx->Refstor()->Add(obj, mem::Reference::ObjectType::GLOBAL); + if (UNLIKELY(ref == nullptr)) { + InteropCtx::ThrowJSError(env, "Cannot create ets reference"); + return nullptr; + } + UniqueEtsObjectReference ets_ref(ref); ctx->SetNewInstance(std::move(ets_ref)); napi_value js_ctor = ets_class_wrapper->GetJsCtor(env); diff --git a/plugins/ets/runtime/interop_js/ets_proxy/mem/items_pool.h b/plugins/ets/runtime/interop_js/ets_proxy/mem/items_pool.h new file mode 100644 index 000000000..ec1d850eb --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/mem/items_pool.h @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ITEM_POOL_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ITEM_POOL_H_ + +#include "libpandabase/macros.h" +#include "libpandabase/utils/math_helpers.h" + +namespace panda::ets::interop::js::ets_proxy { + +namespace testing { +class ItemsPoolTest; +} // namespace testing + +template +class ItemsPool { + union PaddedItem { + Item item; + PaddedItem *next; + std::array aligned; + }; + + static constexpr size_t MAX_INDEX = 1U << NR_INDEX_BITS; + static constexpr size_t PADDED_ITEM_SIZE = sizeof(PaddedItem); + + static PaddedItem *GetPaddedItem(Item *item) + { + ASSERT(uintptr_t(item) % PADDED_ITEM_SIZE == 0); + return reinterpret_cast(item); + } + +public: + static constexpr size_t MAX_POOL_SIZE = (1U << NR_INDEX_BITS) * PADDED_ITEM_SIZE; + + ItemsPool(void *data, size_t size) + : data_(reinterpret_cast(data)), + data_end_(reinterpret_cast(uintptr_t(data_) + size)), + current_pos_(reinterpret_cast(data)) + { + ASSERT(data != nullptr); + ASSERT(size % PADDED_ITEM_SIZE == 0); + } + ~ItemsPool() = default; + + Item *AllocItem() + { + if (free_list_ != nullptr) { + Item *new_item = &free_list_->item; + free_list_ = free_list_->next; + return new_item; + } + + if (UNLIKELY(current_pos_ >= data_end_)) { + // Out of memory + return nullptr; + } + + Item *new_item = ¤t_pos_->item; + ++current_pos_; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return new_item; + } + + void FreeItem(Item *item) + { + PaddedItem *padded_item = GetPaddedItem(item); + padded_item->next = free_list_; + free_list_ = padded_item; + } + + // NOTE: + // This method only checks the validity of the item in the allocated interval + // This method does not check whether the item has been allocated or not + bool IsValidItem(const Item *item) const + { + if (UNLIKELY(!IsAligned(uintptr_t(item)))) { + return false; + } + auto addr = uintptr_t(item); + return UNLIKELY(uintptr_t(data_) <= addr && addr < uintptr_t(data_end_)); + } + + inline uint32_t GetIndexByItem(Item *item) + { + ASSERT(IsValidItem(item)); + ASSERT(uintptr_t(item) % PADDED_ITEM_SIZE == 0); + + PaddedItem *padded_item = GetPaddedItem(item); + return padded_item - data_; + } + + inline Item *GetItemByIndex(uint32_t idx) + { + ASSERT(idx < MAX_INDEX); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-union-access) + return &data_[idx].item; + } + + NO_COPY_SEMANTIC(ItemsPool); + NO_MOVE_SEMANTIC(ItemsPool); + +private: + PaddedItem *const data_ {}; + PaddedItem *const data_end_ {}; + PaddedItem *current_pos_ {}; + PaddedItem *free_list_ {}; + + friend testing::ItemsPoolTest; +}; + +} // namespace panda::ets::interop::js::ets_proxy + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ITEM_POOL_H_ diff --git a/plugins/ets/runtime/interop_js/interop_context.h b/plugins/ets/runtime/interop_js/interop_context.h index 1cdb1b94d..4e436b498 100644 --- a/plugins/ets/runtime/interop_js/interop_context.h +++ b/plugins/ets/runtime/interop_js/interop_context.h @@ -95,7 +95,9 @@ public: static void Init(EtsCoroutine *coro) { // Initialize InteropCtx in VM ExternalData - new (InteropCtx::Current(coro)) InteropCtx(coro); + auto *ctx = new (InteropCtx::Current(coro)) InteropCtx(coro); + ctx->ets_object_wrappers_storage_ = ets_proxy::EtsObjectWrappersStorage::Create(); + ASSERT(ctx->ets_object_wrappers_storage_.get() != nullptr); } static InteropCtx *Current(PandaEtsVM *ets_vm) @@ -270,7 +272,7 @@ public: ets_proxy::EtsObjectWrappersStorage *GetEtsObjectWrappersStorage() { - return &ets_object_wrappers_storage_; + return ets_object_wrappers_storage_.get(); } private: @@ -305,7 +307,7 @@ private: ets_proxy::UniqueEtsObjectReference new_instance_ {}; ets_proxy::EtsMethodWrappersCache ets_method_wrappers_cache_ {}; ets_proxy::EtsClassWrappersCache ets_class_wrappers_cache_ {}; - ets_proxy::EtsObjectWrappersStorage ets_object_wrappers_storage_ {}; + std::unique_ptr ets_object_wrappers_storage_ {}; friend class EtsJSNapiEnvScope; }; diff --git a/plugins/ets/runtime/interop_js/js_job_queue.cpp b/plugins/ets/runtime/interop_js/js_job_queue.cpp index 84fcbc044..e2cb08fdd 100644 --- a/plugins/ets/runtime/interop_js/js_job_queue.cpp +++ b/plugins/ets/runtime/interop_js/js_job_queue.cpp @@ -68,6 +68,7 @@ void JsJobQueue::AddJob(EtsObject *callback) mem::Reference *callback_ref = vm->GetGlobalObjectStorage()->Add(callback->GetCoreType(), mem::Reference::ObjectType::GLOBAL); + ASSERT(callback_ref != nullptr); napi_value then_callback; status = napi_create_function(env, nullptr, 0, ThenCallback, callback_ref, &then_callback); diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt index 7e16e2287..db58791ed 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt @@ -13,3 +13,4 @@ add_subdirectory(call_ets_function) add_subdirectory(use_ets_class) +add_subdirectory(mem) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/CMakeLists.txt new file mode 100644 index 000000000..5b9104e73 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_subdirectory(ets_object_wrappers_storage_1) +add_subdirectory(ets_object_wrappers_storage_2) +add_subdirectory(items_pool) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/CMakeLists.txt new file mode 100644 index 000000000..5bf976bb8 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +panda_ets_interop_js_gtest(ets_interop_js__test_ets_object_wrappers_storage_1 + CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_ets_object_wrappers_storage_1.cpp + ETS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_ets_object_wrappers_storage_1.ets + LIBRARIES ets_interop_js_napi +) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.cpp b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.cpp new file mode 100644 index 000000000..c6108988f --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.cpp @@ -0,0 +1,152 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ets_interop_js_gtest.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h" +#include "plugins/ets/runtime/interop_js/interop_context.h" + +// NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-pro-type-cstyle-cast) +namespace panda::ets::interop::js::ets_proxy::testing { + +class EtsObjectWrappersStorage1GTest : public js::testing::EtsInteropTest { +public: + void SetUp() override + { + storage_ = EtsObjectWrappersStorage::Create(); + + memset(&object_array_, 0, sizeof(object_array_)); + next_free_idx_ = 0; + } + + EtsObject *NewEtsObject() + { + if (next_free_idx_ == MAX_OBJECTS) { + std::cerr << "Out of memory" << std::endl; + std::abort(); + } + return EtsObject::FromCoreType(&object_array_[next_free_idx_++]); + } + + EtsObjectWrapper *CreateWrapper(EtsObject *ets_object) + { + EtsObjectWrapper *wrapper = storage_->CreateObjectEtsWrapper(ets_object); + + // Emulate wrappper usage + ((uintptr_t *)wrapper)[0] = 0xcc00ff23; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + ((uintptr_t *)wrapper)[1] = 0xdd330047; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + return wrapper; + } + +protected: + std::unique_ptr storage_ {}; // NOLINT(misc-non-private-member-variables-in-classes) + +private: + static constexpr size_t MAX_OBJECTS = 128; + std::array object_array_ {}; + size_t next_free_idx_ {}; +}; + +TEST_F(EtsObjectWrappersStorage1GTest, test_0) +{ + EtsObject *ets_object = NewEtsObject(); + + ASSERT_EQ(storage_->HasEtsObjectWrapper(ets_object), false); + EtsObjectWrapper *wrapper = CreateWrapper(ets_object); + ASSERT_EQ(storage_->HasEtsObjectWrapper(ets_object), true); + + EtsObjectWrapper *wrapper_x = storage_->GetEtsObjectWrapper(ets_object); + EtsObjectWrapper *wrapper_y = storage_->GetEtsObjectWrapperOrNull((void *)wrapper); + + ASSERT_EQ(wrapper, wrapper_x); + ASSERT_EQ(wrapper, wrapper_y); + + storage_->RemoveEtsObjectWrapper(wrapper); +} + +TEST_F(EtsObjectWrappersStorage1GTest, test_1) +{ + EtsObject *ets_object = NewEtsObject(); + + EtsObjectWrapper *wrapper = CreateWrapper(ets_object); + + // Check allocated wrapper + EtsObjectWrapper *wrapper_0 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 0)); + ASSERT_EQ(wrapper_0, wrapper); + + // Check unaligned address + EtsObjectWrapper *wrapper_1 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 1)); + EtsObjectWrapper *wrapper_2 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 2)); + EtsObjectWrapper *wrapper_3 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 3)); + EtsObjectWrapper *wrapper_4 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 4)); + EtsObjectWrapper *wrapper_5 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 5)); + EtsObjectWrapper *wrapper_6 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 6)); + EtsObjectWrapper *wrapper_7 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 7)); + ASSERT_EQ(wrapper_1, nullptr); + ASSERT_EQ(wrapper_2, nullptr); + ASSERT_EQ(wrapper_3, nullptr); + ASSERT_EQ(wrapper_4, nullptr); + ASSERT_EQ(wrapper_5, nullptr); + ASSERT_EQ(wrapper_6, nullptr); + ASSERT_EQ(wrapper_7, nullptr); + + // Check next unallocated wrapper + constexpr size_t WRAPPER_SIZE = sizeof(EtsObjectWrapper); + EtsObjectWrapper *wrapper_x = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + WRAPPER_SIZE)); + ASSERT_EQ(wrapper_x, nullptr); + + // Check physical unmapped space + EtsObjectWrapper *wrapper_y = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 16 * 4096)); + ASSERT_EQ(wrapper_y, nullptr); + + storage_->RemoveEtsObjectWrapper(wrapper); +} + +TEST_F(EtsObjectWrappersStorage1GTest, test_2) +{ + EtsObject *ets_object = NewEtsObject(); + EtsObjectWrapper *wrapper = CreateWrapper(ets_object); + + EtsObject *ets_object_1 = NewEtsObject(); + EtsObjectWrapper *wrapper_1 = CreateWrapper(ets_object_1); + + EtsObject *ets_object_2 = NewEtsObject(); + EtsObjectWrapper *wrapper_2 = CreateWrapper(ets_object_2); + + EtsObjectWrapper *wrapper_1a = storage_->GetEtsObjectWrapperOrNull((void *)wrapper_1); + ASSERT_EQ(wrapper_1a, wrapper_1); + + storage_->RemoveEtsObjectWrapper(wrapper_1); + + // Check deallocated wrapper + EtsObjectWrapper *wrapper_1b = storage_->GetEtsObjectWrapperOrNull((void *)wrapper_1); + ASSERT_EQ(wrapper_1b, nullptr); + + // Check + EtsObject *ets_object_4 = NewEtsObject(); + EtsObjectWrapper *wrapper_4 = CreateWrapper(ets_object_4); + ASSERT_EQ(wrapper_4, wrapper_1); + + storage_->RemoveEtsObjectWrapper(wrapper); + storage_->RemoveEtsObjectWrapper(wrapper_2); + storage_->RemoveEtsObjectWrapper(wrapper_4); + + ASSERT_EQ(storage_->GetEtsObjectWrapperOrNull((void *)wrapper), nullptr); + ASSERT_EQ(storage_->GetEtsObjectWrapperOrNull((void *)wrapper_2), nullptr); + ASSERT_EQ(storage_->GetEtsObjectWrapperOrNull((void *)wrapper_4), nullptr); +} + +} // namespace panda::ets::interop::js::ets_proxy::testing +// NOLINTEND(readability-magic-numbers,cppcoreguidelines-pro-type-cstyle-cast) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.ets b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.ets new file mode 100644 index 000000000..7c6be2cef --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.ets @@ -0,0 +1 @@ +// NOTE: this file is required by panda_ets_interop_js_gtest() diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/CMakeLists.txt new file mode 100644 index 000000000..92f6fc441 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2021-2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +panda_ets_interop_js_test(ets_interop_js__test_ets_object_wrappers_storage_2 + ETS_SOURCES ${CMAKE_CURRENT_LIST_DIR}/test_ets_object_wrappers_storage_2.ets + JS_LAUNCHER ${CMAKE_CURRENT_LIST_DIR}/test_ets_object_wrappers_storage_2.js +) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.ets b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.ets new file mode 100644 index 000000000..0a0f29856 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.ets @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ets_object_wrappers_storage_2.test; + +class Point { + constructor(x: double, y: double) { + this.x = x; + this.y = y; + } + public x: double; + public y: double; +} diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.js b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.js new file mode 100644 index 000000000..a3da90c22 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.js @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +let etsVm = require(process.env.MODULE_PATH + '/ets_interop_js_napi.node'); +const etsOpts = { + 'panda-files': process.env.ARK_ETS_INTEROP_JS_GTEST_ABC_PATH, + 'boot-panda-files': `${process.env.ARK_ETS_STDLIB_PATH}:${process.env.ARK_ETS_INTEROP_JS_GTEST_ABC_PATH}`, + 'gc-trigger-type': 'heap-trigger', + 'load-runtimes': 'ets', + 'compiler-enable-jit': 'false', + 'enable-an': 'false', + 'run-gc-in-place': 'true', +}; +const res = etsVm.createRuntime(etsOpts); +if (!res) { + console.log('Cannot create ETS runtime'); + process.exit(1); +} + + +const Point = etsVm.getClass("Lets_object_wrappers_storage_2/test/Point;"); + + +// Call EtsObjectWrappersStorage::CreateObjectEtsWrapper() method +for (let i = 0; i < 4*4096; ++i) { + let p = new Point(i, i); +} + + + +// Method EtsObjectWrappersStorage::RemoveEtsObjectWrapper() will be called before the virtual machine is terminated diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/items_pool/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/items_pool/CMakeLists.txt new file mode 100644 index 000000000..872dff6ad --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/items_pool/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +panda_add_gtest( + NAME ets_interop_js__test_items_pool + SOURCES test_items_pool.cpp + INCLUDE_DIRS ${PANDA_ROOT} + LIBRARIES arkbase + SANITIZERS ${PANDA_SANITIZERS_LIST} +) +add_dependencies(ets_interop_js_gtests ets_interop_js__test_items_pool_gtests) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/items_pool/test_items_pool.cpp b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/items_pool/test_items_pool.cpp new file mode 100644 index 000000000..e76fe8570 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/items_pool/test_items_pool.cpp @@ -0,0 +1,414 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "libpandabase/os/mem.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/mem/items_pool.h" + +namespace panda::ets::interop::js::ets_proxy::testing { + +class ItemsPoolTest { + static constexpr size_t ARRAY_SIZE = 2045; + static constexpr size_t PADDED_ITEM_SIZE = 2048; + +public: + using Item = std::array; + using Pool = ItemsPool; + using PaddedItem = Pool::PaddedItem; + static_assert(sizeof(PaddedItem) == PADDED_ITEM_SIZE); + + static uintptr_t GetData(const std::unique_ptr &pool) + { + return uintptr_t(pool->data_); + } + + static uintptr_t &GetCurrentPos(const std::unique_ptr &pool) + { + return *reinterpret_cast(&pool->current_pos_); + } + + static uintptr_t &GetFreeList(const std::unique_ptr &pool) + { + return *reinterpret_cast(&pool->free_list_); + } + + static std::unique_ptr CreatePool() + { + size_t size = Pool::MAX_POOL_SIZE; + + void *data = os::mem::MapRWAnonymousRaw(size); + if (data == nullptr) { + std::cerr << "Cannot allocate Pool, size=" << std::hex << size << std::dec << std::endl; + std::abort(); + } + return std::make_unique(data, size); + } +}; + +constexpr size_t PADDED_ITEM_SIZE = sizeof(ItemsPoolTest::PaddedItem); + +template +static uintptr_t GetData(const T &pool) +{ + return ItemsPoolTest::GetData(pool); +} + +template +static uintptr_t &GetCurrentPos(const T &pool) +{ + return ItemsPoolTest::GetCurrentPos(pool); +} + +template +static uintptr_t &GetFreeList(const T &pool) +{ + return ItemsPoolTest::GetFreeList(pool); +} + +class ItemsPoolGTest : public ::testing::Test {}; + +TEST_F(ItemsPoolGTest, test_1) +{ + auto pool = ItemsPoolTest::CreatePool(); + uintptr_t data = GetData(pool); + ASSERT_NE(data, 0); + + uintptr_t &free_list = GetFreeList(pool); + ASSERT_EQ(free_list, 0); + + auto ¤t_pos = GetCurrentPos(pool); + ASSERT_EQ(current_pos, data); +} + +TEST_F(ItemsPoolGTest, test_2) +{ + // clang-format off +// | INDEX | Step1 | Step2 | Step3 | Step4 | Step5 | Step6 | Step7 | Step8 | Step9 | Step10| Step11| Step12| Step13| Step14| Step15| Step16| Step17| Step18| Step19| +// +=========+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+ +// | 000 | item0 | item0 | item0 | item0 | item0 | item0 | item0 | item0 | item0 | item0 | item0 | item0 | | | | | | item13| item13| +// | 001 | | item1 | item1 | | item3 | item3 | item3 | item3 | item3 | item3 | item3 | item3 | item3 | item3 | item3 | item3 | item3 | item3 | item3 | +// | 010 | | | item2 | item2 | item2 | item2 | item2 | item2 | item2 | item2 | item2 | item2 | item2 | item2 | item2 | item2 | item2 | item2 | item2 | +// | 011 | | | | | | item4 | item4 | item4 | item4 | item4 | item4 | item4 | item4 | item4 | | item11| item11| item11| item11| +// | 100 | | | | | | | item5 | item5 | item5 | item5 | item5 | item5 | item5 | | | | item12| item12| item12| +// | 101 | | | | | | | | item6 | item6 | item6 | item6 | item6 | item6 | item6 | item6 | item6 | item6 | item6 | item6 | +// | 110 | | | | | | | | | item7 | item7 | item7 | item7 | item7 | item7 | item7 | item7 | item7 | item7 | item7 | +// | 111 | | | | | | | | | | item8 | item8 | item8 | item8 | item8 | item8 | item8 | item8 | item8 | item8 | +// +=========+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+ +// |free_list| | | | | | | | | | | | | 0 | 4 | 3 | 4 | 0 | | | +// | | | | | | | | | | | | | | | 0 | 4 | 0 | | | | +// | | | | | | | | | | | | | | | | 0 | | | | | + // clang-format on + + auto pool = ItemsPoolTest::CreatePool(); + uintptr_t data = GetData(pool); + auto ¤t_pos = GetCurrentPos(pool); + uintptr_t &free_list = GetFreeList(pool); + + ASSERT_NE(data, 0); + ASSERT_EQ(current_pos, data); + ASSERT_EQ(free_list, 0); + + // Step1, Alloc + auto item0 = pool->AllocItem(); + ASSERT_NE(item0, nullptr); + ASSERT_EQ(item0, pool->GetItemByIndex(0)); + ASSERT_EQ(pool->GetIndexByItem(item0), 0b000); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 1 * PADDED_ITEM_SIZE); + + // Step2, Alloc + auto item1 = pool->AllocItem(); + ASSERT_NE(item1, nullptr); + ASSERT_EQ(item1, pool->GetItemByIndex(1)); + ASSERT_EQ(pool->GetIndexByItem(item1), 0b001); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 2 * PADDED_ITEM_SIZE); + + // Step3, Alloc + auto item2 = pool->AllocItem(); + ASSERT_NE(item2, nullptr); + ASSERT_EQ(item2, pool->GetItemByIndex(2)); + ASSERT_EQ(pool->GetIndexByItem(item2), 0b010); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 3 * PADDED_ITEM_SIZE); + + // Step4, Free + pool->FreeItem(item1); + ASSERT_EQ(free_list, data + 1 * PADDED_ITEM_SIZE); + ASSERT_EQ(current_pos, data + 3 * PADDED_ITEM_SIZE); + + // Step5, Alloc + auto item3 = pool->AllocItem(); + ASSERT_NE(item3, nullptr); + ASSERT_EQ(item3, pool->GetItemByIndex(1)); + ASSERT_EQ(pool->GetIndexByItem(item3), 0b001); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 3 * PADDED_ITEM_SIZE); + + // Step6, Alloc + auto item4 = pool->AllocItem(); + ASSERT_NE(item4, nullptr); + ASSERT_EQ(item4, pool->GetItemByIndex(3)); + ASSERT_EQ(pool->GetIndexByItem(item4), 0b011); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 4 * PADDED_ITEM_SIZE); + + // Step7, Alloc + auto item5 = pool->AllocItem(); + ASSERT_NE(item5, nullptr); + ASSERT_EQ(item5, pool->GetItemByIndex(4)); + ASSERT_EQ(pool->GetIndexByItem(item5), 0b100); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 5 * PADDED_ITEM_SIZE); + + // Step8, Alloc + auto item6 = pool->AllocItem(); + ASSERT_NE(item6, nullptr); + ASSERT_EQ(item6, pool->GetItemByIndex(5)); + ASSERT_EQ(pool->GetIndexByItem(item6), 0b101); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 6 * PADDED_ITEM_SIZE); + + // Step9, Alloc + auto item7 = pool->AllocItem(); + ASSERT_NE(item7, nullptr); + ASSERT_EQ(item7, pool->GetItemByIndex(6)); + ASSERT_EQ(pool->GetIndexByItem(item7), 0b110); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 7 * PADDED_ITEM_SIZE); + + // Step10, Alloc + auto item8 = pool->AllocItem(); + ASSERT_NE(item8, nullptr); + ASSERT_EQ(item8, pool->GetItemByIndex(7)); + ASSERT_EQ(pool->GetIndexByItem(item8), 0b111); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Step11, Alloc, out of memory + auto item9 = pool->AllocItem(); + ASSERT_EQ(item9, nullptr); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Step12, Alloc, out of memory + auto item10 = pool->AllocItem(); + ASSERT_EQ(item10, nullptr); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Step13, Free + ASSERT_EQ(pool->GetIndexByItem(item0), 0b000); + pool->FreeItem(item0); + ASSERT_EQ(free_list, data + 0 * PADDED_ITEM_SIZE); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Step14, Free + ASSERT_EQ(pool->GetIndexByItem(item5), 0b100); + pool->FreeItem(item5); + ASSERT_EQ(free_list, data + 4 * PADDED_ITEM_SIZE); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Step15, Free + ASSERT_EQ(pool->GetIndexByItem(item4), 0b011); + pool->FreeItem(item4); + ASSERT_EQ(free_list, data + 3 * PADDED_ITEM_SIZE); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Step16, Alloc + auto item11 = pool->AllocItem(); + ASSERT_NE(item11, nullptr); + ASSERT_EQ(item11, pool->GetItemByIndex(3)); + ASSERT_EQ(pool->GetIndexByItem(item11), 0b011); + ASSERT_EQ(free_list, data + 4 * PADDED_ITEM_SIZE); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Step17, Alloc + auto item12 = pool->AllocItem(); + ASSERT_NE(item12, nullptr); + ASSERT_EQ(item12, pool->GetItemByIndex(4)); + ASSERT_EQ(pool->GetIndexByItem(item12), 0b100); + ASSERT_EQ(free_list, data + 0 * PADDED_ITEM_SIZE); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Step18, Alloc + auto item13 = pool->AllocItem(); + ASSERT_NE(item13, nullptr); + ASSERT_EQ(item13, pool->GetItemByIndex(0)); + ASSERT_EQ(pool->GetIndexByItem(item13), 0b000); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Step19, Alloc, out of memory + auto item14 = pool->AllocItem(); + ASSERT_EQ(item14, nullptr); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Check allocated items possitions + ASSERT_EQ(uintptr_t(item13), data + 0 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item3), data + 1 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item2), data + 2 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item11), data + 3 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item12), data + 4 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item6), data + 5 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item7), data + 6 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item8), data + 7 * PADDED_ITEM_SIZE); +} + +TEST_F(ItemsPoolGTest, test_3) +{ + // clang-format off +// | INDEX | Step1 | Step2 | Step3 | Step4 | +// +=========+=======+=======+=======+=======+ +// | 000 | item0 | | item6 | item6 | +// | 001 | item1 | item1 | item1 | item1 | +// | 010 | item2 | item2 | item2 | item2 | +// | 011 | item3 | | item5 | item5 | +// | 100 | item4 | item4 | item4 | item4 | +// | 101 | | | item7 | item7 | +// | 110 | | | | item8 | +// | 111 | | | | item9 | +// +=========+=======+=======+=======+=======+ +// |free_list| | 3 | | | +// | | | 0 | | | + // clang-format on + + auto pool = ItemsPoolTest::CreatePool(); + uintptr_t data = GetData(pool); + auto ¤t_pos = GetCurrentPos(pool); + uintptr_t &free_list = GetFreeList(pool); + + ASSERT_NE(data, 0); + ASSERT_EQ(current_pos, data + 0 * PADDED_ITEM_SIZE); + ASSERT_EQ(free_list, 0); + + // Step1, Alloc 5 items + auto item0 = pool->AllocItem(); + ASSERT_NE(item0, nullptr); + ASSERT_EQ(item0, pool->GetItemByIndex(0)); + ASSERT_EQ(pool->GetIndexByItem(item0), 0b000); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 1 * PADDED_ITEM_SIZE); + + auto item1 = pool->AllocItem(); + ASSERT_NE(item1, nullptr); + ASSERT_EQ(item1, pool->GetItemByIndex(1)); + ASSERT_EQ(pool->GetIndexByItem(item1), 0b001); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 2 * PADDED_ITEM_SIZE); + + auto item2 = pool->AllocItem(); + ASSERT_NE(item2, nullptr); + ASSERT_EQ(item2, pool->GetItemByIndex(2)); + ASSERT_EQ(pool->GetIndexByItem(item2), 0b010); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 3 * PADDED_ITEM_SIZE); + + auto item3 = pool->AllocItem(); + ASSERT_NE(item3, nullptr); + ASSERT_EQ(item3, pool->GetItemByIndex(3)); + ASSERT_EQ(pool->GetIndexByItem(item3), 0b011); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 4 * PADDED_ITEM_SIZE); + + auto item4 = pool->AllocItem(); + ASSERT_NE(item4, nullptr); + ASSERT_EQ(item4, pool->GetItemByIndex(4)); + ASSERT_EQ(pool->GetIndexByItem(item4), 0b100); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 5 * PADDED_ITEM_SIZE); + + // Step2, Free item0 and item3 + ASSERT_EQ(pool->GetIndexByItem(item0), 0b000); + pool->FreeItem(item0); + ASSERT_EQ(free_list, data + 0 * PADDED_ITEM_SIZE); + ASSERT_EQ(current_pos, data + 5 * PADDED_ITEM_SIZE); + + ASSERT_EQ(pool->GetIndexByItem(item3), 0b011); + pool->FreeItem(item3); + ASSERT_EQ(free_list, data + 3 * PADDED_ITEM_SIZE); + ASSERT_EQ(current_pos, data + 5 * PADDED_ITEM_SIZE); + + // Step3, Alloc 3 items + auto item5 = pool->AllocItem(); + ASSERT_NE(item5, nullptr); + ASSERT_EQ(item5, pool->GetItemByIndex(3)); + ASSERT_EQ(pool->GetIndexByItem(item5), 0b011); + ASSERT_EQ(free_list, data + 0 * PADDED_ITEM_SIZE); + ASSERT_EQ(current_pos, data + 5 * PADDED_ITEM_SIZE); + + auto item6 = pool->AllocItem(); + ASSERT_NE(item6, nullptr); + ASSERT_EQ(item6, pool->GetItemByIndex(0)); + ASSERT_EQ(pool->GetIndexByItem(item6), 0b000); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 5 * PADDED_ITEM_SIZE); + + auto item7 = pool->AllocItem(); + ASSERT_NE(item7, nullptr); + ASSERT_EQ(item7, pool->GetItemByIndex(5)); + ASSERT_EQ(pool->GetIndexByItem(item7), 0b101); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 6 * PADDED_ITEM_SIZE); + + // Step4, Alloc 3 items + auto item8 = pool->AllocItem(); + ASSERT_NE(item8, nullptr); + ASSERT_EQ(item8, pool->GetItemByIndex(6)); + ASSERT_EQ(pool->GetIndexByItem(item8), 0b110); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 7 * PADDED_ITEM_SIZE); + + auto item9 = pool->AllocItem(); + ASSERT_NE(item9, nullptr); + ASSERT_EQ(item9, pool->GetItemByIndex(7)); + ASSERT_EQ(pool->GetIndexByItem(item9), 0b111); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + auto item10 = pool->AllocItem(); + ASSERT_EQ(item10, nullptr); + ASSERT_EQ(free_list, 0); + ASSERT_EQ(current_pos, data + 8 * PADDED_ITEM_SIZE); + + // Check allocated items possitions + ASSERT_EQ(uintptr_t(item6), data + 0 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item1), data + 1 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item2), data + 2 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item5), data + 3 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item4), data + 4 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item7), data + 5 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item8), data + 6 * PADDED_ITEM_SIZE); + ASSERT_EQ(uintptr_t(item9), data + 7 * PADDED_ITEM_SIZE); +} + +TEST_F(ItemsPoolGTest, test_IsValidItem) +{ + auto pool = ItemsPoolTest::CreatePool(); + uintptr_t data = GetData(pool); + uintptr_t data_end = data + pool->MAX_POOL_SIZE; + + auto item = pool->AllocItem(); + + ASSERT_EQ(pool->IsValidItem(item), true); + ASSERT_EQ(pool->IsValidItem(reinterpret_cast(data)), true); + ASSERT_EQ(pool->IsValidItem(reinterpret_cast(data_end)), false); + ASSERT_EQ(pool->IsValidItem(reinterpret_cast(data - sizeof(*item))), false); + ASSERT_EQ(pool->IsValidItem(reinterpret_cast(data_end - sizeof(*item))), true); +} + +} // namespace panda::ets::interop::js::ets_proxy::testing diff --git a/runtime/mem/runslots.h b/runtime/mem/runslots.h index bb374b336..7e9c5f6bd 100644 --- a/runtime/mem/runslots.h +++ b/runtime/mem/runslots.h @@ -25,10 +25,6 @@ namespace panda::mem { -// If the OS has this macro, do not redefine it. -#ifndef PAGE_SIZE -static constexpr size_t PAGE_SIZE = SIZE_1K * 4; -#endif static constexpr size_t PAGES_IN_RUNSLOTS = 1; static constexpr size_t RUNSLOTS_SIZE = PAGES_IN_RUNSLOTS * PAGE_SIZE; static constexpr size_t RUNSLOTS_ALIGNMENT_IN_BYTES = PAGE_SIZE; -- Gitee