* Copyright (c) 2026 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_handle_scope.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tests/test_helper.h"
#include "gtest/gtest.h"
using namespace panda::ecmascript;
namespace panda::test {
class EcmaHandleScopeTest : public BaseTestWithScope<false> {
};
HWTEST_F_L0(EcmaHandleScopeTest, NestingLevels_SingleAndSequential)
{
auto initialLevel = instance->GetOpenHandleScopes();
{
EcmaHandleScope scope(thread);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
}
{
EcmaHandleScope scope1(thread);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
}
{
EcmaHandleScope scope2(thread);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
}
HWTEST_F_L0(EcmaHandleScopeTest, NestingLevels_DoubleNested)
{
auto initialLevel = instance->GetOpenHandleScopes();
{
EcmaHandleScope outerScope(thread);
[[maybe_unused]] auto outerHandle =
EcmaHandleScope::NewHandle(thread, JSTaggedValue::Undefined().GetRawData());
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
{
EcmaHandleScope innerScope(thread);
[[maybe_unused]] auto innerHandle =
EcmaHandleScope::NewHandle(thread, JSTaggedValue::Hole().GetRawData());
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 2);
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
}
HWTEST_F_L0(EcmaHandleScopeTest, NestingLevels_TripleNested)
{
auto initialLevel = instance->GetOpenHandleScopes();
{
EcmaHandleScope s1(thread);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
{
EcmaHandleScope s2(thread);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 2);
{
EcmaHandleScope s3(thread);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 3);
[[maybe_unused]] auto h1 =
EcmaHandleScope::NewHandle(thread, JSTaggedValue::Undefined().GetRawData());
[[maybe_unused]] auto h2 =
EcmaHandleScope::NewHandle(thread, JSTaggedValue::Hole().GetRawData());
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 2);
[[maybe_unused]] auto h3 =
EcmaHandleScope::NewHandle(thread, JSTaggedValue::True().GetRawData());
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
}
HWTEST_F_L0(EcmaHandleScopeTest, NestingLevels_Interleaved)
{
auto initialLevel = instance->GetOpenHandleScopes();
{
EcmaHandleScope a1(thread);
{
EcmaHandleScope b1(thread);
{
EcmaHandleScope c1(thread);
}
EcmaHandleScope c2(thread);
}
EcmaHandleScope b2(thread);
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
}
HWTEST_F_L0(EcmaHandleScopeTest, ManualHeapAllocation)
{
auto initialLevel = instance->GetOpenHandleScopes();
EcmaHandleScope *scope1 = new EcmaHandleScope(thread);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
EcmaHandleScope *scope2 = new EcmaHandleScope(thread);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 2);
delete scope1;
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
delete scope2;
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
}
HWTEST_F_L0(EcmaHandleScopeTest, CreateHandles)
{
auto initialLevel = instance->GetOpenHandleScopes();
EXPECT_GE(initialLevel, 1);
uintptr_t h1 = EcmaHandleScope::NewHandle(thread, JSTaggedValue::Undefined().GetRawData());
EXPECT_NE(h1, 0);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
uintptr_t h2 = EcmaHandleScope::NewPrimitiveHandle(thread, JSTaggedValue::True().GetRawData());
EXPECT_NE(h2, 0);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
constexpr int handleCount = 10;
for (int i = 0; i < handleCount; i++) {
uintptr_t handle = EcmaHandleScope::NewHandle(thread, JSTaggedValue::Hole().GetRawData());
EXPECT_NE(handle, 0);
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
{
EcmaHandleScope scope(thread);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
[[maybe_unused]] uintptr_t h3 =
EcmaHandleScope::NewHandle(thread, JSTaggedValue::Null().GetRawData());
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
[[maybe_unused]] uintptr_t h4 =
EcmaHandleScope::NewPrimitiveHandle(thread, JSTaggedValue::Hole().GetRawData());
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
{
EcmaHandleScope scope1(thread);
[[maybe_unused]] uintptr_t h5 =
EcmaHandleScope::NewHandle(thread, JSTaggedValue::Undefined().GetRawData());
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
}
uintptr_t hLeaked = EcmaHandleScope::NewHandle(thread, JSTaggedValue::Null().GetRawData());
EXPECT_NE(hLeaked, 0);
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
}
HWTEST_F_L0(EcmaHandleScopeTest, NestedScopeWithOuterHandle)
{
auto initialLevel = instance->GetOpenHandleScopes();
uintptr_t outerHandle = 0;
{
EcmaHandleScope outerScope(thread);
outerHandle = EcmaHandleScope::NewHandle(thread, JSTaggedValue::Undefined().GetRawData());
{
EcmaHandleScope innerScope(thread);
[[maybe_unused]] uintptr_t innerHandle =
EcmaHandleScope::NewHandle(thread, JSTaggedValue::True().GetRawData());
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 2);
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel + 1);
}
EXPECT_EQ(instance->GetOpenHandleScopes(), initialLevel);
uintptr_t leakedHandle = EcmaHandleScope::NewHandle(thread, JSTaggedValue::Null().GetRawData());
EXPECT_NE(leakedHandle, 0);
}
}