/* * Copyright (c) 2022 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/js_hclass-inl.h" #include "ecmascript/js_object.h" #include "ecmascript/global_env.h" #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/layout_info.h" #include "ecmascript/object_factory-inl.h" #include "ecmascript/tagged_dictionary.h" #include "ecmascript/tests/test_helper.h" using namespace panda; using namespace panda::ecmascript; namespace panda::test { class JSHClassTest : public BaseTestWithScope { }; HWTEST_F_L0(JSHClassTest, InitializeClass) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle nullHandle(thread, JSTaggedValue::Null()); // Call NewEcmaHClass function set object properties JSHandle objectClass = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::TAGGED_ARRAY, nullHandle); // Get object properties EXPECT_EQ(objectClass->GetLayout(), JSTaggedValue::Null()); EXPECT_EQ(objectClass->GetPrototype(), JSTaggedValue::Null()); EXPECT_EQ(objectClass->GetObjectType(), JSType::TAGGED_ARRAY); EXPECT_TRUE(objectClass->IsExtensible()); EXPECT_TRUE(!objectClass->IsPrototype()); EXPECT_EQ(objectClass->GetTransitions(), JSTaggedValue::Undefined()); EXPECT_EQ(objectClass->GetProtoChangeMarker(), JSTaggedValue::Null()); EXPECT_EQ(objectClass->GetProtoChangeDetails(), JSTaggedValue::Null()); EXPECT_EQ(objectClass->GetEnumCache(), JSTaggedValue::Null()); } HWTEST_F_L0(JSHClassTest, SizeFromJSHClass) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle objectClass = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::TAGGED_ARRAY, nullHandle); EXPECT_TRUE(*objectClass != nullptr); size_t objectSize; #ifndef PANDA_TARGET_32 objectSize = objectClass->SizeFromJSHClass(*objectClass); EXPECT_EQ(objectSize, 40U); #endif EcmaString *string = EcmaStringAccessor::CreateEmptyString(vm); objectSize = string->GetClass()->SizeFromJSHClass(string); EXPECT_EQ(objectSize, 16U); string = factory->AllocTreeStringObject(); objectSize = string->GetClass()->SizeFromJSHClass(string); EXPECT_EQ(objectSize, 32U); objectClass = factory->NewEcmaHClass(MachineCode::SIZE, JSType::MACHINE_CODE_OBJECT, nullHandle); objectSize = objectClass->SizeFromJSHClass(*objectClass); #if defined(PANDA_TARGET_AMD64) || defined(PANDA_TARGET_ARM64) EXPECT_EQ(objectSize, 328U); #else EXPECT_EQ(objectSize, 200U); #endif // size is an integral multiple of eight objectClass = factory->NewEcmaHClass(JSObject::SIZE - 1, JSType::JS_OBJECT, nullHandle); objectSize = objectClass->SizeFromJSHClass(*objectClass); EXPECT_EQ(objectSize, 56U); objectClass = factory->NewEcmaHClass(JSObject::SIZE + 1, JSType::JS_OBJECT, nullHandle); objectSize = objectClass->SizeFromJSHClass(*objectClass); EXPECT_EQ(objectSize, 64U); objectClass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, nullHandle); objectSize = objectClass->SizeFromJSHClass(*objectClass); EXPECT_EQ(objectSize, 64U); } HWTEST_F_L0(JSHClassTest, HasReferenceField) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle obj1Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::LINE_STRING, nullHandle); JSHandle obj2Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::TREE_STRING, nullHandle); JSHandle obj3Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::JS_NATIVE_POINTER, nullHandle); JSHandle obj4Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::JS_OBJECT, nullHandle); JSHandle obj5Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::SLICED_STRING, nullHandle); EXPECT_FALSE(obj1Class->HasReferenceField()); EXPECT_TRUE(obj2Class->HasReferenceField()); EXPECT_FALSE(obj3Class->HasReferenceField()); EXPECT_TRUE(obj4Class->HasReferenceField()); EXPECT_TRUE(obj5Class->HasReferenceField()); } HWTEST_F_L0(JSHClassTest, Clone) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle objectClass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, nullHandle); // withoutInlinedProperties is false JSHandle cloneClass = JSHClass::Clone(thread, objectClass, false); EXPECT_TRUE(*cloneClass != nullptr); EXPECT_TRUE(objectClass->GetObjectSize() == cloneClass->GetObjectSize()); EXPECT_EQ(cloneClass->GetObjectSize(), 64U); // 64 : 64 not missing the size of inlinedproperties EXPECT_TRUE(objectClass->GetLayout() == cloneClass->GetLayout()); EXPECT_EQ(JSTaggedValue::SameValue(objectClass->GetPrototype(), cloneClass->GetPrototype()), true); EXPECT_TRUE(objectClass->GetBitField() == cloneClass->GetBitField()); EXPECT_TRUE(objectClass->GetBitField1() == cloneClass->GetBitField1()); EXPECT_TRUE(objectClass->NumberOfProps() == cloneClass->NumberOfProps()); EXPECT_EQ(cloneClass->GetNextInlinedPropsIndex(), 0); // 0 : 0 mean index // withoutInlinedProperties is true cloneClass = JSHClass::Clone(thread, objectClass, true); EXPECT_TRUE(*cloneClass != nullptr); EXPECT_TRUE(objectClass->GetObjectSize() > cloneClass->GetObjectSize()); EXPECT_EQ(cloneClass->GetObjectSize(), 32U); // 32 : 32 missing the size of inlinedproperties EXPECT_TRUE(objectClass->GetLayout() == cloneClass->GetLayout()); EXPECT_EQ(JSTaggedValue::SameValue(objectClass->GetPrototype(), cloneClass->GetPrototype()), true); EXPECT_TRUE(objectClass->GetBitField() == cloneClass->GetBitField()); EXPECT_TRUE(objectClass->GetBitField1() > cloneClass->GetBitField1()); EXPECT_TRUE(objectClass->NumberOfProps() == cloneClass->NumberOfProps()); EXPECT_EQ(cloneClass->GetNextNonInlinedPropsIndex(), 0); // 0 : 0 mean index } HWTEST_F_L0(JSHClassTest, TransitionElementsToDictionary) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle env = vm->GetGlobalEnv(); JSHandle objFun = env->GetObjectFunction(); JSHandle jsObject = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); JSHandle objectClass(thread, jsObject->GetJSHClass()); JSHandle objKey1(factory->NewFromASCII("key1")); JSHandle objKey2(factory->NewFromASCII("key2")); JSHandle objKey3(factory->NewFromASCII("key3")); JSHandle objValue1(thread, JSTaggedValue(1)); JSHandle objValue2(thread, JSTaggedValue(2)); JSHandle objValue3(thread, JSTaggedValue(3)); JSObject::SetProperty(thread, JSHandle(jsObject), objKey1, objValue1); JSObject::SetProperty(thread, JSHandle(jsObject), objKey2, objValue2); JSObject::SetProperty(thread, JSHandle(jsObject), objKey3, objValue3); // class is not dictionary mode EXPECT_FALSE(jsObject->GetJSHClass()->IsDictionaryMode()); ElementsKind oldKind = jsObject->GetJSHClass()->GetElementsKind(); JSHClass::TransitionElementsToDictionary(thread, jsObject); JSObject::TryMigrateToGenericKindForJSObject(thread, jsObject, oldKind); auto resultDict = NameDictionary::Cast(jsObject->GetProperties().GetTaggedObject()); EXPECT_TRUE(resultDict != nullptr); EXPECT_EQ(resultDict->EntriesCount(), 3); // 3 : 3 entry JSHandle dictionaryClass(thread, jsObject->GetJSHClass()); EXPECT_TRUE(dictionaryClass->IsDictionaryMode()); EXPECT_EQ(dictionaryClass->GetObjectSize() + 32U, objectClass->GetObjectSize()); EXPECT_TRUE(dictionaryClass->IsDictionaryElement()); EXPECT_FALSE(dictionaryClass->IsStableElements()); } static JSHandle CreateJSHClass(JSThread *thread) { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle objectFuncPrototype = env->GetObjectFunctionPrototype(); JSHandle hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, objectFuncPrototype); return hclass; } HWTEST_F_L0(JSHClassTest, SetPropertyOfObjHClass_001) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle accessorData(factory->NewAccessorData()); JSHandle keyHandle = factory->NewFromASCII("key"); JSHandle keyHandle0(factory->NewFromASCII("key0")); JSHandle keyHandle2(factory->NewFromASCII("key2")); JSHandle keyHandle4(factory->NewFromASCII("key4")); // empty layoutInfo JSHandle parentsClass = CreateJSHClass(thread); uint32_t length = 6; JSHandle properties = factory->NewTaggedArray(length); for (int i = 0; i < static_cast(length); i++) { if (i % 2 == 0) { JSHandle newValue(thread, JSTaggedValue(i)); JSHandle newKey = factory->ConcatFromString(keyHandle, JSTaggedValue::ToString(thread, newValue)); properties->Set(thread, i, newKey.GetTaggedValue()); continue; } properties->Set(thread, i, accessorData.GetTaggedValue()); } JSHandle childClass = factory->SetLayoutInObjHClass(properties, 3, parentsClass); JSHandle childObj = factory->NewJSObject(childClass); std::vector keyVector; JSObject::GetAllKeysForSerialization(childObj, keyVector); EXPECT_EQ(keyVector.size(), 3U); EXPECT_TRUE(JSObject::HasProperty(thread, childObj, keyHandle0)); EXPECT_TRUE(JSObject::HasProperty(thread, childObj, keyHandle2)); EXPECT_TRUE(JSObject::HasProperty(thread, childObj, keyHandle4)); } HWTEST_F_L0(JSHClassTest, SetPropertyOfObjHClass_002) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle objClass = CreateJSHClass(thread); JSHandle Obj1 = factory->NewJSObject(objClass); JSHandle Obj2 = factory->NewJSObject(objClass); PropertyAttributes attr = PropertyAttributes::Default(); JSHandle keyE(factory->NewFromASCII("e")); JSHandle keyF(factory->NewFromASCII("f")); // not empty layoutInfo auto valueE = JSHandle(thread, JSTaggedValue(7)); auto valueF = JSHandle(thread, JSTaggedValue(8)); JSObject::SetProperty(thread, Obj1, keyE, valueE); JSObject::SetProperty(thread, Obj2, keyF, valueE); auto repE = PropertyAttributes::TranslateToRep(valueE.GetTaggedValue()); JSHandle propertyHclass = JSHClass::SetPropertyOfObjHClass(thread, objClass, keyE, attr, repE); JSHandle obj1Class(thread, Obj1->GetClass()); EXPECT_TRUE(propertyHclass == obj1Class); auto repF = PropertyAttributes::TranslateToRep(valueF.GetTaggedValue()); propertyHclass = JSHClass::SetPropertyOfObjHClass(thread, objClass, keyF, attr, repF); JSHandle obj2Class(thread, Obj2->GetClass()); EXPECT_TRUE(propertyHclass == obj2Class); } HWTEST_F_L0(JSHClassTest, AddProperty) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle keyHandle = factory->NewFromASCII("key"); JSHandle keyHandle0(factory->NewFromASCII("key0")); JSHandle keyHandle1(factory->NewFromASCII("key1")); JSHandle keyHandle2(factory->NewFromASCII("key2")); PropertyAttributes attr = PropertyAttributes::Default(); attr.SetIsInlinedProps(true); // empty layoutInfo JSHandle objClass1 = CreateJSHClass(thread); JSHandle Obj = factory->NewJSObject(objClass1); JSHandle objClass(thread, Obj->GetClass()); EXPECT_FALSE(objClass1 != objClass); int keyLength = 3; for (int i = 0; i keyValue(thread, JSTaggedValue(i)); JSHandle keyHandleI( factory->ConcatFromString(keyHandle, JSTaggedValue::ToString(thread, keyValue))); attr.SetOffset(i); ElementsKind oldKind = Obj->GetJSHClass()->GetElementsKind(); JSHClass::AddProperty(thread, Obj, keyHandleI, attr); JSObject::TryMigrateToGenericKindForJSObject(thread, Obj, oldKind); } EXPECT_TRUE(objClass1 == objClass); std::vector keyVector; JSObject::GetAllKeysForSerialization(Obj, keyVector); EXPECT_EQ(keyVector.size(), 3U); EXPECT_TRUE(JSObject::HasProperty(thread, Obj, keyHandle0)); EXPECT_TRUE(JSObject::HasProperty(thread, Obj, keyHandle1)); EXPECT_TRUE(JSObject::HasProperty(thread, Obj, keyHandle2)); } HWTEST_F_L0(JSHClassTest, TransitionExtension) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle preExtensionsKey = thread->GlobalConstants()->GetHandledPreventExtensionsString(); JSHandle keyHandle0(factory->NewFromASCII("key0")); JSHandle keyHandle1(factory->NewFromASCII("key1")); JSHandle keyHandle2(factory->NewFromASCII("key2")); PropertyAttributes attr = PropertyAttributes(0); attr.SetIsInlinedProps(true); JSHandle obj1Class = CreateJSHClass(thread); JSHandle obj2Class = CreateJSHClass(thread); obj2Class->SetExtensible(true); JSHandle Obj1 = factory->NewJSObject(obj1Class); JSHandle Obj2 = factory->NewJSObject(obj2Class); JSObject::SetProperty(thread, Obj2, keyHandle0, JSHandle(thread, JSTaggedValue(7))); JSObject::SetProperty(thread, Obj2, keyHandle1, JSHandle(thread, JSTaggedValue(8))); JSObject::SetProperty(thread, Obj2, keyHandle2, JSHandle(thread, JSTaggedValue(9))); // obj has key "PreventExtensions" ElementsKind oldKind = Obj1->GetJSHClass()->GetElementsKind(); JSHClass::AddProperty(thread, Obj1, preExtensionsKey, attr); JSObject::TryMigrateToGenericKindForJSObject(thread, Obj1, oldKind); JSHandle newClass1 = JSHClass::TransitionExtension(thread, obj1Class); JSHandle objClass(thread, Obj1->GetClass()); EXPECT_TRUE(newClass1 == objClass); // obj has no key "PreventExtensions" JSHandle newClass2 = JSHClass::TransitionExtension(thread, obj2Class); EXPECT_FALSE(newClass2->IsExtensible()); JSHandle dictionary(thread, obj2Class->GetTransitions()); // find key std::vector keyVector; dictionary->GetAllKeysIntoVector(keyVector); EXPECT_TRUE((keyVector[0] == keyHandle0.GetTaggedValue()) || (keyVector[0] == preExtensionsKey.GetTaggedValue())); EXPECT_TRUE((keyVector[1] == keyHandle0.GetTaggedValue()) || (keyVector[1] == preExtensionsKey.GetTaggedValue())); } HWTEST_F_L0(JSHClassTest, TransitionProto) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle env =vm->GetGlobalEnv(); JSHandle funcPrototype = env->GetFunctionPrototype(); JSHandle prototypeKey = thread->GlobalConstants()->GetHandledPrototypeString(); JSHandle obj1Key(factory->NewFromASCII("key0")); JSHandle obj2Key(factory->NewFromASCII("key1")); JSHandle obj3Key(factory->NewFromASCII("key2")); PropertyAttributes attr = PropertyAttributes(0); attr.SetIsInlinedProps(true); JSHandle objClass = CreateJSHClass(thread); JSHandle Obj = factory->NewJSObject(objClass); // obj has no key "prototype" ElementsKind oldKind = Obj->GetJSHClass()->GetElementsKind(); JSHClass::AddProperty(thread, Obj, obj1Key, attr); JSObject::TryMigrateToGenericKindForJSObject(thread, Obj, oldKind); oldKind = Obj->GetJSHClass()->GetElementsKind(); JSHClass::AddProperty(thread, Obj, obj2Key, attr); JSObject::TryMigrateToGenericKindForJSObject(thread, Obj, oldKind); oldKind = Obj->GetJSHClass()->GetElementsKind(); JSHClass::AddProperty(thread, Obj, obj3Key, attr); JSObject::TryMigrateToGenericKindForJSObject(thread, Obj, oldKind); JSHandle newClass = JSHClass::TransitionProto(thread, objClass, funcPrototype); EXPECT_EQ(newClass->GetPrototype(), funcPrototype.GetTaggedValue()); JSHandle transitionDictionary(thread, objClass->GetTransitions()); // find key std::vector keyVector; transitionDictionary->GetAllKeysIntoVector(keyVector); EXPECT_EQ(keyVector.size(), 2U); EXPECT_EQ(keyVector[0], obj1Key.GetTaggedValue()); EXPECT_EQ(keyVector[1], prototypeKey.GetTaggedValue()); } HWTEST_F_L0(JSHClassTest, TransitionToDictionary) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle obj1Key(factory->NewFromASCII("key1")); JSHandle obj2Key(factory->NewFromASCII("key2")); JSHandle obj3Key(factory->NewFromASCII("key3")); JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle objClass = CreateJSHClass(thread); objClass->SetIsPrototype(true); JSHandle Obj0 = factory->NewJSObject(objClass); JSHandle obj0Class1(thread, Obj0->GetJSHClass()); JSHandle Obj1 = JSObject::ObjectCreate(thread, nullHandle); JSHandle Obj2 = JSObject::ObjectCreate(thread, Obj1); JSHandle obj2Class(thread, Obj2->GetJSHClass()); JSHandle Obj3 = JSObject::ObjectCreate(thread, Obj2); JSObject::SetProperty(thread, Obj1, obj1Key, JSHandle(thread, JSTaggedValue(100))); JSObject::SetProperty(thread, Obj2, obj2Key, JSHandle(thread, JSTaggedValue(101))); JSObject::SetProperty(thread, Obj3, obj3Key, JSHandle(thread, JSTaggedValue(102))); // empty object JSObject::TransitionToDictionary(thread, Obj0); JSHandle obj0Class(thread, Obj0->GetClass()); EXPECT_TRUE(obj0Class->GetObjectSize() < obj0Class1->GetObjectSize()); EXPECT_EQ(obj0Class->NumberOfProps(), 0U); EXPECT_TRUE(obj0Class->IsDictionaryMode()); EXPECT_TRUE(obj0Class->IsPrototype()); // not empty object JSHandle obj3Class(thread, Obj3->GetJSHClass()); JSHClass::EnableProtoChangeMarker(thread, obj3Class); JSObject::TransitionToDictionary(thread, Obj2); // refresh users JSHandle obj1Class(thread, Obj1->GetJSHClass()); JSTaggedValue protoDetails = obj1Class->GetProtoChangeDetails(); EXPECT_TRUE(protoDetails.IsProtoChangeDetails()); JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetails.GetTaggedObject())->GetChangeListener(); JSHandle listeners(thread, listenersValue.GetTaggedObject()); uint32_t holeIndex = ChangeListener::CheckHole(listeners); EXPECT_TRUE(holeIndex == 0U); // new class JSHandle newClass(thread, Obj2->GetClass()); EXPECT_TRUE(newClass->GetObjectSize() < obj2Class->GetObjectSize()); EXPECT_EQ(newClass->NumberOfProps(), 0U); EXPECT_TRUE(newClass->IsDictionaryMode()); EXPECT_TRUE(newClass->IsPrototype()); } HWTEST_F_L0(JSHClassTest, UpdatePropertyMetaData) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle objKey(factory->NewFromASCII("key0")); PropertyAttributes oldAttr = PropertyAttributes(0); PropertyAttributes newAttr = PropertyAttributes(1); oldAttr.SetIsInlinedProps(true); JSHandle objClass = CreateJSHClass(thread); JSHandle Obj = factory->NewJSObject(objClass); // Set Transitions ElementsKind oldKind = Obj->GetJSHClass()->GetElementsKind(); JSHClass::AddProperty(thread, Obj, objKey, oldAttr); JSObject::TryMigrateToGenericKindForJSObject(thread, Obj, oldKind); // update metaData objClass->UpdatePropertyMetaData(thread, objKey.GetTaggedValue(), newAttr); LayoutInfo *layoutInfo = LayoutInfo::Cast(objClass->GetLayout().GetTaggedObject()); EXPECT_EQ(layoutInfo->GetAttr(oldAttr.GetOffset()).GetPropertyMetaData(), newAttr.GetPropertyMetaData()); } HWTEST_F_L0(JSHClassTest, SetPrototype) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle env =vm->GetGlobalEnv(); JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle objectFuncPrototype = env->GetObjectFunctionPrototype(); JSHandle objectClass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, nullHandle); EXPECT_EQ(objectClass->GetPrototype(), nullHandle.GetTaggedValue()); objectClass->SetPrototype(thread, objectFuncPrototype); EXPECT_EQ(objectClass->GetPrototype(), objectFuncPrototype.GetTaggedValue()); } HWTEST_F_L0(JSHClassTest, ProxyHClassClone) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle objectFn(thread->GetGlobalEnv()->GetObjectFunction()); JSHandle target = factory->NewJSObjectByConstructor(objectFn, JSHandle(objectFn)); JSHandle handler = factory->NewJSObjectByConstructor(objectFn, JSHandle(objectFn)); JSHandle proxy = JSProxy::ProxyCreate(thread, JSHandle(target), JSHandle(handler)); JSHandle hclass(thread, proxy->GetClass()); EXPECT_FALSE(hclass->IsJSObject()); EXPECT_GT(hclass->GetObjectSize(), 0); EXPECT_EQ(hclass->GetInlinedProperties(), 0); JSHandle newHClass = JSHClass::Clone(thread, hclass); EXPECT_EQ(newHClass->GetObjectType(), hclass->GetObjectType()); EXPECT_EQ(newHClass->GetObjectSize(), hclass->GetObjectSize()); EXPECT_EQ(newHClass->GetInlinedPropsStartSize(), hclass->GetInlinedPropsStartSize()); EXPECT_EQ(newHClass->GetInlinedProperties(), hclass->GetInlinedProperties()); EXPECT_EQ(newHClass->GetLayout(), hclass->GetLayout()); } } // namespace panda::test