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 34using namespace panda::ecmascript; 35using namespace panda::ecmascript::builtins; 36using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; 37static int testValue = 0; 38 39namespace panda::test { 40class BuiltinsFinalizationRegistryTest : public BaseTestWithScope<false> { 41public: 42 class TestClass : public base::BuiltinsBase { 43 public: 44 static JSTaggedValue cleanupCallback() 45 { 46 ++testValue; 47 return JSTaggedValue::Undefined(); 48 } 49 }; 50}; 51 52JSTaggedValue 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) 73HWTEST_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 91void 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 99JSHandle<JSFinalizationRegistry> Common(JSThread* thread) 100{ 101 JSTaggedValue result = CreateFinalizationRegistryConstructor(thread); 102 JSHandle<JSFinalizationRegistry> jsfinalizationRegistry(thread, result); 103 return jsfinalizationRegistry; 104} 105 106void 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) 126HWTEST_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 ]) 144HWTEST_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 ]) 158HWTEST_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 ]) 186HWTEST_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 ]) 222HWTEST_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 ]) 259HWTEST_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 ]) 293HWTEST_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 312HWTEST_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 343HWTEST_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 374HWTEST_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