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