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