1/*
2 * Copyright (c) 2022 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/js_hclass-inl.h"
17#include "ecmascript/js_object.h"
18#include "ecmascript/global_env.h"
19#include "ecmascript/ic/proto_change_details.h"
20#include "ecmascript/layout_info.h"
21#include "ecmascript/object_factory-inl.h"
22#include "ecmascript/tagged_dictionary.h"
23#include "ecmascript/tests/test_helper.h"
24
25using namespace panda;
26using namespace panda::ecmascript;
27
28namespace panda::test {
29class JSHClassTest : public BaseTestWithScope<false> {
30};
31
32HWTEST_F_L0(JSHClassTest, InitializeClass)
33{
34    EcmaVM *vm = thread->GetEcmaVM();
35    ObjectFactory *factory = vm->GetFactory();
36    JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
37    // Call NewEcmaHClass function set object properties
38    JSHandle<JSHClass> objectClass =
39        factory->NewEcmaHClass(TaggedArray::SIZE, JSType::TAGGED_ARRAY, nullHandle);
40    // Get object properties
41    EXPECT_EQ(objectClass->GetLayout(), JSTaggedValue::Null());
42    EXPECT_EQ(objectClass->GetPrototype(), JSTaggedValue::Null());
43    EXPECT_EQ(objectClass->GetObjectType(), JSType::TAGGED_ARRAY);
44    EXPECT_TRUE(objectClass->IsExtensible());
45    EXPECT_TRUE(!objectClass->IsPrototype());
46    EXPECT_EQ(objectClass->GetTransitions(), JSTaggedValue::Undefined());
47    EXPECT_EQ(objectClass->GetProtoChangeMarker(), JSTaggedValue::Null());
48    EXPECT_EQ(objectClass->GetProtoChangeDetails(), JSTaggedValue::Null());
49    EXPECT_EQ(objectClass->GetEnumCache(), JSTaggedValue::Null());
50}
51
52HWTEST_F_L0(JSHClassTest, SizeFromJSHClass)
53{
54    EcmaVM *vm = thread->GetEcmaVM();
55    ObjectFactory *factory = vm->GetFactory();
56    JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
57
58    JSHandle<JSHClass> objectClass = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::TAGGED_ARRAY, nullHandle);
59    EXPECT_TRUE(*objectClass != nullptr);
60    size_t objectSize;
61#ifndef PANDA_TARGET_32
62    objectSize = objectClass->SizeFromJSHClass(*objectClass);
63    EXPECT_EQ(objectSize, 40U);
64#endif
65    EcmaString *string = EcmaStringAccessor::CreateEmptyString(vm);
66    objectSize = string->GetClass()->SizeFromJSHClass(string);
67    EXPECT_EQ(objectSize, 16U);
68    string = factory->AllocTreeStringObject();
69    objectSize = string->GetClass()->SizeFromJSHClass(string);
70    EXPECT_EQ(objectSize, 32U);
71
72    objectClass = factory->NewEcmaHClass(MachineCode::SIZE, JSType::MACHINE_CODE_OBJECT, nullHandle);
73    objectSize = objectClass->SizeFromJSHClass(*objectClass);
74#if defined(PANDA_TARGET_AMD64) || defined(PANDA_TARGET_ARM64)
75    EXPECT_EQ(objectSize, 328U);
76#else
77    EXPECT_EQ(objectSize, 200U);
78#endif
79    // size is an integral multiple of eight
80    objectClass = factory->NewEcmaHClass(JSObject::SIZE - 1, JSType::JS_OBJECT, nullHandle);
81    objectSize = objectClass->SizeFromJSHClass(*objectClass);
82    EXPECT_EQ(objectSize, 56U);
83
84    objectClass = factory->NewEcmaHClass(JSObject::SIZE + 1, JSType::JS_OBJECT, nullHandle);
85    objectSize = objectClass->SizeFromJSHClass(*objectClass);
86    EXPECT_EQ(objectSize, 64U);
87
88    objectClass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, nullHandle);
89    objectSize = objectClass->SizeFromJSHClass(*objectClass);
90    EXPECT_EQ(objectSize, 64U);
91}
92
93HWTEST_F_L0(JSHClassTest, HasReferenceField)
94{
95    EcmaVM *vm = thread->GetEcmaVM();
96    ObjectFactory *factory = vm->GetFactory();
97    JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
98
99    JSHandle<JSHClass> obj1Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::LINE_STRING, nullHandle);
100    JSHandle<JSHClass> obj2Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::TREE_STRING, nullHandle);
101    JSHandle<JSHClass> obj3Class =
102        factory->NewEcmaHClass(TaggedArray::SIZE, JSType::JS_NATIVE_POINTER, nullHandle);
103    JSHandle<JSHClass> obj4Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::JS_OBJECT, nullHandle);
104    JSHandle<JSHClass> obj5Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::SLICED_STRING, nullHandle);
105    EXPECT_FALSE(obj1Class->HasReferenceField());
106    EXPECT_TRUE(obj2Class->HasReferenceField());
107    EXPECT_FALSE(obj3Class->HasReferenceField());
108    EXPECT_TRUE(obj4Class->HasReferenceField());
109    EXPECT_TRUE(obj5Class->HasReferenceField());
110}
111
112HWTEST_F_L0(JSHClassTest, Clone)
113{
114    EcmaVM *vm = thread->GetEcmaVM();
115    ObjectFactory *factory = vm->GetFactory();
116    JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
117
118    JSHandle<JSHClass> objectClass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, nullHandle);
119    // withoutInlinedProperties is false
120    JSHandle<JSHClass> cloneClass = JSHClass::Clone(thread, objectClass, false);
121    EXPECT_TRUE(*cloneClass != nullptr);
122    EXPECT_TRUE(objectClass->GetObjectSize() == cloneClass->GetObjectSize());
123    EXPECT_EQ(cloneClass->GetObjectSize(), 64U); // 64 : 64 not missing the size of inlinedproperties
124    EXPECT_TRUE(objectClass->GetLayout() == cloneClass->GetLayout());
125    EXPECT_EQ(JSTaggedValue::SameValue(objectClass->GetPrototype(), cloneClass->GetPrototype()), true);
126    EXPECT_TRUE(objectClass->GetBitField() == cloneClass->GetBitField());
127    EXPECT_TRUE(objectClass->GetBitField1() == cloneClass->GetBitField1());
128    EXPECT_TRUE(objectClass->NumberOfProps() == cloneClass->NumberOfProps());
129    EXPECT_EQ(cloneClass->GetNextInlinedPropsIndex(), 0); // 0 : 0 mean index
130    // withoutInlinedProperties is true
131    cloneClass = JSHClass::Clone(thread, objectClass, true);
132    EXPECT_TRUE(*cloneClass != nullptr);
133    EXPECT_TRUE(objectClass->GetObjectSize() > cloneClass->GetObjectSize());
134    EXPECT_EQ(cloneClass->GetObjectSize(), 32U); // 32 : 32 missing the size of inlinedproperties
135    EXPECT_TRUE(objectClass->GetLayout() == cloneClass->GetLayout());
136    EXPECT_EQ(JSTaggedValue::SameValue(objectClass->GetPrototype(), cloneClass->GetPrototype()), true);
137    EXPECT_TRUE(objectClass->GetBitField() == cloneClass->GetBitField());
138    EXPECT_TRUE(objectClass->GetBitField1() > cloneClass->GetBitField1());
139    EXPECT_TRUE(objectClass->NumberOfProps() == cloneClass->NumberOfProps());
140    EXPECT_EQ(cloneClass->GetNextNonInlinedPropsIndex(), 0); // 0 : 0 mean index
141}
142
143HWTEST_F_L0(JSHClassTest, TransitionElementsToDictionary)
144{
145    EcmaVM *vm = thread->GetEcmaVM();
146    ObjectFactory *factory = vm->GetFactory();
147
148    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
149    JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
150    JSHandle<JSObject> jsObject = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
151    JSHandle<JSHClass> objectClass(thread, jsObject->GetJSHClass());
152    JSHandle<JSTaggedValue> objKey1(factory->NewFromASCII("key1"));
153    JSHandle<JSTaggedValue> objKey2(factory->NewFromASCII("key2"));
154    JSHandle<JSTaggedValue> objKey3(factory->NewFromASCII("key3"));
155
156    JSHandle<JSTaggedValue> objValue1(thread, JSTaggedValue(1));
157    JSHandle<JSTaggedValue> objValue2(thread, JSTaggedValue(2));
158    JSHandle<JSTaggedValue> objValue3(thread, JSTaggedValue(3));
159
160    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(jsObject), objKey1, objValue1);
161    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(jsObject), objKey2, objValue2);
162    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(jsObject), objKey3, objValue3);
163    // class is not dictionary mode
164    EXPECT_FALSE(jsObject->GetJSHClass()->IsDictionaryMode());
165    ElementsKind oldKind = jsObject->GetJSHClass()->GetElementsKind();
166    JSHClass::TransitionElementsToDictionary(thread, jsObject);
167    JSObject::TryMigrateToGenericKindForJSObject(thread, jsObject, oldKind);
168    auto resultDict = NameDictionary::Cast(jsObject->GetProperties().GetTaggedObject());
169    EXPECT_TRUE(resultDict != nullptr);
170    EXPECT_EQ(resultDict->EntriesCount(), 3); // 3 : 3 entry
171
172    JSHandle<JSHClass> dictionaryClass(thread, jsObject->GetJSHClass());
173    EXPECT_TRUE(dictionaryClass->IsDictionaryMode());
174    EXPECT_EQ(dictionaryClass->GetObjectSize() + 32U, objectClass->GetObjectSize());
175    EXPECT_TRUE(dictionaryClass->IsDictionaryElement());
176    EXPECT_FALSE(dictionaryClass->IsStableElements());
177}
178
179static JSHandle<JSHClass> CreateJSHClass(JSThread *thread)
180{
181    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
182    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
183    JSHandle<JSTaggedValue> objectFuncPrototype = env->GetObjectFunctionPrototype();
184    JSHandle<JSHClass> hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, objectFuncPrototype);
185    return hclass;
186}
187
188HWTEST_F_L0(JSHClassTest, SetPropertyOfObjHClass_001)
189{
190    EcmaVM *vm = thread->GetEcmaVM();
191    ObjectFactory *factory = vm->GetFactory();
192    JSHandle<JSTaggedValue> accessorData(factory->NewAccessorData());
193    JSHandle<EcmaString> keyHandle = factory->NewFromASCII("key");
194    JSHandle<JSTaggedValue> keyHandle0(factory->NewFromASCII("key0"));
195    JSHandle<JSTaggedValue> keyHandle2(factory->NewFromASCII("key2"));
196    JSHandle<JSTaggedValue> keyHandle4(factory->NewFromASCII("key4"));
197    // empty layoutInfo
198    JSHandle<JSHClass> parentsClass = CreateJSHClass(thread);
199
200    uint32_t length = 6;
201    JSHandle<TaggedArray> properties = factory->NewTaggedArray(length);
202    for (int i = 0; i < static_cast<int>(length); i++) {
203        if (i % 2 == 0) {
204            JSHandle<JSTaggedValue> newValue(thread, JSTaggedValue(i));
205            JSHandle<EcmaString> newKey =
206                factory->ConcatFromString(keyHandle, JSTaggedValue::ToString(thread, newValue));
207            properties->Set(thread, i, newKey.GetTaggedValue());
208            continue;
209        }
210        properties->Set(thread, i, accessorData.GetTaggedValue());
211    }
212    JSHandle<JSHClass> childClass = factory->SetLayoutInObjHClass(properties, 3, parentsClass);
213    JSHandle<JSObject> childObj = factory->NewJSObject(childClass);
214
215    std::vector<JSTaggedValue> keyVector;
216    JSObject::GetAllKeysForSerialization(childObj, keyVector);
217    EXPECT_EQ(keyVector.size(), 3U);
218    EXPECT_TRUE(JSObject::HasProperty(thread, childObj, keyHandle0));
219    EXPECT_TRUE(JSObject::HasProperty(thread, childObj, keyHandle2));
220    EXPECT_TRUE(JSObject::HasProperty(thread, childObj, keyHandle4));
221}
222
223HWTEST_F_L0(JSHClassTest, SetPropertyOfObjHClass_002)
224{
225    EcmaVM *vm = thread->GetEcmaVM();
226    ObjectFactory *factory = vm->GetFactory();
227    JSHandle<JSHClass> objClass = CreateJSHClass(thread);
228    JSHandle<JSObject> Obj1 = factory->NewJSObject(objClass);
229    JSHandle<JSObject> Obj2 = factory->NewJSObject(objClass);
230    PropertyAttributes attr = PropertyAttributes::Default();
231
232    JSHandle<JSTaggedValue> keyE(factory->NewFromASCII("e"));
233    JSHandle<JSTaggedValue> keyF(factory->NewFromASCII("f"));
234    // not empty layoutInfo
235    auto valueE = JSHandle<JSTaggedValue>(thread, JSTaggedValue(7));
236    auto valueF = JSHandle<JSTaggedValue>(thread, JSTaggedValue(8));
237    JSObject::SetProperty(thread, Obj1, keyE, valueE);
238    JSObject::SetProperty(thread, Obj2, keyF, valueE);
239
240    auto repE = PropertyAttributes::TranslateToRep(valueE.GetTaggedValue());
241    JSHandle<JSHClass> propertyHclass = JSHClass::SetPropertyOfObjHClass(thread, objClass, keyE, attr, repE);
242    JSHandle<JSHClass> obj1Class(thread, Obj1->GetClass());
243    EXPECT_TRUE(propertyHclass == obj1Class);
244
245    auto repF = PropertyAttributes::TranslateToRep(valueF.GetTaggedValue());
246    propertyHclass = JSHClass::SetPropertyOfObjHClass(thread, objClass, keyF, attr, repF);
247    JSHandle<JSHClass> obj2Class(thread, Obj2->GetClass());
248    EXPECT_TRUE(propertyHclass == obj2Class);
249}
250
251HWTEST_F_L0(JSHClassTest, AddProperty)
252{
253    EcmaVM *vm = thread->GetEcmaVM();
254    ObjectFactory *factory = vm->GetFactory();
255    JSHandle<EcmaString> keyHandle = factory->NewFromASCII("key");
256    JSHandle<JSTaggedValue> keyHandle0(factory->NewFromASCII("key0"));
257    JSHandle<JSTaggedValue> keyHandle1(factory->NewFromASCII("key1"));
258    JSHandle<JSTaggedValue> keyHandle2(factory->NewFromASCII("key2"));
259    PropertyAttributes attr = PropertyAttributes::Default();
260    attr.SetIsInlinedProps(true);
261    // empty layoutInfo
262    JSHandle<JSHClass> objClass1 = CreateJSHClass(thread);
263    JSHandle<JSObject> Obj = factory->NewJSObject(objClass1);
264    JSHandle<JSHClass> objClass(thread, Obj->GetClass());
265    EXPECT_FALSE(objClass1 != objClass);
266    int keyLength = 3;
267    for (int i = 0; i <keyLength; i++) {
268        JSHandle<JSTaggedValue> keyValue(thread, JSTaggedValue(i));
269        JSHandle<JSTaggedValue> keyHandleI(
270            factory->ConcatFromString(keyHandle, JSTaggedValue::ToString(thread, keyValue)));
271        attr.SetOffset(i);
272        ElementsKind oldKind = Obj->GetJSHClass()->GetElementsKind();
273        JSHClass::AddProperty(thread, Obj, keyHandleI, attr);
274        JSObject::TryMigrateToGenericKindForJSObject(thread, Obj, oldKind);
275    }
276    EXPECT_TRUE(objClass1 == objClass);
277    std::vector<JSTaggedValue> keyVector;
278    JSObject::GetAllKeysForSerialization(Obj, keyVector);
279    EXPECT_EQ(keyVector.size(), 3U);
280    EXPECT_TRUE(JSObject::HasProperty(thread, Obj, keyHandle0));
281    EXPECT_TRUE(JSObject::HasProperty(thread, Obj, keyHandle1));
282    EXPECT_TRUE(JSObject::HasProperty(thread, Obj, keyHandle2));
283}
284
285HWTEST_F_L0(JSHClassTest, TransitionExtension)
286{
287    EcmaVM *vm = thread->GetEcmaVM();
288    ObjectFactory *factory = vm->GetFactory();
289    JSHandle<JSTaggedValue> preExtensionsKey = thread->GlobalConstants()->GetHandledPreventExtensionsString();
290    JSHandle<JSTaggedValue> keyHandle0(factory->NewFromASCII("key0"));
291    JSHandle<JSTaggedValue> keyHandle1(factory->NewFromASCII("key1"));
292    JSHandle<JSTaggedValue> keyHandle2(factory->NewFromASCII("key2"));
293    PropertyAttributes attr = PropertyAttributes(0);
294    attr.SetIsInlinedProps(true);
295    JSHandle<JSHClass> obj1Class = CreateJSHClass(thread);
296    JSHandle<JSHClass> obj2Class = CreateJSHClass(thread);
297    obj2Class->SetExtensible(true);
298    JSHandle<JSObject> Obj1 = factory->NewJSObject(obj1Class);
299    JSHandle<JSObject> Obj2 = factory->NewJSObject(obj2Class);
300    JSObject::SetProperty(thread, Obj2, keyHandle0, JSHandle<JSTaggedValue>(thread, JSTaggedValue(7)));
301    JSObject::SetProperty(thread, Obj2, keyHandle1, JSHandle<JSTaggedValue>(thread, JSTaggedValue(8)));
302    JSObject::SetProperty(thread, Obj2, keyHandle2, JSHandle<JSTaggedValue>(thread, JSTaggedValue(9)));
303    // obj has key "PreventExtensions"
304    ElementsKind oldKind = Obj1->GetJSHClass()->GetElementsKind();
305    JSHClass::AddProperty(thread, Obj1, preExtensionsKey, attr);
306    JSObject::TryMigrateToGenericKindForJSObject(thread, Obj1, oldKind);
307    JSHandle<JSHClass> newClass1 = JSHClass::TransitionExtension(thread, obj1Class);
308    JSHandle<JSHClass> objClass(thread, Obj1->GetClass());
309    EXPECT_TRUE(newClass1 == objClass);
310    // obj has no key "PreventExtensions"
311    JSHandle<JSHClass> newClass2 = JSHClass::TransitionExtension(thread, obj2Class);
312    EXPECT_FALSE(newClass2->IsExtensible());
313    JSHandle<TransitionsDictionary> dictionary(thread, obj2Class->GetTransitions());
314    // find key
315    std::vector<JSTaggedValue> keyVector;
316    dictionary->GetAllKeysIntoVector(keyVector);
317    EXPECT_TRUE((keyVector[0] == keyHandle0.GetTaggedValue()) || (keyVector[0] == preExtensionsKey.GetTaggedValue()));
318    EXPECT_TRUE((keyVector[1] == keyHandle0.GetTaggedValue()) || (keyVector[1] == preExtensionsKey.GetTaggedValue()));
319}
320
321HWTEST_F_L0(JSHClassTest, TransitionProto)
322{
323    EcmaVM *vm = thread->GetEcmaVM();
324    ObjectFactory *factory = vm->GetFactory();
325    JSHandle<GlobalEnv> env =vm->GetGlobalEnv();
326    JSHandle<JSTaggedValue> funcPrototype = env->GetFunctionPrototype();
327    JSHandle<JSTaggedValue> prototypeKey = thread->GlobalConstants()->GetHandledPrototypeString();
328    JSHandle<JSTaggedValue> obj1Key(factory->NewFromASCII("key0"));
329    JSHandle<JSTaggedValue> obj2Key(factory->NewFromASCII("key1"));
330    JSHandle<JSTaggedValue> obj3Key(factory->NewFromASCII("key2"));
331    PropertyAttributes attr = PropertyAttributes(0);
332    attr.SetIsInlinedProps(true);
333    JSHandle<JSHClass> objClass = CreateJSHClass(thread);
334    JSHandle<JSObject> Obj = factory->NewJSObject(objClass);
335    // obj has no key "prototype"
336    ElementsKind oldKind = Obj->GetJSHClass()->GetElementsKind();
337    JSHClass::AddProperty(thread, Obj, obj1Key, attr);
338    JSObject::TryMigrateToGenericKindForJSObject(thread, Obj, oldKind);
339    oldKind = Obj->GetJSHClass()->GetElementsKind();
340    JSHClass::AddProperty(thread, Obj, obj2Key, attr);
341    JSObject::TryMigrateToGenericKindForJSObject(thread, Obj, oldKind);
342    oldKind = Obj->GetJSHClass()->GetElementsKind();
343    JSHClass::AddProperty(thread, Obj, obj3Key, attr);
344    JSObject::TryMigrateToGenericKindForJSObject(thread, Obj, oldKind);
345    JSHandle<JSHClass> newClass = JSHClass::TransitionProto(thread, objClass, funcPrototype);
346    EXPECT_EQ(newClass->GetPrototype(), funcPrototype.GetTaggedValue());
347    JSHandle<TransitionsDictionary> transitionDictionary(thread, objClass->GetTransitions());
348    // find key
349    std::vector<JSTaggedValue> keyVector;
350    transitionDictionary->GetAllKeysIntoVector(keyVector);
351    EXPECT_EQ(keyVector.size(), 2U);
352    EXPECT_EQ(keyVector[0], obj1Key.GetTaggedValue());
353    EXPECT_EQ(keyVector[1], prototypeKey.GetTaggedValue());
354}
355
356HWTEST_F_L0(JSHClassTest, TransitionToDictionary)
357{
358    EcmaVM *vm = thread->GetEcmaVM();
359    ObjectFactory *factory = vm->GetFactory();
360    JSHandle<JSTaggedValue> obj1Key(factory->NewFromASCII("key1"));
361    JSHandle<JSTaggedValue> obj2Key(factory->NewFromASCII("key2"));
362    JSHandle<JSTaggedValue> obj3Key(factory->NewFromASCII("key3"));
363    JSHandle<JSObject> nullHandle(thread, JSTaggedValue::Null());
364    JSHandle<JSHClass> objClass = CreateJSHClass(thread);
365    objClass->SetIsPrototype(true);
366    JSHandle<JSObject> Obj0 = factory->NewJSObject(objClass);
367    JSHandle<JSHClass> obj0Class1(thread, Obj0->GetJSHClass());
368    JSHandle<JSObject> Obj1 = JSObject::ObjectCreate(thread, nullHandle);
369    JSHandle<JSObject> Obj2 = JSObject::ObjectCreate(thread, Obj1);
370    JSHandle<JSHClass> obj2Class(thread, Obj2->GetJSHClass());
371    JSHandle<JSObject> Obj3 = JSObject::ObjectCreate(thread, Obj2);
372    JSObject::SetProperty(thread, Obj1, obj1Key, JSHandle<JSTaggedValue>(thread, JSTaggedValue(100)));
373    JSObject::SetProperty(thread, Obj2, obj2Key, JSHandle<JSTaggedValue>(thread, JSTaggedValue(101)));
374    JSObject::SetProperty(thread, Obj3, obj3Key, JSHandle<JSTaggedValue>(thread, JSTaggedValue(102)));
375    // empty object
376    JSObject::TransitionToDictionary(thread, Obj0);
377    JSHandle<JSHClass> obj0Class(thread, Obj0->GetClass());
378    EXPECT_TRUE(obj0Class->GetObjectSize() < obj0Class1->GetObjectSize());
379    EXPECT_EQ(obj0Class->NumberOfProps(), 0U);
380    EXPECT_TRUE(obj0Class->IsDictionaryMode());
381    EXPECT_TRUE(obj0Class->IsPrototype());
382    // not empty object
383    JSHandle<JSHClass> obj3Class(thread, Obj3->GetJSHClass());
384    JSHClass::EnableProtoChangeMarker(thread, obj3Class);
385    JSObject::TransitionToDictionary(thread, Obj2);
386    // refresh users
387    JSHandle<JSHClass> obj1Class(thread, Obj1->GetJSHClass());
388    JSTaggedValue protoDetails = obj1Class->GetProtoChangeDetails();
389    EXPECT_TRUE(protoDetails.IsProtoChangeDetails());
390    JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetails.GetTaggedObject())->GetChangeListener();
391    JSHandle<ChangeListener> listeners(thread, listenersValue.GetTaggedObject());
392    uint32_t holeIndex = ChangeListener::CheckHole(listeners);
393    EXPECT_TRUE(holeIndex == 0U);
394    // new class
395    JSHandle<JSHClass> newClass(thread, Obj2->GetClass());
396    EXPECT_TRUE(newClass->GetObjectSize() < obj2Class->GetObjectSize());
397    EXPECT_EQ(newClass->NumberOfProps(), 0U);
398    EXPECT_TRUE(newClass->IsDictionaryMode());
399    EXPECT_TRUE(newClass->IsPrototype());
400}
401
402HWTEST_F_L0(JSHClassTest, UpdatePropertyMetaData)
403{
404    EcmaVM *vm = thread->GetEcmaVM();
405    ObjectFactory *factory = vm->GetFactory();
406    JSHandle<JSTaggedValue> objKey(factory->NewFromASCII("key0"));
407    PropertyAttributes oldAttr = PropertyAttributes(0);
408    PropertyAttributes newAttr = PropertyAttributes(1);
409    oldAttr.SetIsInlinedProps(true);
410    JSHandle<JSHClass> objClass = CreateJSHClass(thread);
411    JSHandle<JSObject> Obj = factory->NewJSObject(objClass);
412    // Set Transitions
413    ElementsKind oldKind = Obj->GetJSHClass()->GetElementsKind();
414    JSHClass::AddProperty(thread, Obj, objKey, oldAttr);
415    JSObject::TryMigrateToGenericKindForJSObject(thread, Obj, oldKind);
416    // update metaData
417    objClass->UpdatePropertyMetaData(thread, objKey.GetTaggedValue(), newAttr);
418    LayoutInfo *layoutInfo = LayoutInfo::Cast(objClass->GetLayout().GetTaggedObject());
419    EXPECT_EQ(layoutInfo->GetAttr(oldAttr.GetOffset()).GetPropertyMetaData(), newAttr.GetPropertyMetaData());
420}
421
422HWTEST_F_L0(JSHClassTest, SetPrototype)
423{
424    EcmaVM *vm = thread->GetEcmaVM();
425    ObjectFactory *factory = vm->GetFactory();
426    JSHandle<GlobalEnv> env =vm->GetGlobalEnv();
427    JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
428    JSHandle<JSTaggedValue> objectFuncPrototype = env->GetObjectFunctionPrototype();
429
430    JSHandle<JSHClass> objectClass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, nullHandle);
431    EXPECT_EQ(objectClass->GetPrototype(), nullHandle.GetTaggedValue());
432    objectClass->SetPrototype(thread, objectFuncPrototype);
433    EXPECT_EQ(objectClass->GetPrototype(), objectFuncPrototype.GetTaggedValue());
434}
435
436HWTEST_F_L0(JSHClassTest, ProxyHClassClone)
437{
438    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
439
440    JSHandle<JSFunction> objectFn(thread->GetGlobalEnv()->GetObjectFunction());
441    JSHandle<JSObject> target = factory->NewJSObjectByConstructor(objectFn, JSHandle<JSTaggedValue>(objectFn));
442    JSHandle<JSObject> handler = factory->NewJSObjectByConstructor(objectFn, JSHandle<JSTaggedValue>(objectFn));
443
444    JSHandle<JSProxy> proxy = JSProxy::ProxyCreate(thread,
445        JSHandle<JSTaggedValue>(target), JSHandle<JSTaggedValue>(handler));
446
447    JSHandle<JSHClass> hclass(thread, proxy->GetClass());
448    EXPECT_FALSE(hclass->IsJSObject());
449    EXPECT_GT(hclass->GetObjectSize(), 0);
450    EXPECT_EQ(hclass->GetInlinedProperties(), 0);
451
452    JSHandle<JSHClass> newHClass = JSHClass::Clone(thread, hclass);
453    EXPECT_EQ(newHClass->GetObjectType(), hclass->GetObjectType());
454    EXPECT_EQ(newHClass->GetObjectSize(), hclass->GetObjectSize());
455    EXPECT_EQ(newHClass->GetInlinedPropsStartSize(), hclass->GetInlinedPropsStartSize());
456    EXPECT_EQ(newHClass->GetInlinedProperties(), hclass->GetInlinedProperties());
457    EXPECT_EQ(newHClass->GetLayout(), hclass->GetLayout());
458}
459}  // namespace panda::test
460