1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/cross_vm/unified_gc/unified_gc_marker.h"
17 #include "ecmascript/tests/unified_gc_test_helper.h"
18 #include "ecmascript/mem/concurrent_marker.h"
19 #include "ecmascript/mem/region-inl.h"
20
21 using namespace panda::ecmascript;
22
23 namespace panda::test {
24 class UnifiedGCTest : public BaseTestWithScope<false> {};
25
26 class UnifiedGCVerificationTest : public UnifiedGCTest {
27 public:
SetUp()28 void SetUp() override
29 {
30 JSRuntimeOptions options;
31 options.SetArkProperties(options.GetArkProperties() | ArkProperties::ENABLE_HEAP_VERIFY);
32 instance = JSNApi::CreateEcmaVM(options);
33 ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
34 thread = instance->GetJSThread();
35 thread->ManagedCodeBegin();
36 scope = new EcmaHandleScope(thread);
37 }
38 };
39
HWTEST_F_L0(UnifiedGCTest,UnifiedGCMarkRootsScopeTest)40 HWTEST_F_L0(UnifiedGCTest, UnifiedGCMarkRootsScopeTest)
41 {
42 EcmaVM *vm = thread->GetEcmaVM();
43 JSHandle<TaggedArray> weakRefArray = vm->GetFactory()->NewTaggedArray(INT_VALUE_2, JSTaggedValue::Hole());
44 vm->SetEnableForceGC(false);
45 {
46 [[maybe_unused]] EcmaHandleScope ecmaHandleScope(thread);
47 JSHandle<JSTaggedValue> arrayInXRefRoot = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_1));
48 JSHandle<JSTaggedValue> arrayInRoot = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_1));
49 thread->NewXRefGlobalHandle(arrayInXRefRoot.GetTaggedType());
50 thread->NewGlobalHandle(arrayInRoot.GetTaggedType());
51 weakRefArray->Set(thread, INT_VALUE_0, arrayInXRefRoot.GetTaggedValue().CreateAndGetWeakRef());
52 weakRefArray->Set(thread, INT_VALUE_1, arrayInRoot.GetTaggedValue().CreateAndGetWeakRef());
53 }
54 [[maybe_unused]] UnifiedGCMarkRootsScope unifiedGCMarkRootsScope(thread);
55 vm->CollectGarbage(TriggerGCType::FULL_GC);
56 EXPECT_TRUE(weakRefArray->Get(INT_VALUE_0).IsUndefined());
57 EXPECT_TRUE(!weakRefArray->Get(INT_VALUE_1).IsUndefined());
58 vm->SetEnableForceGC(true);
59 }
60
61 #ifdef PANDA_JS_ETS_HYBRID_MODE
HWTEST_F_L0(UnifiedGCTest,DoHandshakeTest)62 HWTEST_F_L0(UnifiedGCTest, DoHandshakeTest)
63 {
64 EcmaVM *vm = thread->GetEcmaVM();
65 // Construct fake stsVMInterface and ecmaVMInterface for test, ecmaVMInterface used for STSVM so can be nullptr.
66 auto stsVMInterface = std::make_unique<STSVMInterfaceTest>();
67 void *ecmaVMInterface = nullptr;
68 CrossVMOperator::DoHandshake(vm, stsVMInterface.get(), &ecmaVMInterface);
69
70 EXPECT_TRUE(vm->GetCrossVMOperator()->GetSTSVMInterface() == stsVMInterface.get());
71 EXPECT_TRUE(vm->GetCrossVMOperator()->GetEcmaVMInterface() ==
72 static_cast<arkplatform::EcmaVMInterface *>(ecmaVMInterface));
73 }
74
HWTEST_F_L0(UnifiedGCTest,TriggerUnifiedGCMarkTest1)75 HWTEST_F_L0(UnifiedGCTest, TriggerUnifiedGCMarkTest1)
76 {
77 EcmaVM *vm = thread->GetEcmaVM();
78 auto stsVMInterface = std::make_unique<STSVMInterfaceTest>();
79 void *ecmaVMInterface = nullptr;
80 CrossVMOperator::DoHandshake(vm, stsVMInterface.get(), &ecmaVMInterface);
81
82 CrossReferenceObjectBuilder CrossReferenceObject(vm, thread);
83 SharedHeap::GetInstance()->TriggerUnifiedGCMark<TriggerGCType::UNIFIED_GC, GCReason::CROSSREF_CAUSE>(thread);
84 while (!thread->HasSuspendRequest()) {}
85 thread->WaitSuspension();
86
87 CrossReferenceObject.CheckResultAfterUnifiedGC();
88 }
89
HWTEST_F_L0(UnifiedGCTest,TriggerUnifiedGCMarkTest2)90 HWTEST_F_L0(UnifiedGCTest, TriggerUnifiedGCMarkTest2)
91 {
92 EcmaVM *vm = thread->GetEcmaVM();
93 auto stsVMInterface = std::make_unique<STSVMInterfaceTest>();
94 void *ecmaVMInterface = nullptr;
95 CrossVMOperator::DoHandshake(vm, stsVMInterface.get(), &ecmaVMInterface);
96
97 auto heap = vm->GetHeap();
98 heap->GetConcurrentMarker()->Mark();
99 SharedHeap::GetInstance()->TriggerUnifiedGCMark<TriggerGCType::UNIFIED_GC, GCReason::CROSSREF_CAUSE>(thread);
100 while (!thread->HasSuspendRequest()) {}
101 thread->WaitSuspension();
102 }
103
HWTEST_F_L0(UnifiedGCTest,TriggerUnifiedGCMarkTest3)104 HWTEST_F_L0(UnifiedGCTest, TriggerUnifiedGCMarkTest3)
105 {
106 EcmaVM *vm = thread->GetEcmaVM();
107 auto stsVMInterface = std::make_unique<STSVMInterfaceTest>();
108 void *ecmaVMInterface = nullptr;
109 CrossVMOperator::DoHandshake(vm, stsVMInterface.get(), &ecmaVMInterface);
110
111 SharedHeap::GetInstance()->TriggerUnifiedGCMark<TriggerGCType::UNIFIED_GC, GCReason::CROSSREF_CAUSE>(thread);
112 while (!thread->HasSuspendRequest()) {}
113 thread->WaitSuspension();
114 vm->CollectGarbage(TriggerGCType::YOUNG_GC);
115 }
116
IsObjectMarked(TaggedObject * object)117 static inline bool IsObjectMarked(TaggedObject *object)
118 {
119 Region *objectRegion = Region::ObjectAddressToRange(object);
120 return objectRegion->Test(object);
121 }
122
HWTEST_F_L0(UnifiedGCTest,IsObjectAliveTest)123 HWTEST_F_L0(UnifiedGCTest, IsObjectAliveTest)
124 {
125 EcmaVM *vm = thread->GetEcmaVM();
126 vm->SetEnableForceGC(false);
127
128 JSHandle<JSTaggedValue> arrayInXRefRoot = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_1));
129 JSHandle<JSTaggedValue> arrayRefByXRefRoot = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_2));
130 thread->NewXRefGlobalHandle(arrayInXRefRoot.GetTaggedType());
131 JSArray::FastSetPropertyByValue(thread, arrayInXRefRoot, INT_VALUE_0, arrayRefByXRefRoot);
132
133 auto oldArray = vm->GetFactory()->NewFromASCII("123");
134 EXPECT_TRUE(!panda::JSNApi::IsObjectAlive(vm, reinterpret_cast<uintptr_t>(&oldArray)));
135 }
136
HWTEST_F_L0(UnifiedGCTest,IsValidHeapObjectTest)137 HWTEST_F_L0(UnifiedGCTest, IsValidHeapObjectTest)
138 {
139 EcmaVM *vm = thread->GetEcmaVM();
140 vm->SetEnableForceGC(false);
141
142 JSHandle<JSTaggedValue> arrayInXRefRoot = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_1));
143 JSHandle<JSTaggedValue> arrayRefByXRefRoot = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_2));
144 thread->NewXRefGlobalHandle(arrayInXRefRoot.GetTaggedType());
145 JSArray::FastSetPropertyByValue(thread, arrayInXRefRoot, INT_VALUE_0, arrayRefByXRefRoot);
146
147 auto oldArray = vm->GetFactory()->NewFromASCII("123");
148 EXPECT_TRUE(!panda::JSNApi::IsValidHeapObject(vm, reinterpret_cast<uintptr_t>(&oldArray)));
149 }
150
HWTEST_F_L0(UnifiedGCTest,MarkFromObjectTest)151 HWTEST_F_L0(UnifiedGCTest, MarkFromObjectTest)
152 {
153 EcmaVM *vm = thread->GetEcmaVM();
154 vm->SetEnableForceGC(false);
155
156 JSHandle<JSTaggedValue> arrayInXRefRoot = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_1));
157 JSHandle<JSTaggedValue> arrayRefByXRefRoot = JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_2));
158 thread->NewXRefGlobalHandle(arrayInXRefRoot.GetTaggedType());
159 JSArray::FastSetPropertyByValue(thread, arrayInXRefRoot, INT_VALUE_0, arrayRefByXRefRoot);
160
161 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
162 vm->GetCrossVMOperator()->MarkFromObject(arrayInXRefRoot->GetRawData());
163 heap->WaitRunningTaskFinished();
164 EXPECT_TRUE(IsObjectMarked(arrayInXRefRoot->GetHeapObject()));
165 EXPECT_TRUE(IsObjectMarked(arrayRefByXRefRoot->GetHeapObject()));
166
167 // Clear mark bit
168 heap->Resume(TriggerGCType::UNIFIED_GC);
169 heap->WaitClearTaskFinished();
170 EXPECT_TRUE(!IsObjectMarked(arrayInXRefRoot->GetHeapObject()));
171 EXPECT_TRUE(!IsObjectMarked(arrayRefByXRefRoot->GetHeapObject()));
172 vm->SetEnableForceGC(true);
173 }
174
HWTEST_F_L0(UnifiedGCTest,TriggerUnifiedGCFailTest)175 HWTEST_F_L0(UnifiedGCTest, TriggerUnifiedGCFailTest)
176 {
177 EcmaVM *vm = thread->GetEcmaVM();
178 auto stsVMInterface = std::make_unique<STSVMInterfaceTest>();
179 void *ecmaVMInterface = nullptr;
180 CrossVMOperator::DoHandshake(vm, stsVMInterface.get(), &ecmaVMInterface);
181
182 CrossReferenceObjectBuilder CrossReferenceObject(vm, thread);
183 SharedHeap::GetInstance()->TriggerUnifiedGCMark<TriggerGCType::UNIFIED_GC, GCReason::CROSSREF_CAUSE>(thread);
184 vm->GetCrossVMOperator()->GetEcmaVMInterface()->NotifyXGCInterruption();
185 while (!thread->HasSuspendRequest()) {}
186 thread->WaitSuspension();
187 CrossReferenceObject.CheckResultAfterUnifiedGCTriggerFail();
188 }
189
HWTEST_F_L0(UnifiedGCVerificationTest,VerifyUnifiedGCMarkTest)190 HWTEST_F_L0(UnifiedGCVerificationTest, VerifyUnifiedGCMarkTest)
191 {
192 EcmaVM *vm = thread->GetEcmaVM();
193 vm->SetEnableForceGC(false);
194 auto stsVMInterface = std::make_unique<STSVMInterfaceTest>();
195 void *ecmaVMInterface = nullptr;
196 CrossVMOperator::DoHandshake(vm, stsVMInterface.get(), &ecmaVMInterface);
197 SharedHeap::GetInstance()->TriggerUnifiedGCMark<TriggerGCType::UNIFIED_GC, GCReason::CROSSREF_CAUSE>(thread);
198 while (!thread->HasSuspendRequest()) {}
199 thread->WaitSuspension();
200 vm->SetEnableForceGC(true);
201 }
202 #endif // PANDA_JS_ETS_HYBRID_MODE
203 } // namespace panda::test