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