1/*
2 * Copyright (c) 2021-2024 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 <algorithm>
17
18#include "ecmascript/ecma_context.h"
19#include "ecmascript/global_env_constants-inl.h"
20#include "ecmascript/pgo_profiler/pgo_profiler.h"
21#include "ecmascript/pgo_profiler/pgo_profiler_layout.h"
22#include "ecmascript/ic/proto_change_details.h"
23#include "ecmascript/js_function.h"
24#include "ecmascript/js_object-inl.h"
25
26namespace panda::ecmascript {
27using ProfileType = pgo::ProfileType;
28
29JSHandle<TransitionsDictionary> TransitionsDictionary::PutIfAbsent(const JSThread *thread,
30                                                                   const JSHandle<TransitionsDictionary> &dictionary,
31                                                                   const JSHandle<JSTaggedValue> &key,
32                                                                   const JSHandle<JSTaggedValue> &value,
33                                                                   const JSHandle<JSTaggedValue> &metaData)
34{
35    uint32_t hash = static_cast<uint32_t>(TransitionsDictionary::Hash(key.GetTaggedValue(), metaData.GetTaggedValue()));
36
37    /* no need to add key if exist */
38    int entry = dictionary->FindEntry(key.GetTaggedValue(), metaData.GetTaggedValue());
39    if (entry != -1) {
40        if (dictionary->GetValue(entry).IsUndefined()) {
41            JSTaggedValue weakValue = JSTaggedValue(value->CreateAndGetWeakRef());
42            dictionary->SetValue(thread, entry, weakValue);
43        }
44        return dictionary;
45    }
46
47    // Check whether the dictionary should be extended.
48    JSHandle<TransitionsDictionary> newDictionary(HashTableT::GrowHashTable(thread, dictionary));
49    // Compute the key object.
50    entry = newDictionary->FindInsertIndex(hash);
51    JSTaggedValue val = value.GetTaggedValue();
52    newDictionary->SetEntry(thread, entry, key.GetTaggedValue(), val, metaData.GetTaggedValue());
53
54    newDictionary->IncreaseEntries(thread);
55    return newDictionary;
56}
57
58int TransitionsDictionary::FindEntry(const JSTaggedValue &key, const JSTaggedValue &metaData)
59{
60    size_t size = static_cast<size_t>(Size());
61    uint32_t count = 1;
62    int32_t hash = TransitionsDictionary::Hash(key, metaData);
63    // GrowHashTable will guarantee the hash table is never full.
64    for (uint32_t entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) {
65        JSTaggedValue element = GetKey(entry);
66        if (element.IsHole()) {
67            continue;
68        }
69        if (element.IsUndefined()) {
70            return -1;
71        }
72
73        if (TransitionsDictionary::IsMatch(key, metaData, element, GetAttributes(entry).GetWeakRawValue())) {
74            return static_cast<int>(entry);
75        }
76    }
77    return -1;
78}
79
80JSHandle<TransitionsDictionary> TransitionsDictionary::Remove(const JSThread *thread,
81                                                              const JSHandle<TransitionsDictionary> &table,
82                                                              const JSHandle<JSTaggedValue> &key,
83                                                              const JSTaggedValue &metaData)
84{
85    int entry = table->FindEntry(key.GetTaggedValue(), metaData);
86    if (entry == -1) {
87        return table;
88    }
89
90    table->RemoveElement(thread, entry);
91    return TransitionsDictionary::Shrink(thread, table);
92}
93
94void TransitionsDictionary::Rehash(const JSThread *thread, TransitionsDictionary *newTable)
95{
96    DISALLOW_GARBAGE_COLLECTION;
97    if (newTable == nullptr) {
98        return;
99    }
100    int size = this->Size();
101    // Rehash elements to new table
102    int entryCount = 0;
103    for (int i = 0; i < size; i++) {
104        int fromIndex = GetEntryIndex(i);
105        JSTaggedValue k = this->GetKey(i);
106        JSTaggedValue v = this->GetValue(i);
107        if (IsKey(k) && TransitionsDictionary::CheckWeakExist(v)) {
108            uint32_t hash = static_cast<uint32_t>(TransitionsDictionary::Hash(k, this->GetAttributes(i)));
109            int insertionIndex = GetEntryIndex(newTable->FindInsertIndex(hash));
110            JSTaggedValue tv = Get(fromIndex);
111            newTable->Set(thread, insertionIndex, tv);
112            for (int j = 1; j < TransitionsDictionary::ENTRY_SIZE; j++) {
113                tv = Get(fromIndex + j);
114                newTable->Set(thread, insertionIndex + j, tv);
115            }
116            entryCount++;
117        }
118    }
119    newTable->SetEntriesCount(thread, entryCount);
120    newTable->SetHoleEntriesCount(thread, 0);
121}
122
123void JSHClass::InitializeWithDefaultValue(const JSThread *thread, uint32_t size, JSType type, uint32_t inlinedProps)
124{
125    DISALLOW_GARBAGE_COLLECTION;
126    ClearBitField();
127    if (IsJSTypeObject(type)) {
128        SetObjectSize(size + inlinedProps * JSTaggedValue::TaggedTypeSize());
129        SetInlinedPropsStart(size);
130    } else {
131        SetObjectSize(size);
132    }
133    SetLayout(thread, JSTaggedValue::Null());
134    if (type >= JSType::JS_FUNCTION_FIRST && type <= JSType::JS_FUNCTION_LAST) {
135        SetIsJSFunction(true);
136    }
137    SetPrototype(thread, JSTaggedValue::Null());
138
139    SetObjectType(type);
140    SetExtensible(true);
141    SetIsPrototype(false);
142    SetHasDeleteProperty(false);
143    SetIsAllTaggedProp(true);
144    SetElementsKind(ElementsKind::GENERIC);
145    SetTransitions(thread, JSTaggedValue::Undefined());
146    SetParent(thread, JSTaggedValue::Undefined());
147    SetProtoChangeMarker(thread, JSTaggedValue::Null());
148    SetProtoChangeDetails(thread, JSTaggedValue::Null());
149    SetEnumCache(thread, JSTaggedValue::Null());
150    SetLevel(0);
151}
152
153bool JSHClass::IsJSTypeShared(JSType type)
154{
155    bool isShared = false;
156    switch (type) {
157        case JSType::JS_SHARED_OBJECT:
158        case JSType::JS_SHARED_FUNCTION:
159        case JSType::JS_SHARED_ASYNC_FUNCTION:
160        case JSType::JS_SHARED_SET:
161        case JSType::JS_SHARED_MAP:
162        case JSType::JS_SHARED_ARRAY:
163        case JSType::JS_SHARED_TYPED_ARRAY:
164        case JSType::JS_SHARED_INT8_ARRAY:
165        case JSType::JS_SHARED_UINT8_ARRAY:
166        case JSType::JS_SHARED_UINT8_CLAMPED_ARRAY:
167        case JSType::JS_SHARED_INT16_ARRAY:
168        case JSType::JS_SHARED_UINT16_ARRAY:
169        case JSType::JS_SHARED_INT32_ARRAY:
170        case JSType::JS_SHARED_UINT32_ARRAY:
171        case JSType::JS_SHARED_FLOAT32_ARRAY:
172        case JSType::JS_SHARED_FLOAT64_ARRAY:
173        case JSType::JS_SHARED_BIGINT64_ARRAY:
174        case JSType::JS_SHARED_BIGUINT64_ARRAY:
175        case JSType::JS_SENDABLE_ARRAY_BUFFER:
176        case JSType::BIGINT:
177        case JSType::LINE_STRING:
178        case JSType::CONSTANT_STRING:
179        case JSType::SLICED_STRING:
180        case JSType::TREE_STRING:
181            isShared = true;
182            break;
183        default:
184            break;
185    }
186    return isShared;
187}
188
189// class JSHClass
190void JSHClass::Initialize(const JSThread *thread, uint32_t size, JSType type, uint32_t inlinedProps)
191{
192    InitializeWithDefaultValue(thread, size, type, inlinedProps);
193    if (IsJSTypeObject(type)) {
194        SetLayout(thread, thread->GlobalConstants()->GetEmptyLayoutInfo());
195    }
196}
197
198// for sharedHeap
199void JSHClass::Initialize(const JSThread *thread, uint32_t size, JSType type,
200    uint32_t inlinedProps, const JSHandle<JSTaggedValue> &layout)
201{
202    InitializeWithDefaultValue(thread, size, type, inlinedProps);
203    if (IsJSTypeObject(type)) {
204        SetLayout(thread, layout);
205    }
206    if (IsJSTypeShared(type)) {
207        SetIsJSShared(true);
208    }
209}
210
211JSHandle<JSHClass> JSHClass::Clone(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
212                                   bool withoutInlinedProperties, uint32_t incInlinedProperties)
213{
214    JSType type = jshclass->GetObjectType();
215    uint32_t size = IsJSTypeObject(type) ? jshclass->GetInlinedPropsStartSize() : jshclass->GetObjectSize();
216    uint32_t numInlinedProps = withoutInlinedProperties ? 0 : jshclass->GetInlinedProperties() + incInlinedProperties;
217    JSHandle<JSHClass> newJsHClass;
218    if (jshclass.GetTaggedValue().IsInSharedHeap()) {
219        newJsHClass = thread->GetEcmaVM()->GetFactory()->NewSEcmaHClass(size, type, numInlinedProps);
220    } else {
221        newJsHClass = thread->GetEcmaVM()->GetFactory()->NewEcmaHClass(size, type, numInlinedProps);
222    }
223    // Copy all
224    newJsHClass->Copy(thread, *jshclass);
225    newJsHClass->SetTransitions(thread, JSTaggedValue::Undefined());
226    newJsHClass->SetParent(thread, JSTaggedValue::Undefined());
227    newJsHClass->SetProtoChangeDetails(thread, JSTaggedValue::Null());
228    newJsHClass->SetEnumCache(thread, JSTaggedValue::Null());
229    // reuse Attributes first.
230    newJsHClass->SetLayout(thread, jshclass->GetLayout());
231
232    if (jshclass->IsTS()) {
233        newJsHClass->SetTS(false);
234    }
235
236    return newJsHClass;
237}
238
239JSHandle<JSHClass> JSHClass::CloneWithElementsKind(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
240                                                   const ElementsKind kind, bool isPrototype)
241{
242    JSHandle<JSHClass> newHClass = Clone(thread, jshclass);
243    newHClass->SetIsPrototype(isPrototype);
244    newHClass->SetElementsKind(kind);
245    return newHClass;
246}
247
248// use for transition to dictionary
249JSHandle<JSHClass> JSHClass::CloneWithoutInlinedProperties(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
250{
251    return Clone(thread, jshclass, true);
252}
253
254void JSHClass::TransitionElementsToDictionary(const JSThread *thread, const JSHandle<JSObject> &obj)
255{
256    // property transition to slow first
257    if (!obj->GetJSHClass()->IsDictionaryMode()) {
258        JSObject::TransitionToDictionary(thread, obj);
259        RETURN_IF_ABRUPT_COMPLETION(thread);
260    } else {
261        TransitionToDictionary(thread, obj);
262    }
263    obj->GetJSHClass()->SetIsDictionaryElement(true);
264    obj->GetJSHClass()->SetIsStableElements(false);
265    obj->GetJSHClass()->SetElementsKind(ElementsKind::DICTIONARY);
266}
267
268void JSHClass::OptimizeAsFastElements(const JSThread *thread, JSHandle<JSObject> obj)
269{
270    if (obj->GetJSHClass()->IsDictionaryMode()) {
271        JSObject::OptimizeAsFastProperties(thread, obj);
272    } else {
273        OptimizeAsFastProperties(thread, obj);
274    }
275    obj->GetJSHClass()->SetIsDictionaryElement(false);
276    obj->GetJSHClass()->SetIsStableElements(true);
277    obj->GetJSHClass()->SetElementsKind(ElementsKind::HOLE_TAGGED);
278}
279
280void JSHClass::AddProperty(const JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
281                           const PropertyAttributes &attr, const Representation &rep)
282{
283    JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
284    auto metadata = JSTaggedValue(attr.GetPropertyMetaData());
285    JSHClass *newClass = jshclass->FindTransitions(key.GetTaggedValue(), metadata, rep);
286    if (newClass != nullptr) {
287        // The transition hclass from AOT, which does not have a prototype, needs to be reset here.
288        if (newClass->IsTS()) {
289            newClass->SetPrototype(thread, jshclass->GetPrototype());
290        }
291        // Because we currently only supports Fast ElementsKind
292        RestoreElementsKindToGeneric(newClass);
293        obj->SynchronizedSetClass(thread, newClass);
294#if ECMASCRIPT_ENABLE_IC
295        // The transition hclass from AOT, which does not have protochangemarker, needs to be reset here
296        JSHandle<JSHClass> newHClass = JSHandle<JSHClass>(thread, newClass);
297        if (newClass->IsTS() && newClass->IsPrototype()) {
298            if (JSHClass::IsNeedNotifyHclassChangedForAotTransition(thread, jshclass, key.GetTaggedValue())) {
299                JSHClass::EnableProtoChangeMarker(thread, newHClass);
300                JSHClass::NotifyHclassChanged(thread, jshclass, newHClass, key.GetTaggedValue());
301            } else {
302                JSHClass::RefreshUsers(thread, jshclass, newHClass);
303            }
304            JSHClass::EnablePHCProtoChangeMarker(thread, newHClass);
305        } else {
306            JSHClass::NotifyHclassChanged(thread, jshclass, newHClass, key.GetTaggedValue());
307        }
308#endif
309        return;
310    }
311    JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
312    RestoreElementsKindToGeneric(*newJsHClass);
313    AddPropertyToNewHClass(thread, jshclass, newJsHClass, key, attr);
314    // update hclass in object.
315#if ECMASCRIPT_ENABLE_IC
316    JSHClass::NotifyHclassChanged(thread, jshclass, newJsHClass, key.GetTaggedValue());
317#endif
318    // Because we currently only supports Fast ElementsKind
319    obj->SynchronizedSetClass(thread, *newJsHClass);
320}
321
322JSHandle<JSHClass> JSHClass::TransitionExtension(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
323{
324    JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPreventExtensionsString());
325    {
326        auto *newClass = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(0), Representation::NONE);
327        if (newClass != nullptr) {
328            newClass->SetPrototype(thread, jshclass->GetPrototype());
329            return JSHandle<JSHClass>(thread, newClass);
330        }
331    }
332    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
333    // 2. new a hclass
334    JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
335    newJsHClass->SetExtensible(false);
336
337    JSTaggedValue attrs = newJsHClass->GetLayout();
338    {
339        JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, attrs);
340        layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
341        newJsHClass->SetLayout(thread, layoutInfoHandle);
342    }
343
344    // 3. Add newClass to old hclass's parent's transitions.
345    AddExtensionTransitions(thread, jshclass, newJsHClass, key);
346    // parent is the same as jshclass, already copy
347    return newJsHClass;
348}
349
350JSHandle<JSHClass> JSHClass::TransitionProto(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
351                                             const JSHandle<JSTaggedValue> &proto, bool isChangeProto)
352{
353    JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPrototypeString());
354
355    {
356        auto *newClass = jshclass->FindProtoTransitions(key.GetTaggedValue(), proto.GetTaggedValue());
357        if (newClass != nullptr) {
358            return JSHandle<JSHClass>(thread, newClass);
359        }
360        newClass = FindTransitionProtoForAOT(thread, jshclass, proto);
361        if (newClass != nullptr) {
362            return JSHandle<JSHClass>(thread, newClass);
363        }
364    }
365
366    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
367    // 2. new a hclass
368    JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
369    newJsHClass->SetPrototype(thread, proto.GetTaggedValue(), isChangeProto);
370
371    JSTaggedValue layout = newJsHClass->GetLayout();
372    {
373        JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, layout);
374        layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
375        newJsHClass->SetLayout(thread, layoutInfoHandle);
376    }
377
378    // 3. Add newJsHClass to old jshclass's parent's transitions.
379    AddProtoTransitions(thread, jshclass, newJsHClass, key, proto);
380
381    // parent is the same as jshclass, already copy
382    return newJsHClass;
383}
384
385// static
386JSHClass *JSHClass::FindTransitionProtoForAOT(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
387                                              const JSHandle<JSTaggedValue> &proto)
388{
389    if (!proto->IsECMAObject()) {
390        return nullptr;
391    }
392    JSHandle<JSHClass> baseIhc(thread, proto->GetTaggedObject()->GetClass());
393    if (!jshclass->IsTS() || !baseIhc->IsTS()) {
394        return nullptr;
395    }
396    auto transitionTable = thread->GetCurrentEcmaContext()->GetFunctionProtoTransitionTable();
397    auto transHc = transitionTable->FindTransitionByHClass(thread,
398                                                           JSHandle<JSTaggedValue>(jshclass),
399                                                           JSHandle<JSTaggedValue>(baseIhc));
400    JSHandle<JSTaggedValue> transIhc(thread, transHc.first);
401    JSHandle<JSTaggedValue> transPhc(thread, transHc.second);
402    if (transIhc->IsUndefined() || transPhc->IsUndefined()) {
403        return nullptr;
404    }
405    ReBuildFunctionInheritanceRelationship(thread, proto, JSHandle<JSTaggedValue>(baseIhc), transIhc, transPhc);
406    return JSHClass::Cast(transIhc->GetTaggedObject());
407}
408
409// static
410void JSHClass::ReBuildFunctionInheritanceRelationship(const JSThread *thread,
411                                                      const JSHandle<JSTaggedValue> &proto,
412                                                      const JSHandle<JSTaggedValue> &baseIhc,
413                                                      const JSHandle<JSTaggedValue> &transIhc,
414                                                      const JSHandle<JSTaggedValue> &transPhc)
415{
416    JSHandle<JSHClass>::Cast(transIhc)->SetProto(thread, proto.GetTaggedValue());
417    if (baseIhc.GetTaggedType() == transPhc.GetTaggedType()) {
418        return;
419    }
420    // use transPhc to replace the hclass of proto
421    JSHandle<JSHClass> oldPhc(thread, proto->GetTaggedObject()->GetClass());
422    proto->GetTaggedObject()->SynchronizedSetClass(thread, JSHClass::Cast(transPhc->GetTaggedObject()));
423    ASSERT(JSHClass::Cast(transPhc->GetTaggedObject())->IsPrototype());
424    // update the prototype of new phc
425    JSHClass::Cast(transPhc->GetTaggedObject())->SetPrototype(thread, oldPhc->GetPrototype());
426    // enable prototype change marker
427    JSTaggedValue phcPrototype = JSHClass::Cast(transPhc->GetTaggedObject())->GetPrototype();
428    JSHandle<JSTaggedValue> parentPrototype(thread, phcPrototype);
429    ASSERT(parentPrototype->IsECMAObject());
430    JSHClass::EnablePHCProtoChangeMarker(thread,
431        JSHandle<JSHClass>(thread, parentPrototype->GetTaggedObject()->GetClass()));
432    JSHClass::EnableProtoChangeMarker(thread, JSHandle<JSHClass>(transIhc));
433}
434
435JSHandle<JSHClass> JSHClass::CloneWithAddProto(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
436                                               const JSHandle<JSTaggedValue> &key,
437                                               const JSHandle<JSTaggedValue> &proto)
438{
439    // 1. new a hclass
440    JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
441    newJsHClass->SetPrototype(thread, proto.GetTaggedValue());
442
443    // 2. Add newJsHClass to old jshclass's parent's transitions.
444    AddProtoTransitions(thread, jshclass, newJsHClass, key, proto);
445    // parent is the same as jshclass, already copy
446    return newJsHClass;
447}
448
449JSHandle<JSHClass> JSHClass::TransProtoWithoutLayout(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
450                                                     const JSHandle<JSTaggedValue> &proto)
451{
452    JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPrototypeString());
453
454    {
455        auto *newClass = jshclass->FindProtoTransitions(key.GetTaggedValue(), proto.GetTaggedValue());
456        if (newClass != nullptr) {
457            return JSHandle<JSHClass>(thread, newClass);
458        }
459    }
460
461    return CloneWithAddProto(thread, jshclass, key, proto);
462}
463
464void JSHClass::SetPrototype(const JSThread *thread, JSTaggedValue proto, bool isChangeProto)
465{
466    // Because the heap-space of hclass is non-movable, this function can be non-static.
467    JSHandle<JSTaggedValue> protoHandle(thread, proto);
468    SetPrototype(thread, protoHandle, isChangeProto);
469}
470
471JSHandle<JSHClass> JSHClass::SetPrototypeWithNotification(const JSThread *thread,
472                                                          const JSHandle<JSHClass> &hclass,
473                                                          const JSHandle<JSTaggedValue> &proto,
474                                                          bool isChangeProto)
475{
476    // `hclass` can become prototype inside `TransitionProto` if `hclass` is HClass of `proto`.
477    // In this case we don't need to notify
478    auto wasPrototype = hclass->IsPrototype();
479    JSHandle<JSHClass> newClass = JSHClass::TransitionProto(thread, hclass, proto, isChangeProto);
480    if (wasPrototype) {
481        ASSERT(hclass->IsPrototype());
482        JSHClass::NotifyHclassChanged(thread, hclass, newClass);
483    }
484    return newClass;
485}
486
487void JSHClass::SetPrototypeTransition(JSThread *thread, const JSHandle<JSObject> &object,
488                                      const JSHandle<JSTaggedValue> &proto, bool isChangeProto)
489{
490    JSHandle<JSHClass> hclass(thread, object->GetJSHClass());
491    auto newClass = SetPrototypeWithNotification(thread, hclass, proto, isChangeProto);
492    RestoreElementsKindToGeneric(*newClass);
493    object->SynchronizedSetClass(thread, *newClass);
494    thread->NotifyStableArrayElementsGuardians(object, StableArrayChangeKind::PROTO);
495    ObjectOperator::UpdateDetectorOnSetPrototype(thread, object.GetTaggedValue());
496}
497
498void JSHClass::SetPrototype(const JSThread *thread, const JSHandle<JSTaggedValue> &proto, bool isChangeProto)
499{
500    // Because the heap-space of hclass is non-movable, this function can be non-static.
501    if (proto->IsJSObject()) {
502        OptimizePrototypeForIC(thread, proto, isChangeProto);
503    }
504    SetProto(thread, proto);
505}
506
507void JSHClass::OptimizePrototypeForIC(const JSThread *thread, const JSHandle<JSTaggedValue> &proto, bool isChangeProto)
508{
509    JSHandle<JSHClass> hclass(thread, proto->GetTaggedObject()->GetClass());
510    ASSERT(!Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(*hclass))->InReadOnlySpace());
511    if (!hclass->IsPrototype()) {
512        // Situations for clone proto hclass:
513        // 1: unshared non-ts hclass
514        // 2: no matter whether hclass is ts or not when set function prototype
515        if ((!hclass->IsTS() && !hclass->IsJSShared()) || isChangeProto) {
516            // The local IC and on-proto IC are different, because the former don't need to notify the whole
517            // prototype-chain or listen the changes of prototype chain, but the latter do. Therefore, when
518            // an object becomes a prototype object at the first time, we need to copy its hidden class in
519            // order to maintain the previously generated local IC and support the on-proto IC in the future.
520            // For example, a local IC adds a new property x for o1 and the o1.hclass1 -> o1.hclass2, when the
521            // o1 becomes a prototype object of object o2 and an on-proto IC loading x from o2 will rely on the
522            // stability of the prototype-chain o2 -> o1. If directly marking the o1.hclass1 as a prototype hclass,
523            // the previous IC of adding property x won't trigger IC-miss and fails to notify the IC on o2.
524
525            // At here, When a JSArray with initial hclass is set as a proto,
526            // we substitute its hclass with preserved proto hclass.
527            JSHandle<JSHClass> newProtoClass;
528            if (ProtoIsFastJSArray(thread, proto, hclass)) {
529                newProtoClass = JSHandle<JSHClass>(thread, thread->GetArrayInstanceHClass(hclass->GetElementsKind(),
530                                                                                          true));
531            } else {
532                newProtoClass = JSHClass::Clone(thread, hclass);
533            }
534            JSTaggedValue layout = newProtoClass->GetLayout();
535            // If the type of object is JSObject, the layout info value is initialized to the default value,
536            // if the value is not JSObject, the layout info value is initialized to null.
537            if (!layout.IsNull()) {
538                JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, layout);
539                layoutInfoHandle.Update(
540                    thread->GetEcmaVM()->GetFactory()->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
541                newProtoClass->SetLayout(thread, layoutInfoHandle);
542            }
543
544#if ECMASCRIPT_ENABLE_IC
545            // After the hclass is updated, check whether the proto chain status of ic is updated.
546            NotifyHclassChanged(thread, hclass, newProtoClass);
547#endif
548            JSObject::Cast(proto->GetTaggedObject())->SynchronizedSetClass(thread, *newProtoClass);
549            newProtoClass->SetIsPrototype(true);
550            // still dump for class in this path now
551            if (!isChangeProto) {
552                thread->GetEcmaVM()->GetPGOProfiler()->UpdateRootProfileTypeSafe(*hclass, *newProtoClass);
553            }
554        } else {
555            // There is no sharing in AOT hclass. Therefore, it is not necessary or possible to clone here.
556            hclass->SetIsPrototype(true);
557        }
558    }
559}
560
561void JSHClass::TransitionToDictionary(const JSThread *thread, const JSHandle<JSObject> &obj)
562{
563    // 1. new a hclass
564    JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
565    JSHandle<JSHClass> newJsHClass = CloneWithoutInlinedProperties(thread, jshclass);
566
567    {
568        DISALLOW_GARBAGE_COLLECTION;
569        // 2. Copy
570        newJsHClass->SetNumberOfProps(0);
571        newJsHClass->SetIsDictionaryMode(true);
572        ASSERT(newJsHClass->GetInlinedProperties() == 0);
573
574        // 3. Add newJsHClass to ?
575#if ECMASCRIPT_ENABLE_IC
576        JSHClass::NotifyHclassChanged(thread, JSHandle<JSHClass>(thread, obj->GetJSHClass()), newJsHClass);
577#endif
578        RestoreElementsKindToGeneric(*newJsHClass);
579        obj->SynchronizedSetClass(thread, *newJsHClass);
580    }
581}
582
583void JSHClass::OptimizeAsFastProperties(const JSThread *thread, const JSHandle<JSObject> &obj,
584                                        const std::vector<int> &indexOrder, bool isDictionary)
585{
586    // 1. new a hclass
587    JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
588    JSHandle<JSHClass> newJsHClass = Clone(thread, jshclass, isDictionary);
589
590    // 2. If it is dictionary, migrate should change layout. otherwise, copy the hclass only.
591    if (isDictionary) {
592        JSHandle<NameDictionary> properties(thread, obj->GetProperties());
593        int numberOfProperties = properties->EntriesCount();
594        ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
595        JSHandle<LayoutInfo> layoutInfoHandle = factory->CreateLayoutInfo(numberOfProperties);
596        int numberOfInlinedProps = static_cast<int>(newJsHClass->GetInlinedProperties());
597        for (int i = 0; i < numberOfProperties; i++) {
598            JSTaggedValue key = properties->GetKey(indexOrder[i]);
599            PropertyAttributes attributes = properties->GetAttributes(indexOrder[i]);
600            if (i < numberOfInlinedProps) {
601                attributes.SetIsInlinedProps(true);
602            } else {
603                attributes.SetIsInlinedProps(false);
604            }
605            attributes.SetOffset(i);
606            layoutInfoHandle->AddKey(thread, i, key, attributes);
607        }
608
609        {
610            DISALLOW_GARBAGE_COLLECTION;
611            newJsHClass->SetNumberOfProps(numberOfProperties);
612            newJsHClass->SetLayout(thread, layoutInfoHandle);
613        }
614    }
615
616    {
617        DISALLOW_GARBAGE_COLLECTION;
618        // 3. Copy
619        newJsHClass->SetIsDictionaryMode(false);
620        // 4. Add newJsHClass to ?
621#if ECMASCRIPT_ENABLE_IC
622        JSHClass::NotifyHclassChanged(thread, JSHandle<JSHClass>(thread, obj->GetJSHClass()), newJsHClass);
623#endif
624        obj->SynchronizedSetClass(thread, *newJsHClass);
625    }
626}
627
628void JSHClass::TransitionForRepChange(const JSThread *thread, const JSHandle<JSObject> &receiver,
629    const JSHandle<JSTaggedValue> &key, PropertyAttributes attr)
630{
631    JSHandle<JSHClass> oldHClass(thread, receiver->GetJSHClass());
632
633    // 1. Create hclass and copy layout
634    JSHandle<JSHClass> newHClass = JSHClass::Clone(thread, oldHClass);
635    RestoreElementsKindToGeneric(*newHClass);
636
637    JSHandle<LayoutInfo> oldLayout(thread, newHClass->GetLayout());
638    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
639    JSHandle<LayoutInfo> newLayout(factory->CopyLayoutInfo(oldLayout));
640    newHClass->SetLayout(thread, newLayout);
641
642    // 2. update attr
643    auto hclass = JSHClass::Cast(newHClass.GetTaggedValue().GetTaggedObject());
644    int entry = JSHClass::FindPropertyEntry(thread, hclass, key.GetTaggedValue());
645    ASSERT(entry != -1);
646    newLayout->SetNormalAttr(thread, entry, attr);
647
648    // 3. update hclass in object.
649#if ECMASCRIPT_ENABLE_IC
650    JSHClass::NotifyHclassChanged(thread, oldHClass, newHClass, key.GetTaggedValue());
651#endif
652
653    receiver->SynchronizedSetClass(thread, *newHClass);
654    // 4. Maybe Transition And Maintain subtypeing check
655}
656
657bool JSHClass::IsInitialArrayHClassWithElementsKind(const JSThread *thread, const JSHClass *targetHClass,
658                                                    const ElementsKind targetKind)
659{
660    const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
661    auto newKindIter = arrayHClassIndexMap.find(targetKind);
662    if (newKindIter != arrayHClassIndexMap.end()) {
663        auto indexPair = newKindIter->second;
664        auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(static_cast<size_t>(indexPair.first));
665        auto hclassWithProtoVal = thread->GlobalConstants()->
666            GetGlobalConstantObject(static_cast<size_t>(indexPair.second));
667        JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
668        JSHClass *hclassWithProto = JSHClass::Cast(hclassWithProtoVal.GetTaggedObject());
669        return (targetHClass == hclass || targetHClass == hclassWithProto);
670    }
671    return false;
672}
673
674bool JSHClass::TransitToElementsKindUncheck(const JSThread *thread, const JSHandle<JSObject> &obj,
675                                            ElementsKind newKind)
676{
677    ElementsKind current = obj->GetJSHClass()->GetElementsKind();
678    // currently we only support initial array hclass
679    JSHClass *objHclass = obj->GetClass();
680    if (IsInitialArrayHClassWithElementsKind(thread, objHclass, current)) {
681        const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
682        auto newKindIter = arrayHClassIndexMap.find(newKind);
683        bool objHclassIsPrototype = objHclass->IsPrototype();
684        if (newKindIter != arrayHClassIndexMap.end()) {
685            auto indexPair = newKindIter->second;
686            auto index = objHclassIsPrototype ? static_cast<size_t>(indexPair.second) :
687                                                static_cast<size_t>(indexPair.first);
688            auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
689            JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
690            obj->SynchronizedSetClass(thread, hclass);
691#if ECMASCRIPT_ENABLE_IC
692            JSHClass::NotifyHclassChanged(thread, JSHandle<JSHClass>(thread, objHclass),
693                                          JSHandle<JSHClass>(thread, hclass));
694#endif
695            return true;
696        }
697        LOG_ECMA(FATAL) << "Unknown newKind: " << static_cast<int32_t>(newKind);
698    }
699    return false;
700}
701
702void JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle<JSArray> &array,
703                                     ElementsKind newKind)
704{
705    JSTaggedValue elements = array->GetElements();
706    if (!elements.IsTaggedArray()) {
707        return;
708    }
709    ElementsKind current = array->GetJSHClass()->GetElementsKind();
710    newKind = Elements::MergeElementsKind(newKind, current);
711    if (newKind == current) {
712        return;
713    }
714
715    ASSERT(IsInitialArrayHClassWithElementsKind(thread, array->GetJSHClass(), current));
716    TransitToElementsKindUncheck(thread, JSHandle<JSObject>(array), newKind);
717}
718
719bool JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle<JSObject> &object,
720                                     const JSHandle<JSTaggedValue> &value, ElementsKind kind)
721{
722    if (!object->IsJSArray()) {
723        return false;
724    }
725    ElementsKind current = object->GetJSHClass()->GetElementsKind();
726    if (Elements::IsGeneric(current)) {
727        return false;
728    }
729    auto newKind = Elements::ToElementsKind(value.GetTaggedValue(), kind);
730    // Merge current kind and new kind
731    newKind = Elements::MergeElementsKind(current, newKind);
732    if (newKind == current) {
733        return false;
734    }
735    // Currently, we only support fast array elementsKind
736    ASSERT(IsInitialArrayHClassWithElementsKind(thread, object->GetJSHClass(), current));
737    if (!TransitToElementsKindUncheck(thread, object, newKind)) {
738        return false;
739    }
740
741    if (!thread->GetEcmaVM()->IsEnableElementsKind()) {
742        // Update TrackInfo
743        if (!thread->IsPGOProfilerEnable()) {
744            return true;
745        }
746        auto trackInfoVal = JSHandle<JSArray>(object)->GetTrackInfo();
747        thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackElementsKind(trackInfoVal, newKind);
748        return true;
749    }
750    return true;
751}
752
753void JSHClass::UpdateFieldType(JSHClass *hclass, const PropertyAttributes &attr)
754{
755    DISALLOW_GARBAGE_COLLECTION;
756    JSHClass *ownHClass = FindFieldOwnHClass(hclass, attr);
757    VisitAndUpdateLayout(ownHClass, attr);
758}
759
760JSHClass *JSHClass::FindFieldOwnHClass(JSHClass *hclass, const PropertyAttributes &attr)
761{
762    uint32_t offset = attr.GetOffset();
763    JSTaggedValue parent(hclass);
764    JSHClass *curHClass = hclass;
765    while (parent.IsJSHClass()) {
766        auto parentHClass = JSHClass::Cast(parent.GetTaggedObject());
767        if (parentHClass->NumberOfProps() <= offset) {
768            break;
769        }
770        curHClass = parentHClass;
771        parent = curHClass->GetParent();
772    }
773    return curHClass;
774}
775
776void JSHClass::VisitAndUpdateLayout(JSHClass *ownHClass, const PropertyAttributes &attr)
777{
778    uint32_t offset = attr.GetOffset();
779    auto targetTrackType = attr.GetTrackType();
780    std::queue<JSHClass *> backHClass;
781    backHClass.push(ownHClass);
782    while (!backHClass.empty()) {
783        JSHClass *current = backHClass.front();
784        backHClass.pop();
785
786        auto layout = LayoutInfo::Cast(current->GetLayout().GetTaggedObject());
787        if (layout->GetAttr(offset).GetTrackType() != targetTrackType) {
788            layout->UpdateTrackTypeAttr(offset, attr);
789        }
790
791        auto transitions = current->GetTransitions();
792        if (transitions.IsUndefined()) {
793            continue;
794        }
795        if (transitions.IsWeak()) {
796            auto cache = transitions.GetTaggedWeakRef();
797            backHClass.push(JSHClass::Cast(cache));
798            continue;
799        }
800
801        ASSERT(transitions.IsTaggedArray());
802        TransitionsDictionary *dict = TransitionsDictionary::Cast(transitions.GetTaggedObject());
803        dict->IterateEntryValue([&backHClass] (JSHClass *cache) {
804            backHClass.push(JSHClass::Cast(cache));
805        });
806    }
807}
808
809TransitionResult JSHClass::ConvertOrTransitionWithRep(const JSThread *thread,
810    const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
811    PropertyAttributes &attr)
812{
813    auto hclass = receiver->GetJSHClass();
814    auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
815    attr = layout->GetAttr(attr.GetOffset());
816    if (thread->IsPGOProfilerEnable() && !hclass->IsJSShared() && attr.UpdateTrackType(value.GetTaggedValue())) {
817        UpdateFieldType(hclass, attr);
818    }
819
820    Representation oldRep = attr.GetRepresentation();
821    if (oldRep == Representation::DOUBLE) {
822        if (value->IsInt()) {
823            double doubleValue = value->GetInt();
824            return {false, false, JSTaggedValue(bit_cast<JSTaggedType>(doubleValue))};
825        } else if (value->IsObject()) {
826            // Is Object
827            attr.SetRepresentation(Representation::TAGGED);
828            // Transition
829            JSHClass::TransitionForRepChange(thread, receiver, key, attr);
830            return {true, true, value.GetTaggedValue()};
831        } else {
832            // Is TaggedDouble
833            return {false, false, JSTaggedValue(bit_cast<JSTaggedType>(value->GetDouble()))};
834        }
835    } else if (oldRep == Representation::INT) {
836        if (value->IsInt()) {
837            int intValue = value->GetInt();
838            return {false, false, JSTaggedValue(static_cast<JSTaggedType>(intValue))};
839        } else {
840            attr.SetRepresentation(Representation::TAGGED);
841            JSHClass::TransitionForRepChange(thread, receiver, key, attr);
842            return {true, true, value.GetTaggedValue()};
843        }
844    }
845    return {true, false, value.GetTaggedValue()};
846}
847
848JSHandle<JSTaggedValue> JSHClass::EnableProtoChangeMarker(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
849{
850    JSTaggedValue proto = jshclass->GetPrototype();
851    if (!proto.IsECMAObject()) {
852        // Return JSTaggedValue directly. No proto check is needed.
853        LOG_ECMA(INFO) << "proto is not ecmaobject: " << proto.GetRawData();
854        return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Null());
855    }
856    JSHandle<JSObject> protoHandle(thread, proto);
857    JSHandle<JSHClass> protoClass(thread, protoHandle->GetJSHClass());
858    // in AOT's IC mechanism (VTable), when the prototype chain changes, it needs to notify each subclass
859    // PHC (prototype-HClass) and its IHC (instance-HClass) from the current PHC along the chain.
860    // therefore, when registering, it is also necessary to register IHC into its
861    // PHC's Listener to ensure that it can be notified.
862    RegisterOnProtoChain(thread, protoClass);
863
864    JSTaggedValue protoChangeMarker = protoClass->GetProtoChangeMarker();
865    if (protoChangeMarker.IsProtoChangeMarker()) {
866        JSHandle<ProtoChangeMarker> markerHandle(thread, ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject()));
867        if (!markerHandle->GetHasChanged()) {
868            return JSHandle<JSTaggedValue>(markerHandle);
869        }
870    }
871    JSHandle<ProtoChangeMarker> markerHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeMarker();
872    markerHandle->SetHasChanged(false);
873    // ShareToLocal is prohibited
874    if (!protoClass->IsJSShared()) {
875        protoClass->SetProtoChangeMarker(thread, markerHandle.GetTaggedValue());
876    }
877    return JSHandle<JSTaggedValue>(markerHandle);
878}
879
880JSHandle<JSTaggedValue> JSHClass::EnablePHCProtoChangeMarker(const JSThread *thread,
881    const JSHandle<JSHClass> &protoClass)
882{
883    RegisterOnProtoChain(thread, protoClass);
884
885    JSTaggedValue protoChangeMarker = protoClass->GetProtoChangeMarker();
886    if (protoChangeMarker.IsProtoChangeMarker()) {
887        JSHandle<ProtoChangeMarker> markerHandle(thread, ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject()));
888        if (!markerHandle->GetHasChanged()) {
889            return JSHandle<JSTaggedValue>(markerHandle);
890        }
891    }
892    JSHandle<ProtoChangeMarker> markerHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeMarker();
893    markerHandle->SetHasChanged(false);
894    protoClass->SetProtoChangeMarker(thread, markerHandle.GetTaggedValue());
895    return JSHandle<JSTaggedValue>(markerHandle);
896}
897
898void JSHClass::NotifyHclassChanged(const JSThread *thread, JSHandle<JSHClass> oldHclass, JSHandle<JSHClass> newHclass,
899                                   JSTaggedValue addedKey)
900{
901    if (!oldHclass->IsPrototype()) {
902        return;
903    }
904    // The old hclass is the same as new one
905    if (oldHclass.GetTaggedValue() == newHclass.GetTaggedValue()) {
906        return;
907    }
908    // For now, at pgo profiling stage, we use ProfileType::Kind to mark a hclass is CHC, PHC or IHC.
909    // We can have case like the following:
910    //
911    //     class C3 {
912    //         constructor(a5) {
913    //         }
914    //     }
915    //     class C18 extends C3 {
916    //         constructor() {
917    //             super(1);
918    //             C3.valueOf = 1;
919    //         }
920    //     }
921    //     const v37 = new C18();
922    //
923    // C3 is profiled as CHC even though its IsPrototype bit is marked as true when 'class C18 extends C3' is executed.
924    // Since C3 is marked as CHC and it has ProfileType::Kind::ConstructorId,
925    // when generating hclass at aot, its child hclass and itself will not have IsPrototype bit set as true.
926    //
927    // However, we currently support hclass substitution when executing 'C3.valueOf' for C3's oldHclass at runtime.
928    // Therefore, oldHclass's IsPrototype bit is set as true; But for newHclass, it is generated at aot stage,
929    // it will not have IsPrototype bit set as true.
930    //
931    // Good neww is our AOT hclass can not be shared, hence we can set newHclass IsPrototype as true at here.
932    if (newHclass->IsTS() && !newHclass->IsPrototype()) {
933        newHclass->SetIsPrototype(true);
934    }
935    JSHClass::NoticeThroughChain(thread, oldHclass, addedKey);
936    JSHClass::RefreshUsers(thread, oldHclass, newHclass);
937}
938
939void JSHClass::NotifyAccessorChanged(const JSThread *thread, JSHandle<JSHClass> hclass)
940{
941    DISALLOW_GARBAGE_COLLECTION;
942    JSTaggedValue markerValue = hclass->GetProtoChangeMarker();
943    if (markerValue.IsProtoChangeMarker()) {
944        ProtoChangeMarker *protoChangeMarker = ProtoChangeMarker::Cast(markerValue.GetTaggedObject());
945        protoChangeMarker->SetAccessorHasChanged(true);
946    }
947
948    JSTaggedValue protoDetailsValue = hclass->GetProtoChangeDetails();
949    if (!protoDetailsValue.IsProtoChangeDetails()) {
950        return;
951    }
952    JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
953    if (!listenersValue.IsTaggedArray()) {
954        return;
955    }
956    ChangeListener *listeners = ChangeListener::Cast(listenersValue.GetTaggedObject());
957    for (uint32_t i = 0; i < listeners->GetEnd(); i++) {
958        JSTaggedValue temp = listeners->Get(i);
959        if (temp.IsJSHClass()) {
960            NotifyAccessorChanged(thread, JSHandle<JSHClass>(thread, listeners->Get(i).GetTaggedObject()));
961        }
962    }
963}
964
965void JSHClass::RegisterOnProtoChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
966{
967    // ShareToLocal is prohibited
968    if (jshclass->IsJSShared()) {
969        return;
970    }
971    JSHandle<JSHClass> user = jshclass;
972    JSHandle<ProtoChangeDetails> userDetails = GetProtoChangeDetails(thread, user);
973
974    while (true) {
975        // Find the prototype chain as far as the hclass has not been registered.
976        if (userDetails->GetRegisterIndex() != static_cast<uint32_t>(ProtoChangeDetails::UNREGISTERED)) {
977            return;
978        }
979
980        JSTaggedValue proto = user->GetPrototype();
981        if (!proto.IsHeapObject()) {
982            return;
983        }
984        if (proto.IsJSProxy()) {
985            return;
986        }
987        // ShareToLocal is prohibited
988        if (proto.IsJSShared()) {
989            return;
990        }
991        ASSERT(proto.IsECMAObject());
992        JSHandle<JSObject> protoHandle(thread, proto);
993        JSHandle<ProtoChangeDetails> protoDetails =
994            GetProtoChangeDetails(thread, JSHandle<JSHClass>(thread, protoHandle->GetJSHClass()));
995        JSTaggedValue listeners = protoDetails->GetChangeListener();
996        JSHandle<ChangeListener> listenersHandle;
997        if (listeners.IsUndefined()) {
998            listenersHandle = JSHandle<ChangeListener>(ChangeListener::Create(thread));
999        } else {
1000            listenersHandle = JSHandle<ChangeListener>(thread, listeners);
1001        }
1002        uint32_t registerIndex = 0;
1003        JSHandle<ChangeListener> newListeners = ChangeListener::Add(thread, listenersHandle, user, &registerIndex);
1004        userDetails->SetRegisterIndex(registerIndex);
1005        protoDetails->SetChangeListener(thread, newListeners.GetTaggedValue());
1006        userDetails = protoDetails;
1007        user = JSHandle<JSHClass>(thread, protoHandle->GetJSHClass());
1008    }
1009}
1010
1011bool JSHClass::UnregisterOnProtoChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
1012{
1013    ASSERT(jshclass->IsPrototype());
1014    if (!jshclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
1015        return false;
1016    }
1017    if (!jshclass->GetPrototype().IsECMAObject()) {
1018        JSTaggedValue listeners =
1019            ProtoChangeDetails::Cast(jshclass->GetProtoChangeDetails().GetTaggedObject())->GetChangeListener();
1020        return !listeners.IsUndefined();
1021    }
1022    JSHandle<ProtoChangeDetails> currentDetails = GetProtoChangeDetails(thread, jshclass);
1023    uint32_t index = currentDetails->GetRegisterIndex();
1024    if (index == static_cast<uint32_t>(ProtoChangeDetails::UNREGISTERED)) {
1025        return false;
1026    }
1027    JSTaggedValue proto = jshclass->GetPrototype();
1028    ASSERT(proto.IsECMAObject());
1029    JSTaggedValue protoDetailsValue = JSObject::Cast(proto.GetTaggedObject())->GetJSHClass()->GetProtoChangeDetails();
1030    if (protoDetailsValue.IsUndefined() || protoDetailsValue.IsNull()) {
1031        return false;
1032    }
1033    ASSERT(protoDetailsValue.IsProtoChangeDetails());
1034    JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
1035    ASSERT(!listenersValue.IsUndefined());
1036    JSHandle<ChangeListener> listeners(thread, listenersValue.GetTaggedObject());
1037    ASSERT(listeners->Get(index) == jshclass.GetTaggedValue());
1038    listeners->Delete(thread, index);
1039    return true;
1040}
1041
1042JSHandle<ProtoChangeDetails> JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
1043{
1044    JSTaggedValue protoDetails = jshclass->GetProtoChangeDetails();
1045    if (protoDetails.IsProtoChangeDetails()) {
1046        return JSHandle<ProtoChangeDetails>(thread, protoDetails);
1047    }
1048    JSHandle<ProtoChangeDetails> protoDetailsHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeDetails();
1049    jshclass->SetProtoChangeDetails(thread, protoDetailsHandle.GetTaggedValue());
1050    return protoDetailsHandle;
1051}
1052
1053JSHandle<ProtoChangeDetails> JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle<JSObject> &obj)
1054{
1055    JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
1056    return GetProtoChangeDetails(thread, jshclass);
1057}
1058
1059void JSHClass::MarkProtoChanged([[maybe_unused]] const JSThread *thread, const JSHandle<JSHClass> &jshclass)
1060{
1061    DISALLOW_GARBAGE_COLLECTION;
1062    ASSERT(jshclass->IsPrototype());
1063    JSTaggedValue markerValue = jshclass->GetProtoChangeMarker();
1064    if (markerValue.IsProtoChangeMarker()) {
1065        ProtoChangeMarker *protoChangeMarker = ProtoChangeMarker::Cast(markerValue.GetTaggedObject());
1066        protoChangeMarker->SetHasChanged(true);
1067    }
1068}
1069
1070void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
1071                                  JSTaggedValue addedKey)
1072{
1073    DISALLOW_GARBAGE_COLLECTION;
1074    MarkProtoChanged(thread, jshclass);
1075    JSTaggedValue protoDetailsValue = jshclass->GetProtoChangeDetails();
1076    if (!protoDetailsValue.IsProtoChangeDetails()) {
1077        return;
1078    }
1079    JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
1080    if (!listenersValue.IsTaggedArray()) {
1081        return;
1082    }
1083    ChangeListener *listeners = ChangeListener::Cast(listenersValue.GetTaggedObject());
1084    for (uint32_t i = 0; i < listeners->GetEnd(); i++) {
1085        JSTaggedValue temp = listeners->Get(i);
1086        if (temp.IsJSHClass()) {
1087            NoticeThroughChain(thread, JSHandle<JSHClass>(thread, listeners->Get(i).GetTaggedObject()), addedKey);
1088        }
1089    }
1090}
1091
1092void JSHClass::RefreshUsers(const JSThread *thread, const JSHandle<JSHClass> &oldHclass,
1093                            const JSHandle<JSHClass> &newHclass)
1094{
1095    ASSERT(oldHclass->IsPrototype());
1096    ASSERT(newHclass->IsPrototype());
1097    bool onceRegistered = UnregisterOnProtoChain(thread, oldHclass);
1098
1099    // oldHclass is already marked. Only update newHclass.protoChangeDetails if it doesn't exist for further use.
1100    if (!newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
1101        newHclass->SetProtoChangeDetails(thread, oldHclass->GetProtoChangeDetails());
1102    }
1103    oldHclass->SetProtoChangeDetails(thread, JSTaggedValue::Undefined());
1104    if (onceRegistered) {
1105        if (newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
1106            ProtoChangeDetails::Cast(newHclass->GetProtoChangeDetails().GetTaggedObject())
1107                ->SetRegisterIndex(ProtoChangeDetails::UNREGISTERED);
1108        }
1109        RegisterOnProtoChain(thread, newHclass);
1110    }
1111}
1112
1113PropertyLookupResult JSHClass::LookupPropertyInAotHClass(const JSThread *thread, JSHClass *hclass, JSTaggedValue key)
1114{
1115    DISALLOW_GARBAGE_COLLECTION;
1116    ASSERT(hclass->IsTS());
1117
1118    PropertyLookupResult result;
1119    if (hclass->IsDictionaryMode()) {
1120        // not fuond
1121        result.SetIsFound(false);
1122        return result;
1123    }
1124
1125    int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
1126    // found in local
1127    if (entry != -1) {
1128        result.SetIsFound(true);
1129        result.SetIsLocal(true);
1130        PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry);
1131        if (attr.IsInlinedProps()) {
1132            result.SetIsInlinedProps(true);
1133            result.SetOffset(hclass->GetInlinedPropertiesOffset(entry));
1134        } else {
1135            result.SetIsInlinedProps(false);
1136            result.SetOffset(attr.GetOffset() - hclass->GetInlinedProperties());
1137        }
1138        if (attr.IsNotHole()) {
1139            result.SetIsNotHole(true);
1140        }
1141        if (attr.IsAccessor()) {
1142            result.SetIsAccessor(true);
1143        }
1144        result.SetRepresentation(attr.GetRepresentation());
1145        result.SetIsWritable(attr.IsWritable());
1146        return result;
1147    }
1148
1149    // not found
1150    result.SetIsFound(false);
1151    return result;
1152}
1153
1154PropertyLookupResult JSHClass::LookupPropertyInPGOHClass(const JSThread *thread, JSHClass *hclass, JSTaggedValue key)
1155{
1156    DISALLOW_GARBAGE_COLLECTION;
1157
1158    PropertyLookupResult result;
1159    if (hclass->IsDictionaryMode()) {
1160        result.SetIsFound(false);
1161        return result;
1162    }
1163
1164    int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
1165    // found in local
1166    if (entry != -1) {
1167        result.SetIsFound(true);
1168        result.SetIsLocal(true);
1169        PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry);
1170        if (attr.IsInlinedProps()) {
1171            result.SetIsInlinedProps(true);
1172            result.SetOffset(hclass->GetInlinedPropertiesOffset(entry));
1173        } else {
1174            result.SetIsInlinedProps(false);
1175            result.SetOffset(attr.GetOffset() - hclass->GetInlinedProperties());
1176        }
1177
1178        if (attr.IsNotHole()) {
1179            result.SetIsNotHole(true);
1180        }
1181        if (attr.IsAccessor()) {
1182            result.SetIsAccessor(true);
1183        }
1184        result.SetRepresentation(attr.GetRepresentation());
1185        result.SetIsWritable(attr.IsWritable());
1186        return result;
1187    }
1188
1189    // not fuond
1190    result.SetIsFound(false);
1191    return result;
1192}
1193
1194PropertyLookupResult JSHClass::LookupPropertyInBuiltinPrototypeHClass(const JSThread *thread, JSHClass *hclass,
1195                                                                      JSTaggedValue key)
1196{
1197    DISALLOW_GARBAGE_COLLECTION;
1198    ASSERT(hclass->IsPrototype());
1199
1200    PropertyLookupResult result;
1201    if (hclass->IsDictionaryMode()) {
1202        // not fuond
1203        result.SetIsFound(false);
1204        return result;
1205    }
1206    int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
1207    // When the property is not found, the value of 'entry' is -1.
1208    // Currently, not all methods on the prototype of 'builtin' have been changed to inlined.
1209    // Therefore, when a non-inlined method is encountered, it is also considered not found.
1210    if (entry == -1 || static_cast<uint32_t>(entry) >= hclass->GetInlinedProperties()) {
1211        result.SetIsFound(false);
1212        return result;
1213    }
1214
1215    result.SetIsFound(true);
1216    result.SetIsLocal(true);
1217    PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry);
1218    if (attr.IsInlinedProps()) {
1219        result.SetIsInlinedProps(true);
1220        result.SetOffset(hclass->GetInlinedPropertiesOffset(entry));
1221    } else {
1222        result.SetIsInlinedProps(false);
1223        result.SetOffset(attr.GetOffset() - hclass->GetInlinedProperties());
1224    }
1225    result.SetIsNotHole(true);
1226    if (attr.IsAccessor()) {
1227        result.SetIsAccessor(true);
1228    }
1229    result.SetRepresentation(attr.GetRepresentation());
1230    result.SetIsWritable(attr.IsWritable());
1231    return result;
1232}
1233
1234JSHandle<JSTaggedValue> JSHClass::ParseKeyFromPGOCString(ObjectFactory* factory,
1235                                                         const CString& cstring,
1236                                                         const PGOHandler& handler)
1237{
1238    if (handler.GetIsSymbol()) {
1239        JSHandle<JSSymbol> symbol;
1240        auto str = cstring.substr(0, 6); // `method` length is 6
1241        if (str == "method") { // cstring is `method_0ULL` after _ is private id of symbol
1242            symbol = factory->NewPublicSymbolWithChar("method");
1243            ASSERT(cstring.size() > 0);
1244            str = cstring.substr(7, cstring.size() - 1); // `method_` length is 7
1245            symbol->SetPrivateId(CStringToULL(str));
1246        } else { // cstring is private id of symbol
1247            symbol = factory->NewJSSymbol();
1248            symbol->SetPrivateId(CStringToULL(cstring));
1249        }
1250        return JSHandle<JSTaggedValue>(symbol);
1251    } else {
1252        return JSHandle<JSTaggedValue>(factory->NewFromStdString(std::string(cstring)));
1253    }
1254}
1255
1256JSHandle<JSHClass> JSHClass::CreateRootHClassFromPGO(const JSThread* thread,
1257                                                     const HClassLayoutDesc* desc,
1258                                                     uint32_t maxNum,
1259                                                     bool isCache)
1260{
1261    if (isCache) {
1262        if (ObjectFactory::CanObjectLiteralHClassCache(maxNum)) {
1263            return CreateRootHClassWithCached(thread, desc, maxNum);
1264        }
1265    }
1266    auto rootDesc = reinterpret_cast<const pgo::RootHClassLayoutDesc *>(desc);
1267    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1268    uint32_t numOfProps = rootDesc->NumOfProps();
1269    uint32_t index = 0;
1270    JSType type = rootDesc->GetObjectType();
1271    size_t size = rootDesc->GetObjectSize();
1272    JSHandle<JSHClass> hclass = factory->NewEcmaHClass(size, type, maxNum);
1273    // Dictionary?
1274    JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(maxNum, MemSpaceType::SEMI_SPACE, GrowMode::KEEP);
1275    rootDesc->IterateProps([thread, factory, &index, hclass, layout](const pgo::PropertyDesc& propDesc) {
1276        auto& cstring = propDesc.first;
1277        auto& handler = propDesc.second;
1278        JSHandle<JSTaggedValue> key = ParseKeyFromPGOCString(factory, cstring, handler);
1279        PropertyAttributes attributes = PropertyAttributes::Default();
1280        if (handler.SetAttribute(thread, attributes)) {
1281            hclass->SetIsAllTaggedProp(false);
1282        }
1283        attributes.SetIsInlinedProps(true);
1284        attributes.SetOffset(index);
1285        layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
1286        index++;
1287    });
1288    hclass->SetLayout(thread, layout);
1289    hclass->SetNumberOfProps(numOfProps);
1290    hclass->SetTS(true);
1291    return hclass;
1292}
1293
1294JSHandle<JSHClass> JSHClass::CreateRootHClassWithCached(const JSThread* thread,
1295                                                        const HClassLayoutDesc* desc,
1296                                                        uint32_t maxNum)
1297{
1298    auto rootDesc = reinterpret_cast<const pgo::RootHClassLayoutDesc *>(desc);
1299    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1300    uint32_t index = 0;
1301    ASSERT(rootDesc->GetObjectSize() == JSObject::SIZE);
1302    ASSERT(rootDesc->GetObjectType() == JSType::JS_OBJECT);
1303    JSHandle<JSHClass> hclass = factory->GetObjectLiteralRootHClass(maxNum);
1304    JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(maxNum, MemSpaceType::SEMI_SPACE, GrowMode::KEEP);
1305    hclass->SetPrototype(thread, JSTaggedValue::Null());
1306    hclass->SetLayout(thread, layout);
1307    hclass->SetTS(true);
1308    rootDesc->IterateProps([thread, factory, &index, &hclass](const pgo::PropertyDesc& propDesc) {
1309        auto& cstring = propDesc.first;
1310        auto& handler = propDesc.second;
1311        JSHandle<JSTaggedValue> key = ParseKeyFromPGOCString(factory, cstring, handler);
1312        PropertyAttributes attributes = PropertyAttributes::Default();
1313        if (handler.SetAttribute(thread, attributes)) {
1314            hclass->SetIsAllTaggedProp(false);
1315        }
1316        attributes.SetIsInlinedProps(true);
1317        attributes.SetOffset(index++);
1318        auto rep = attributes.GetRepresentation();
1319
1320        JSHandle<JSHClass> child = SetPropertyOfObjHClass(thread, hclass, key, attributes, rep);
1321        child->SetParent(thread, hclass);
1322        child->SetPrototype(thread, JSTaggedValue::Null());
1323        child->SetTS(true);
1324        hclass = child;
1325    });
1326    return hclass;
1327}
1328
1329JSHandle<JSHClass> JSHClass::CreateChildHClassFromPGO(const JSThread* thread,
1330                                                      const JSHandle<JSHClass>& parent,
1331                                                      const HClassLayoutDesc* desc)
1332{
1333    pgo::PropertyDesc propDesc = reinterpret_cast<const pgo::ChildHClassLayoutDesc *>(desc)->GetPropertyDesc();
1334    auto& cstring = propDesc.first;
1335    auto& handler = propDesc.second;
1336    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1337    uint32_t numOfProps = parent->NumberOfProps();
1338
1339    JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, parent);
1340    newJsHClass->SetTS(true);
1341    ASSERT(newJsHClass->GetInlinedProperties() >= (numOfProps + 1));
1342    uint32_t offset = numOfProps;
1343    {
1344        JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, newJsHClass->GetLayout());
1345        if (layoutInfoHandle->NumberOfElements() != static_cast<int>(offset)) {
1346            layoutInfoHandle.Update(factory->CopyAndReSort(layoutInfoHandle, offset, offset + 1));
1347            newJsHClass->SetLayout(thread, layoutInfoHandle);
1348        } else if (layoutInfoHandle->GetPropertiesCapacity() <= static_cast<int>(offset)) {  // need to Grow
1349            layoutInfoHandle.Update(
1350                factory->ExtendLayoutInfo(layoutInfoHandle, offset));
1351            newJsHClass->SetLayout(thread, layoutInfoHandle);
1352        }
1353        JSHandle<JSTaggedValue> key = ParseKeyFromPGOCString(factory, cstring, handler);
1354        PropertyAttributes attributes = PropertyAttributes::Default();
1355        if (handler.SetAttribute(thread, attributes)) {
1356            newJsHClass->SetIsAllTaggedProp(false);
1357        }
1358        attributes.SetOffset(offset);
1359        attributes.SetIsInlinedProps(true);
1360        layoutInfoHandle->AddKey(thread, offset, key.GetTaggedValue(), attributes);
1361        newJsHClass->IncNumberOfProps();
1362        AddTransitions(thread, parent, newJsHClass, key, attributes);
1363        JSHClass::NotifyHclassChanged(thread, parent, newJsHClass, key.GetTaggedValue());
1364    }
1365
1366    return newJsHClass;
1367}
1368
1369bool JSHClass::DumpRootHClassByPGO(const JSHClass* hclass, HClassLayoutDesc* desc)
1370{
1371    DISALLOW_GARBAGE_COLLECTION;
1372    if (hclass->IsDictionaryMode()) {
1373        return false;
1374    }
1375
1376    LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1377    int element = static_cast<int>(hclass->NumberOfProps());
1378    for (int i = 0; i < element; i++) {
1379        layout->DumpFieldIndexByPGO(i, desc);
1380    }
1381    return true;
1382}
1383
1384bool JSHClass::DumpChildHClassByPGO(const JSHClass* hclass, HClassLayoutDesc* desc)
1385{
1386    DISALLOW_GARBAGE_COLLECTION;
1387    if (hclass->IsDictionaryMode()) {
1388        return false;
1389    }
1390    if (hclass->PropsIsEmpty()) {
1391        return false;
1392    }
1393    uint32_t last = hclass->LastPropIndex();
1394    LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1395    layoutInfo->DumpFieldIndexByPGO(last, desc);
1396    return true;
1397}
1398
1399bool JSHClass::UpdateChildLayoutDescByPGO(const JSHClass* hclass, HClassLayoutDesc* childDesc)
1400{
1401    DISALLOW_GARBAGE_COLLECTION;
1402    if (hclass->IsDictionaryMode()) {
1403        return false;
1404    }
1405    if (hclass->PropsIsEmpty()) {
1406        return false;
1407    }
1408    uint32_t last = hclass->LastPropIndex();
1409    LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1410    return layoutInfo->UpdateFieldIndexByPGO(last, childDesc);
1411}
1412
1413bool JSHClass::UpdateRootLayoutDescByPGO(const JSHClass* hclass, HClassLayoutDesc* desc)
1414{
1415    DISALLOW_GARBAGE_COLLECTION;
1416    if (hclass->IsDictionaryMode()) {
1417        return false;
1418    }
1419
1420    auto rootDesc = reinterpret_cast<const pgo::RootHClassLayoutDesc *>(desc);
1421    int rootPropLen = static_cast<int>(rootDesc->NumOfProps());
1422    LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1423    for (int i = 0; i < rootPropLen; i++) {
1424        layout->UpdateFieldIndexByPGO(i, desc);
1425    }
1426    return true;
1427}
1428
1429CString JSHClass::DumpToString(JSTaggedType hclassVal)
1430{
1431    DISALLOW_GARBAGE_COLLECTION;
1432    auto hclass = JSHClass::Cast(JSTaggedValue(hclassVal).GetTaggedObject());
1433    if (hclass->IsDictionaryMode()) {
1434        return "";
1435    }
1436
1437    CString result;
1438    LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1439    int element = static_cast<int>(hclass->NumberOfProps());
1440    for (int i = 0; i < element; i++) {
1441        auto key = layout->GetKey(i);
1442        if (key.IsString()) {
1443            uint64_t value = EcmaStringAccessor(key).GetHashcode();
1444            value <<= sizeof(uint32_t) * BITS_PER_BYTE;
1445            auto attr = layout->GetAttr(i);
1446            auto defaultAttr = PropertyAttributes(attr.GetPropertyMetaData());
1447            defaultAttr.SetTrackType(attr.GetTrackType());
1448            value += defaultAttr.GetValue();
1449            result += ToCString(value);
1450        } else if (key.IsSymbol()) {
1451            result += JSSymbol::Cast(key)->GetPrivateId();
1452            auto attr = layout->GetAttr(i);
1453            result += static_cast<int32_t>(attr.GetTrackType());
1454            result += attr.GetPropertyMetaData();
1455        } else {
1456            LOG_ECMA(FATAL) << "JSHClass::DumpToString UNREACHABLE";
1457        }
1458    }
1459    return result;
1460}
1461
1462PropertyLookupResult JSHClass::LookupPropertyInBuiltinHClass(const JSThread *thread, JSHClass *hclass,
1463                                                             JSTaggedValue key)
1464{
1465    DISALLOW_GARBAGE_COLLECTION;
1466
1467    PropertyLookupResult result;
1468    if (hclass->IsDictionaryMode()) {
1469        result.SetIsFound(false);
1470        return result;
1471    }
1472
1473    int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
1474    // found in local
1475    if (entry != -1) {
1476        result.SetIsFound(true);
1477        result.SetIsLocal(true);
1478        PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry);
1479        if (attr.IsInlinedProps()) {
1480            result.SetIsInlinedProps(true);
1481            result.SetOffset(hclass->GetInlinedPropertiesOffset(entry));
1482        } else {
1483            result.SetIsInlinedProps(false);
1484            result.SetOffset(attr.GetOffset() - hclass->GetInlinedProperties());
1485        }
1486
1487        if (attr.IsNotHole()) {
1488            result.SetIsNotHole(true);
1489        }
1490        if (attr.IsAccessor()) {
1491            result.SetIsAccessor(true);
1492        }
1493        result.SetRepresentation(attr.GetRepresentation());
1494        result.SetIsWritable(attr.IsWritable());
1495        return result;
1496    }
1497
1498    // not fuond
1499    result.SetIsFound(false);
1500    return result;
1501}
1502
1503JSHandle<JSHClass> JSHClass::CreateSHClass(JSThread *thread,
1504                                           const std::vector<PropertyDescriptor> &descs,
1505                                           const JSHClass *parentHClass,
1506                                           bool isFunction)
1507{
1508    EcmaVM *vm = thread->GetEcmaVM();
1509    ObjectFactory *factory = vm->GetFactory();
1510
1511    uint32_t length = descs.size();
1512    uint32_t maxInline = isFunction ? JSSharedFunction::MAX_INLINE : JSSharedObject::MAX_INLINE;
1513
1514    if (parentHClass) {
1515        if (parentHClass->IsDictionaryMode()) {
1516            auto dict = reinterpret_cast<NameDictionary *>(parentHClass->GetLayout().GetTaggedObject());
1517            length += static_cast<uint32_t>(dict->EntriesCount());
1518        } else {
1519            length += parentHClass->NumberOfProps();
1520        }
1521    }
1522
1523    JSHandle<JSHClass> hclass =
1524        isFunction ? factory->NewSEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, length)
1525                   : factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, length);
1526    if (LIKELY(length <= maxInline)) {
1527        CreateSInlinedLayout(thread, descs, hclass, parentHClass);
1528    } else {
1529        CreateSDictLayout(thread, descs, hclass, parentHClass);
1530    }
1531
1532    return hclass;
1533}
1534
1535JSHandle<JSHClass> JSHClass::CreateSConstructorHClass(JSThread *thread, const std::vector<PropertyDescriptor> &descs)
1536{
1537    auto hclass = CreateSHClass(thread, descs, nullptr, true);
1538    hclass->SetClassConstructor(true);
1539    hclass->SetConstructor(true);
1540    return hclass;
1541}
1542
1543JSHandle<JSHClass> JSHClass::CreateSPrototypeHClass(JSThread *thread, const std::vector<PropertyDescriptor> &descs)
1544{
1545    auto hclass = CreateSHClass(thread, descs);
1546    hclass->SetClassPrototype(true);
1547    hclass->SetIsPrototype(true);
1548    return hclass;
1549}
1550
1551void JSHClass::CreateSInlinedLayout(JSThread *thread,
1552                                    const std::vector<PropertyDescriptor> &descs,
1553                                    const JSHandle<JSHClass> &hclass,
1554                                    const JSHClass *parentHClass)
1555{
1556    EcmaVM *vm = thread->GetEcmaVM();
1557    ObjectFactory *factory = vm->GetFactory();
1558
1559    uint32_t parentLength{0};
1560    if (parentHClass) {
1561        parentLength = parentHClass->NumberOfProps();
1562    }
1563    auto length = descs.size();
1564    auto layout = factory->CreateSLayoutInfo(length + parentLength);
1565
1566    JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1567    for (uint32_t i = 0; i < length; ++i) {
1568        key.Update(descs[i].GetKey());
1569        PropertyAttributes attr =
1570            PropertyAttributes::Default(descs[i].IsWritable(), descs[i].IsEnumerable(), descs[i].IsConfigurable());
1571        if (UNLIKELY(descs[i].GetValue()->IsAccessor())) {
1572            attr.SetIsAccessor(true);
1573        }
1574        attr.SetIsInlinedProps(true);
1575        attr.SetRepresentation(Representation::TAGGED);
1576        attr.SetSharedFieldType(descs[i].GetSharedFieldType());
1577        attr.SetOffset(i);
1578        layout->AddKey(thread, i, key.GetTaggedValue(), attr);
1579    }
1580
1581    auto index = length;
1582    if (parentHClass) {
1583        JSHandle<LayoutInfo> old(thread, parentHClass->GetLayout());
1584        for (uint32_t i = 0; i < parentLength; i++) {
1585            key.Update(old->GetKey(i));
1586            auto entry = layout->FindElementWithCache(thread, *hclass, key.GetTaggedValue(), index);
1587            if (entry != -1) {
1588                continue;
1589            }
1590            auto attr = PropertyAttributes(old->GetAttr(i));
1591            attr.SetOffset(index);
1592            layout->AddKey(thread, index, old->GetKey(i), attr);
1593            ++index;
1594        }
1595    }
1596
1597    hclass->SetLayout(thread, layout);
1598    hclass->SetNumberOfProps(index);
1599    auto inlinedPropsLength = hclass->GetInlinedProperties();
1600    if (inlinedPropsLength > index) {
1601        uint32_t duplicatedSize = (inlinedPropsLength - index) * JSTaggedValue::TaggedTypeSize();
1602        hclass->SetObjectSize(hclass->GetObjectSize() - duplicatedSize);
1603    }
1604}
1605
1606void JSHClass::CreateSDictLayout(JSThread *thread,
1607                                 const std::vector<PropertyDescriptor> &descs,
1608                                 const JSHandle<JSHClass> &hclass,
1609                                 const JSHClass *parentHClass)
1610{
1611    uint32_t parentLength{0};
1612    if (parentHClass) {
1613        if (parentHClass->IsDictionaryMode()) {
1614            parentLength = static_cast<uint32_t>(
1615                reinterpret_cast<NameDictionary *>(parentHClass->GetLayout().GetTaggedObject())->EntriesCount());
1616        } else {
1617            parentLength = parentHClass->NumberOfProps();
1618        }
1619    }
1620    auto length = descs.size();
1621    JSMutableHandle<NameDictionary> dict(
1622        thread,
1623        NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(length + parentLength)));
1624    JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1625    auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
1626    JSHandle<JSTaggedValue> value = globalConst->GetHandledUndefined();
1627
1628    for (uint32_t i = 0; i < length; ++i) {
1629        key.Update(descs[i].GetKey());
1630        PropertyAttributes attr =
1631            PropertyAttributes::Default(descs[i].IsWritable(), descs[i].IsEnumerable(), descs[i].IsConfigurable());
1632        attr.SetSharedFieldType(descs[i].GetSharedFieldType());
1633        attr.SetBoxType(PropertyBoxType::UNDEFINED);
1634        JSHandle<NameDictionary> newDict = NameDictionary::Put(thread, dict, key, value, attr);
1635        dict.Update(newDict);
1636    }
1637
1638    if (parentHClass) {
1639        if (parentHClass->IsDictionaryMode()) {
1640            JSHandle<NameDictionary> old(thread, parentHClass->GetLayout());
1641            std::vector<int> indexOrder = old->GetEnumerationOrder();
1642            for (uint32_t i = 0; i < parentLength; i++) {
1643                key.Update(old->GetKey(indexOrder[i]));
1644                JSHandle<NameDictionary> newDict = NameDictionary::Put(
1645                    thread, dict, key, value, PropertyAttributes(old->GetAttributes(indexOrder[i])));
1646                dict.Update(newDict);
1647            }
1648        } else {
1649            JSHandle<LayoutInfo> old(thread, parentHClass->GetLayout());
1650            for (uint32_t i = 0; i < parentLength; i++) {
1651                key.Update(old->GetKey(i));
1652                JSHandle<NameDictionary> newDict =
1653                    NameDictionary::Put(thread, dict, key, value, PropertyAttributes(old->GetAttr(i)));
1654                dict.Update(newDict);
1655            }
1656        }
1657    }
1658
1659    hclass->SetLayout(thread, dict);
1660    hclass->SetNumberOfProps(0);
1661    hclass->SetIsDictionaryMode(true);
1662}
1663bool JSHClass::IsNeedNotifyHclassChangedForAotTransition(const JSThread *thread, const JSHandle<JSHClass> &hclass,
1664                                                         JSTaggedValue key)
1665{
1666    JSMutableHandle<JSObject> protoHandle(thread, hclass->GetPrototype());
1667    while (protoHandle.GetTaggedValue().IsHeapObject()) {
1668        JSHClass *protoHclass = protoHandle->GetJSHClass();
1669        if (JSHClass::FindPropertyEntry(thread, protoHclass, key) != -1) {
1670            return true;
1671        }
1672        protoHandle.Update(protoHclass->GetPrototype());
1673    }
1674    return false;
1675}
1676}  // namespace panda::ecmascript
1677