• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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/elements.h"
17 #include "ecmascript/js_hclass-inl.h"
18 
19 #include <algorithm>
20 
21 #include "ecmascript/base/config.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/pgo_profiler/pgo_profiler.h"
24 #include "ecmascript/pgo_profiler/pgo_profiler_layout.h"
25 #include "ecmascript/tagged_array.h"
26 #include "ecmascript/vtable.h"
27 #include "ecmascript/ic/proto_change_details.h"
28 #include "ecmascript/js_object-inl.h"
29 #include "ecmascript/js_symbol.h"
30 #include "ecmascript/mem/c_containers.h"
31 #include "ecmascript/subtyping_operator.h"
32 #include "ecmascript/tagged_array-inl.h"
33 #include "ecmascript/tagged_dictionary.h"
34 #include "ecmascript/weak_vector.h"
35 
36 namespace panda::ecmascript {
PutIfAbsent(const JSThread * thread,const JSHandle<TransitionsDictionary> & dictionary,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & metaData)37 JSHandle<TransitionsDictionary> TransitionsDictionary::PutIfAbsent(const JSThread *thread,
38                                                                    const JSHandle<TransitionsDictionary> &dictionary,
39                                                                    const JSHandle<JSTaggedValue> &key,
40                                                                    const JSHandle<JSTaggedValue> &value,
41                                                                    const JSHandle<JSTaggedValue> &metaData)
42 {
43     int hash = TransitionsDictionary::Hash(key.GetTaggedValue(), metaData.GetTaggedValue());
44 
45     /* no need to add key if exist */
46     int entry = dictionary->FindEntry(key.GetTaggedValue(), metaData.GetTaggedValue());
47     if (entry != -1) {
48         if (dictionary->GetValue(entry).IsUndefined()) {
49             JSTaggedValue weakValue = JSTaggedValue(value->CreateAndGetWeakRef());
50             dictionary->SetValue(thread, entry, weakValue);
51         }
52         return dictionary;
53     }
54 
55     // Check whether the dictionary should be extended.
56     JSHandle<TransitionsDictionary> newDictionary(HashTableT::GrowHashTable(thread, dictionary));
57     // Compute the key object.
58     entry = newDictionary->FindInsertIndex(hash);
59     JSTaggedValue val = value.GetTaggedValue();
60     newDictionary->SetEntry(thread, entry, key.GetTaggedValue(), val, metaData.GetTaggedValue());
61 
62     newDictionary->IncreaseEntries(thread);
63     return newDictionary;
64 }
65 
FindEntry(const JSTaggedValue & key,const JSTaggedValue & metaData)66 int TransitionsDictionary::FindEntry(const JSTaggedValue &key, const JSTaggedValue &metaData)
67 {
68     size_t size = static_cast<size_t>(Size());
69     uint32_t count = 1;
70     int32_t hash = TransitionsDictionary::Hash(key, metaData);
71     // GrowHashTable will guarantee the hash table is never full.
72     for (uint32_t entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) {
73         JSTaggedValue element = GetKey(entry);
74         if (element.IsHole()) {
75             continue;
76         }
77         if (element.IsUndefined()) {
78             return -1;
79         }
80 
81         if (TransitionsDictionary::IsMatch(key, metaData, element, GetAttributes(entry).GetWeakRawValue())) {
82             return static_cast<int>(entry);
83         }
84     }
85     return -1;
86 }
87 
Remove(const JSThread * thread,const JSHandle<TransitionsDictionary> & table,const JSHandle<JSTaggedValue> & key,const JSTaggedValue & metaData)88 JSHandle<TransitionsDictionary> TransitionsDictionary::Remove(const JSThread *thread,
89                                                               const JSHandle<TransitionsDictionary> &table,
90                                                               const JSHandle<JSTaggedValue> &key,
91                                                               const JSTaggedValue &metaData)
92 {
93     int entry = table->FindEntry(key.GetTaggedValue(), metaData);
94     if (entry == -1) {
95         return table;
96     }
97 
98     table->RemoveElement(thread, entry);
99     return TransitionsDictionary::Shrink(thread, table);
100 }
101 
Rehash(const JSThread * thread,TransitionsDictionary * newTable)102 void TransitionsDictionary::Rehash(const JSThread *thread, TransitionsDictionary *newTable)
103 {
104     DISALLOW_GARBAGE_COLLECTION;
105     if (newTable == nullptr) {
106         return;
107     }
108     int size = this->Size();
109     // Rehash elements to new table
110     int entryCount = 0;
111     for (int i = 0; i < size; i++) {
112         int fromIndex = GetEntryIndex(i);
113         JSTaggedValue k = this->GetKey(i);
114         JSTaggedValue v = this->GetValue(i);
115         if (IsKey(k) && TransitionsDictionary::CheckWeakExist(v)) {
116             int hash = TransitionsDictionary::Hash(k, this->GetAttributes(i));
117             int insertionIndex = GetEntryIndex(newTable->FindInsertIndex(hash));
118             JSTaggedValue tv = Get(fromIndex);
119             newTable->Set(thread, insertionIndex, tv);
120             for (int j = 1; j < TransitionsDictionary::ENTRY_SIZE; j++) {
121                 tv = Get(fromIndex + j);
122                 newTable->Set(thread, insertionIndex + j, tv);
123             }
124             entryCount++;
125         }
126     }
127     newTable->SetEntriesCount(thread, entryCount);
128     newTable->SetHoleEntriesCount(thread, 0);
129 }
130 
131 // class JSHClass
Initialize(const JSThread * thread,uint32_t size,JSType type,uint32_t inlinedProps)132 void JSHClass::Initialize(const JSThread *thread, uint32_t size, JSType type, uint32_t inlinedProps)
133 {
134     DISALLOW_GARBAGE_COLLECTION;
135     ClearBitField();
136     if (JSType::JS_OBJECT_FIRST <= type && type <= JSType::JS_OBJECT_LAST) {
137         SetObjectSize(size + inlinedProps * JSTaggedValue::TaggedTypeSize());
138         SetInlinedPropsStart(size);
139         SetLayout(thread, thread->GlobalConstants()->GetEmptyLayoutInfo());
140     } else {
141         SetObjectSize(size);
142         SetLayout(thread, JSTaggedValue::Null());
143     }
144     if (type >= JSType::JS_FUNCTION_FIRST && type <= JSType::JS_FUNCTION_LAST) {
145         SetIsJSFunction(true);
146     }
147     SetPrototype(thread, JSTaggedValue::Null());
148 
149     SetObjectType(type);
150     SetExtensible(true);
151     SetIsPrototype(false);
152     SetHasDeleteProperty(false);
153     SetIsAllTaggedProp(true);
154     SetElementsKind(ElementsKind::GENERIC);
155     SetTransitions(thread, JSTaggedValue::Undefined());
156     SetParent(thread, JSTaggedValue::Undefined());
157     SetProtoChangeMarker(thread, JSTaggedValue::Null());
158     SetProtoChangeDetails(thread, JSTaggedValue::Null());
159     SetEnumCache(thread, JSTaggedValue::Null());
160     InitTSInheritInfo(thread);
161 }
162 
InitTSInheritInfo(const JSThread * thread)163 void JSHClass::InitTSInheritInfo(const JSThread *thread)
164 {
165     // Supers and Level are used to record the relationship between TSHClass.
166     if (ShouldSetDefaultSupers()) {
167         ASSERT(thread->GlobalConstants()->GetDefaultSupers().IsTaggedArray());
168         SetSupers(thread, thread->GlobalConstants()->GetDefaultSupers());
169     } else {
170         SetSupers(thread, JSTaggedValue::Undefined());
171     }
172     SetLevel(0);
173 
174     // VTable records the location information of properties and methods of TSHClass,
175     // which is used to perform efficient IC at runtime
176     SetVTable(thread, JSTaggedValue::Undefined());
177 }
178 
Clone(const JSThread * thread,const JSHandle<JSHClass> & jshclass,bool withoutInlinedProperties,uint32_t incInlinedProperties)179 JSHandle<JSHClass> JSHClass::Clone(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
180                                    bool withoutInlinedProperties, uint32_t incInlinedProperties)
181 {
182     JSType type = jshclass->GetObjectType();
183     uint32_t size = jshclass->GetInlinedPropsStartSize();
184     uint32_t numInlinedProps = withoutInlinedProperties ? 0 : jshclass->GetInlinedProperties() + incInlinedProperties;
185     JSHandle<JSHClass> newJsHClass = thread->GetEcmaVM()->GetFactory()->NewEcmaHClass(size, type, numInlinedProps);
186     // Copy all
187     newJsHClass->Copy(thread, *jshclass);
188     newJsHClass->SetTransitions(thread, JSTaggedValue::Undefined());
189     newJsHClass->SetParent(thread, JSTaggedValue::Undefined());
190     newJsHClass->SetProtoChangeDetails(thread, JSTaggedValue::Null());
191     newJsHClass->SetEnumCache(thread, JSTaggedValue::Null());
192     // reuse Attributes first.
193     newJsHClass->SetLayout(thread, jshclass->GetLayout());
194 
195     if (jshclass->IsTS()) {
196         newJsHClass->SetTS(false);
197     }
198 
199     return newJsHClass;
200 }
201 
202 // use for transition to dictionary
CloneWithoutInlinedProperties(const JSThread * thread,const JSHandle<JSHClass> & jshclass)203 JSHandle<JSHClass> JSHClass::CloneWithoutInlinedProperties(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
204 {
205     return Clone(thread, jshclass, true);
206 }
207 
TransitionElementsToDictionary(const JSThread * thread,const JSHandle<JSObject> & obj)208 void JSHClass::TransitionElementsToDictionary(const JSThread *thread, const JSHandle<JSObject> &obj)
209 {
210     // property transition to slow first
211     if (!obj->GetJSHClass()->IsDictionaryMode()) {
212         JSObject::TransitionToDictionary(thread, obj);
213     } else {
214         TransitionToDictionary(thread, obj);
215     }
216     obj->GetJSHClass()->SetIsDictionaryElement(true);
217     obj->GetJSHClass()->SetIsStableElements(false);
218     obj->GetJSHClass()->SetElementsKind(ElementsKind::DICTIONARY);
219 }
220 
OptimizeAsFastElements(const JSThread * thread,JSHandle<JSObject> obj)221 void JSHClass::OptimizeAsFastElements(const JSThread *thread, JSHandle<JSObject> obj)
222 {
223     if (obj->GetJSHClass()->IsDictionaryMode()) {
224         JSObject::OptimizeAsFastProperties(thread, obj);
225     } else {
226         OptimizeAsFastProperties(thread, obj);
227     }
228     obj->GetJSHClass()->SetIsDictionaryElement(false);
229     obj->GetJSHClass()->SetIsStableElements(true);
230     obj->GetJSHClass()->SetElementsKind(ElementsKind::HOLE_TAGGED);
231 }
232 
AddProperty(const JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const PropertyAttributes & attr)233 void JSHClass::AddProperty(const JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
234                            const PropertyAttributes &attr)
235 {
236     JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
237     JSHClass *newClass = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(attr.GetPropertyMetaData()));
238     if (newClass != nullptr) {
239         // The transition hclass from AOT, which does not have a prototype, needs to be reset here.
240         if (newClass->IsTS()) {
241             newClass->SetPrototype(thread, jshclass->GetPrototype());
242         }
243         obj->SynchronizedSetClass(thread, newClass);
244         // Because we currently only supports Fast ElementsKind
245         JSHandle<JSHClass> newHClass(thread, newClass);
246         TryRestoreElementsKind(thread, newHClass, obj);
247 #if ECMASCRIPT_ENABLE_IC
248         JSHClass::NotifyHclassChanged(thread, jshclass, JSHandle<JSHClass>(thread, newClass), key.GetTaggedValue());
249 #endif
250         // The transition hclass from AOT, which does not have protochangemarker, needs to be reset here
251         if (newClass->IsTS() && newClass->IsPrototype()) {
252             JSHClass::RefreshUsers(thread, jshclass, JSHandle<JSHClass>(thread, newClass));
253             JSHClass::EnableProtoChangeMarker(thread, JSHandle<JSHClass>(thread, newClass));
254         }
255         return;
256     }
257     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
258     AddPropertyToNewHClass(thread, jshclass, newJsHClass, key, attr);
259     // update hclass in object.
260 #if ECMASCRIPT_ENABLE_IC
261     JSHClass::NotifyHclassChanged(thread, jshclass, newJsHClass, key.GetTaggedValue());
262 #endif
263     obj->SynchronizedSetClass(thread, *newJsHClass);
264     // Because we currently only supports Fast ElementsKind
265     TryRestoreElementsKind(thread, newJsHClass, obj);
266 
267     // Maintaining subtyping is no longer required when transition succeeds.
268     if (jshclass->HasTSSubtyping()) {
269         SubtypingOperator::TryMaintainTSSubtyping(thread, jshclass, newJsHClass, key);
270     }
271 }
272 
TryRestoreElementsKind(const JSThread * thread,JSHandle<JSHClass> newJsHClass,const JSHandle<JSObject> & obj)273 void JSHClass::TryRestoreElementsKind(const JSThread *thread, JSHandle<JSHClass> newJsHClass,
274                                       const JSHandle<JSObject> &obj)
275 {
276     ElementsKind newKind = ElementsKind::GENERIC;
277     if (newJsHClass->GetObjectType() == JSType::JS_ARRAY &&
278         obj->GetElements().IsMutantTaggedArray()) {
279         ElementsKind oldKind = newJsHClass->GetElementsKind();
280         Elements::MigrateArrayWithKind(thread, obj, oldKind, newKind);
281     }
282     newJsHClass->SetElementsKind(newKind);
283 }
284 
TransitionExtension(const JSThread * thread,const JSHandle<JSHClass> & jshclass)285 JSHandle<JSHClass> JSHClass::TransitionExtension(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
286 {
287     JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPreventExtensionsString());
288     {
289         auto *newClass = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(0));
290         if (newClass != nullptr) {
291             newClass->SetPrototype(thread, jshclass->GetPrototype());
292             return JSHandle<JSHClass>(thread, newClass);
293         }
294     }
295     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
296     // 2. new a hclass
297     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
298     newJsHClass->SetExtensible(false);
299 
300     JSTaggedValue attrs = newJsHClass->GetLayout();
301     {
302         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, attrs);
303         layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
304         newJsHClass->SetLayout(thread, layoutInfoHandle);
305     }
306 
307     // 3. Add newClass to old hclass's parent's transitions.
308     AddExtensionTransitions(thread, jshclass, newJsHClass, key);
309     // parent is the same as jshclass, already copy
310     return newJsHClass;
311 }
312 
TransitionProto(const JSThread * thread,const JSHandle<JSHClass> & jshclass,const JSHandle<JSTaggedValue> & proto)313 JSHandle<JSHClass> JSHClass::TransitionProto(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
314                                              const JSHandle<JSTaggedValue> &proto)
315 {
316     JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPrototypeString());
317 
318     {
319         auto *newClass = jshclass->FindProtoTransitions(key.GetTaggedValue(), proto.GetTaggedValue());
320         if (newClass != nullptr) {
321             return JSHandle<JSHClass>(thread, newClass);
322         }
323     }
324 
325     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
326     // 2. new a hclass
327     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
328     newJsHClass->SetPrototype(thread, proto.GetTaggedValue());
329 
330     JSTaggedValue layout = newJsHClass->GetLayout();
331     {
332         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, layout);
333         layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
334         newJsHClass->SetLayout(thread, layoutInfoHandle);
335     }
336 
337     // 3. Add newJsHClass to old jshclass's parent's transitions.
338     AddProtoTransitions(thread, jshclass, newJsHClass, key, proto);
339 
340     // parent is the same as jshclass, already copy
341     return newJsHClass;
342 }
343 
CloneWithAddProto(const JSThread * thread,const JSHandle<JSHClass> & jshclass,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & proto)344 JSHandle<JSHClass> JSHClass::CloneWithAddProto(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
345                                                const JSHandle<JSTaggedValue> &key,
346                                                const JSHandle<JSTaggedValue> &proto)
347 {
348     // 1. new a hclass
349     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
350     newJsHClass->SetPrototype(thread, proto.GetTaggedValue());
351 
352     // 2. Add newJsHClass to old jshclass's parent's transitions.
353     AddProtoTransitions(thread, jshclass, newJsHClass, key, proto);
354     // parent is the same as jshclass, already copy
355     return newJsHClass;
356 }
357 
TransProtoWithoutLayout(const JSThread * thread,const JSHandle<JSHClass> & jshclass,const JSHandle<JSTaggedValue> & proto)358 JSHandle<JSHClass> JSHClass::TransProtoWithoutLayout(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
359                                                      const JSHandle<JSTaggedValue> &proto)
360 {
361     JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPrototypeString());
362 
363     {
364         auto *newClass = jshclass->FindProtoTransitions(key.GetTaggedValue(), proto.GetTaggedValue());
365         if (newClass != nullptr) {
366             return JSHandle<JSHClass>(thread, newClass);
367         }
368     }
369 
370     return CloneWithAddProto(thread, jshclass, key, proto);
371 }
372 
SetPrototype(const JSThread * thread,JSTaggedValue proto)373 void JSHClass::SetPrototype(const JSThread *thread, JSTaggedValue proto)
374 {
375     // Because the heap-space of hclass is non-movable, this function can be non-static.
376     JSHandle<JSTaggedValue> protoHandle(thread, proto);
377     SetPrototype(thread, protoHandle);
378 }
379 
SetPrototypeWithNotification(const JSThread * thread,const JSHandle<JSTaggedValue> & hclass,const JSHandle<JSTaggedValue> & proto)380 JSHandle<JSTaggedValue> JSHClass::SetPrototypeWithNotification(const JSThread *thread,
381                                                                const JSHandle<JSTaggedValue> &hclass,
382                                                                const JSHandle<JSTaggedValue> &proto)
383 {
384     JSHandle<JSHClass> newClass = JSHClass::TransitionProto(thread, JSHandle<JSHClass>::Cast(hclass), proto);
385     JSHClass::NotifyHclassChanged(thread, JSHandle<JSHClass>::Cast(hclass), newClass);
386     return JSHandle<JSTaggedValue>(newClass);
387 }
388 
SetPrototype(const JSThread * thread,const JSHandle<JSTaggedValue> & proto)389 void JSHClass::SetPrototype(const JSThread *thread, const JSHandle<JSTaggedValue> &proto)
390 {
391     // Because the heap-space of hclass is non-movable, this function can be non-static.
392     if (proto->IsJSObject()) {
393         OptimizePrototypeForIC(thread, proto);
394     }
395     SetProto(thread, proto);
396 }
397 
OptimizePrototypeForIC(const JSThread * thread,const JSHandle<JSTaggedValue> & proto)398 void JSHClass::OptimizePrototypeForIC(const JSThread *thread, const JSHandle<JSTaggedValue> &proto)
399 {
400     JSHandle<JSHClass> hclass(thread, proto->GetTaggedObject()->GetClass());
401     ASSERT(!Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(*hclass))->InReadOnlySpace());
402     if (!hclass->IsPrototype()) {
403         if (!hclass->IsTS()) {
404             // The local IC and on-proto IC are different, because the former don't need to notify the whole
405             // prototype-chain or listen the changes of prototype chain, but the latter do. Therefore, when
406             // an object becomes a prototype object at the first time, we need to copy its hidden class in
407             // order to maintain the previously generated local IC and support the on-proto IC in the future.
408             // For example, a local IC adds a new property x for o1 and the o1.hclass1 -> o1.hclass2, when the
409             // o1 becomes a prototype object of object o2 and an on-proto IC loading x from o2 will rely on the
410             // stability of the prototype-chain o2 -> o1. If directly marking the o1.hclass1 as a prototype hclass,
411             // the previous IC of adding property x won't trigger IC-miss and fails to notify the IC on o2.
412             JSHandle<JSHClass> newProtoClass = JSHClass::Clone(thread, hclass);
413             JSTaggedValue layout = newProtoClass->GetLayout();
414             // If the type of object is JSObject, the layout info value is initialized to the default value,
415             // if the value is not JSObject, the layout info value is initialized to null.
416             if (!layout.IsNull()) {
417                 JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, layout);
418                 layoutInfoHandle.Update(
419                     thread->GetEcmaVM()->GetFactory()->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
420                 newProtoClass->SetLayout(thread, layoutInfoHandle);
421             }
422 
423 #if ECMASCRIPT_ENABLE_IC
424             // After the hclass is updated, check whether the proto chain status of ic is updated.
425             NotifyHclassChanged(thread, hclass, newProtoClass);
426 #endif
427             JSObject::Cast(proto->GetTaggedObject())->SynchronizedSetClass(thread, *newProtoClass);
428             newProtoClass->SetIsPrototype(true);
429             thread->GetEcmaVM()->GetPGOProfiler()->UpdateRootProfileType(*hclass, *newProtoClass);
430         } else {
431             // There is no sharing in AOT hclass. Therefore, it is not necessary or possible to clone here.
432             hclass->SetIsPrototype(true);
433         }
434     }
435 }
436 
TransitionToDictionary(const JSThread * thread,const JSHandle<JSObject> & obj)437 void JSHClass::TransitionToDictionary(const JSThread *thread, const JSHandle<JSObject> &obj)
438 {
439     // 1. new a hclass
440     JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
441     JSHandle<JSHClass> newJsHClass = CloneWithoutInlinedProperties(thread, jshclass);
442     UpdateRootHClass(thread, jshclass, newJsHClass);
443 
444     {
445         DISALLOW_GARBAGE_COLLECTION;
446         // 2. Copy
447         newJsHClass->SetNumberOfProps(0);
448         newJsHClass->SetIsDictionaryMode(true);
449         ASSERT(newJsHClass->GetInlinedProperties() == 0);
450 
451         // 3. Add newJsHClass to ?
452 #if ECMASCRIPT_ENABLE_IC
453         JSHClass::NotifyHclassChanged(thread, JSHandle<JSHClass>(thread, obj->GetJSHClass()), newJsHClass);
454 #endif
455         obj->SynchronizedSetClass(thread, *newJsHClass);
456         TryRestoreElementsKind(thread, newJsHClass, obj);
457     }
458 }
459 
OptimizeAsFastProperties(const JSThread * thread,const JSHandle<JSObject> & obj,const std::vector<int> & indexOrder,bool isDictionary)460 void JSHClass::OptimizeAsFastProperties(const JSThread *thread, const JSHandle<JSObject> &obj,
461                                         const std::vector<int> &indexOrder, bool isDictionary)
462 {
463     // 1. new a hclass
464     JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
465     JSHandle<JSHClass> newJsHClass = Clone(thread, jshclass, isDictionary);
466     UpdateRootHClass(thread, jshclass, newJsHClass);
467 
468     // 2. If it is dictionary, migrate should change layout. otherwise, copy the hclass only.
469     JSHandle<NameDictionary> properties(thread, obj->GetProperties());
470     int numberOfProperties = properties->EntriesCount();
471     if (isDictionary) {
472         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
473         JSHandle<LayoutInfo> layoutInfoHandle = factory->CreateLayoutInfo(numberOfProperties);
474         int numberOfInlinedProps = newJsHClass->GetInlinedProperties();
475         for (int i = 0; i < numberOfProperties; i++) {
476             JSTaggedValue key = properties->GetKey(indexOrder[i]);
477             PropertyAttributes attributes = properties->GetAttributes(indexOrder[i]);
478             if (i < numberOfInlinedProps) {
479                 attributes.SetIsInlinedProps(true);
480             } else {
481                 attributes.SetIsInlinedProps(false);
482             }
483             attributes.SetOffset(i);
484             layoutInfoHandle->AddKey(thread, i, key, attributes);
485         }
486 
487         {
488             DISALLOW_GARBAGE_COLLECTION;
489             newJsHClass->SetNumberOfProps(numberOfProperties);
490             newJsHClass->SetLayout(thread, layoutInfoHandle);
491         }
492     }
493 
494     {
495         DISALLOW_GARBAGE_COLLECTION;
496         // 3. Copy
497         newJsHClass->SetIsDictionaryMode(false);
498         // 4. Add newJsHClass to ?
499 #if ECMASCRIPT_ENABLE_IC
500         JSHClass::NotifyHclassChanged(thread, JSHandle<JSHClass>(thread, obj->GetJSHClass()), newJsHClass);
501 #endif
502         obj->SynchronizedSetClass(thread, *newJsHClass);
503     }
504 }
505 
TransitionForRepChange(const JSThread * thread,const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & key,PropertyAttributes attr)506 void JSHClass::TransitionForRepChange(const JSThread *thread, const JSHandle<JSObject> &receiver,
507     const JSHandle<JSTaggedValue> &key, PropertyAttributes attr)
508 {
509     JSHandle<JSHClass> oldHClass(thread, receiver->GetJSHClass());
510 
511     // 1. Create hclass and copy layout
512     JSHandle<JSHClass> newHClass = JSHClass::Clone(thread, oldHClass);
513 
514     JSHandle<LayoutInfo> oldLayout(thread, newHClass->GetLayout());
515     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
516     JSHandle<LayoutInfo> newLayout(factory->CopyLayoutInfo(oldLayout));
517     newHClass->SetLayout(thread, newLayout);
518 
519     // 2. update attr
520     auto hclass = JSHClass::Cast(newHClass.GetTaggedValue().GetTaggedObject());
521     int entry = JSHClass::FindPropertyEntry(thread, hclass, key.GetTaggedValue());
522     ASSERT(entry != -1);
523     newLayout->SetNormalAttr(thread, entry, attr);
524 
525     // 3. update hclass in object.
526 #if ECMASCRIPT_ENABLE_IC
527     JSHClass::NotifyHclassChanged(thread, oldHClass, newHClass, key.GetTaggedValue());
528 #endif
529 
530     receiver->SynchronizedSetClass(thread, *newHClass);
531     TryRestoreElementsKind(thread, newHClass, receiver);
532     // 4. Maybe Transition And Maintain subtypeing check
533 }
534 
GetInitialArrayHClassWithElementsKind(const JSThread * thread,const ElementsKind kind)535 JSHClass* JSHClass::GetInitialArrayHClassWithElementsKind(const JSThread *thread, const ElementsKind kind)
536 {
537     const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
538     auto newKindIter = arrayHClassIndexMap.find(kind);
539     if (newKindIter != arrayHClassIndexMap.end()) {
540         auto index = static_cast<size_t>(newKindIter->second);
541         auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
542         JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
543         return hclass;
544     }
545     return nullptr;
546 }
547 
TransitToElementsKind(const JSThread * thread,const JSHandle<JSArray> & array,ElementsKind newKind)548 void JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle<JSArray> &array,
549                                      ElementsKind newKind)
550 {
551     JSTaggedValue elements = array->GetElements();
552     if (!elements.IsTaggedArray()) {
553         return;
554     }
555     ElementsKind current = array->GetJSHClass()->GetElementsKind();
556     newKind = Elements::MergeElementsKind(newKind, current);
557     if (newKind == current) {
558         return;
559     }
560     // Currently, we only support fast array elementsKind
561     ASSERT(array->GetClass() == GetInitialArrayHClassWithElementsKind(thread, current));
562     const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
563     auto newKindIter = arrayHClassIndexMap.find(newKind);
564     if (newKindIter != arrayHClassIndexMap.end()) {
565         auto index = static_cast<size_t>(newKindIter->second);
566         auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
567         JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
568         array->SynchronizedSetClass(thread, hclass);
569     }
570 }
571 
TransitToElementsKind(const JSThread * thread,const JSHandle<JSObject> & object,const JSHandle<JSTaggedValue> & value,ElementsKind kind)572 bool JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle<JSObject> &object,
573                                      const JSHandle<JSTaggedValue> &value, ElementsKind kind)
574 {
575     if (!object->IsJSArray()) {
576         return false;
577     }
578     ElementsKind current = object->GetJSHClass()->GetElementsKind();
579     if (Elements::IsGeneric(current)) {
580         return false;
581     }
582     auto newKind = Elements::ToElementsKind(value.GetTaggedValue(), kind);
583     // Merge current kind and new kind
584     newKind = Elements::MergeElementsKind(current, newKind);
585     if (newKind == current) {
586         return false;
587     }
588     // Currently, we only support fast array elementsKind
589     ASSERT(object->GetClass() == GetInitialArrayHClassWithElementsKind(thread, current));
590     const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
591     auto newKindIter = arrayHClassIndexMap.find(newKind);
592     if (newKindIter != arrayHClassIndexMap.end()) {
593         auto index = static_cast<size_t>(newKindIter->second);
594         auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
595         JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
596         object->SynchronizedSetClass(thread, hclass);
597         // Update TrackInfo
598         if (!thread->IsPGOProfilerEnable()) {
599             return true;
600         }
601         auto trackInfoVal = JSHandle<JSArray>(object)->GetTrackInfo();
602         thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackElementsKind(trackInfoVal, newKind);
603         return true;
604     }
605     return false;
606 }
607 
ConvertOrTransitionWithRep(const JSThread * thread,const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,PropertyAttributes & attr)608 std::tuple<bool, bool, JSTaggedValue> JSHClass::ConvertOrTransitionWithRep(const JSThread *thread,
609     const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
610     PropertyAttributes &attr)
611 {
612     auto hclass = receiver->GetJSHClass();
613     auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
614     attr = layout->GetAttr(attr.GetOffset());
615     if (thread->IsPGOProfilerEnable() && !hclass->IsJSShared() && attr.UpdateTrackType(value.GetTaggedValue())) {
616         layout->SetNormalAttr(thread, attr.GetOffset(), attr);
617     }
618 
619     Representation oldRep = attr.GetRepresentation();
620     if (oldRep == Representation::DOUBLE) {
621         if (value->IsInt()) {
622             double doubleValue = value->GetInt();
623             return std::tuple<bool, bool, JSTaggedValue>(false, false, JSTaggedValue(bit_cast<JSTaggedType>(doubleValue)));
624         } else if (value->IsObject()) {
625             // Is Object
626             attr.SetRepresentation(Representation::TAGGED);
627             // Transition
628             JSHClass::TransitionForRepChange(thread, receiver, key, attr);
629             return std::tuple<bool, bool, JSTaggedValue>(true, true, value.GetTaggedValue());
630         } else {
631             // Is TaggedDouble
632             return std::tuple<bool, bool, JSTaggedValue>(false, false, JSTaggedValue(bit_cast<JSTaggedType>(value->GetDouble())));
633         }
634     } else if (oldRep == Representation::INT) {
635         if (value->IsInt()) {
636             int intValue = value->GetInt();
637             return std::tuple<bool, bool, JSTaggedValue>(false, false, JSTaggedValue(static_cast<JSTaggedType>(intValue)));
638         } else {
639             attr.SetRepresentation(Representation::TAGGED);
640             JSHClass::TransitionForRepChange(thread, receiver, key, attr);
641             return std::tuple<bool, bool, JSTaggedValue>(true, true, value.GetTaggedValue());
642         }
643     }
644     return std::tuple<bool, bool, JSTaggedValue>(true, false, value.GetTaggedValue());
645 }
646 
EnableProtoChangeMarker(const JSThread * thread,const JSHandle<JSHClass> & jshclass)647 JSHandle<JSTaggedValue> JSHClass::EnableProtoChangeMarker(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
648 {
649     JSTaggedValue proto = jshclass->GetPrototype();
650     if (!proto.IsECMAObject()) {
651         // Return JSTaggedValue directly. No proto check is needed.
652         LOG_ECMA(INFO) << "proto is not ecmaobject: " << proto.GetRawData();
653         return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Null());
654     }
655     JSHandle<JSObject> protoHandle(thread, proto);
656     JSHandle<JSHClass> protoClass(thread, protoHandle->GetJSHClass());
657     // in AOT's IC mechanism (VTable), when the prototype chain changes, it needs to notify each subclass
658     // PHC (prototype-HClass) and its IHC (instance-HClass) from the current PHC along the chain.
659     // therefore, when registering, it is also necessary to register IHC into its
660     // PHC's Listener to ensure that it can be notified.
661     if (jshclass->IsTSIHCWithInheritInfo()) {
662         RegisterOnProtoChain(thread, jshclass);
663     } else {
664         RegisterOnProtoChain(thread, protoClass);
665     }
666 
667     JSTaggedValue protoChangeMarker = protoClass->GetProtoChangeMarker();
668     if (protoChangeMarker.IsProtoChangeMarker()) {
669         JSHandle<ProtoChangeMarker> markerHandle(thread, ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject()));
670         if (!markerHandle->GetHasChanged()) {
671             return JSHandle<JSTaggedValue>(markerHandle);
672         }
673     }
674     JSHandle<ProtoChangeMarker> markerHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeMarker();
675     markerHandle->SetHasChanged(false);
676     protoClass->SetProtoChangeMarker(thread, markerHandle.GetTaggedValue());
677     return JSHandle<JSTaggedValue>(markerHandle);
678 }
679 
EnablePHCProtoChangeMarker(const JSThread * thread,const JSHandle<JSHClass> & protoClass)680 JSHandle<JSTaggedValue> JSHClass::EnablePHCProtoChangeMarker(const JSThread *thread,
681     const JSHandle<JSHClass> &protoClass)
682 {
683     RegisterOnProtoChain(thread, protoClass);
684 
685     JSTaggedValue protoChangeMarker = protoClass->GetProtoChangeMarker();
686     if (protoChangeMarker.IsProtoChangeMarker()) {
687         JSHandle<ProtoChangeMarker> markerHandle(thread, ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject()));
688         if (!markerHandle->GetHasChanged()) {
689             return JSHandle<JSTaggedValue>(markerHandle);
690         }
691     }
692     JSHandle<ProtoChangeMarker> markerHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeMarker();
693     markerHandle->SetHasChanged(false);
694     protoClass->SetProtoChangeMarker(thread, markerHandle.GetTaggedValue());
695     return JSHandle<JSTaggedValue>(markerHandle);
696 }
697 
NotifyHclassChanged(const JSThread * thread,JSHandle<JSHClass> oldHclass,JSHandle<JSHClass> newHclass,JSTaggedValue addedKey)698 void JSHClass::NotifyHclassChanged(const JSThread *thread, JSHandle<JSHClass> oldHclass, JSHandle<JSHClass> newHclass,
699                                    JSTaggedValue addedKey)
700 {
701     if (!oldHclass->IsPrototype()) {
702         return;
703     }
704     // The old hclass is the same as new one
705     if (oldHclass.GetTaggedValue() == newHclass.GetTaggedValue()) {
706         return;
707     }
708     ASSERT(newHclass->IsPrototype());
709     JSHClass::NoticeThroughChain(thread, oldHclass, addedKey);
710     JSHClass::RefreshUsers(thread, oldHclass, newHclass);
711 }
712 
NotifyAccessorChanged(const JSThread * thread,JSHandle<JSHClass> hclass)713 void JSHClass::NotifyAccessorChanged(const JSThread *thread, JSHandle<JSHClass> hclass)
714 {
715     DISALLOW_GARBAGE_COLLECTION;
716     JSTaggedValue markerValue = hclass->GetProtoChangeMarker();
717     if (markerValue.IsProtoChangeMarker()) {
718         ProtoChangeMarker *protoChangeMarker = ProtoChangeMarker::Cast(markerValue.GetTaggedObject());
719         protoChangeMarker->SetAccessorHasChanged(true);
720     }
721 
722     JSTaggedValue protoDetailsValue = hclass->GetProtoChangeDetails();
723     if (!protoDetailsValue.IsProtoChangeDetails()) {
724         return;
725     }
726     JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
727     if (!listenersValue.IsTaggedArray()) {
728         return;
729     }
730     ChangeListener *listeners = ChangeListener::Cast(listenersValue.GetTaggedObject());
731     for (uint32_t i = 0; i < listeners->GetEnd(); i++) {
732         JSTaggedValue temp = listeners->Get(i);
733         if (temp.IsJSHClass()) {
734             NotifyAccessorChanged(thread, JSHandle<JSHClass>(thread, listeners->Get(i).GetTaggedObject()));
735         }
736     }
737 }
738 
RegisterOnProtoChain(const JSThread * thread,const JSHandle<JSHClass> & jshclass)739 void JSHClass::RegisterOnProtoChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
740 {
741     JSHandle<JSHClass> user = jshclass;
742     JSHandle<ProtoChangeDetails> userDetails = GetProtoChangeDetails(thread, user);
743 
744     while (true) {
745         // Find the prototype chain as far as the hclass has not been registered.
746         if (userDetails->GetRegisterIndex() != static_cast<uint32_t>(ProtoChangeDetails::UNREGISTERED)) {
747             return;
748         }
749 
750         JSTaggedValue proto = user->GetPrototype();
751         if (!proto.IsHeapObject()) {
752             return;
753         }
754         if (proto.IsJSProxy()) {
755             return;
756         }
757         ASSERT(proto.IsECMAObject());
758         JSHandle<JSObject> protoHandle(thread, proto);
759         JSHandle<ProtoChangeDetails> protoDetails =
760             GetProtoChangeDetails(thread, JSHandle<JSHClass>(thread, protoHandle->GetJSHClass()));
761         JSTaggedValue listeners = protoDetails->GetChangeListener();
762         JSHandle<ChangeListener> listenersHandle;
763         if (listeners.IsUndefined()) {
764             listenersHandle = JSHandle<ChangeListener>(ChangeListener::Create(thread));
765         } else {
766             listenersHandle = JSHandle<ChangeListener>(thread, listeners);
767         }
768         uint32_t registerIndex = 0;
769         JSHandle<ChangeListener> newListeners = ChangeListener::Add(thread, listenersHandle, user, &registerIndex);
770         userDetails->SetRegisterIndex(registerIndex);
771         protoDetails->SetChangeListener(thread, newListeners.GetTaggedValue());
772         userDetails = protoDetails;
773         user = JSHandle<JSHClass>(thread, protoHandle->GetJSHClass());
774     }
775 }
776 
UnregisterOnProtoChain(const JSThread * thread,const JSHandle<JSHClass> & jshclass)777 bool JSHClass::UnregisterOnProtoChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
778 {
779     ASSERT(jshclass->IsPrototype());
780     if (!jshclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
781         return false;
782     }
783     if (!jshclass->GetPrototype().IsECMAObject()) {
784         JSTaggedValue listeners =
785             ProtoChangeDetails::Cast(jshclass->GetProtoChangeDetails().GetTaggedObject())->GetChangeListener();
786         return !listeners.IsUndefined();
787     }
788     JSHandle<ProtoChangeDetails> currentDetails = GetProtoChangeDetails(thread, jshclass);
789     uint32_t index = currentDetails->GetRegisterIndex();
790     if (index == static_cast<uint32_t>(ProtoChangeDetails::UNREGISTERED)) {
791         return false;
792     }
793     JSTaggedValue proto = jshclass->GetPrototype();
794     ASSERT(proto.IsECMAObject());
795     JSTaggedValue protoDetailsValue = JSObject::Cast(proto.GetTaggedObject())->GetJSHClass()->GetProtoChangeDetails();
796     if (protoDetailsValue.IsUndefined()) {
797         return false;
798     }
799     ASSERT(protoDetailsValue.IsProtoChangeDetails());
800     JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
801     ASSERT(!listenersValue.IsUndefined());
802     JSHandle<ChangeListener> listeners(thread, listenersValue.GetTaggedObject());
803     ASSERT(listeners->Get(index) == jshclass.GetTaggedValue());
804     listeners->Delete(thread, index);
805     return true;
806 }
807 
GetProtoChangeDetails(const JSThread * thread,const JSHandle<JSHClass> & jshclass)808 JSHandle<ProtoChangeDetails> JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
809 {
810     JSTaggedValue protoDetails = jshclass->GetProtoChangeDetails();
811     if (protoDetails.IsProtoChangeDetails()) {
812         return JSHandle<ProtoChangeDetails>(thread, protoDetails);
813     }
814     JSHandle<ProtoChangeDetails> protoDetailsHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeDetails();
815     jshclass->SetProtoChangeDetails(thread, protoDetailsHandle.GetTaggedValue());
816     return protoDetailsHandle;
817 }
818 
GetProtoChangeDetails(const JSThread * thread,const JSHandle<JSObject> & obj)819 JSHandle<ProtoChangeDetails> JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle<JSObject> &obj)
820 {
821     JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
822     return GetProtoChangeDetails(thread, jshclass);
823 }
824 
MarkProtoChanged(const JSThread * thread,const JSHandle<JSHClass> & jshclass,JSTaggedValue addedKey)825 void JSHClass::MarkProtoChanged(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
826                                 JSTaggedValue addedKey)
827 {
828     DISALLOW_GARBAGE_COLLECTION;
829     ASSERT(jshclass->IsPrototype() || jshclass->HasTSSubtyping());
830     JSTaggedValue markerValue = jshclass->GetProtoChangeMarker();
831     if (markerValue.IsProtoChangeMarker()) {
832         ProtoChangeMarker *protoChangeMarker = ProtoChangeMarker::Cast(markerValue.GetTaggedObject());
833         protoChangeMarker->SetHasChanged(true);
834     }
835 
836     if (jshclass->HasTSSubtyping()) {
837         if (addedKey.IsString()) {
838             JSHandle<JSTaggedValue> key(thread, addedKey);
839             if (SubtypingOperator::TryMaintainTSSubtypingOnPrototype(thread, jshclass, key)) {
840                 return;
841             }
842         }
843         jshclass->InitTSInheritInfo(thread);
844     }
845 }
846 
NoticeThroughChain(const JSThread * thread,const JSHandle<JSHClass> & jshclass,JSTaggedValue addedKey)847 void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
848                                   JSTaggedValue addedKey)
849 {
850     DISALLOW_GARBAGE_COLLECTION;
851     MarkProtoChanged(thread, jshclass, addedKey);
852     JSTaggedValue protoDetailsValue = jshclass->GetProtoChangeDetails();
853     if (!protoDetailsValue.IsProtoChangeDetails()) {
854         return;
855     }
856     JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
857     if (!listenersValue.IsTaggedArray()) {
858         return;
859     }
860     ChangeListener *listeners = ChangeListener::Cast(listenersValue.GetTaggedObject());
861     for (uint32_t i = 0; i < listeners->GetEnd(); i++) {
862         JSTaggedValue temp = listeners->Get(i);
863         if (temp.IsJSHClass()) {
864             NoticeThroughChain(thread, JSHandle<JSHClass>(thread, listeners->Get(i).GetTaggedObject()), addedKey);
865         }
866     }
867 }
868 
RefreshUsers(const JSThread * thread,const JSHandle<JSHClass> & oldHclass,const JSHandle<JSHClass> & newHclass)869 void JSHClass::RefreshUsers(const JSThread *thread, const JSHandle<JSHClass> &oldHclass,
870                             const JSHandle<JSHClass> &newHclass)
871 {
872     ASSERT(oldHclass->IsPrototype());
873     ASSERT(newHclass->IsPrototype());
874     bool onceRegistered = UnregisterOnProtoChain(thread, oldHclass);
875 
876     // oldHclass is already marked. Only update newHclass.protoChangeDetails if it doesn't exist for further use.
877     if (!newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
878         newHclass->SetProtoChangeDetails(thread, oldHclass->GetProtoChangeDetails());
879     }
880     oldHclass->SetProtoChangeDetails(thread, JSTaggedValue::Undefined());
881     if (onceRegistered) {
882         if (newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
883             ProtoChangeDetails::Cast(newHclass->GetProtoChangeDetails().GetTaggedObject())
884                 ->SetRegisterIndex(ProtoChangeDetails::UNREGISTERED);
885         }
886         RegisterOnProtoChain(thread, newHclass);
887     }
888 }
889 
HasTSSubtyping() const890 bool JSHClass::HasTSSubtyping() const
891 {
892     // if fill TS inherit info, supers must not be empty
893     WeakVector *supers = WeakVector::Cast(GetSupers().GetTaggedObject());
894     return !(supers->Empty());
895 }
896 
IsTSIHCWithInheritInfo() const897 bool JSHClass::IsTSIHCWithInheritInfo() const
898 {
899     return IsTS() && !IsPrototype() && HasTSSubtyping();
900 }
901 
LookupPropertyInAotHClass(const JSThread * thread,JSHClass * hclass,JSTaggedValue key)902 PropertyLookupResult JSHClass::LookupPropertyInAotHClass(const JSThread *thread, JSHClass *hclass, JSTaggedValue key)
903 {
904     DISALLOW_GARBAGE_COLLECTION;
905     ASSERT(hclass->IsTS());
906 
907     PropertyLookupResult result;
908     if (hclass->IsDictionaryMode()) {
909         // not fuond
910         result.SetIsFound(false);
911         return result;
912     }
913 
914     int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
915     // found in local
916     if (entry != -1) {
917         result.SetIsFound(true);
918         result.SetIsLocal(true);
919         PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry);
920         if (attr.IsInlinedProps()) {
921             result.SetIsInlinedProps(true);
922             result.SetOffset(hclass->GetInlinedPropertiesOffset(entry));
923         } else {
924             result.SetIsInlinedProps(false);
925             result.SetOffset(attr.GetOffset() - hclass->GetInlinedProperties());
926         }
927         if (attr.IsNotHole()) {
928             result.SetIsNotHole(true);
929         }
930         if (attr.IsAccessor()) {
931             result.SetIsAccessor(true);
932         }
933         result.SetRepresentation(attr.GetRepresentation());
934         result.SetIsWritable(attr.IsWritable());
935         return result;
936     }
937 
938     // found in vtable
939     if (hclass->GetVTable().IsUndefined()) {
940         result.SetIsFound(false);
941         return result;
942     }
943     JSHandle<VTable> vtable(thread, hclass->GetVTable());
944     entry = vtable->GetTupleIndexByName(key);
945     if (entry != -1) {
946         result.SetIsVtable();
947         uint32_t offset = static_cast<uint32_t>(entry * VTable::TUPLE_SIZE);
948         result.SetOffset(offset);
949         if (vtable->IsAccessor(entry)) {
950             result.SetIsAccessor(true);
951         }
952         return result;
953     }
954 
955     // not found
956     result.SetIsFound(false);
957     return result;
958 }
959 
LookupPropertyInPGOHClass(const JSThread * thread,JSHClass * hclass,JSTaggedValue key)960 PropertyLookupResult JSHClass::LookupPropertyInPGOHClass(const JSThread *thread, JSHClass *hclass, JSTaggedValue key)
961 {
962     DISALLOW_GARBAGE_COLLECTION;
963     ASSERT(hclass->IsTS());
964 
965     PropertyLookupResult result;
966     if (hclass->IsDictionaryMode()) {
967         result.SetIsFound(false);
968         return result;
969     }
970 
971     int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
972     // found in local
973     if (entry != -1) {
974         result.SetIsFound(true);
975         result.SetIsLocal(true);
976         PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry);
977         if (attr.IsInlinedProps()) {
978             result.SetIsInlinedProps(true);
979             result.SetOffset(hclass->GetInlinedPropertiesOffset(entry));
980         } else {
981             result.SetIsInlinedProps(false);
982             result.SetOffset(attr.GetOffset() - hclass->GetInlinedProperties());
983         }
984 
985         if (attr.IsNotHole()) {
986             result.SetIsNotHole(true);
987         }
988         if (attr.IsAccessor()) {
989             result.SetIsAccessor(true);
990         }
991         result.SetRepresentation(attr.GetRepresentation());
992         result.SetIsWritable(attr.IsWritable());
993         return result;
994     }
995 
996     // not fuond
997     result.SetIsFound(false);
998     return result;
999 }
1000 
LookupPropertyInBuiltinPrototypeHClass(const JSThread * thread,JSHClass * hclass,JSTaggedValue key)1001 PropertyLookupResult JSHClass::LookupPropertyInBuiltinPrototypeHClass(const JSThread *thread, JSHClass *hclass,
1002                                                                       JSTaggedValue key)
1003 {
1004     DISALLOW_GARBAGE_COLLECTION;
1005     ASSERT(hclass->IsPrototype());
1006 
1007     PropertyLookupResult result;
1008     if (hclass->IsDictionaryMode()) {
1009         // not fuond
1010         result.SetIsFound(false);
1011         return result;
1012     }
1013     int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
1014     // When the property is not found, the value of 'entry' is -1.
1015     // Currently, not all methods on the prototype of 'builtin' have been changed to inlined.
1016     // Therefore, when a non-inlined method is encountered, it is also considered not found.
1017     if (entry == -1 || static_cast<uint32_t>(entry) >= hclass->GetInlinedProperties()) {
1018         result.SetIsFound(false);
1019         return result;
1020     }
1021 
1022     result.SetIsFound(true);
1023     result.SetIsLocal(true);
1024     PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry);
1025     if (attr.IsInlinedProps()) {
1026         result.SetIsInlinedProps(true);
1027         result.SetOffset(hclass->GetInlinedPropertiesOffset(entry));
1028     } else {
1029         result.SetIsInlinedProps(false);
1030         result.SetOffset(attr.GetOffset() - hclass->GetInlinedProperties());
1031     }
1032     result.SetIsNotHole(true);
1033     if (attr.IsAccessor()) {
1034         result.SetIsAccessor(true);
1035     }
1036     result.SetRepresentation(attr.GetRepresentation());
1037     result.SetIsWritable(attr.IsWritable());
1038     return result;
1039 }
1040 
CopyTSInheritInfo(const JSThread * thread,const JSHandle<JSHClass> & oldHClass,JSHandle<JSHClass> & newHClass)1041 void JSHClass::CopyTSInheritInfo(const JSThread *thread, const JSHandle<JSHClass> &oldHClass,
1042                                  JSHandle<JSHClass> &newHClass)
1043 {
1044     JSHandle<WeakVector> supers(thread, oldHClass->GetSupers());
1045     JSHandle<WeakVector> copySupers = WeakVector::Copy(thread, supers);
1046     newHClass->SetSupers(thread, copySupers);
1047 
1048     uint8_t level = oldHClass->GetLevel();
1049     newHClass->SetLevel(level);
1050 
1051     JSHandle<VTable> vtable(thread, oldHClass->GetVTable());
1052     JSHandle<VTable> copyVtable = VTable::Copy(thread, vtable);
1053     newHClass->SetVTable(thread, copyVtable);
1054 }
1055 
CreateRootHClass(const JSThread * thread,const HClassLayoutDesc * desc,uint32_t maxNum)1056 JSHandle<JSHClass> JSHClass::CreateRootHClass(const JSThread *thread, const HClassLayoutDesc *desc, uint32_t maxNum)
1057 {
1058     auto rootDesc = reinterpret_cast<const pgo::RootHClassLayoutDesc *>(desc);
1059     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1060     uint32_t numOfProps = rootDesc->NumOfProps();
1061     uint32_t index = 0;
1062     JSType type = rootDesc->GetObjectType();
1063     size_t size = rootDesc->GetObjectSize();
1064     JSHandle<JSHClass> hclass = factory->NewEcmaHClass(size, type, maxNum);
1065     // Dictionary?
1066     JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(maxNum, MemSpaceType::SEMI_SPACE, GrowMode::KEEP);
1067     rootDesc->IterateProps([thread, factory, &index, hclass, layout] (const pgo::PropertyDesc &propDesc) {
1068         JSHandle<EcmaString> key = factory->NewFromStdString(std::string(propDesc.first));
1069         PropertyAttributes attributes = PropertyAttributes::Default();
1070         if (propDesc.second.SetAttribute(thread, attributes)) {
1071             hclass->SetIsAllTaggedProp(false);
1072         }
1073         attributes.SetIsInlinedProps(true);
1074         attributes.SetOffset(index);
1075         layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
1076         index++;
1077     });
1078     hclass->SetLayout(thread, layout);
1079     hclass->SetNumberOfProps(numOfProps);
1080     hclass->SetTS(true);
1081     return hclass;
1082 }
1083 
CreateChildHClass(const JSThread * thread,const JSHandle<JSHClass> & parent,const HClassLayoutDesc * desc)1084 JSHandle<JSHClass> JSHClass::CreateChildHClass(
1085     const JSThread *thread, const JSHandle<JSHClass> &parent, const HClassLayoutDesc *desc)
1086 {
1087     pgo::PropertyDesc propDesc = reinterpret_cast<const pgo::ChildHClassLayoutDesc *>(desc)->GetPropertyDesc();
1088     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1089     uint32_t numOfProps = parent->NumberOfProps();
1090 
1091     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, parent);
1092     newJsHClass->SetTS(true);
1093     ASSERT(newJsHClass->GetInlinedProperties() >= (numOfProps + 1));
1094     uint32_t offset = numOfProps;
1095     {
1096         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, newJsHClass->GetLayout());
1097         if (layoutInfoHandle->NumberOfElements() != static_cast<int>(offset)) {
1098             layoutInfoHandle.Update(factory->CopyAndReSort(layoutInfoHandle, offset, offset + 1));
1099             newJsHClass->SetLayout(thread, layoutInfoHandle);
1100         } else if (layoutInfoHandle->GetPropertiesCapacity() <= static_cast<int>(offset)) {  // need to Grow
1101             layoutInfoHandle.Update(
1102                 factory->ExtendLayoutInfo(layoutInfoHandle, offset));
1103             newJsHClass->SetLayout(thread, layoutInfoHandle);
1104         }
1105         JSHandle<EcmaString> key = factory->NewFromStdString(std::string(propDesc.first));
1106         PropertyAttributes attributes = PropertyAttributes::Default();
1107         if (propDesc.second.SetAttribute(thread, attributes)) {
1108             newJsHClass->SetIsAllTaggedProp(false);
1109         }
1110         attributes.SetOffset(offset);
1111         attributes.SetIsInlinedProps(true);
1112         layoutInfoHandle->AddKey(thread, offset, key.GetTaggedValue(), attributes);
1113         newJsHClass->IncNumberOfProps();
1114         AddTransitions(thread, parent, newJsHClass, JSHandle<JSTaggedValue>(key), attributes);
1115         JSHClass::NotifyHclassChanged(thread, parent, newJsHClass, key.GetTaggedValue());
1116     }
1117 
1118     return newJsHClass;
1119 }
1120 
DumpForRootHClass(const JSHClass * hclass,HClassLayoutDesc * desc)1121 bool JSHClass::DumpForRootHClass(const JSHClass *hclass, HClassLayoutDesc *desc)
1122 {
1123     DISALLOW_GARBAGE_COLLECTION;
1124     if (hclass->IsDictionaryMode()) {
1125         return false;
1126     }
1127 
1128     LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1129     int element = static_cast<int>(hclass->NumberOfProps());
1130     for (int i = 0; i < element; i++) {
1131         layout->DumpFieldIndex(i, desc);
1132     }
1133     return true;
1134 }
1135 
DumpForChildHClass(const JSHClass * hclass,HClassLayoutDesc * desc)1136 bool JSHClass::DumpForChildHClass(const JSHClass *hclass, HClassLayoutDesc *desc)
1137 {
1138     DISALLOW_GARBAGE_COLLECTION;
1139     if (hclass->IsDictionaryMode()) {
1140         return false;
1141     }
1142 
1143     uint32_t last = hclass->NumberOfProps() - 1;
1144     LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1145     layoutInfo->DumpFieldIndex(last, desc);
1146     return true;
1147 }
1148 
UpdateChildLayoutDesc(const JSHClass * hclass,HClassLayoutDesc * childDesc)1149 bool JSHClass::UpdateChildLayoutDesc(const JSHClass *hclass, HClassLayoutDesc *childDesc)
1150 {
1151     DISALLOW_GARBAGE_COLLECTION;
1152     if (hclass->IsDictionaryMode()) {
1153         return false;
1154     }
1155 
1156     uint32_t last = hclass->NumberOfProps() - 1;
1157     LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1158     return layoutInfo->UpdateFieldIndex(last, childDesc);
1159 }
1160 
UpdateRootLayoutDesc(const JSHClass * hclass,const PGOHClassTreeDesc * treeDesc,HClassLayoutDesc * desc)1161 bool JSHClass::UpdateRootLayoutDesc(const JSHClass *hclass, const PGOHClassTreeDesc *treeDesc, HClassLayoutDesc *desc)
1162 {
1163     DISALLOW_GARBAGE_COLLECTION;
1164     if (hclass->IsDictionaryMode()) {
1165         return false;
1166     }
1167 
1168     auto rootDesc = reinterpret_cast<const pgo::RootHClassLayoutDesc *>(desc);
1169     int rootPropLen = static_cast<int>(rootDesc->NumOfProps());
1170     int element = static_cast<int>(hclass->NumberOfProps());
1171     ASSERT(element >= rootPropLen);
1172     LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1173     for (int i = 0; i < rootPropLen; i++) {
1174         layout->UpdateFieldIndex(i, desc);
1175     }
1176     auto lastDesc = desc;
1177     for (int i = rootPropLen; i < element - 1; i++) {
1178         if (lastDesc == nullptr || lastDesc->GetChildSize() == 0) {
1179             break;
1180         }
1181         lastDesc->IterateChilds([treeDesc, layout, i, &lastDesc] (const ProfileType &childType) -> bool {
1182             lastDesc = treeDesc->GetHClassLayoutDesc(childType);
1183             if (lastDesc == nullptr) {
1184                 return true;
1185             }
1186             return !layout->UpdateFieldIndex(i, lastDesc);
1187         });
1188     }
1189     return true;
1190 }
1191 
DumpToString(JSTaggedType hclassVal)1192 CString JSHClass::DumpToString(JSTaggedType hclassVal)
1193 {
1194     DISALLOW_GARBAGE_COLLECTION;
1195     auto hclass = JSHClass::Cast(JSTaggedValue(hclassVal).GetTaggedObject());
1196     if (hclass->IsDictionaryMode()) {
1197         return "";
1198     }
1199 
1200     CString result;
1201     LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1202     int element = static_cast<int>(hclass->NumberOfProps());
1203     for (int i = 0; i < element; i++) {
1204         auto key = layout->GetKey(i);
1205         if (key.IsString()) {
1206             result += EcmaStringAccessor(key).ToCString();
1207             auto attr = layout->GetAttr(i);
1208             result += static_cast<int32_t>(attr.GetTrackType());
1209             result += attr.GetPropertyMetaData();
1210         } else if (key.IsSymbol()) {
1211             result += "IsSymbolkey";
1212         } else {
1213             LOG_ECMA(FATAL) << "JSHClass::DumpToString UNREACHABLE";
1214         }
1215     }
1216     return result;
1217 }
1218 
LookupPropertyInBuiltinHClass(const JSThread * thread,JSHClass * hclass,JSTaggedValue key)1219 PropertyLookupResult JSHClass::LookupPropertyInBuiltinHClass(const JSThread *thread, JSHClass *hclass,
1220                                                              JSTaggedValue key)
1221 {
1222     DISALLOW_GARBAGE_COLLECTION;
1223 
1224     PropertyLookupResult result;
1225     if (hclass->IsDictionaryMode()) {
1226         result.SetIsFound(false);
1227         return result;
1228     }
1229 
1230     int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
1231     // found in local
1232     if (entry != -1) {
1233         result.SetIsFound(true);
1234         result.SetIsLocal(true);
1235         PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry);
1236         if (attr.IsInlinedProps()) {
1237             result.SetIsInlinedProps(true);
1238             result.SetOffset(hclass->GetInlinedPropertiesOffset(entry));
1239         } else {
1240             result.SetIsInlinedProps(false);
1241             result.SetOffset(attr.GetOffset() - hclass->GetInlinedProperties());
1242         }
1243 
1244         if (attr.IsNotHole()) {
1245             result.SetIsNotHole(true);
1246         }
1247         if (attr.IsAccessor()) {
1248             result.SetIsAccessor(true);
1249         }
1250         result.SetRepresentation(attr.GetRepresentation());
1251         result.SetIsWritable(attr.IsWritable());
1252         return result;
1253     }
1254 
1255     // not fuond
1256     result.SetIsFound(false);
1257     return result;
1258 }
1259 }  // namespace panda::ecmascript
1260