From 15d76dd66ff5fed484dfa74547f06046ba9f522c Mon Sep 17 00:00:00 2001 From: dingwen Date: Fri, 5 Sep 2025 16:03:22 +0800 Subject: [PATCH] Optimize serialize allocate to void OOM ISSUE:https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/ICWLP2 Description:Optimize serialize allocate to void OOM Signed-off-by: dingwen Change-Id: I97aabddd51f99efd8ddc2fcbbcdd0823345d0a98 --- ecmascript/serializer/base_deserializer.cpp | 180 ++++++++++++------ ecmascript/serializer/base_deserializer.h | 18 +- ecmascript/serializer/tests/BUILD.gn | 26 +++ .../serializer/tests/cmc_serializer_test.cpp | 130 +++++++++++++ .../serializer/tests/serializer_test.cpp | 89 +++++++++ test/resource/js_runtime/ohos_test.xml | 5 + 6 files changed, 379 insertions(+), 69 deletions(-) create mode 100644 ecmascript/serializer/tests/cmc_serializer_test.cpp diff --git a/ecmascript/serializer/base_deserializer.cpp b/ecmascript/serializer/base_deserializer.cpp index 87b99f11c2..0a70ea0da3 100644 --- a/ecmascript/serializer/base_deserializer.cpp +++ b/ecmascript/serializer/base_deserializer.cpp @@ -654,43 +654,69 @@ JSTaggedType BaseDeserializer::RelocateObjectProtoAddr(uint8_t objectType) void BaseDeserializer::AllocateToDifferentSpaces() { if (g_isEnableCMCGC) { - size_t regularSpaceSize = data_->GetRegularSpaceSize(); - if (regularSpaceSize > 0) { - // statistic object size - AllocateToRegularSpace(regularSpaceSize); - } - size_t pinSpaceSize = data_->GetPinSpaceSize(); - if (pinSpaceSize > 0) { - // statistic object size - AllocateToPinSpace(pinSpaceSize); - } + AllocateToDifferentCMCSpaces(); } else { - size_t oldSpaceSize = data_->GetOldSpaceSize(); - if (oldSpaceSize > 0) { - heap_->GetOldSpace()->IncreaseLiveObjectSize(oldSpaceSize); - AllocateToOldSpace(oldSpaceSize); + if (!AllocateToDifferentLocalSpaces(true)) { + // If allocate local obj fail, trigger old gc and retry again + heap_->CollectGarbage(TriggerGCType::OLD_GC, GCReason::ALLOCATION_FAILED); + AllocateToDifferentLocalSpaces(false); } - size_t nonMovableSpaceSize = data_->GetNonMovableSpaceSize(); - if (nonMovableSpaceSize > 0) { - heap_->GetNonMovableSpace()->IncreaseLiveObjectSize(nonMovableSpaceSize); - AllocateToNonMovableSpace(nonMovableSpaceSize); - } - size_t machineCodeSpaceSize = data_->GetMachineCodeSpaceSize(); - if (machineCodeSpaceSize > 0) { - heap_->GetMachineCodeSpace()->IncreaseLiveObjectSize(machineCodeSpaceSize); - AllocateToMachineCodeSpace(machineCodeSpaceSize); + AllocateToDifferentSharedSpaces(); + } +} + +bool BaseDeserializer::AllocateToDifferentLocalSpaces(bool isFirstAllocate) +{ + size_t oldSpaceSize = data_->GetOldSpaceSize(); + if (oldSpaceSize > 0) { + // Old space should increase space live object size here since the allocate is independent + heap_->GetOldSpace()->IncreaseLiveObjectSize(oldSpaceSize); + if (!AllocateToOldSpace(oldSpaceSize, isFirstAllocate)) { + heap_->GetOldSpace()->DecreaseLiveObjectSize(oldSpaceSize); + return false; } - size_t sOldSpaceSize = data_->GetSharedOldSpaceSize(); - if (sOldSpaceSize > 0) { - sheap_->GetOldSpace()->IncreaseLiveObjectSize(sOldSpaceSize); - AllocateToSharedOldSpace(sOldSpaceSize); + } + size_t nonMovableSpaceSize = data_->GetNonMovableSpaceSize(); + if (nonMovableSpaceSize > 0) { + if (!AllocateToNonMovableSpace(nonMovableSpaceSize, isFirstAllocate)) { + return false; } - size_t sNonMovableSpaceSize = data_->GetSharedNonMovableSpaceSize(); - if (sNonMovableSpaceSize > 0) { - sheap_->GetNonMovableSpace()->IncreaseLiveObjectSize(sNonMovableSpaceSize); - AllocateToSharedNonMovableSpace(sNonMovableSpaceSize); + } + size_t machineCodeSpaceSize = data_->GetMachineCodeSpaceSize(); + if (machineCodeSpaceSize > 0) { + if (!AllocateToMachineCodeSpace(machineCodeSpaceSize, isFirstAllocate)) { + return false; } } + return true; +} + +void BaseDeserializer::AllocateToDifferentSharedSpaces() +{ + size_t sOldSpaceSize = data_->GetSharedOldSpaceSize(); + if (sOldSpaceSize > 0) { + sheap_->GetOldSpace()->IncreaseLiveObjectSize(sOldSpaceSize); + AllocateToSharedOldSpace(sOldSpaceSize); + } + size_t sNonMovableSpaceSize = data_->GetSharedNonMovableSpaceSize(); + if (sNonMovableSpaceSize > 0) { + sheap_->GetNonMovableSpace()->IncreaseLiveObjectSize(sNonMovableSpaceSize); + AllocateToSharedNonMovableSpace(sNonMovableSpaceSize); + } +} + +void BaseDeserializer::AllocateToDifferentCMCSpaces() +{ + size_t regularSpaceSize = data_->GetRegularSpaceSize(); + if (regularSpaceSize > 0) { + // statistic object size + AllocateToRegularSpace(regularSpaceSize); + } + size_t pinSpaceSize = data_->GetPinSpaceSize(); + if (pinSpaceSize > 0) { + // statistic object size + AllocateToPinSpace(pinSpaceSize); + } } void BaseDeserializer::AllocateToRegularSpace(size_t regularSpaceSize) @@ -765,8 +791,8 @@ uintptr_t BaseDeserializer::AllocateMultiCMCRegion(size_t spaceObjSize, size_t & return firstRegionAddr; } -void BaseDeserializer::AllocateMultiRegion(SparseSpace *space, size_t spaceObjSize, size_t ®ionIndex, - SerializedObjectSpace spaceType) +bool BaseDeserializer::AllocateMultiRegion(SparseSpace *space, size_t spaceObjSize, size_t ®ionIndex, + SerializedObjectSpace spaceType, bool isFirstAllocate) { ASSERT(spaceType != SerializedObjectSpace::NON_MOVABLE_SPACE); regionIndex = regionVector_.size(); @@ -779,7 +805,11 @@ void BaseDeserializer::AllocateMultiRegion(SparseSpace *space, size_t spaceObjSi space->GetCurrentRegion()->IncreaseAliveObject(regionAliveObjSize); space->ResetTopPointer(space->GetCurrentRegion()->GetBegin() + regionAliveObjSize); if (!space->Expand()) { - DeserializeFatalOutOfMemory(spaceObjSize); + if (!isFirstAllocate) { + DeserializeFatalOutOfMemory(spaceObjSize); + } + regionVector_.clear(); + return false; } Region *currentRegion = space->GetCurrentRegion(); FreeObject::FillFreeObject(heap_, currentRegion->GetBegin(), currentRegion->GetSize()); @@ -789,30 +819,49 @@ void BaseDeserializer::AllocateMultiRegion(SparseSpace *space, size_t spaceObjSi size_t lastRegionRemainSize = regionAlignedSize - spaceObjSize; space->GetCurrentRegion()->IncreaseAliveObject(Region::GetRegionAvailableSize() - lastRegionRemainSize); space->ResetTopPointer(space->GetCurrentRegion()->GetEnd() - lastRegionRemainSize); + return true; } -void BaseDeserializer::AllocateMultiNonmovableRegion(SparseSpace *space, size_t spaceObjSize, size_t ®ionIndex, - [[maybe_unused]] SerializedObjectSpace spaceType) +bool BaseDeserializer::AllocateMultiNonmovableRegion(SparseSpace *space, size_t spaceObjSize, size_t ®ionIndex, + [[maybe_unused]] SerializedObjectSpace spaceType, + bool isFirstAllocate) { ASSERT(spaceType == SerializedObjectSpace::NON_MOVABLE_SPACE); regionIndex = regionVector_.size(); size_t allocatedSize = 0; + const std::vector &remainSize = data_->GetRegionRemainSizeVectors().at(static_cast(spaceType)); + size_t numRegion = 0; + size_t totalRegion = remainSize.size(); while (allocatedSize < spaceObjSize) { size_t leftSize = spaceObjSize - allocatedSize; size_t size = std::min(leftSize, Region::GetRegionAvailableSize()); uintptr_t obj = space->Allocate(size, false); - if (obj > 0) { - if (allocatedSize == 0) { - // The first region - nonMovableSpaceBeginAddr_ = obj; - } else { - regionVector_.push_back(obj); + if (obj == 0) { + if (!isFirstAllocate) { + DeserializeFatalOutOfMemory(spaceObjSize); } + regionVector_.clear(); + return false; + } + if (allocatedSize == 0) { + // The first region + nonMovableSpaceBeginAddr_ = obj; } else { - DeserializeFatalOutOfMemory(spaceObjSize); + regionVector_.push_back(obj); } allocatedSize += size; + if (numRegion < totalRegion) { + size_t wastedSize = remainSize[numRegion++]; + // Fill actual used size + FreeObject::FillFreeObject(heap_, obj, size - wastedSize); + // Fill wasted size + FreeObject::FillFreeObject(heap_, obj + size - wastedSize, wastedSize); + } else { + // Fill last allocate size + FreeObject::FillFreeObject(heap_, obj, size); + } } + return true; } void BaseDeserializer::AllocateMultiSharedRegion(SharedSparseSpace *space, size_t spaceObjSize, size_t ®ionIndex, @@ -844,52 +893,59 @@ void BaseDeserializer::AllocateMultiSharedRegion(SharedSparseSpace *space, size_ space->MergeDeserializeAllocateRegions(allocateRegions); } -void BaseDeserializer::AllocateToOldSpace(size_t oldSpaceSize) +bool BaseDeserializer::AllocateToOldSpace(size_t oldSpaceSize, bool isFirstAllocate) { OldSpace *space = heap_->GetOldSpace(); uintptr_t object = space->AllocateSlow(oldSpaceSize, true); if (UNLIKELY(object == 0U)) { if (space->CommittedSizeExceed()) { - DeserializeFatalOutOfMemory(oldSpaceSize); + if (!isFirstAllocate) { + DeserializeFatalOutOfMemory(oldSpaceSize); + } + return false; } oldSpaceBeginAddr_ = space->GetCurrentRegion()->GetBegin(); FreeObject::FillFreeObject(heap_, oldSpaceBeginAddr_, space->GetCurrentRegion()->GetSize()); - AllocateMultiRegion(space, oldSpaceSize, oldRegionIndex_, SerializedObjectSpace::OLD_SPACE); - } else { - FreeObject::FillFreeObject(heap_, object, oldSpaceSize); - oldSpaceBeginAddr_ = object; + return AllocateMultiRegion(space, oldSpaceSize, oldRegionIndex_, + SerializedObjectSpace::OLD_SPACE, isFirstAllocate); } + FreeObject::FillFreeObject(heap_, object, oldSpaceSize); + oldSpaceBeginAddr_ = object; + return true; } -void BaseDeserializer::AllocateToNonMovableSpace(size_t nonMovableSpaceSize) +bool BaseDeserializer::AllocateToNonMovableSpace(size_t nonMovableSpaceSize, bool isFirstAllocate) { SparseSpace *space = heap_->GetNonMovableSpace(); uintptr_t object = space->Allocate(nonMovableSpaceSize, false); if (UNLIKELY(object == 0U)) { - AllocateMultiNonmovableRegion(space, nonMovableSpaceSize, nonMovableRegionIndex_, - SerializedObjectSpace::NON_MOVABLE_SPACE); - } else { - FreeObject::FillFreeObject(heap_, object, nonMovableSpaceSize); - nonMovableSpaceBeginAddr_ = object; + return AllocateMultiNonmovableRegion(space, nonMovableSpaceSize, nonMovableRegionIndex_, + SerializedObjectSpace::NON_MOVABLE_SPACE, isFirstAllocate); } + FreeObject::FillFreeObject(heap_, object, nonMovableSpaceSize); + nonMovableSpaceBeginAddr_ = object; + return true; } -void BaseDeserializer::AllocateToMachineCodeSpace(size_t machineCodeSpaceSize) +bool BaseDeserializer::AllocateToMachineCodeSpace(size_t machineCodeSpaceSize, bool isFirstAllocate) { SparseSpace *space = heap_->GetMachineCodeSpace(); uintptr_t object = space->Allocate(machineCodeSpaceSize, false); if (UNLIKELY(object == 0U)) { if (space->CommittedSizeExceed()) { - DeserializeFatalOutOfMemory(machineCodeSpaceSize); + if (!isFirstAllocate) { + DeserializeFatalOutOfMemory(machineCodeSpaceSize); + } + return false; } machineCodeSpaceBeginAddr_ = space->GetCurrentRegion()->GetBegin(); FreeObject::FillFreeObject(heap_, machineCodeSpaceBeginAddr_, space->GetCurrentRegion()->GetSize()); - AllocateMultiRegion(space, machineCodeSpaceSize, machineCodeRegionIndex_, - SerializedObjectSpace::MACHINE_CODE_SPACE); - } else { - FreeObject::FillFreeObject(heap_, object, machineCodeSpaceSize); - machineCodeSpaceBeginAddr_ = object; + return AllocateMultiRegion(space, machineCodeSpaceSize, machineCodeRegionIndex_, + SerializedObjectSpace::MACHINE_CODE_SPACE, isFirstAllocate); } + FreeObject::FillFreeObject(heap_, object, machineCodeSpaceSize); + machineCodeSpaceBeginAddr_ = object; + return true; } void BaseDeserializer::AllocateToSharedOldSpace(size_t sOldSpaceSize) diff --git a/ecmascript/serializer/base_deserializer.h b/ecmascript/serializer/base_deserializer.h index fc53a0791a..5a6f167b05 100644 --- a/ecmascript/serializer/base_deserializer.h +++ b/ecmascript/serializer/base_deserializer.h @@ -108,6 +108,9 @@ private: void ResetNativePointerBuffer(uintptr_t objAddr, void *bufferPointer); void AllocateToDifferentSpaces(); + bool AllocateToDifferentLocalSpaces(bool isFirstAllocate); + void AllocateToDifferentSharedSpaces(); + void AllocateToDifferentCMCSpaces(); enum class RegionType : uint8_t { RegularRegion, PinRegion, @@ -115,17 +118,18 @@ private: void AllocateToRegularSpace(size_t regularSpaceSize); void AllocateToPinSpace(size_t pinSpaceSize); uintptr_t AllocateMultiCMCRegion(size_t spaceObjSize, size_t ®ionIndex, RegionType regionType); - void AllocateMultiRegion(SparseSpace *space, size_t spaceObjSize, size_t ®ionIndex, - SerializedObjectSpace spaceType); - void AllocateMultiNonmovableRegion(SparseSpace *space, size_t spaceObjSize, size_t ®ionIndex, - SerializedObjectSpace spaceType); + bool AllocateMultiRegion(SparseSpace *space, size_t spaceObjSize, size_t ®ionIndex, + SerializedObjectSpace spaceType, bool isFirstAllocate); + bool AllocateMultiNonmovableRegion(SparseSpace *space, size_t spaceObjSize, size_t ®ionIndex, + SerializedObjectSpace spaceType, bool isFirstAllocate); void AllocateMultiSharedRegion(SharedSparseSpace *space, size_t spaceObjSize, size_t ®ionIndex, SerializedObjectSpace spaceType); - void AllocateToOldSpace(size_t oldSpaceSize); - void AllocateToNonMovableSpace(size_t nonMovableSpaceSize); - void AllocateToMachineCodeSpace(size_t machineCodeSpaceSize); + bool AllocateToOldSpace(size_t oldSpaceSize, bool isFirstAllocate); + bool AllocateToNonMovableSpace(size_t nonMovableSpaceSize, bool isFirstAllocate); + bool AllocateToMachineCodeSpace(size_t machineCodeSpaceSize, bool isFirstAllocate); void AllocateToSharedOldSpace(size_t sOldSpaceSize); void AllocateToSharedNonMovableSpace(size_t sNonMovableSpaceSize); + bool GetAndResetWeak() { bool isWeak = isWeak_; diff --git a/ecmascript/serializer/tests/BUILD.gn b/ecmascript/serializer/tests/BUILD.gn index 4385a292d6..a9909f785a 100644 --- a/ecmascript/serializer/tests/BUILD.gn +++ b/ecmascript/serializer/tests/BUILD.gn @@ -62,11 +62,35 @@ host_unittest_action("InterOpSerializerTest") { deps += hiviewdfx_deps } +host_unittest_action("CMCSerializerTest") { + module_out_path = module_output_path + + sources = [ + # test file + "cmc_serializer_test.cpp", + ] + + configs = [ "$js_root:ecma_test_config" ] + + deps = [ "$js_root:libark_jsruntime_test" ] + + # hiviewdfx libraries + external_deps = hiviewdfx_ext_deps + external_deps += [ + "icu:shared_icui18n", + "icu:shared_icuuc", + "zlib:libz", + sdk_libc_secshared_dep, + ] + deps += hiviewdfx_deps +} + group("unittest") { testonly = true deps = [ ":SerializerTest", ":InterOpSerializerTest", + ":CMCSerializerTest", ] } @@ -75,12 +99,14 @@ group("host_unittest") { deps = [ ":SerializerTestAction", ":InterOpSerializerTest", + ":CMCSerializerTest", ] if (is_mac) { deps -= [ ":SerializerTestAction", ":InterOpSerializerTest", + ":CMCSerializerTest", ] } } diff --git a/ecmascript/serializer/tests/cmc_serializer_test.cpp b/ecmascript/serializer/tests/cmc_serializer_test.cpp new file mode 100644 index 0000000000..104f5b71b0 --- /dev/null +++ b/ecmascript/serializer/tests/cmc_serializer_test.cpp @@ -0,0 +1,130 @@ +/* + * 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 "ecmascript/ecma_vm.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +#include "ecmascript/serializer/value_serializer.h" +#include "ecmascript/serializer/base_deserializer.h" + +using namespace panda::ecmascript; +using namespace testing::ext; + +namespace panda::test { + +class CMCJSDeserializerTest { +public: + CMCJSDeserializerTest() : ecmaVm(nullptr), scope(nullptr), thread(nullptr) {} + + void Init() + { + JSRuntimeOptions options; + options.SetEnableCMCGC(true); + ecmaVm = JSNApi::CreateEcmaVM(options); + EXPECT_TRUE(ecmaVm != nullptr) << "Cannot create Runtime"; + thread = ecmaVm->GetJSThread(); + scope = new EcmaHandleScope(thread); + thread->ManagedCodeBegin(); + } + void Destroy() + { + thread->ManagedCodeEnd(); + delete scope; + scope = nullptr; + thread->ClearException(); + JSNApi::DestroyJSVM(ecmaVm); + } + + void SerializeTaggedArrayTest(SerializeData *data) + { + Init(); + BaseDeserializer deserializer(thread, data); + JSHandle res = deserializer.ReadValue(); + EXPECT_TRUE(!res.IsEmpty()); + EXPECT_TRUE(res->IsTaggedArray()); + Destroy(); + } +private: + EcmaVM *ecmaVm = nullptr; + EcmaHandleScope *scope = nullptr; + JSThread *thread = nullptr; +}; + +class CMCJSSerializerTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + JSRuntimeOptions options; + options.SetEnableCMCGC(true); + ecmaVm = JSNApi::CreateEcmaVM(options); + EXPECT_TRUE(ecmaVm != nullptr) << "Cannot create Runtime"; + thread = ecmaVm->GetJSThread(); + scope = new EcmaHandleScope(thread); + thread->ManagedCodeBegin(); + } + + void TearDown() override + { + thread->ManagedCodeEnd(); + delete scope; + scope = nullptr; + thread->ClearException(); + JSNApi::DestroyJSVM(ecmaVm); + } + + JSThread *thread {nullptr}; + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; +}; + +HWTEST_F_L0(CMCJSSerializerTest, SerializeTaggedArrayTest) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle array = factory->NewTaggedArray(10, JSTaggedValue::Hole()); // 10: array length + for (int i = 0; i < 5; i++) { // 5: new array cycles + JSHandle array1 = + factory->NewTaggedArray(10, JSTaggedValue::Hole()); // 10: array length + JSHandle array2 = + factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::NON_MOVABLE); // 10: array length + array->Set(thread, i, array1); + array->Set(thread, i + 1, array2); + } + ValueSerializer *serializer = new ValueSerializer(thread, false, true); + bool success = serializer->WriteValue(thread, JSHandle(array), + JSHandle(thread, JSTaggedValue::Undefined()), + JSHandle(thread, JSTaggedValue::Undefined())); + EXPECT_TRUE(success); + std::unique_ptr data = serializer->Release(); + CMCJSDeserializerTest jsDeserializerTest; + std::thread t1(&CMCJSDeserializerTest::SerializeTaggedArrayTest, jsDeserializerTest, data.release()); + ecmascript::ThreadSuspensionScope scope(thread); + t1.join(); + delete serializer; +}; +} // namespace panda::test diff --git a/ecmascript/serializer/tests/serializer_test.cpp b/ecmascript/serializer/tests/serializer_test.cpp index 8773cf217a..28b59bd25b 100644 --- a/ecmascript/serializer/tests/serializer_test.cpp +++ b/ecmascript/serializer/tests/serializer_test.cpp @@ -669,6 +669,50 @@ public: Destroy(); } + void SerializeOldObjOOMTest(SerializeData *data) + { + Init(); + ObjectFactory *factory = ecmaVm->GetFactory(); + BaseDeserializer deserializer(thread, data); + if (ecmaVm->GetHeap()->GetHeapLimitSize() >= 448_MB) { // 448_MB: default heap size + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // allocate ~400MB array + for (int i = 0; i < 1450; i++) { // 1450: new array cycles + [[maybe_unused]] JSHandle element = + factory->NewTaggedArray(100 * 1024 / 8, JSTaggedValue::Hole()); // 100 * 1024 / 8: array length + [[maybe_unused]] JSHandle property = + factory->NewTaggedArray(100 * 1024 / 8, JSTaggedValue::Hole()); // 100 * 1024 / 8: array length + } + } + JSHandle res = deserializer.ReadValue(); + EXPECT_TRUE(!res.IsEmpty()); + EXPECT_TRUE(res->IsTaggedArray()); + Destroy(); + } + + void SerializeNonMoveObjOOMTest(SerializeData *data) + { + Init(); + ObjectFactory *factory = ecmaVm->GetFactory(); + BaseDeserializer deserializer(thread, data); + if (ecmaVm->GetHeap()->GetHeapLimitSize() >= 448_MB) { // 448_MB: default heap size + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // allocate ~60MB array + for (int i = 0; i < 240; i++) { // 240: new array cycles + [[maybe_unused]] JSHandle element = + factory->NewTaggedArray(100 * 1024 / 8, JSTaggedValue::Hole(), // 100 * 1024 / 8: array length + MemSpaceType::NON_MOVABLE); + [[maybe_unused]] JSHandle property = + factory->NewTaggedArray(100 * 1024 / 8, JSTaggedValue::Hole(), // 100 * 1024 / 8: array length + MemSpaceType::NON_MOVABLE); + } + } + JSHandle res = deserializer.ReadValue(); + EXPECT_TRUE(!res.IsEmpty()); + EXPECT_TRUE(res->IsTaggedArray()); + Destroy(); + } + void JSSharedSetBasicTest1(SerializeData *data) { Init(); @@ -1260,6 +1304,51 @@ public: EcmaHandleScope *scope {nullptr}; }; +HWTEST_F_L0(JSSerializerTest, SerializeOldObjOOMTest) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle array = factory->NewTaggedArray(80, JSTaggedValue::Hole()); + for (int i = 0; i < 80; i++) { + JSHandle element = + factory->NewTaggedArray(100 * 1024 / 8, JSTaggedValue::Hole()); // 100 * 1024 / 8: array length + array->Set(thread, i, element); + } + ValueSerializer *serializer = new ValueSerializer(thread, false, true); + bool success = serializer->WriteValue(thread, JSHandle(array), + JSHandle(thread, JSTaggedValue::Undefined()), + JSHandle(thread, JSTaggedValue::Undefined())); + EXPECT_TRUE(success); + std::unique_ptr data = serializer->Release(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::SerializeOldObjOOMTest, jsDeserializerTest, data.release()); + ecmascript::ThreadSuspensionScope scope(thread); + t1.join(); + delete serializer; +}; + +HWTEST_F_L0(JSSerializerTest, SerializeNonMoveObjOOMTest) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle array = factory->NewTaggedArray(80, JSTaggedValue::Hole()); + for (int i = 0; i < 80; i++) { + JSHandle element = + factory->NewTaggedArray(100 * 1024 / 8, JSTaggedValue::Hole(), // 100 * 1024 / 8: array length + MemSpaceType::NON_MOVABLE); + array->Set(thread, i, element); + } + ValueSerializer *serializer = new ValueSerializer(thread, false, true); + bool success = serializer->WriteValue(thread, JSHandle(array), + JSHandle(thread, JSTaggedValue::Undefined()), + JSHandle(thread, JSTaggedValue::Undefined())); + EXPECT_TRUE(success); + std::unique_ptr data = serializer->Release(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::SerializeNonMoveObjOOMTest, jsDeserializerTest, data.release()); + ecmascript::ThreadSuspensionScope scope(thread); + t1.join(); + delete serializer; +}; + HWTEST_F_L0(JSSerializerTest, SerializeJSSpecialValue) { ValueSerializer *serializer = new ValueSerializer(thread); diff --git a/test/resource/js_runtime/ohos_test.xml b/test/resource/js_runtime/ohos_test.xml index c910cde7ce..2883676157 100755 --- a/test/resource/js_runtime/ohos_test.xml +++ b/test/resource/js_runtime/ohos_test.xml @@ -933,6 +933,11 @@