* Copyright (c) 2023-2024 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 "ecmascript/ecma_global_storage.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_array.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tests/test_helper.h"
using namespace panda::ecmascript;
using namespace testing::ext;
constexpr int32_t INT_VALUE_0 = 0;
constexpr int32_t INT_VALUE_1 = 1;
constexpr int32_t INT_VALUE_2 = 2;
namespace panda::test {
class EcmaGlobalStorageTest : public BaseTestWithScope<false> {
public:
void CalculateGlobalNodeCount(uint64_t &allGlobalNodeCount, uint64_t &normalGlobalNodeCount,
EcmaGlobalStorage<Node> *globalStorage)
{
globalStorage->SetNodeKind(NodeKind::UNIFIED_NODE);
ASSERT(globalStorage->GetNodeKind() == NodeKind::UNIFIED_NODE);
globalStorage->IterateUsageGlobal([&normalGlobalNodeCount] ([[maybe_unused]] Node *node) {
normalGlobalNodeCount++;
});
globalStorage->SetNodeKind(NodeKind::NORMAL_NODE);
ASSERT(globalStorage->GetNodeKind() == NodeKind::NORMAL_NODE);
globalStorage->IterateUsageGlobal([&allGlobalNodeCount] ([[maybe_unused]] Node *node) {
allGlobalNodeCount++;
});
}
};
HWTEST_F_L0(EcmaGlobalStorageTest, XRefGlobalNodes)
{
EcmaVM *vm = thread->GetEcmaVM();
JSNApi::InitHybridVMEnv(vm);
JSHandle<TaggedArray> weakRefArray = vm->GetFactory()->NewTaggedArray(INT_VALUE_2, JSTaggedValue::Hole());
uintptr_t xRefArrayAddress;
vm->SetEnableForceGC(false);
{
[[maybe_unused]] EcmaHandleScope scope(thread);
JSHandle<JSTaggedValue> xRefArray = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_1));
JSHandle<JSTaggedValue> normalArray = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_2));
xRefArrayAddress = thread->NewXRefGlobalHandle(xRefArray.GetTaggedType());
weakRefArray->Set(thread, INT_VALUE_0, xRefArray.GetTaggedValue().CreateAndGetWeakRef());
weakRefArray->Set(thread, INT_VALUE_1, normalArray.GetTaggedValue().CreateAndGetWeakRef());
}
vm->CollectGarbage(TriggerGCType::FULL_GC);
EXPECT_TRUE(!weakRefArray->Get(thread, INT_VALUE_0).IsUndefined());
EXPECT_TRUE(weakRefArray->Get(thread, INT_VALUE_1).IsUndefined());
thread->DisposeXRefGlobalHandle(xRefArrayAddress);
vm->CollectGarbage(TriggerGCType::FULL_GC);
vm->SetEnableForceGC(true);
EXPECT_TRUE(weakRefArray->Get(thread, INT_VALUE_0).IsUndefined());
}
HWTEST_F_L0(EcmaGlobalStorageTest, SetNodeKind)
{
EcmaVM *vm = thread->GetEcmaVM();
JSNApi::InitHybridVMEnv(vm);
JSHandle<TaggedArray> weakRefArray = vm->GetFactory()->NewTaggedArray(INT_VALUE_1, JSTaggedValue::Hole());
vm->SetEnableForceGC(false);
uintptr_t xrefAddr = 0;
{
[[maybe_unused]] EcmaHandleScope scope(thread);
JSHandle<JSTaggedValue> xRefArray = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_1));
xrefAddr = thread->NewXRefGlobalHandle(xRefArray.GetTaggedType());
weakRefArray->Set(thread, INT_VALUE_0, xRefArray.GetTaggedValue().CreateAndGetWeakRef());
}
thread->SetNodeKind(NodeKind::UNIFIED_NODE);
vm->CollectGarbage(TriggerGCType::FULL_GC);
thread->SetNodeKind(NodeKind::NORMAL_NODE);
thread->DisposeXRefGlobalHandle(xrefAddr);
vm->SetEnableForceGC(true);
EXPECT_TRUE(weakRefArray->Get(thread, INT_VALUE_0).IsUndefined());
}
HWTEST_F_L0(EcmaGlobalStorageTest, EcmaGlobalStorage)
{
EcmaVM *vm = thread->GetEcmaVM();
auto chunk = vm->GetChunk();
EcmaGlobalStorage<Node> *globalStorage =
chunk->New<EcmaGlobalStorage<Node>>(nullptr, vm->GetNativeAreaAllocator());
uintptr_t xRefArrayAddress;
uintptr_t normalArrayAddress;
JSHandle<JSTaggedValue> xRefArray = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_1));
JSHandle<JSTaggedValue> normalArray = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_2));
uint64_t allGlobalNodeCountBefore = INT_VALUE_0;
uint64_t normalGlobalNodeCountBefore = INT_VALUE_0;
ASSERT(globalStorage->GetNodeKind() == NodeKind::NORMAL_NODE);
CalculateGlobalNodeCount(allGlobalNodeCountBefore, normalGlobalNodeCountBefore, globalStorage);
EXPECT_TRUE(normalGlobalNodeCountBefore == allGlobalNodeCountBefore);
uint64_t allGlobalNodeCountAfter = INT_VALUE_0;
uint64_t normalGlobalNodeCountAfter = INT_VALUE_0;
xRefArrayAddress = globalStorage->NewGlobalHandle<NodeKind::UNIFIED_NODE>(xRefArray.GetTaggedType());
CalculateGlobalNodeCount(allGlobalNodeCountAfter, normalGlobalNodeCountAfter, globalStorage);
EXPECT_TRUE(allGlobalNodeCountAfter == allGlobalNodeCountBefore + INT_VALUE_1);
EXPECT_TRUE(normalGlobalNodeCountAfter == normalGlobalNodeCountBefore);
allGlobalNodeCountAfter = INT_VALUE_0;
normalGlobalNodeCountAfter = INT_VALUE_0;
normalArrayAddress = globalStorage->NewGlobalHandle<NodeKind::NORMAL_NODE>(normalArray.GetTaggedType());
CalculateGlobalNodeCount(allGlobalNodeCountAfter, normalGlobalNodeCountAfter, globalStorage);
EXPECT_TRUE(allGlobalNodeCountAfter == allGlobalNodeCountBefore + INT_VALUE_2);
EXPECT_TRUE(normalGlobalNodeCountAfter == normalGlobalNodeCountBefore + INT_VALUE_1);
allGlobalNodeCountAfter = INT_VALUE_0;
normalGlobalNodeCountAfter = INT_VALUE_0;
globalStorage->DisposeGlobalHandle<NodeKind::UNIFIED_NODE>(xRefArrayAddress);
CalculateGlobalNodeCount(allGlobalNodeCountAfter, normalGlobalNodeCountAfter, globalStorage);
EXPECT_TRUE(allGlobalNodeCountAfter == allGlobalNodeCountBefore + INT_VALUE_1);
EXPECT_TRUE(normalGlobalNodeCountAfter == normalGlobalNodeCountBefore + INT_VALUE_1);
allGlobalNodeCountAfter = INT_VALUE_0;
normalGlobalNodeCountAfter = INT_VALUE_0;
globalStorage->DisposeGlobalHandle<NodeKind::NORMAL_NODE>(normalArrayAddress);
CalculateGlobalNodeCount(allGlobalNodeCountAfter, normalGlobalNodeCountAfter, globalStorage);
EXPECT_TRUE(allGlobalNodeCountAfter == allGlobalNodeCountBefore);
EXPECT_TRUE(normalGlobalNodeCountAfter == normalGlobalNodeCountBefore);
chunk->Delete(globalStorage);
}
HWTEST_F(EcmaGlobalStorageTest, EcmaGlobalStorageYoungGC, TestSize.Level0)
{
EcmaVM *vm = thread->GetEcmaVM();
auto chunk = vm->GetChunk();
EcmaGlobalStorage<Node> *globalStorage =
chunk->New<EcmaGlobalStorage<Node>>(nullptr, vm->GetNativeAreaAllocator());
[[maybe_unused]] JSHandle<JSTaggedValue> xRefArray = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_1));
[[maybe_unused]] JSHandle<JSTaggedValue> normalArray = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_2));
uint64_t allGlobalNodeCountBefore = INT_VALUE_0;
uint64_t normalGlobalNodeCountBefore = INT_VALUE_0;
ASSERT(globalStorage->GetNodeKind() == NodeKind::NORMAL_NODE);
auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->CollectGarbage(TriggerGCType::YOUNG_GC);
CalculateGlobalNodeCount(allGlobalNodeCountBefore, normalGlobalNodeCountBefore, globalStorage);
EXPECT_TRUE(normalGlobalNodeCountBefore == allGlobalNodeCountBefore);
chunk->Delete(globalStorage);
}
HWTEST_F(EcmaGlobalStorageTest, EcmaGlobalStorageFullGC, TestSize.Level0)
{
EcmaVM *vm = thread->GetEcmaVM();
auto chunk = vm->GetChunk();
EcmaGlobalStorage<Node> *globalStorage =
chunk->New<EcmaGlobalStorage<Node>>(nullptr, vm->GetNativeAreaAllocator());
[[maybe_unused]] JSHandle<JSTaggedValue> xRefArray = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_1));
[[maybe_unused]] JSHandle<JSTaggedValue> normalArray = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_2));
uint64_t allGlobalNodeCountBefore = INT_VALUE_0;
uint64_t normalGlobalNodeCountBefore = INT_VALUE_0;
ASSERT(globalStorage->GetNodeKind() == NodeKind::NORMAL_NODE);
auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->CollectGarbage(TriggerGCType::FULL_GC);
CalculateGlobalNodeCount(allGlobalNodeCountBefore, normalGlobalNodeCountBefore, globalStorage);
EXPECT_TRUE(normalGlobalNodeCountBefore == allGlobalNodeCountBefore);
chunk->Delete(globalStorage);
}
};