/* * Copyright (c) 2021 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/base/builtins_base.h" #include "ecmascript/ecma_runtime_call_info.h" #include "ecmascript/ecma_string.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/js_function.h" #include "ecmascript/js_handle.h" #include "ecmascript/js_hclass.h" #include "ecmascript/js_object-inl.h" #include "ecmascript/js_thread.h" #include "ecmascript/object_factory.h" #include "ecmascript/object_operator.h" #include "ecmascript/tagged_array-inl.h" #include "ecmascript/tagged_dictionary.h" #include "ecmascript/tests/test_helper.h" #include "ecmascript/weak_vector.h" using namespace panda::ecmascript; using namespace panda::ecmascript::base; namespace panda::test { class JSObjectTest : public testing::Test { public: static void SetUpTestCase() { GTEST_LOG_(INFO) << "SetUpTestCase"; } static void TearDownTestCase() { GTEST_LOG_(INFO) << "TearDownCase"; } void SetUp() override { TestHelper::CreateEcmaVMWithScope(instance, thread, scope); } void TearDown() override { TestHelper::DestroyEcmaVMWithScope(instance, scope); } EcmaVM *instance {nullptr}; ecmascript::EcmaHandleScope *scope {nullptr}; JSThread *thread {nullptr}; }; static JSFunction *JSObjectTestCreate(JSThread *thread) { JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); return globalEnv->GetObjectFunction().GetObject(); } HWTEST_F_L0(JSObjectTest, Create) { JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle jsobject = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); EXPECT_TRUE(*jsobject != nullptr); } HWTEST_F_L0(JSObjectTest, SetProperty) { JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle jsobject = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); EXPECT_TRUE(*jsobject != nullptr); char array[] = "x"; JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromASCII(array)); JSHandle value(thread, JSTaggedValue(1)); JSObject::SetProperty(thread, JSHandle(jsobject), key, value); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsobject), key).GetValue()->GetInt(), 1); JSHandle value2(thread, JSTaggedValue(2)); JSObject::SetProperty(thread, JSHandle(jsobject), key, value2); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsobject), key).GetValue()->GetInt(), 2); } HWTEST_F_L0(JSObjectTest, GetProperty) { JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); EXPECT_TRUE(*obj != nullptr); char array[] = "x"; JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromASCII(array)); JSHandle value(thread, JSTaggedValue(1)); EXPECT_TRUE(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->IsUndefined()); JSObject::SetProperty(thread, JSHandle(obj), key, value); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); } HWTEST_F_L0(JSObjectTest, DeleteProperty) { JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); EXPECT_TRUE(*obj != nullptr); char array[] = "print"; JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromASCII(array)); JSHandle value(thread, JSTaggedValue(1)); JSObject::DeleteProperty(thread, (obj), key); EXPECT_TRUE(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->IsUndefined()); JSObject::SetProperty(thread, JSHandle(obj), key, value); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromASCII("print_test")); JSObject::SetProperty(thread, JSHandle(obj), key2, JSHandle(thread, JSTaggedValue(10))); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key2).GetValue()->GetInt(), 10); JSObject::DeleteProperty(thread, (obj), key); EXPECT_TRUE(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->IsUndefined()); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key2).GetValue()->GetInt(), 10); } HWTEST_F_L0(JSObjectTest, DeletePropertyGlobal) { JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle global(thread, globalEnv->GetGlobalObject()); JSHandle printKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("print")); JSHandle printTestKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("print_test")); JSHandle value = JSObject::GetProperty(thread, global, printKey).GetValue(); JSObject::SetProperty(thread, global, printTestKey, value); JSTaggedValue val2 = JSObject::GetProperty(thread, global, printTestKey).GetValue().GetTaggedValue(); EXPECT_EQ(val2, value.GetTaggedValue()); JSTaggedValue::DeletePropertyOrThrow(thread, global, printTestKey); JSTaggedValue val3 = JSObject::GetProperty(thread, global, printKey).GetValue().GetTaggedValue(); EXPECT_NE(val3, JSTaggedValue::Undefined()); } HWTEST_F_L0(JSObjectTest, GetPropertyInPrototypeChain) { JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle grandfather = JSObject::ObjectCreate(thread, nullHandle); JSHandle father = JSObject::ObjectCreate(thread, grandfather); JSHandle son = JSObject::ObjectCreate(thread, father); JSHandle sonKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key1")); JSHandle fatherKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2")); JSHandle grandfatherKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key3")); JSHandle sonValue(thread, JSTaggedValue(1)); JSHandle fatherValue(thread, JSTaggedValue(2)); JSHandle grandfatherValue(thread, JSTaggedValue(3)); JSObject::SetProperty(thread, JSHandle(son), sonKey, sonValue); JSObject::SetProperty(thread, JSHandle(father), fatherKey, fatherValue); JSObject::SetProperty(thread, JSHandle(grandfather), grandfatherKey, grandfatherValue); EXPECT_EQ(sonValue.GetTaggedValue(), JSObject::GetProperty(thread, JSHandle(son), sonKey).GetValue().GetTaggedValue()); EXPECT_EQ(fatherValue.GetTaggedValue(), JSObject::GetProperty(thread, JSHandle(son), fatherKey).GetValue().GetTaggedValue()); EXPECT_EQ(grandfatherValue.GetTaggedValue(), JSObject::GetProperty(thread, JSHandle(son), grandfatherKey).GetValue().GetTaggedValue()); } HWTEST_F_L0(JSObjectTest, PropertyAttribute) { JSHandle constructor(thread, JSObjectTestCreate(thread)); JSHandle obj1 = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); JSHandle obj2 = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key3")); JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key3")); JSHandle value1(thread, JSTaggedValue(1)); JSHandle value2(thread, JSTaggedValue(2)); // test set property PropertyDescriptor desc(thread); desc.SetValue(value1); desc.SetWritable(false); JSObject::DefineOwnProperty(thread, obj1, key1, desc); JSObject::SetProperty(thread, JSHandle(obj1), key1, value2); JSObject::SetProperty(thread, JSHandle(obj2), key1, value1); JSObject::SetProperty(thread, JSHandle(obj2), key1, value2); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), value1.GetTaggedValue()); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj2), key1).GetValue().GetTaggedValue(), value2.GetTaggedValue()); // test delete property PropertyDescriptor desc1(thread); desc1.SetValue(value1); desc1.SetConfigurable(false); JSObject::DefineOwnProperty(thread, obj1, key2, desc1); JSObject::SetProperty(thread, JSHandle(obj1), key2, value1); JSObject::SetProperty(thread, JSHandle(obj2), key2, value1); JSObject::DeleteProperty(thread, (obj1), key2); JSObject::DeleteProperty(thread, (obj2), key2); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key2).GetValue().GetTaggedValue(), value1.GetTaggedValue()); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj2), key2).GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); } HWTEST_F_L0(JSObjectTest, CreateDataProperty) { JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); EXPECT_TRUE(*obj != nullptr); char array[] = "x"; JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromASCII(array)); JSHandle value(thread, JSTaggedValue(1)); bool success = JSObject::CreateDataProperty(thread, obj, key, value); EXPECT_TRUE(success); success = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(obj), key); EXPECT_TRUE(success); PropertyDescriptor desc(thread); success = JSObject::GetOwnProperty(thread, obj, key, desc); EXPECT_TRUE(success); EXPECT_EQ(true, desc.IsWritable()); EXPECT_EQ(true, desc.IsEnumerable()); EXPECT_EQ(true, desc.IsConfigurable()); } HWTEST_F_L0(JSObjectTest, CreateMethodProperty) { JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); EXPECT_TRUE(*obj != nullptr); char array[] = "x"; JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromASCII(array)); JSHandle value(thread, JSTaggedValue(1)); bool success = JSObject::CreateMethodProperty(thread, obj, key, value); EXPECT_TRUE(success); success = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(obj), key); EXPECT_TRUE(success); PropertyDescriptor desc(thread); success = JSObject::GetOwnProperty(thread, obj, key, desc); EXPECT_TRUE(success); EXPECT_EQ(true, desc.IsWritable()); EXPECT_EQ(false, desc.IsEnumerable()); EXPECT_EQ(true, desc.IsConfigurable()); } HWTEST_F_L0(JSObjectTest, DefinePropertyOrThrow) { JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); EXPECT_TRUE(*obj != nullptr); char array[] = "x"; JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromASCII(array)); PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); bool success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc1); EXPECT_TRUE(success); PropertyDescriptor descRes1(thread); success = JSObject::GetOwnProperty(thread, obj, key, descRes1); EXPECT_TRUE(success); EXPECT_EQ(1, descRes1.GetValue()->GetInt()); EXPECT_EQ(true, descRes1.IsWritable()); EXPECT_EQ(true, descRes1.IsEnumerable()); EXPECT_EQ(true, descRes1.IsConfigurable()); PropertyDescriptor desc2(thread, false, true, true); success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc2); EXPECT_TRUE(success); PropertyDescriptor descRes2(thread); success = JSObject::GetOwnProperty(thread, obj, key, descRes2); EXPECT_TRUE(success); EXPECT_EQ(1, descRes2.GetValue()->GetInt()); EXPECT_EQ(false, descRes2.IsWritable()); EXPECT_EQ(true, descRes2.IsEnumerable()); EXPECT_EQ(true, descRes2.IsConfigurable()); PropertyDescriptor desc3(thread); desc3.SetWritable(false); desc3.SetEnumerable(false); desc3.SetConfigurable(false); success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc3); EXPECT_TRUE(success); PropertyDescriptor descRes3(thread); success = JSObject::GetOwnProperty(thread, obj, key, descRes3); EXPECT_TRUE(success); EXPECT_EQ(1, descRes3.GetValue()->GetInt()); EXPECT_EQ(false, descRes3.IsWritable()); EXPECT_EQ(false, descRes3.IsEnumerable()); EXPECT_EQ(false, descRes3.IsConfigurable()); PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(2))); success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc4); EXPECT_FALSE(success); } HWTEST_F_L0(JSObjectTest, HasProperty) { JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); EXPECT_TRUE(*obj != nullptr); char array[] = "x"; JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromASCII(array)); JSHandle value(thread, JSTaggedValue(1)); bool flag = JSObject::HasProperty(thread, obj, key); EXPECT_FALSE(flag); JSObject::SetProperty(thread, JSHandle(obj), key, value); flag = JSObject::HasProperty(thread, obj, key); EXPECT_TRUE(flag); JSObject::DeleteProperty(thread, (obj), key); flag = JSObject::HasProperty(thread, obj, key); EXPECT_FALSE(flag); } HWTEST_F_L0(JSObjectTest, HasPropertyWithProtoType) { JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle grandfather = JSObject::ObjectCreate(thread, nullHandle); JSHandle father = JSObject::ObjectCreate(thread, grandfather); JSHandle son = JSObject::ObjectCreate(thread, father); JSHandle testGrand(thread, JSTaggedValue::GetPrototype(thread, JSHandle(grandfather))); JSHandle testFather(thread, JSTaggedValue::GetPrototype(thread, JSHandle(father))); JSHandle testSon(thread, JSTaggedValue::GetPrototype(thread, JSHandle(son))); EXPECT_TRUE(testSon.GetTaggedValue() != testFather.GetTaggedValue()); EXPECT_TRUE(testGrand.GetTaggedValue() != testFather.GetTaggedValue()); JSHandle sonKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key1")); JSHandle fatherKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2")); JSHandle grandfatherKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key3")); JSHandle sonValue(thread, JSTaggedValue(1)); JSHandle fatherValue(thread, JSTaggedValue(2)); JSHandle grandfatherValue(thread, JSTaggedValue(3)); JSObject::SetProperty(thread, JSHandle(son), sonKey, sonValue); JSObject::SetProperty(thread, JSHandle(father), fatherKey, fatherValue); JSObject::SetProperty(thread, JSHandle(grandfather), grandfatherKey, grandfatherValue); bool flag = JSObject::HasProperty(thread, son, sonKey); EXPECT_TRUE(flag); flag = JSObject::HasProperty(thread, son, fatherKey); EXPECT_TRUE(flag); flag = JSObject::HasProperty(thread, son, grandfatherKey); EXPECT_TRUE(flag); } HWTEST_F_L0(JSObjectTest, HasOwnProperty) { JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle grandfather = JSObject::ObjectCreate(thread, nullHandle); JSHandle father = JSObject::ObjectCreate(thread, grandfather); JSHandle son = JSObject::ObjectCreate(thread, father); JSHandle sonKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key1")); JSHandle fatherKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2")); JSHandle grandfatherKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key3")); JSHandle sonValue(thread, JSTaggedValue(1)); JSHandle fatherValue(thread, JSTaggedValue(2)); JSHandle grandfatherValue(thread, JSTaggedValue(3)); JSObject::SetProperty(thread, JSHandle(son), sonKey, sonValue); JSObject::SetProperty(thread, JSHandle(father), fatherKey, fatherValue); JSObject::SetProperty(thread, JSHandle(grandfather), grandfatherKey, grandfatherValue); bool flag = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(son), sonKey); EXPECT_TRUE(flag); flag = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(son), fatherKey); EXPECT_FALSE(flag); flag = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(son), grandfatherKey); EXPECT_FALSE(flag); } HWTEST_F_L0(JSObjectTest, GetOwnPropertyKeys) { JSHandle constructor(thread, JSObjectTestCreate(thread)); JSHandle obj = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromASCII("x")); JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromASCII("y")); JSHandle key3(thread->GetEcmaVM()->GetFactory()->NewFromASCII("3")); JSHandle key4(thread->GetEcmaVM()->GetFactory()->NewFromASCII("4")); JSHandle value1(thread, JSTaggedValue(1)); JSHandle value2(thread, JSTaggedValue(2)); JSHandle value3(thread, JSTaggedValue(3)); JSHandle value4(thread, JSTaggedValue(4)); JSObject::SetProperty(thread, JSHandle(obj), key1, value1); JSObject::SetProperty(thread, JSHandle(obj), key2, value2); JSObject::SetProperty(thread, JSHandle(obj), key3, value3); JSObject::SetProperty(thread, JSHandle(obj), key4, value4); JSHandle array = JSObject::GetOwnPropertyKeys(thread, obj); uint32_t length = array->GetLength(); EXPECT_EQ(length, 4U); int sum = 0; for (uint32_t i = 0; i < length; i++) { JSHandle key(thread, array->Get(i)); sum += JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(); } EXPECT_EQ(sum, 10); } HWTEST_F_L0(JSObjectTest, ObjectCreateMethod) { JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle grandfather = JSObject::ObjectCreate(thread, nullHandle); JSHandle father = JSObject::ObjectCreate(thread, grandfather); JSHandle son = JSObject::ObjectCreate(thread, father); EXPECT_EQ(JSTaggedValue::GetPrototype(thread, JSHandle(son)), father.GetTaggedValue()); EXPECT_EQ(JSTaggedValue::GetPrototype(thread, JSHandle(father)), grandfather.GetTaggedValue()); EXPECT_EQ(JSTaggedValue::GetPrototype(thread, JSHandle(grandfather)), JSTaggedValue::Null()); } HWTEST_F_L0(JSObjectTest, GetMethod) { JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle obj = JSObject::ObjectCreate(thread, nullHandle); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle func(thread->GetEcmaVM()->GetFactory()->NewJSFunction(env)); JSHandle::Cast(func)->GetJSHClass()->SetCallable(true); EXPECT_TRUE(*func != nullptr); JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("1")); JSObject::SetProperty(thread, JSHandle(obj), key, func); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(), func.GetTaggedValue()); } HWTEST_F_L0(JSObjectTest, EnumerableOwnNames) { JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); EXPECT_TRUE(*obj != nullptr); CString tagCStr = "x"; JSHandle tagString = thread->GetEcmaVM()->GetFactory()->NewFromASCII(&tagCStr[0]); JSHandle key(tagString); JSHandle value(thread, JSTaggedValue(1)); JSObject::SetProperty(thread, JSHandle(obj), key, value); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); JSHandle names = JSObject::EnumerableOwnNames(thread, obj); JSHandle keyFromNames(thread, JSTaggedValue(names->Get(0))); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), keyFromNames).GetValue()->GetInt(), 1); PropertyDescriptor descNoEnum(thread); descNoEnum.SetEnumerable(false); JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, descNoEnum); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); JSHandle namesNoEnum = JSObject::EnumerableOwnNames(thread, obj); EXPECT_TRUE(namesNoEnum->GetLength() == 0U); PropertyDescriptor descEnum(thread); descEnum.SetConfigurable(false); descEnum.SetEnumerable(true); JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, descEnum); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); JSHandle namesNoConfig = JSObject::EnumerableOwnNames(thread, obj); JSHandle keyNoConfig(thread, JSTaggedValue(namesNoConfig->Get(0))); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), keyNoConfig).GetValue()->GetInt(), 1); } HWTEST_F_L0(JSObjectTest, SetIntegrityLevelSealed) { JSHandle hclass1(thread, JSObjectTestCreate(thread)); JSHandle obj1 = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(hclass1), hclass1); EXPECT_TRUE(*obj1 != nullptr); CString undefinedCStr = "x"; JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromASCII(&undefinedCStr[0])); JSHandle value1(thread, JSTaggedValue(1)); JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); // test SetIntegrityLevel::SEALED JSHandle jsobject(obj1); bool status1 = JSObject::SetIntegrityLevel(thread, jsobject, IntegrityLevel::SEALED); EXPECT_TRUE(status1); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), value1.GetTaggedValue()); PropertyDescriptor desc1(thread); bool success1 = JSObject::GetOwnProperty(thread, jsobject, key1, desc1); EXPECT_TRUE(success1); EXPECT_EQ(true, desc1.IsWritable()); EXPECT_EQ(true, desc1.IsEnumerable()); EXPECT_EQ(false, desc1.IsConfigurable()); } HWTEST_F_L0(JSObjectTest, SetIntegrityLevelFrozen) { JSHandle hclass1(thread, JSObjectTestCreate(thread)); JSHandle obj1 = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(hclass1), hclass1); EXPECT_TRUE(*obj1 != nullptr); CString undefinedCStr = "x"; JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromASCII(&undefinedCStr[0])); JSHandle value1(thread, JSTaggedValue(1)); JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); // test SetIntegrityLevel::FROZEN bool status1 = JSObject::SetIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN); EXPECT_TRUE(status1); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), value1.GetTaggedValue()); PropertyDescriptor desc1(thread); bool success1 = JSObject::GetOwnProperty(thread, obj1, key1, desc1); EXPECT_TRUE(success1); EXPECT_EQ(false, desc1.IsWritable()); EXPECT_EQ(true, desc1.IsEnumerable()); EXPECT_EQ(false, desc1.IsConfigurable()); } HWTEST_F_L0(JSObjectTest, TestIntegrityLevelSealed) { JSHandle hclass1(thread, JSObjectTestCreate(thread)); JSHandle obj1 = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(hclass1), hclass1); CString undefinedCStr = "level"; JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromASCII(&undefinedCStr[0])); JSHandle value1(thread, JSTaggedValue(1)); JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); obj1->GetJSHClass()->SetExtensible(false); // test SetIntegrityLevel::SEALED bool status1 = JSObject::SetIntegrityLevel(thread, obj1, IntegrityLevel::SEALED); EXPECT_TRUE(status1); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), value1.GetTaggedValue()); PropertyDescriptor desc1(thread); bool success1 = JSObject::GetOwnProperty(thread, obj1, key1, desc1); EXPECT_TRUE(success1); EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::SEALED)); EXPECT_EQ(false, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN)); } HWTEST_F_L0(JSObjectTest, TestIntegrityLevelFrozen) { JSHandle hclass1(thread, JSObjectTestCreate(thread)); JSHandle obj1 = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(hclass1), hclass1); CString undefinedCStr = "level"; JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromASCII(&undefinedCStr[0])); JSHandle value1(thread, JSTaggedValue(1)); JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); obj1->GetJSHClass()->SetExtensible(false); // test SetIntegrityLevel::FROZEN bool status1 = JSObject::SetIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN); EXPECT_TRUE(status1); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), value1.GetTaggedValue()); PropertyDescriptor desc1(thread); bool success1 = JSObject::GetOwnProperty(thread, obj1, key1, desc1); EXPECT_TRUE(success1); EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::SEALED)); EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN)); } HWTEST_F_L0(JSObjectTest, TestIntegrityLevelWithoutProperty) { JSHandle hclass1(thread, JSObjectTestCreate(thread)); JSHandle obj1( thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(hclass1), hclass1)); JSHandle::Cast(obj1)->GetJSHClass()->SetExtensible(false); CString undefinedCStr = "level"; JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromASCII(&undefinedCStr[0])); // test SetIntegrityLevel::FROZEN JSHandle jsobject(obj1); bool status1 = JSObject::SetIntegrityLevel(thread, jsobject, IntegrityLevel::SEALED); EXPECT_TRUE(status1); PropertyDescriptor desc1(thread); bool success1 = JSObject::GetOwnProperty(thread, jsobject, key1, desc1); EXPECT_TRUE(!success1); EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, jsobject, IntegrityLevel::SEALED)); EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, jsobject, IntegrityLevel::FROZEN)); } JSTaggedValue TestGetter(EcmaRuntimeCallInfo *argv) { auto thread = argv->GetThread(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle obj(BuiltinsBase::GetThis(argv)); JSHandle key(factory->NewFromASCII("y")); JSTaggedValue value = JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(); return JSTaggedValue(value.GetInt() + 1); } HWTEST_F_L0(JSObjectTest, Getter) { JSHandle hclass1(thread, JSObjectTestCreate(thread)); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(hclass1), hclass1); JSHandle key1(factory->NewFromASCII("x")); JSHandle key2(factory->NewFromASCII("y")); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle getter = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestGetter)); PropertyDescriptor desc1(thread); desc1.SetGetter(JSHandle::Cast(getter)); bool success1 = JSObject::DefineOwnProperty(thread, obj, key1, desc1); EXPECT_TRUE(success1); PropertyDescriptor desc2(thread); desc2.SetValue(JSHandle(thread, JSTaggedValue(1))); success1 = JSObject::DefineOwnProperty(thread, obj, key2, desc2); EXPECT_TRUE(success1); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key1).GetValue().GetTaggedValue(), JSTaggedValue(2)); } JSTaggedValue TestSetter(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle obj(BuiltinsBase::GetThis(argv)); JSHandle key(factory->NewFromASCII("y")); JSTaggedValue value(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue()); JSHandle valueHandle(thread, JSTaggedValue(value.GetInt() + 1)); JSObject::SetProperty(thread, JSHandle(obj), key, valueHandle); return JSTaggedValue(JSTaggedValue::True()); } HWTEST_F_L0(JSObjectTest, Setter) { JSHandle hclass1(thread, JSObjectTestCreate(thread)); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(hclass1), hclass1); JSHandle key1(factory->NewFromASCII("x")); JSHandle key2(factory->NewFromASCII("y")); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle setter = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestSetter)); PropertyDescriptor desc1(thread); desc1.SetSetter(JSHandle::Cast(setter)); bool success1 = JSObject::DefineOwnProperty(thread, obj, key1, desc1); EXPECT_TRUE(success1); PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); success1 = JSObject::DefineOwnProperty(thread, obj, key2, desc2); EXPECT_TRUE(success1); JSHandle valueHandle(thread, JSTaggedValue::Undefined()); EXPECT_TRUE(JSObject::SetProperty(thread, JSHandle(obj), key1, valueHandle)); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key2).GetValue().GetTaggedValue(), JSTaggedValue(2)); } HWTEST_F_L0(JSObjectTest, SpeciesConstructor) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSHandle constructorFunc = factory->NewJSFunction(env, static_cast(nullptr), FunctionKind::BASE_CONSTRUCTOR); JSHandle constructorFuncValue(constructorFunc); constructorFunc->GetJSHClass()->SetExtensible(true); JSFunction::NewJSFunctionPrototype(thread, constructorFunc); JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle undefinedValue(thread, JSTaggedValue::Undefined()); JSHandle protoObj = JSObject::ObjectCreate(thread, nullHandle); JSHandle protoObjValue(protoObj); JSHandle constructorKey = globalConst->GetHandledConstructorString(); JSObject::SetProperty(thread, protoObjValue, constructorKey, constructorFuncValue); factory->NewJSObjectByConstructor(constructorFunc, JSHandle::Cast(constructorFunc)); JSHandle speciesConstruct = factory->NewJSFunction(env, static_cast(nullptr), FunctionKind::BASE_CONSTRUCTOR); JSHandle speciesConstructValue(speciesConstruct); constructorFunc->GetJSHClass()->SetExtensible(true); JSFunction::MakeConstructor(thread, speciesConstruct, undefinedValue); JSHandle speciesSymbol = env->GetSpeciesSymbol(); JSObject::SetProperty(thread, constructorFuncValue, speciesSymbol, speciesConstructValue); JSTaggedValue speciesValue = JSObject::SpeciesConstructor(thread, protoObj, constructorFuncValue).GetTaggedValue(); EXPECT_EQ(speciesValue, speciesConstructValue.GetTaggedValue()); } JSTaggedValue TestUndefinedGetter([[maybe_unused]] EcmaRuntimeCallInfo *argv) { // 10 : test case return JSTaggedValue(10); } JSTaggedValue TestUndefinedSetter([[maybe_unused]] EcmaRuntimeCallInfo *argv) { // 10 : test case return JSTaggedValue(10); } HWTEST_F_L0(JSObjectTest, GetterIsUndefined) { JSHandle hclass1(thread, JSObjectTestCreate(thread)); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(hclass1), hclass1); JSHandle key(factory->NewFromASCII("property")); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle getter = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedGetter)); JSHandle setter = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedSetter)); JSHandle unGetter(thread, JSTaggedValue::Undefined()); PropertyDescriptor desc1(thread); desc1.SetGetter(JSHandle::Cast(getter)); desc1.SetSetter(JSHandle::Cast(setter)); desc1.SetConfigurable(true); desc1.SetEnumerable(true); bool success1 = JSObject::DefineOwnProperty(thread, obj, key, desc1); EXPECT_TRUE(success1); PropertyDescriptor desc2(thread); desc2.SetGetter(unGetter); bool success2 = JSObject::DefineOwnProperty(thread, obj, key, desc2); EXPECT_TRUE(success2); PropertyDescriptor desc(thread); bool success = JSObject::GetOwnProperty(thread, obj, key, desc); EXPECT_TRUE(success); EXPECT_TRUE(desc.GetSetter()->IsJSFunction()); EXPECT_TRUE(desc.GetGetter()->IsUndefined()); } HWTEST_F_L0(JSObjectTest, SetterIsUndefined) { JSHandle hclass1(thread, JSObjectTestCreate(thread)); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(hclass1), hclass1); JSHandle key(factory->NewFromASCII("property")); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle getter = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedGetter)); JSHandle setter = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedSetter)); JSHandle unSetter(thread, JSTaggedValue::Undefined()); PropertyDescriptor desc1(thread); desc1.SetGetter(JSHandle::Cast(getter)); desc1.SetSetter(JSHandle::Cast(setter)); desc1.SetConfigurable(true); desc1.SetEnumerable(true); bool success1 = JSObject::DefineOwnProperty(thread, obj, key, desc1); EXPECT_TRUE(success1); PropertyDescriptor desc2(thread); desc2.SetSetter(unSetter); bool success2 = JSObject::DefineOwnProperty(thread, obj, key, desc2); EXPECT_TRUE(success2); PropertyDescriptor desc(thread); bool success = JSObject::GetOwnProperty(thread, obj, key, desc); EXPECT_TRUE(success); EXPECT_TRUE(desc.GetSetter()->IsUndefined()); EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(), JSTaggedValue(10)); } HWTEST_F_L0(JSObjectTest, Transitions) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); JSHandle obj2 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); JSHandle hc1(thread, obj1->GetJSHClass()); JSHandle hc2(thread, obj2->GetJSHClass()); EXPECT_EQ(hc1.GetTaggedValue(), hc2.GetTaggedValue()); JSHandle key1(factory->NewFromASCII("x")); JSHandle key2(factory->NewFromASCII("y")); JSHandle value(thread, JSTaggedValue(1)); // key1 JSObject::SetProperty(thread, JSHandle(obj1), key1, value); JSHandle hc3(thread, obj1->GetJSHClass()); EXPECT_NE(hc1.GetTaggedValue(), hc3.GetTaggedValue()); JSObject::SetProperty(thread, JSHandle(obj2), key1, value); JSHandle hc4(thread, obj2->GetJSHClass()); EXPECT_EQ(hc3.GetTaggedValue(), hc4.GetTaggedValue()); // key2 JSObject::SetProperty(thread, JSHandle(obj1), key2, value); JSHandle hc5(thread, obj1->GetJSHClass()); EXPECT_NE(hc3.GetTaggedValue(), hc5.GetTaggedValue()); JSObject::SetProperty(thread, JSHandle(obj2), key2, value); JSHandle hc6(thread, obj2->GetJSHClass()); EXPECT_EQ(hc5.GetTaggedValue(), hc6.GetTaggedValue()); } HWTEST_F_L0(JSObjectTest, FastToSlow) { auto ecmaVM = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVM->GetFactory(); JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); JSMutableHandle key(thread, factory->NewFromASCII("x")); JSMutableHandle number(thread, JSTaggedValue(0)); JSMutableHandle newkey(thread, JSTaggedValue(0)); JSHandle value(thread, JSTaggedValue(1)); ecmaVM->SetEnableForceGC(false); for (uint32_t i = 0; i < PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES; i++) { number.Update(JSTaggedValue(i)); number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); newkey.Update(JSTaggedValue(newString)); JSObject::SetProperty(thread, JSHandle(obj1), newkey, value); } ecmaVM->SetEnableForceGC(true); EXPECT_TRUE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); number.Update(JSTaggedValue(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)); number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); newkey.Update(JSTaggedValue(newString)); JSObject::SetProperty(thread, JSHandle(obj1), newkey, value); EXPECT_TRUE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); NameDictionary *dict = NameDictionary::Cast(obj1->GetProperties().GetTaggedObject()); EXPECT_EQ(dict->EntriesCount(), static_cast(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES + 1)); EXPECT_EQ(dict->NextEnumerationIndex(thread), static_cast(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES + 1)); } HWTEST_F_L0(JSObjectTest, DeleteMiddle) { auto ecmaVM = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVM->GetFactory(); JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); JSMutableHandle key(thread, factory->NewFromASCII("x")); JSMutableHandle number(thread, JSTaggedValue(0)); JSMutableHandle newkey(thread, JSTaggedValue(0)); JSHandle value(thread, JSTaggedValue(1)); for (uint32_t i = 0; i < 10; i++) { number.Update(JSTaggedValue(i)); number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); newkey.Update(JSTaggedValue(newString)); JSObject::SetProperty(thread, JSHandle(obj1), newkey, value); } EXPECT_FALSE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); JSMutableHandle key5(thread, factory->NewFromASCII("x5")); JSObject::DeleteProperty(thread, (obj1), key5); EXPECT_TRUE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); NameDictionary *dict = NameDictionary::Cast(obj1->GetProperties().GetTaggedObject()); EXPECT_EQ(dict->EntriesCount(), 9); EXPECT_FALSE(JSObject::HasProperty(thread, obj1, key5)); } HWTEST_F_L0(JSObjectTest, ElementFastToSlow) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle key0(thread, JSTaggedValue(0)); JSHandle key1(thread, JSTaggedValue(1)); JSHandle key2(thread, JSTaggedValue(2)); JSHandle key2000(thread, JSTaggedValue(2000)); JSHandle keyStr(factory->NewFromASCII("str")); // test dictionary [0,1,2,...,2000] JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); EXPECT_TRUE(!TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); JSObject::SetProperty(thread, JSHandle(obj1), keyStr, key2); JSObject::SetProperty(thread, JSHandle(obj1), key0, key0); EXPECT_TRUE(!TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); JSHandle hclass(thread, obj1->GetJSHClass()); JSObject::SetProperty(thread, JSHandle(obj1), key1, key1); EXPECT_TRUE(!TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); EXPECT_EQ(obj1->GetJSHClass(), *hclass); JSObject::SetProperty(thread, JSHandle(obj1), key2000, key2000); EXPECT_TRUE(TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); JSTaggedValue value = JSObject::GetProperty(thread, JSHandle(obj1), keyStr).GetValue().GetTaggedValue(); EXPECT_EQ(value, key2.GetTaggedValue()); // test holey [0,,2] JSHandle obj2 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); JSObject::SetProperty(thread, JSHandle(obj2), key0, key0); EXPECT_TRUE(!TaggedArray::Cast(obj2->GetElements().GetTaggedObject())->IsDictionaryMode()); JSHandle hclass2(thread, obj2->GetJSHClass()); JSObject::SetProperty(thread, JSHandle(obj2), key2, key2); EXPECT_TRUE(!TaggedArray::Cast(obj2->GetElements().GetTaggedObject())->IsDictionaryMode()); EXPECT_EQ(obj2->GetJSHClass(), *hclass2); // test change attr JSHandle obj3 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); JSObject::SetProperty(thread, JSHandle(obj3), key0, key0); JSObject::SetProperty(thread, JSHandle(obj3), key1, key1); JSObject::SetProperty(thread, JSHandle(obj3), key2, key2); EXPECT_TRUE(!TaggedArray::Cast(obj3->GetElements().GetTaggedObject())->IsDictionaryMode()); PropertyDescriptor desc(thread); desc.SetValue(key1); desc.SetWritable(false); JSObject::DefineOwnProperty(thread, obj3, key1, desc); EXPECT_TRUE(TaggedArray::Cast(obj3->GetElements().GetTaggedObject())->IsDictionaryMode()); // test delete element JSHandle obj4 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); JSObject::SetProperty(thread, JSHandle(obj4), key0, key0); JSObject::SetProperty(thread, JSHandle(obj4), key1, key1); JSObject::SetProperty(thread, JSHandle(obj4), key2, key2); EXPECT_TRUE(!TaggedArray::Cast(obj4->GetElements().GetTaggedObject())->IsDictionaryMode()); JSObject::DeleteProperty(thread, (obj4), key1); EXPECT_TRUE(TaggedArray::Cast(obj4->GetElements().GetTaggedObject())->IsDictionaryMode()); JSHandle value1001(thread, JSTaggedValue(1001)); JSHandle obj100 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); PropertyDescriptor desc1(thread); desc1.SetValue(value1001); desc1.SetWritable(false); desc1.SetEnumerable(false); desc1.SetConfigurable(false); JSObject::SetProperty(thread, JSHandle(obj100), key0, key1); JSObject::DefineOwnProperty(thread, obj100, key0, desc1); JSTaggedValue result1001 = JSObject::GetProperty(thread, JSHandle(obj100), key0).GetValue().GetTaggedValue(); EXPECT_EQ(result1001, value1001.GetTaggedValue()); } HWTEST_F_L0(JSObjectTest, EnableProtoChangeMarker) { JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); JSHandle obj3 = JSObject::ObjectCreate(thread, obj2); JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key1")); JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2")); JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key3")); JSHandle obj1Value(thread, JSTaggedValue(1)); JSHandle obj2Value(thread, JSTaggedValue(2)); JSHandle obj3Value(thread, JSTaggedValue(3)); JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); JSHandle obj3Class(thread, obj3->GetJSHClass()); JSHandle resultMarker = JSHClass::EnableProtoChangeMarker(thread, obj3Class); EXPECT_TRUE(resultMarker->IsProtoChangeMarker()); bool hasChanged = ProtoChangeMarker::Cast(resultMarker->GetTaggedObject())->GetHasChanged(); EXPECT_TRUE(!hasChanged); JSHandle obj1Class(thread, obj1->GetJSHClass()); JSHandle obj2Class(thread, obj2->GetJSHClass()); JSTaggedValue obj2Marker = obj2Class->GetProtoChangeMarker(); EXPECT_TRUE(obj2Marker.IsProtoChangeMarker()); bool hasChanged2 = ProtoChangeMarker::Cast(obj2Marker.GetTaggedObject())->GetHasChanged(); EXPECT_TRUE(!hasChanged2); JSTaggedValue obj1Marker = obj1Class->GetProtoChangeMarker(); EXPECT_TRUE(!obj1Marker.IsProtoChangeMarker()); JSTaggedValue protoDetails2 = obj2Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); JSTaggedValue protoDetails1 = obj1Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); JSTaggedValue listeners1 = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); EXPECT_TRUE(!listeners1.IsUndefined()); JSTaggedValue listeners2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); EXPECT_TRUE(listeners2.IsUndefined()); uint32_t index = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); JSTaggedValue listenersResult = ChangeListener::Cast(listeners1.GetTaggedObject())->Get(index); EXPECT_TRUE(listenersResult == obj2Class.GetTaggedValue()); } HWTEST_F_L0(JSObjectTest, BuildRegisterTree) { JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); JSHandle obj3 = JSObject::ObjectCreate(thread, obj2); JSHandle obj4 = JSObject::ObjectCreate(thread, obj2); JSHandle obj5 = JSObject::ObjectCreate(thread, obj4); JSHandle obj6 = JSObject::ObjectCreate(thread, obj2); JSHandle obj7 = JSObject::ObjectCreate(thread, obj6); JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key1")); JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2")); JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key3")); JSHandle obj4Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key4")); JSHandle obj5Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key5")); JSHandle obj6Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key6")); JSHandle obj7Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key7")); JSHandle obj1Value(thread, JSTaggedValue(1)); JSHandle obj2Value(thread, JSTaggedValue(2)); JSHandle obj3Value(thread, JSTaggedValue(3)); JSHandle obj4Value(thread, JSTaggedValue(4)); JSHandle obj5Value(thread, JSTaggedValue(5)); JSHandle obj6Value(thread, JSTaggedValue(6)); JSHandle obj7Value(thread, JSTaggedValue(7)); JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); JSObject::SetProperty(thread, JSHandle(obj4), obj4Key, obj4Value); JSObject::SetProperty(thread, JSHandle(obj5), obj5Key, obj5Value); JSObject::SetProperty(thread, JSHandle(obj6), obj6Key, obj6Value); JSObject::SetProperty(thread, JSHandle(obj7), obj7Key, obj7Value); JSHandle obj1Class(thread, obj1->GetJSHClass()); JSHandle obj2Class(thread, obj2->GetJSHClass()); JSHandle obj3Class(thread, obj3->GetJSHClass()); JSHandle obj4Class(thread, obj4->GetJSHClass()); JSHandle obj5Class(thread, obj5->GetJSHClass()); JSHandle obj6Class(thread, obj6->GetJSHClass()); JSHandle obj7Class(thread, obj7->GetJSHClass()); JSHandle result3Marker = JSHClass::EnableProtoChangeMarker(thread, obj3Class); JSHandle result5Marker = JSHClass::EnableProtoChangeMarker(thread, obj5Class); EXPECT_TRUE(result3Marker->IsProtoChangeMarker()); EXPECT_TRUE(!(ProtoChangeMarker::Cast(result3Marker->GetTaggedObject())->GetHasChanged())); EXPECT_TRUE(result5Marker->IsProtoChangeMarker()); EXPECT_TRUE(!(ProtoChangeMarker::Cast(result5Marker->GetTaggedObject())->GetHasChanged())); EXPECT_TRUE(obj4Class->GetProtoChangeMarker().IsProtoChangeMarker()); EXPECT_TRUE(!obj6Class->GetProtoChangeMarker().IsProtoChangeMarker()); JSHandle result7Marker = JSHClass::EnableProtoChangeMarker(thread, obj7Class); EXPECT_TRUE(result7Marker->IsProtoChangeMarker()); EXPECT_TRUE(!(ProtoChangeMarker::Cast(result7Marker->GetTaggedObject())->GetHasChanged())); JSTaggedValue protoDetails1 = obj1Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); JSTaggedValue listeners1Value = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); EXPECT_TRUE(listeners1Value != JSTaggedValue(0)); JSHandle listeners1(thread, listeners1Value.GetTaggedObject()); JSTaggedValue protoDetails2 = obj2Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); uint32_t index2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); EXPECT_TRUE(listeners1->Get(index2) == obj2Class.GetTaggedValue()); JSTaggedValue listeners2Value = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); EXPECT_TRUE(listeners2Value != JSTaggedValue(0)); JSHandle listeners2(thread, listeners2Value.GetTaggedObject()); JSTaggedValue protoDetails4 = obj4Class->GetProtoChangeDetails(); JSTaggedValue protoDetails6 = obj6Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails4.IsProtoChangeDetails()); EXPECT_TRUE(protoDetails6.IsProtoChangeDetails()); uint32_t index4 = ProtoChangeDetails::Cast(protoDetails4.GetTaggedObject())->GetRegisterIndex(); EXPECT_TRUE(listeners2->Get(index4) == obj4Class.GetTaggedValue()); uint32_t index6 = ProtoChangeDetails::Cast(protoDetails6.GetTaggedObject())->GetRegisterIndex(); EXPECT_TRUE(listeners2->Get(index6) == obj6Class.GetTaggedValue()); EXPECT_TRUE(listeners1->GetEnd() == 1U); EXPECT_TRUE(listeners2->GetEnd() == 2U); } HWTEST_F_L0(JSObjectTest, NoticeThroughChain) { JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); JSHandle obj3 = JSObject::ObjectCreate(thread, obj2); JSHandle obj4 = JSObject::ObjectCreate(thread, obj2); JSHandle obj5 = JSObject::ObjectCreate(thread, obj4); JSHandle obj6 = JSObject::ObjectCreate(thread, obj2); JSHandle obj7 = JSObject::ObjectCreate(thread, obj6); JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key1")); JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2")); JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key3")); JSHandle obj4Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key4")); JSHandle obj5Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key5")); JSHandle obj6Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key6")); JSHandle obj7Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key7")); JSHandle obj1Value(thread, JSTaggedValue(1)); JSHandle obj2Value(thread, JSTaggedValue(2)); JSHandle obj3Value(thread, JSTaggedValue(3)); JSHandle obj4Value(thread, JSTaggedValue(4)); JSHandle obj5Value(thread, JSTaggedValue(5)); JSHandle obj6Value(thread, JSTaggedValue(6)); JSHandle obj7Value(thread, JSTaggedValue(7)); JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); JSObject::SetProperty(thread, JSHandle(obj4), obj4Key, obj4Value); JSObject::SetProperty(thread, JSHandle(obj5), obj5Key, obj5Value); JSObject::SetProperty(thread, JSHandle(obj6), obj6Key, obj6Value); JSObject::SetProperty(thread, JSHandle(obj7), obj7Key, obj7Value); JSHandle obj1Class(thread, obj1->GetJSHClass()); JSHandle obj2Class(thread, obj2->GetJSHClass()); JSHandle obj3Class(thread, obj3->GetJSHClass()); JSHandle obj4Class(thread, obj4->GetJSHClass()); JSHandle obj5Class(thread, obj5->GetJSHClass()); JSHandle obj6Class(thread, obj6->GetJSHClass()); JSHandle obj7Class(thread, obj7->GetJSHClass()); JSHClass::EnableProtoChangeMarker(thread, obj3Class); JSHClass::EnableProtoChangeMarker(thread, obj7Class); JSHClass::EnableProtoChangeMarker(thread, obj5Class); JSHClass::NoticeThroughChain(thread, obj2Class); JSHClass::UnregisterOnProtoChain(thread, obj2Class); JSTaggedValue protoDetails1 = obj1Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); JSTaggedValue listeners1Value = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); EXPECT_TRUE(listeners1Value != JSTaggedValue(0)); JSHandle listeners1(thread, listeners1Value.GetTaggedObject()); uint32_t holeIndex = ChangeListener::CheckHole(listeners1); EXPECT_TRUE(holeIndex == 0U); JSTaggedValue protoDetails2 = obj2Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); JSTaggedValue listeners2Value = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); EXPECT_TRUE(listeners2Value != JSTaggedValue(0)); uint32_t index2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); EXPECT_TRUE(listeners1->Get(index2).IsHole()); JSTaggedValue obj6Marker = obj6Class->GetProtoChangeMarker(); EXPECT_TRUE(obj6Marker.IsProtoChangeMarker()); bool hasChanged6 = ProtoChangeMarker::Cast(obj6Marker.GetTaggedObject())->GetHasChanged(); EXPECT_TRUE(hasChanged6); JSTaggedValue obj4Marker = obj4Class->GetProtoChangeMarker(); EXPECT_TRUE(obj4Marker.IsProtoChangeMarker()); bool hasChanged4 = ProtoChangeMarker::Cast(obj4Marker.GetTaggedObject())->GetHasChanged(); EXPECT_TRUE(hasChanged4); } HWTEST_F_L0(JSObjectTest, ChangeProtoAndNoticeTheChain) { JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); JSHandle obj3 = JSObject::ObjectCreate(thread, obj1); JSHandle obj4 = JSObject::ObjectCreate(thread, obj2); JSHandle obj5 = JSObject::ObjectCreate(thread, obj4); JSHandle obj6 = JSObject::ObjectCreate(thread, obj2); JSHandle obj7 = JSObject::ObjectCreate(thread, obj6); JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key1")); JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2")); JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key3")); JSHandle obj4Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key4")); JSHandle obj5Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key5")); JSHandle obj6Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key6")); JSHandle obj7Key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("key7")); JSHandle obj1Value(thread, JSTaggedValue(1)); JSHandle obj2Value(thread, JSTaggedValue(2)); JSHandle obj3Value(thread, JSTaggedValue(3)); JSHandle obj4Value(thread, JSTaggedValue(4)); JSHandle obj5Value(thread, JSTaggedValue(5)); JSHandle obj6Value(thread, JSTaggedValue(6)); JSHandle obj7Value(thread, JSTaggedValue(7)); JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); JSObject::SetProperty(thread, JSHandle(obj4), obj4Key, obj4Value); JSObject::SetProperty(thread, JSHandle(obj5), obj5Key, obj5Value); JSObject::SetProperty(thread, JSHandle(obj6), obj6Key, obj6Value); JSObject::SetProperty(thread, JSHandle(obj7), obj7Key, obj7Value); JSHandle obj5Class(thread, obj5->GetJSHClass()); JSHandle obj7Class(thread, obj7->GetJSHClass()); JSHClass::EnableProtoChangeMarker(thread, obj7Class); JSHClass::EnableProtoChangeMarker(thread, obj5Class); JSObject::SetPrototype(thread, obj2, JSHandle(obj3)); JSHandle obj1Class(thread, obj1->GetJSHClass()); JSHandle obj2Class(thread, obj2->GetJSHClass()); JSHandle obj3Class(thread, obj3->GetJSHClass()); JSHandle obj4Class(thread, obj4->GetJSHClass()); JSHandle obj6Class(thread, obj6->GetJSHClass()); JSTaggedValue obj6Marker = obj6Class->GetProtoChangeMarker(); EXPECT_TRUE(obj6Marker.IsProtoChangeMarker()); bool hasChanged6 = ProtoChangeMarker::Cast(obj6Marker.GetTaggedObject())->GetHasChanged(); EXPECT_TRUE(hasChanged6); JSTaggedValue obj4Marker = obj4Class->GetProtoChangeMarker(); EXPECT_TRUE(obj4Marker.IsProtoChangeMarker()); bool hasChanged4 = ProtoChangeMarker::Cast(obj4Marker.GetTaggedObject())->GetHasChanged(); EXPECT_TRUE(hasChanged4); JSTaggedValue protoDetails1 = obj1Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); JSTaggedValue protoDetails2 = obj2Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); JSTaggedValue protoDetails3 = obj3Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails3.IsProtoChangeDetails()); JSTaggedValue protoDetails4 = obj4Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails4.IsProtoChangeDetails()); JSTaggedValue protoDetails6 = obj6Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails6.IsProtoChangeDetails()); JSTaggedValue listeners1 = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); EXPECT_TRUE(listeners1 != JSTaggedValue(0)); JSTaggedValue listeners2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); EXPECT_TRUE(listeners2 != JSTaggedValue(0)); JSTaggedValue listeners3 = ProtoChangeDetails::Cast(protoDetails3.GetTaggedObject())->GetChangeListener(); EXPECT_TRUE(listeners3 != JSTaggedValue(0)); uint32_t index2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); uint32_t index3 = ProtoChangeDetails::Cast(protoDetails3.GetTaggedObject())->GetRegisterIndex(); uint32_t index4 = ProtoChangeDetails::Cast(protoDetails4.GetTaggedObject())->GetRegisterIndex(); uint32_t index6 = ProtoChangeDetails::Cast(protoDetails6.GetTaggedObject())->GetRegisterIndex(); JSTaggedValue result2 = ChangeListener::Cast(listeners3.GetTaggedObject())->Get(index2); JSTaggedValue result3 = ChangeListener::Cast(listeners1.GetTaggedObject())->Get(index3); JSTaggedValue result4 = ChangeListener::Cast(listeners2.GetTaggedObject())->Get(index4); JSTaggedValue result6 = ChangeListener::Cast(listeners2.GetTaggedObject())->Get(index6); EXPECT_TRUE(result2 == obj2Class.GetTaggedValue()); EXPECT_TRUE(result3 == obj3Class.GetTaggedValue()); EXPECT_TRUE(result4 == obj4Class.GetTaggedValue()); EXPECT_TRUE(result6 == obj6Class.GetTaggedValue()); } HWTEST_F_L0(JSObjectTest, NativePointerField) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); obj->SetHash(87); EXPECT_TRUE(obj->GetHash() == 87); obj->SetNativePointerFieldCount(1); char array[] = "Hello World!"; obj->SetNativePointerField(0, array, nullptr, nullptr); int32_t count = obj->GetNativePointerFieldCount(); EXPECT_TRUE(count == 1); void *pointer = obj->GetNativePointerField(0); EXPECT_TRUE(pointer == array); } static JSHandle CreateTestHClass(JSThread *thread) { JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle objectFuncPrototype = globalEnv->GetObjectFunctionPrototype(); JSHandle hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, objectFuncPrototype); return hclass; } HWTEST_F_L0(JSObjectTest, UpdateWeakTransitions) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); EcmaVM *vm = thread->GetEcmaVM(); PropertyAttributes attr = PropertyAttributes::Default(); // Initialize three objects by hc0 JSHandle hc0 = CreateTestHClass(thread); [[maybe_unused]] JSHandle obj0 = factory->NewJSObject(hc0); // need it to ensure hc0 not be collected JSHandle obj1 = factory->NewJSObject(hc0); JSHandle obj2 = factory->NewJSObject(hc0); JSHandle keyA(factory->NewFromASCII("a")); JSHandle keyB(factory->NewFromASCII("b")); { // need a new handle scope to ensure no scope refers hc1, hc2. [[maybe_unused]] EcmaHandleScope handleScope(thread); // / hc1 (obj1) // occur transition, hc0 (obj0) // \ hc2 (obj2) JSObject::SetProperty(thread, obj1, keyA, JSHandle(thread, JSTaggedValue(1))); JSObject::SetProperty(thread, obj2, keyB, JSHandle(thread, JSTaggedValue(2))); EXPECT_TRUE(hc0->GetTransitions().IsTaggedArray()); EXPECT_EQ(hc0->FindTransitions(keyA.GetTaggedValue(), attr.GetTaggedValue()), obj1->GetClass()); EXPECT_EQ(hc0->FindTransitions(keyB.GetTaggedValue(), attr.GetTaggedValue()), obj2->GetClass()); // / hc1 --> hc3 (obj1) // hc0 (obj0) // \ hc2 --> hc4 (obj2) JSObject::SetProperty(thread, obj1, keyB, JSHandle(thread, JSTaggedValue(3))); JSObject::SetProperty(thread, obj2, keyA, JSHandle(thread, JSTaggedValue(4))); } // collect hc1, hc2 vm->CollectGarbage(TriggerGCType::FULL_GC); EXPECT_EQ(hc0->FindTransitions(keyA.GetTaggedValue(), attr.GetTaggedValue()), nullptr); EXPECT_EQ(hc0->FindTransitions(keyB.GetTaggedValue(), attr.GetTaggedValue()), nullptr); JSHandle obj3 = factory->NewJSObject(hc0); JSHandle obj4 = factory->NewJSObject(hc0); // / hc5 (obj3) // re-occur transition, hc0 (obj0) // \ hc6 (obj4) JSObject::SetProperty(thread, obj3, keyA, JSHandle(thread, JSTaggedValue(5))); JSObject::SetProperty(thread, obj4, keyB, JSHandle(thread, JSTaggedValue(6))); EXPECT_TRUE(hc0->GetTransitions().IsTaggedArray()); EXPECT_EQ(hc0->FindTransitions(keyA.GetTaggedValue(), attr.GetTaggedValue()), obj3->GetClass()); EXPECT_EQ(hc0->FindTransitions(keyB.GetTaggedValue(), attr.GetTaggedValue()), obj4->GetClass()); } } // namespace panda::test