* 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 "ecmascript/builtins/builtins_ark_tools.h"
#include "ecmascript/checkpoint/thread_state_transition.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/mem/full_gc.h"
#include "ecmascript/object_factory-inl.h"
#include "ecmascript/mem/concurrent_marker.h"
#include "ecmascript/mem/partial_gc.h"
#include "ecmascript/serializer/serialize_chunk.h"
#include "ecmascript/tests/ecma_test_common.h"
#include "ecmascript/string/external_string.h"
using namespace panda;
using namespace panda::ecmascript;
namespace panda::test {
class TestData {
public:
uint32_t GetCount()
{
return count_;
}
void AddCount()
{
count_++;
}
private:
uint32_t count_ {0U};
};
class ExternalData {
public:
std::string GetName()
{
return name_;
}
std::string GetContext()
{
return context_;
}
void SetName(std::string name)
{
name_ = name;
}
void SetContext(std::string context)
{
context_ = context;
}
private:
std::string name_;
std::string context_;
};
class GCTest : public BaseTestWithScope<false> {
public:
static constexpr uint32_t TYPE_INFO_SIZE = 4;
static constexpr uint32_t UTF8_CACHED_DATA_SIZE = 4;
static constexpr uint32_t UTF16_CACHED_DATA_SIZE = 8;
static constexpr uint32_t UTF16_STRING_LENGTH = UTF16_CACHED_DATA_SIZE / sizeof(uint16_t);
static inline void CallBackFn(void *data, void *hint)
{
delete static_cast<ExternalData *>(data);
delete static_cast<TestData *>(hint);
}
void SetUp() override
{
JSRuntimeOptions options;
instance = JSNApi::CreateEcmaVM(options);
ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
thread = instance->GetJSThread();
thread->ManagedCodeBegin();
scope = new EcmaHandleScope(thread);
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::ENABLE);
heap->GetSweeper()->EnableConcurrentSweep(EnableConcurrentSweepType::ENABLE);
}
};
HWTEST_F_L0(GCTest, ExternalStringGCAddStringTest)
{
auto sHeap = SharedHeap::GetInstance();
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 0);
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
TestData *hint = new TestData();
ExternalData *data = new ExternalData();
EcmaString *ecmaString = EcmaStringAccessor::CreateFromExternalResource(
instance, reinterpret_cast<void *>(data), UTF8_CACHED_DATA_SIZE,
true, GCTest::CallBackFn, reinterpret_cast<void *>(hint));
JSHandle<EcmaString> ecmaStrHandle(thread, ecmaString);
EXPECT_NE(ecmaString, nullptr);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 1);
}
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 0);
};
HWTEST_F_L0(GCTest, ExternalStringGCAddStringHandleTest)
{
auto sHeap = SharedHeap::GetInstance();
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 0);
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
TestData *hint = new TestData();
ExternalData *data = new ExternalData();
EcmaString *ecmaString = EcmaStringAccessor::CreateFromExternalResource(
instance, reinterpret_cast<void *>(data), UTF8_CACHED_DATA_SIZE,
true, GCTest::CallBackFn, reinterpret_cast<void *>(hint));
JSHandle<EcmaString> ecmaStrHandle(thread, ecmaString);
EXPECT_NE(ecmaString, nullptr);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 1);
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 1);
}
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 0);
};
HWTEST_F_L0(GCTest, ExternalStringSharedFullGCCallBackSaveTest)
{
auto sHeap = SharedHeap::GetInstance();
TestData *hint = new TestData();
auto callback = [] (void* data, void* hint) -> void {
reinterpret_cast<TestData *>(hint)->AddCount();
delete static_cast<ExternalData *>(data);
};
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
size_t oldCount = sHeap->GetExternalStringTable()->GetListSize();
EXPECT_EQ(oldCount, 0);
EXPECT_EQ(hint->GetCount(), 0);
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
for (int i = 0; i < 32; i++) {
ExternalData *data = new ExternalData();
EcmaString *ecmaString = EcmaStringAccessor::CreateFromExternalResource(
instance, reinterpret_cast<void *>(data), UTF8_CACHED_DATA_SIZE,
true, callback, reinterpret_cast<void *>(hint));
JSHandle<EcmaString> ecmaStrHandle(thread, ecmaString);
CachedExternalEcmaString *cachedEcmaString = CachedExternalEcmaString::Cast(ecmaString);
CachedExternalString *cachedExternalString = cachedEcmaString->ToCachedExternalString();
EXPECT_NE(cachedExternalString, nullptr);
}
EXPECT_EQ(hint->GetCount(), 0);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 32);
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
EXPECT_EQ(hint->GetCount(), 0);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 32);
}
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
EXPECT_EQ(hint->GetCount(), 32);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 0);
delete hint;
};
HWTEST_F_L0(GCTest, ExternalStringSharedGCCallBackSaveTest)
{
auto sHeap = SharedHeap::GetInstance();
TestData *hint = new TestData();
auto callback = [] (void* data, void* hint) -> void {
reinterpret_cast<TestData *>(hint)->AddCount();
delete static_cast<ExternalData *>(data);
};
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
size_t oldCount = sHeap->GetExternalStringTable()->GetListSize();
EXPECT_EQ(oldCount, 0);
EXPECT_EQ(hint->GetCount(), 0);
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
for (int i = 0; i < 32; i++) {
ExternalData *data = new ExternalData();
EcmaString *ecmaString = EcmaStringAccessor::CreateFromExternalResource(
instance, reinterpret_cast<void *>(data), UTF8_CACHED_DATA_SIZE,
true, callback, reinterpret_cast<void *>(hint));
JSHandle<EcmaString> ecmaStrHandle(thread, ecmaString);
CachedExternalEcmaString *cachedEcmaString = CachedExternalEcmaString::Cast(ecmaString);
CachedExternalString *cachedExternalString = cachedEcmaString->ToCachedExternalString();
EXPECT_NE(cachedExternalString, nullptr);
}
EXPECT_EQ(hint->GetCount(), 0);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 32);
sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
EXPECT_EQ(hint->GetCount(), 0);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 32);
}
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
EXPECT_EQ(hint->GetCount(), 32);
EXPECT_EQ(sHeap->GetExternalStringTable()->GetListSize(), 0);
delete hint;
};
}