1 /*
2 * Copyright (c) 2021 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/builtins/builtins_finalization_registry.h"
17 #include "ecmascript/ecma_vm.h"
18 #include "ecmascript/global_env.h"
19 #include "ecmascript/jobs/micro_job_queue.h"
20 #include "ecmascript/js_array.h"
21 #include "ecmascript/js_array_iterator.h"
22 #include "ecmascript/js_finalization_registry.h"
23 #include "ecmascript/js_handle.h"
24 #include "ecmascript/js_hclass.h"
25 #include "ecmascript/js_object-inl.h"
26 #include "ecmascript/js_tagged_value.h"
27 #include "ecmascript/js_tagged_value-inl.h"
28 #include "ecmascript/js_thread.h"
29
30 #include "ecmascript/object_factory.h"
31 #include "ecmascript/object_operator.h"
32 #include "ecmascript/tests/test_helper.h"
33
34 using namespace panda::ecmascript;
35 using namespace panda::ecmascript::builtins;
36 using BuiltinsBase = panda::ecmascript::base::BuiltinsBase;
37 static int testValue = 0;
38
39 namespace panda::test {
40 class BuiltinsFinalizationRegistryTest : public BaseTestWithScope<false> {
41 public:
42 class TestClass : public base::BuiltinsBase {
43 public:
cleanupCallback()44 static JSTaggedValue cleanupCallback()
45 {
46 ++testValue;
47 return JSTaggedValue::Undefined();
48 }
49 };
50 };
51
CreateFinalizationRegistryConstructor(JSThread *thread)52 JSTaggedValue CreateFinalizationRegistryConstructor(JSThread *thread)
53 {
54 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
55 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
56
57 JSHandle<JSFunction> finalizationRegistry(env->GetBuiltinsFinalizationRegistryFunction());
58 JSHandle<JSFunction> handleFunc = factory->NewJSFunction(
59 env, reinterpret_cast<void *>(BuiltinsFinalizationRegistryTest::TestClass::cleanupCallback));
60
61 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*finalizationRegistry), 6);
62 ecmaRuntimeCallInfo->SetFunction(finalizationRegistry.GetTaggedValue());
63 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
64 ecmaRuntimeCallInfo->SetCallArg(0, handleFunc.GetTaggedValue());
65
66 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
67 JSTaggedValue res = BuiltinsFinalizationRegistry::FinalizationRegistryConstructor(ecmaRuntimeCallInfo);
68 TestHelper::TearDownFrame(thread, prev);
69 return res;
70 }
71
72 // new FinalizationRegistry (cleanupCallback)
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, FinalizationRegistryConstructor)73 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, FinalizationRegistryConstructor)
74 {
75 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
76 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
77
78 JSHandle<JSFunction> finalizationRegistry(env->GetBuiltinsFinalizationRegistryFunction());
79 JSHandle<JSFunction> handleFunc = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::cleanupCallback));
80
81 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*finalizationRegistry), 6);
82 ecmaRuntimeCallInfo->SetFunction(finalizationRegistry.GetTaggedValue());
83 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
84 ecmaRuntimeCallInfo->SetCallArg(0, handleFunc.GetTaggedValue());
85
86 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
87 JSTaggedValue result = BuiltinsFinalizationRegistry::FinalizationRegistryConstructor(ecmaRuntimeCallInfo);
88 ASSERT_TRUE(result.IsECMAObject());
89 }
90
KeyValueCommon(JSThread* thread, JSHandle<JSTaggedValue>& target)91 void KeyValueCommon(JSThread* thread, JSHandle<JSTaggedValue>& target)
92 {
93 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
94 JSHandle<JSTaggedValue> key(factory->NewFromASCII("1"));
95 JSHandle<JSTaggedValue> value(thread, JSTaggedValue(1));
96 JSObject::SetProperty(thread, target, key, value);
97 }
98
Common(JSThread* thread)99 JSHandle<JSFinalizationRegistry> Common(JSThread* thread)
100 {
101 JSTaggedValue result = CreateFinalizationRegistryConstructor(thread);
102 JSHandle<JSFinalizationRegistry> jsfinalizationRegistry(thread, result);
103 return jsfinalizationRegistry;
104 }
105
RegisterUnRegisterCommon(JSThread *thread, JSHandle<JSFinalizationRegistry> &jsfinalizationRegistry, std::vector<JSTaggedValue> &args, int32_t maxArg, bool unRegister = false)106 void RegisterUnRegisterCommon(JSThread *thread, JSHandle<JSFinalizationRegistry> &jsfinalizationRegistry,
107 std::vector<JSTaggedValue> &args, int32_t maxArg, bool unRegister = false)
108 {
109 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), maxArg);
110 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
111 ecmaRuntimeCallInfo->SetThis(jsfinalizationRegistry.GetTaggedValue());
112 for (size_t i = 0; i < args.size(); i++) {
113 ecmaRuntimeCallInfo->SetCallArg(i, args[i]);
114 }
115
116 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
117 if (unRegister) {
118 BuiltinsFinalizationRegistry::Unregister(ecmaRuntimeCallInfo);
119 } else {
120 BuiltinsFinalizationRegistry::Register(ecmaRuntimeCallInfo);
121 }
122 TestHelper::TearDownFrame(thread, prev);
123 }
124
125 // finalizationRegistry.Register(target, heldValue)
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register0)126 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register0)
127 {
128 testValue = 0;
129 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
130 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
131 JSHandle<JSTaggedValue> objectFunc = env->GetObjectFunction();
132
133 auto jsfinalizationRegistry = Common(thread);
134 JSHandle<JSTaggedValue> target(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc));
135 KeyValueCommon(thread, target);
136
137 std::vector<JSTaggedValue> args{target.GetTaggedValue(), JSTaggedValue(10)};
138
139 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 8);
140 ASSERT_EQ(testValue, 0);
141 }
142
143 // finalizationRegistry.Register(target, heldValue [ , unregisterToken ])
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register1)144 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register1)
145 {
146 testValue = 0;
147 auto jsfinalizationRegistry = Common(thread);
148 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
149 JSHandle<JSTaggedValue> objectFunc = thread->GetEcmaVM()->GetGlobalEnv()->GetObjectFunction();
150 JSHandle<JSTaggedValue> target(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc));
151 KeyValueCommon(thread, target);
152 std::vector<JSTaggedValue> args{target.GetTaggedValue(), JSTaggedValue(10), target.GetTaggedValue()};
153 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
154 ASSERT_EQ(testValue, 0);
155 }
156
157 // finalizationRegistry.Register(target, heldValue [ , unregisterToken ])
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register2)158 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register2)
159 {
160 testValue = 0;
161 EcmaVM *vm = thread->GetEcmaVM();
162 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
163 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
164 JSHandle<JSTaggedValue> objectFunc = env->GetObjectFunction();
165
166 auto jsfinalizationRegistry = Common(thread);
167 vm->SetEnableForceGC(false);
168 JSTaggedValue target = JSTaggedValue::Undefined();
169 {
170 [[maybe_unused]] EcmaHandleScope handleScope(thread);
171 auto obj =
172 factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc);
173 target = obj.GetTaggedValue();
174 std::vector<JSTaggedValue> args{target, JSTaggedValue(10), target};
175 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
176 }
177 vm->CollectGarbage(TriggerGCType::FULL_GC);
178 if (!thread->HasPendingException()) {
179 job::MicroJobQueue::ExecutePendingJob(thread, vm->GetJSThread()->GetCurrentEcmaContext()->GetMicroJobQueue());
180 }
181 vm->SetEnableForceGC(true);
182 ASSERT_EQ(testValue, 1);
183 }
184
185 // finalizationRegistry.Register(target, heldValue [ , unregisterToken ])
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register3)186 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register3)
187 {
188 testValue = 0;
189 EcmaVM *vm = thread->GetEcmaVM();
190 auto jsfinalizationRegistry = Common(thread);
191
192 vm->SetEnableForceGC(false);
193 JSTaggedValue target = JSTaggedValue::Undefined();
194 JSTaggedValue target1 = JSTaggedValue::Undefined();
195 {
196 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
197 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
198 JSHandle<JSTaggedValue> objectFunc = env->GetObjectFunction();
199 [[maybe_unused]] EcmaHandleScope handleScope(thread);
200 auto obj =
201 factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc);
202 auto obj1 =
203 factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc);
204 target = obj.GetTaggedValue();
205
206 std::vector<JSTaggedValue> args{target, JSTaggedValue(10), target};
207 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
208
209 target1 = obj1.GetTaggedValue();
210 std::vector<JSTaggedValue> args1{target1, JSTaggedValue(10), target1};
211 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
212 }
213 vm->CollectGarbage(TriggerGCType::FULL_GC);
214 if (!thread->HasPendingException()) {
215 job::MicroJobQueue::ExecutePendingJob(thread, vm->GetJSThread()->GetCurrentEcmaContext()->GetMicroJobQueue());
216 }
217 vm->SetEnableForceGC(true);
218 ASSERT_EQ(testValue, 2);
219 }
220
221 // finalizationRegistry.Register(target, heldValue [ , unregisterToken ])
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register4)222 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register4)
223 {
224 testValue = 0;
225 EcmaVM *vm = thread->GetEcmaVM();
226 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
227 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
228 JSHandle<JSTaggedValue> objectFunc = env->GetObjectFunction();
229
230 auto jsfinalizationRegistry = Common(thread);
231 auto jsfinalizationRegistry1 = Common(thread);
232 vm->SetEnableForceGC(false);
233 JSTaggedValue target = JSTaggedValue::Undefined();
234 JSTaggedValue target1 = JSTaggedValue::Undefined();
235 {
236 [[maybe_unused]] EcmaHandleScope handleScope(thread);
237 auto obj =
238 factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc);
239 auto obj1 =
240 factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc);
241 target = obj.GetTaggedValue();
242 target1 = obj1.GetTaggedValue();
243
244 std::vector<JSTaggedValue> args{target, JSTaggedValue(10), target};
245 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
246
247 std::vector<JSTaggedValue> args1{target1, JSTaggedValue(10), target1};
248 RegisterUnRegisterCommon(thread, jsfinalizationRegistry1, args, 10);
249 }
250 vm->CollectGarbage(TriggerGCType::FULL_GC);
251 if (!thread->HasPendingException()) {
252 job::MicroJobQueue::ExecutePendingJob(thread, vm->GetJSThread()->GetCurrentEcmaContext()->GetMicroJobQueue());
253 }
254 vm->SetEnableForceGC(true);
255 ASSERT_EQ(testValue, 2);
256 }
257
258 // finalizationRegistry.Register(target, heldValue [ , unregisterToken ])
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register5)259 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register5)
260 {
261 testValue = 0;
262 EcmaVM *vm = thread->GetEcmaVM();
263 auto jsfinalizationRegistry = Common(thread);
264 vm->SetEnableForceGC(false);
265 JSTaggedValue target = JSTaggedValue::Undefined();
266 JSTaggedValue target1 = JSTaggedValue::Undefined();
267 {
268 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
269 JSHandle<JSTaggedValue> objectFunc = thread->GetEcmaVM()->GetGlobalEnv()->GetObjectFunction();
270 [[maybe_unused]] EcmaHandleScope handleScope(thread);
271 auto obj =
272 factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc);
273 auto obj1 =
274 factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc);
275 target = obj.GetTaggedValue();
276 target1 = obj1.GetTaggedValue();
277
278 std::vector<JSTaggedValue> args{target, JSTaggedValue(10), target};
279 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
280
281 std::vector<JSTaggedValue> args1{target1, JSTaggedValue(10), target};
282 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
283 }
284 vm->CollectGarbage(TriggerGCType::FULL_GC);
285 if (!thread->HasPendingException()) {
286 job::MicroJobQueue::ExecutePendingJob(thread, vm->GetJSThread()->GetCurrentEcmaContext()->GetMicroJobQueue());
287 }
288 vm->SetEnableForceGC(true);
289 ASSERT_EQ(testValue, 2);
290 }
291
292 // finalizationRegistry.Unregister(unregisterToken ])
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Unregister1)293 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Unregister1)
294 {
295 testValue = 0;
296 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
297 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
298 JSHandle<JSTaggedValue> objectFunc = env->GetObjectFunction();
299
300 auto jsfinalizationRegistry = Common(thread);
301 JSHandle<JSTaggedValue> target(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc));
302 KeyValueCommon(thread, target);
303
304 std::vector<JSTaggedValue> args{target.GetTaggedValue(), JSTaggedValue(10), target.GetTaggedValue()};
305 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
306
307 std::vector<JSTaggedValue> args1{target.GetTaggedValue()};
308 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args1, 6, true);
309 ASSERT_EQ(testValue, 0);
310 }
311
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Unregister2)312 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Unregister2)
313 {
314 testValue = 0;
315 EcmaVM *vm = thread->GetEcmaVM();
316 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
317 JSHandle<JSTaggedValue> objectFunc = thread->GetEcmaVM()->GetGlobalEnv()->GetObjectFunction();
318
319 auto jsfinalizationRegistry = Common(thread);
320 vm->SetEnableForceGC(false);
321 JSTaggedValue target = JSTaggedValue::Undefined();
322 {
323 [[maybe_unused]] EcmaHandleScope handleScope(thread);
324 auto obj =
325 factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc);
326 target = obj.GetTaggedValue();
327
328 std::vector<JSTaggedValue> args{target, JSTaggedValue(10), target};
329 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
330
331 std::vector<JSTaggedValue> args1{target};
332 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args1, 6, true);
333 }
334 vm->CollectGarbage(TriggerGCType::FULL_GC);
335 if (!thread->HasPendingException()) {
336 job::MicroJobQueue::ExecutePendingJob(thread, vm->GetJSThread()->GetCurrentEcmaContext()->GetMicroJobQueue());
337 }
338 vm->SetEnableForceGC(true);
339 ASSERT_EQ(testValue, 0);
340 }
341
342 // finalizationRegistry.Register(target, heldValue [ , unregisterToken ]) target and unregisterToken Symbol
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, RegisterTargetSymbol)343 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, RegisterTargetSymbol)
344 {
345 testValue = 0;
346 EcmaVM *vm = thread->GetEcmaVM();
347
348 auto jsfinalizationRegistry = Common(thread);
349
350 vm->SetEnableForceGC(false);
351 JSTaggedValue target = JSTaggedValue::Undefined();
352 JSTaggedValue target1 = JSTaggedValue::Undefined();
353 {
354 [[maybe_unused]] EcmaHandleScope handleScope(thread);
355 JSHandle<JSSymbol> symbol1 = thread->GetEcmaVM()->GetFactory()->NewJSSymbol();
356 JSHandle<JSSymbol> symbol2 = thread->GetEcmaVM()->GetFactory()->NewJSSymbol();
357 target = symbol1.GetTaggedValue();
358 target1 = symbol2.GetTaggedValue();
359 std::vector<JSTaggedValue> args{target, JSTaggedValue(10), target};
360 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
361
362 std::vector<JSTaggedValue> args1{target1, JSTaggedValue(10), target1};
363 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args1, 10);
364 }
365 vm->CollectGarbage(TriggerGCType::FULL_GC);
366 if (!thread->HasPendingException()) {
367 job::MicroJobQueue::ExecutePendingJob(thread, vm->GetJSThread()->GetCurrentEcmaContext()->GetMicroJobQueue());
368 }
369 vm->SetEnableForceGC(true);
370 ASSERT_EQ(testValue, 2);
371 }
372
373 // finalizationRegistry.Unregister(unregisterToken) unregisterToken Symbol
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, UnregisterTokenSymbol)374 HWTEST_F_L0(BuiltinsFinalizationRegistryTest, UnregisterTokenSymbol)
375 {
376 testValue = 0;
377 EcmaVM *vm = thread->GetEcmaVM();
378
379 auto jsfinalizationRegistry = Common(thread);
380 vm->SetEnableForceGC(false);
381 JSTaggedValue target = JSTaggedValue::Undefined();
382 {
383 [[maybe_unused]] EcmaHandleScope handleScope(thread);
384 JSHandle<JSSymbol> symbol = thread->GetEcmaVM()->GetFactory()->NewJSSymbol();
385 target = symbol.GetTaggedValue();
386
387 std::vector<JSTaggedValue> args{target, JSTaggedValue(10), target};
388 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args, 10);
389
390 std::vector<JSTaggedValue> args1{target};
391 RegisterUnRegisterCommon(thread, jsfinalizationRegistry, args1, 6, true);
392 }
393 vm->CollectGarbage(TriggerGCType::FULL_GC);
394 if (!thread->HasPendingException()) {
395 job::MicroJobQueue::ExecutePendingJob(thread, vm->GetJSThread()->GetCurrentEcmaContext()->GetMicroJobQueue());
396 }
397 vm->SetEnableForceGC(true);
398 ASSERT_EQ(testValue, 0);
399 }
400 } // namespace panda::test
401