• 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/js_hclass-inl.h"
17 
18 #include <algorithm>
19 
20 #include "ecmascript/base/config.h"
21 #include "ecmascript/global_env.h"
22 #include "ecmascript/tagged_array.h"
23 #include "ecmascript/vtable.h"
24 #include "ecmascript/ic/proto_change_details.h"
25 #include "ecmascript/js_object-inl.h"
26 #include "ecmascript/js_symbol.h"
27 #include "ecmascript/mem/c_containers.h"
28 #include "ecmascript/subtyping_operator.h"
29 #include "ecmascript/tagged_array-inl.h"
30 #include "ecmascript/weak_vector.h"
31 
32 namespace panda::ecmascript {
PutIfAbsent(const JSThread * thread,const JSHandle<TransitionsDictionary> & dictionary,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & metaData)33 JSHandle<TransitionsDictionary> TransitionsDictionary::PutIfAbsent(const JSThread *thread,
34                                                                    const JSHandle<TransitionsDictionary> &dictionary,
35                                                                    const JSHandle<JSTaggedValue> &key,
36                                                                    const JSHandle<JSTaggedValue> &value,
37                                                                    const JSHandle<JSTaggedValue> &metaData)
38 {
39     int hash = TransitionsDictionary::Hash(key.GetTaggedValue(), metaData.GetTaggedValue());
40 
41     /* no need to add key if exist */
42     int entry = dictionary->FindEntry(key.GetTaggedValue(), metaData.GetTaggedValue());
43     if (entry != -1) {
44         if (dictionary->GetValue(entry).IsUndefined()) {
45             JSTaggedValue weakValue = JSTaggedValue(value->CreateAndGetWeakRef());
46             dictionary->SetValue(thread, entry, weakValue);
47         }
48         return dictionary;
49     }
50 
51     // Check whether the dictionary should be extended.
52     JSHandle<TransitionsDictionary> newDictionary(HashTableT::GrowHashTable(thread, dictionary));
53     // Compute the key object.
54     entry = newDictionary->FindInsertIndex(hash);
55     JSTaggedValue val = value.GetTaggedValue();
56     newDictionary->SetEntry(thread, entry, key.GetTaggedValue(), val, metaData.GetTaggedValue());
57 
58     newDictionary->IncreaseEntries(thread);
59     return newDictionary;
60 }
61 
FindEntry(const JSTaggedValue & key,const JSTaggedValue & metaData)62 int TransitionsDictionary::FindEntry(const JSTaggedValue &key, const JSTaggedValue &metaData)
63 {
64     size_t size = static_cast<size_t>(Size());
65     uint32_t count = 1;
66     int32_t hash = TransitionsDictionary::Hash(key, metaData);
67     // GrowHashTable will guarantee the hash table is never full.
68     for (uint32_t entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) {
69         JSTaggedValue element = GetKey(entry);
70         if (element.IsHole()) {
71             continue;
72         }
73         if (element.IsUndefined()) {
74             return -1;
75         }
76 
77         if (TransitionsDictionary::IsMatch(key, metaData, element, GetAttributes(entry).GetWeakRawValue())) {
78             return static_cast<int>(entry);
79         }
80     }
81     return -1;
82 }
83 
Remove(const JSThread * thread,const JSHandle<TransitionsDictionary> & table,const JSHandle<JSTaggedValue> & key,const JSTaggedValue & metaData)84 JSHandle<TransitionsDictionary> TransitionsDictionary::Remove(const JSThread *thread,
85                                                               const JSHandle<TransitionsDictionary> &table,
86                                                               const JSHandle<JSTaggedValue> &key,
87                                                               const JSTaggedValue &metaData)
88 {
89     int entry = table->FindEntry(key.GetTaggedValue(), metaData);
90     if (entry == -1) {
91         return table;
92     }
93 
94     table->RemoveElement(thread, entry);
95     return TransitionsDictionary::Shrink(thread, table);
96 }
97 
Rehash(const JSThread * thread,TransitionsDictionary * newTable)98 void TransitionsDictionary::Rehash(const JSThread *thread, TransitionsDictionary *newTable)
99 {
100     DISALLOW_GARBAGE_COLLECTION;
101     if (newTable == nullptr) {
102         return;
103     }
104     int size = this->Size();
105     // Rehash elements to new table
106     int entryCount = 0;
107     for (int i = 0; i < size; i++) {
108         int fromIndex = GetEntryIndex(i);
109         JSTaggedValue k = this->GetKey(i);
110         JSTaggedValue v = this->GetValue(i);
111         if (IsKey(k) && TransitionsDictionary::CheckWeakExist(v)) {
112             int hash = TransitionsDictionary::Hash(k, this->GetAttributes(i));
113             int insertionIndex = GetEntryIndex(newTable->FindInsertIndex(hash));
114             JSTaggedValue tv = Get(fromIndex);
115             newTable->Set(thread, insertionIndex, tv);
116             for (int j = 1; j < TransitionsDictionary::ENTRY_SIZE; j++) {
117                 tv = Get(fromIndex + j);
118                 newTable->Set(thread, insertionIndex + j, tv);
119             }
120             entryCount++;
121         }
122     }
123     newTable->SetEntriesCount(thread, entryCount);
124     newTable->SetHoleEntriesCount(thread, 0);
125 }
126 
127 // class JSHClass
Initialize(const JSThread * thread,uint32_t size,JSType type,uint32_t inlinedProps,bool isOptimized,bool canFastCall)128 void JSHClass::Initialize(const JSThread *thread, uint32_t size, JSType type, uint32_t inlinedProps,
129                           bool isOptimized, bool canFastCall)
130 {
131     DISALLOW_GARBAGE_COLLECTION;
132     ClearBitField();
133     if (JSType::JS_OBJECT_FIRST <= type && type <= JSType::JS_OBJECT_LAST) {
134         SetObjectSize(size + inlinedProps * JSTaggedValue::TaggedTypeSize());
135         SetInlinedPropsStart(size);
136         SetLayout(thread, thread->GlobalConstants()->GetEmptyLayoutInfo());
137     } else {
138         SetObjectSize(size);
139         SetLayout(thread, JSTaggedValue::Null());
140     }
141     if (type >= JSType::JS_FUNCTION_FIRST && type <= JSType::JS_FUNCTION_LAST) {
142         SetIsJSFunction(true);
143         SetIsOptimized(isOptimized);
144         SetCanFastCall(canFastCall);
145     }
146     SetPrototype(thread, JSTaggedValue::Null());
147 
148     SetObjectType(type);
149     SetExtensible(true);
150     SetIsPrototype(false);
151     SetHasDeleteProperty(false);
152     SetIsAllTaggedProp(true);
153     SetElementsKind(ElementsKind::GENERIC);
154     SetTransitions(thread, JSTaggedValue::Undefined());
155     SetProtoChangeMarker(thread, JSTaggedValue::Null());
156     SetProtoChangeDetails(thread, JSTaggedValue::Null());
157     SetEnumCache(thread, JSTaggedValue::Null());
158     InitTSInheritInfo(thread);
159 }
160 
InitTSInheritInfo(const JSThread * thread)161 void JSHClass::InitTSInheritInfo(const JSThread *thread)
162 {
163     // Supers and Level are used to record the relationship between TSHClass.
164     if (IsECMAObject()) {
165         SetSupers(thread, thread->GlobalConstants()->GetDefaultSupers());
166     } else {
167         SetSupers(thread, JSTaggedValue::Undefined());
168     }
169     SetLevel(0);
170 
171     // VTable records the location information of properties and methods of TSHClass,
172     // which is used to perform efficient IC at runtime
173     SetVTable(thread, JSTaggedValue::Undefined());
174 }
175 
Clone(const JSThread * thread,const JSHandle<JSHClass> & jshclass,bool withoutInlinedProperties)176 JSHandle<JSHClass> JSHClass::Clone(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
177                                    bool withoutInlinedProperties)
178 {
179     JSType type = jshclass->GetObjectType();
180     uint32_t size = jshclass->GetInlinedPropsStartSize();
181     uint32_t numInlinedProps = withoutInlinedProperties ? 0 : jshclass->GetInlinedProperties();
182     JSHandle<JSHClass> newJsHClass = thread->GetEcmaVM()->GetFactory()->NewEcmaHClass(size, type, numInlinedProps);
183     // Copy all
184     newJsHClass->Copy(thread, *jshclass);
185     newJsHClass->SetTransitions(thread, JSTaggedValue::Undefined());
186     newJsHClass->SetProtoChangeDetails(thread, JSTaggedValue::Null());
187     newJsHClass->SetEnumCache(thread, JSTaggedValue::Null());
188     // reuse Attributes first.
189     newJsHClass->SetLayout(thread, jshclass->GetLayout());
190 
191     if (jshclass->IsTS()) {
192         newJsHClass->SetTS(false);
193     }
194 
195     return newJsHClass;
196 }
197 
198 // use for transition to dictionary
CloneWithoutInlinedProperties(const JSThread * thread,const JSHandle<JSHClass> & jshclass)199 JSHandle<JSHClass> JSHClass::CloneWithoutInlinedProperties(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
200 {
201     return Clone(thread, jshclass, true);
202 }
203 
TransitionElementsToDictionary(const JSThread * thread,const JSHandle<JSObject> & obj)204 void JSHClass::TransitionElementsToDictionary(const JSThread *thread, const JSHandle<JSObject> &obj)
205 {
206     // property transition to slow first
207     if (!obj->GetJSHClass()->IsDictionaryMode()) {
208         JSObject::TransitionToDictionary(thread, obj);
209     }
210     obj->GetJSHClass()->SetIsDictionaryElement(true);
211     obj->GetJSHClass()->SetIsStableElements(false);
212     obj->GetJSHClass()->SetElementsKind(ElementsKind::GENERIC);
213 }
214 
SetPropertyOfObjHClass(const JSThread * thread,JSHandle<JSHClass> & jshclass,const JSHandle<JSTaggedValue> & key,const PropertyAttributes & attr)215 JSHandle<JSHClass> JSHClass::SetPropertyOfObjHClass(const JSThread *thread, JSHandle<JSHClass> &jshclass,
216                                                     const JSHandle<JSTaggedValue> &key,
217                                                     const PropertyAttributes &attr)
218 {
219     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
220     JSHClass *newClass = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(attr.GetPropertyMetaData()));
221     if (newClass != nullptr) {
222         return JSHandle<JSHClass>(thread, newClass);
223     }
224 
225     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
226     newJsHClass->IncNumberOfProps();
227     uint32_t offset = attr.GetOffset();
228     {
229         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, newJsHClass->GetLayout());
230         if (layoutInfoHandle->NumberOfElements() != static_cast<int>(offset)) {
231             layoutInfoHandle.Update(factory->CopyAndReSort(layoutInfoHandle, offset, offset + 1));
232         } else if (layoutInfoHandle->GetPropertiesCapacity() <= static_cast<int>(offset)) { // need to Grow
233             layoutInfoHandle.Update(
234                 factory->ExtendLayoutInfo(layoutInfoHandle, offset));
235         }
236         newJsHClass->SetLayout(thread, layoutInfoHandle);
237         layoutInfoHandle->AddKey(thread, offset, key.GetTaggedValue(), attr);
238     }
239 
240     AddTransitions(thread, jshclass, newJsHClass, key, attr);
241     return newJsHClass;
242 }
243 
AddProperty(const JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const PropertyAttributes & attr)244 void JSHClass::AddProperty(const JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
245                            const PropertyAttributes &attr)
246 {
247     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
248     JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
249     JSHClass *newClass = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(attr.GetPropertyMetaData()));
250     if (newClass != nullptr) {
251         obj->SynchronizedSetClass(newClass);
252 #if ECMASCRIPT_ENABLE_IC
253         JSHClass::NotifyHclassChanged(thread, jshclass, JSHandle<JSHClass>(thread, newClass), key.GetTaggedValue());
254 #endif
255         return;
256     }
257 
258     // 2. Create hclass
259     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
260 
261     // 3. Add Property and metaData
262     uint32_t offset = attr.GetOffset();
263     newJsHClass->IncNumberOfProps();
264 
265     {
266         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, newJsHClass->GetLayout());
267 
268         if (layoutInfoHandle->NumberOfElements() != static_cast<int>(offset)) {
269             layoutInfoHandle.Update(factory->CopyAndReSort(layoutInfoHandle, offset, offset + 1));
270         } else if (layoutInfoHandle->GetPropertiesCapacity() <= static_cast<int>(offset)) {  // need to Grow
271             layoutInfoHandle.Update(
272                 factory->ExtendLayoutInfo(layoutInfoHandle, offset));
273         }
274         newJsHClass->SetLayout(thread, layoutInfoHandle);
275         layoutInfoHandle->AddKey(thread, offset, key.GetTaggedValue(), attr);
276     }
277 
278     // 4. Add newClass to old hclass's transitions.
279     AddTransitions(thread, jshclass, newJsHClass, key, attr);
280 
281     // 5. update hclass in object.
282 #if ECMASCRIPT_ENABLE_IC
283     JSHClass::NotifyHclassChanged(thread, jshclass, newJsHClass, key.GetTaggedValue());
284 #endif
285     obj->SynchronizedSetClass(*newJsHClass);
286 
287     // Maintaining subtyping is no longer required when transition succeeds.
288     if (jshclass->HasTSSubtyping()) {
289         SubtypingOperator::TryMaintainTSSubtyping(thread, jshclass, newJsHClass, key);
290     }
291 }
292 
TransitionExtension(const JSThread * thread,const JSHandle<JSHClass> & jshclass)293 JSHandle<JSHClass> JSHClass::TransitionExtension(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
294 {
295     JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPreventExtensionsString());
296     {
297         auto *newClass = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(0));
298         if (newClass != nullptr) {
299             return JSHandle<JSHClass>(thread, newClass);
300         }
301     }
302     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
303     // 2. new a hclass
304     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
305     newJsHClass->SetExtensible(false);
306 
307     JSTaggedValue attrs = newJsHClass->GetLayout();
308     {
309         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, attrs);
310         layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
311         newJsHClass->SetLayout(thread, layoutInfoHandle);
312     }
313 
314     // 3. Add newClass to old hclass's parent's transitions.
315     AddExtensionTransitions(thread, jshclass, newJsHClass, key);
316     // parent is the same as jshclass, already copy
317     return newJsHClass;
318 }
319 
TransitionProto(const JSThread * thread,const JSHandle<JSHClass> & jshclass,const JSHandle<JSTaggedValue> & proto)320 JSHandle<JSHClass> JSHClass::TransitionProto(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
321                                              const JSHandle<JSTaggedValue> &proto)
322 {
323     JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPrototypeString());
324 
325     {
326         auto *newClass = jshclass->FindProtoTransitions(key.GetTaggedValue(), proto.GetTaggedValue());
327         if (newClass != nullptr) {
328             return JSHandle<JSHClass>(thread, newClass);
329         }
330     }
331 
332     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
333     // 2. new a hclass
334     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
335     newJsHClass->SetPrototype(thread, proto.GetTaggedValue());
336 
337     JSTaggedValue layout = newJsHClass->GetLayout();
338     {
339         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, layout);
340         layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
341         newJsHClass->SetLayout(thread, layoutInfoHandle);
342     }
343 
344     // 3. Add newJsHClass to old jshclass's parent's transitions.
345     AddProtoTransitions(thread, jshclass, newJsHClass, key, proto);
346 
347     // parent is the same as jshclass, already copy
348     return newJsHClass;
349 }
350 
TransProtoWithoutLayout(const JSThread * thread,const JSHandle<JSHClass> & jshclass,const JSHandle<JSTaggedValue> & proto)351 JSHandle<JSHClass> JSHClass::TransProtoWithoutLayout(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
352                                                      const JSHandle<JSTaggedValue> &proto)
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     }
362 
363     // 2. new a hclass
364     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
365     newJsHClass->SetPrototype(thread, proto.GetTaggedValue());
366 
367     // 3. Add newJsHClass to old jshclass's parent's transitions.
368     AddProtoTransitions(thread, jshclass, newJsHClass, key, proto);
369 
370     // parent is the same as jshclass, already copy
371     return newJsHClass;
372 }
373 
SetPrototype(const JSThread * thread,JSTaggedValue proto)374 void JSHClass::SetPrototype(const JSThread *thread, JSTaggedValue proto)
375 {
376     JSHandle<JSTaggedValue> protoHandle(thread, proto);
377     SetPrototype(thread, protoHandle);
378 }
379 
SetPrototype(const JSThread * thread,const JSHandle<JSTaggedValue> & proto)380 void JSHClass::SetPrototype(const JSThread *thread, const JSHandle<JSTaggedValue> &proto)
381 {
382     // In the original version, whether the objcet is EcmaObject is determined,
383     // but proxy is not allowd.
384     if (proto->IsJSObject()) {
385         ShouldUpdateProtoClass(thread, proto);
386     }
387     SetProto(thread, proto);
388 }
389 
ShouldUpdateProtoClass(const JSThread * thread,const JSHandle<JSTaggedValue> & proto)390 void JSHClass::ShouldUpdateProtoClass(const JSThread *thread, const JSHandle<JSTaggedValue> &proto)
391 {
392     JSHandle<JSHClass> hclass(thread, proto->GetTaggedObject()->GetClass());
393     ASSERT(!Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(*hclass))->InReadOnlySpace());
394     if (!hclass->IsPrototype()) {
395         // If the objcet should be changed to the proto of an object,
396         // the original hclass cannot be shared.
397         JSHandle<JSHClass> newProtoClass = JSHClass::Clone(thread, hclass);
398         JSTaggedValue layout = newProtoClass->GetLayout();
399         // If the type of object is JSObject, the layout info value is initialized to the default value,
400         // if the value is not JSObject, the layout info value is initialized to null.
401         if (!layout.IsNull()) {
402             JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, layout);
403             layoutInfoHandle.Update(
404                 thread->GetEcmaVM()->GetFactory()->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
405             newProtoClass->SetLayout(thread, layoutInfoHandle);
406         }
407 
408 #if ECMASCRIPT_ENABLE_IC
409         // After the hclass is updated, check whether the proto chain status of ic is updated.
410         NotifyHclassChanged(thread, hclass, newProtoClass);
411 #endif
412         JSObject::Cast(proto->GetTaggedObject())->SynchronizedSetClass(*newProtoClass);
413         newProtoClass->SetIsPrototype(true);
414     }
415 }
416 
TransitionToDictionary(const JSThread * thread,const JSHandle<JSObject> & obj)417 void JSHClass::TransitionToDictionary(const JSThread *thread, const JSHandle<JSObject> &obj)
418 {
419     // 1. new a hclass
420     JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
421     JSHandle<JSHClass> newJsHClass = CloneWithoutInlinedProperties(thread, jshclass);
422 
423     {
424         DISALLOW_GARBAGE_COLLECTION;
425         // 2. Copy
426         newJsHClass->SetNumberOfProps(0);
427         newJsHClass->SetIsDictionaryMode(true);
428         ASSERT(newJsHClass->GetInlinedProperties() == 0);
429 
430         // 3. Add newJsHClass to ?
431 #if ECMASCRIPT_ENABLE_IC
432         JSHClass::NotifyHclassChanged(thread, JSHandle<JSHClass>(thread, obj->GetJSHClass()), newJsHClass);
433 #endif
434         obj->SynchronizedSetClass(*newJsHClass);
435     }
436 }
437 
TransitionForRepChange(const JSThread * thread,const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & key,PropertyAttributes attr)438 void JSHClass::TransitionForRepChange(const JSThread *thread, const JSHandle<JSObject> &receiver,
439     const JSHandle<JSTaggedValue> &key, PropertyAttributes attr)
440 {
441     JSHandle<JSHClass> oldHClass(thread, receiver->GetJSHClass());
442 
443     // 1. Create hclass and copy layout
444     JSHandle<JSHClass> newHClass = JSHClass::Clone(thread, oldHClass);
445 
446     JSHandle<LayoutInfo> oldLayout(thread, newHClass->GetLayout());
447     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
448     JSHandle<LayoutInfo> newLayout(factory->CopyLayoutInfo(oldLayout));
449     newHClass->SetLayout(thread, newLayout);
450 
451     // 2. update attr
452     auto hclass = JSHClass::Cast(newHClass.GetTaggedValue().GetTaggedObject());
453     int entry = JSHClass::FindPropertyEntry(thread, hclass, key.GetTaggedValue());
454     ASSERT(entry != -1);
455     newLayout->SetNormalAttr(thread, entry, attr);
456 
457     // 3. update hclass in object.
458 #if ECMASCRIPT_ENABLE_IC
459     JSHClass::NotifyHclassChanged(thread, oldHClass, newHClass, key.GetTaggedValue());
460 #endif
461 
462     receiver->SynchronizedSetClass(*newHClass);
463     // 4. Maybe Transition And Maintain subtypeing check
464 }
465 
TransitToElementsKind(const JSThread * thread,const JSHandle<JSArray> & array)466 void JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle<JSArray> &array)
467 {
468     JSTaggedValue elements = array->GetElements();
469     if (!elements.IsTaggedArray()) {
470         return;
471     }
472     ElementsKind newKind = ElementsKind::NONE;
473     auto elementArray = TaggedArray::Cast(elements);
474     int length = elementArray->GetLength();
475     for (int i = 0; i < length; i++) {
476         JSTaggedValue value = elementArray->Get(i);
477         newKind = Elements::ToElementsKind(value, newKind);
478     }
479     ElementsKind current = array->GetJSHClass()->GetElementsKind();
480     if (newKind == current) {
481         return;
482     }
483     auto arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
484     if (arrayHClassIndexMap.find(newKind) != arrayHClassIndexMap.end()) {
485         auto index = static_cast<size_t>(thread->GetArrayHClassIndexMap().at(newKind));
486         auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
487         JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
488         array->SetClass(hclass);
489     }
490 }
491 
TransitToElementsKind(const JSThread * thread,const JSHandle<JSObject> & object,const JSHandle<JSTaggedValue> & value,ElementsKind kind)492 void JSHClass::TransitToElementsKind(
493     const JSThread *thread, const JSHandle<JSObject> &object, const JSHandle<JSTaggedValue> &value, ElementsKind kind)
494 {
495     if (!object->IsJSArray()) {
496         return;
497     }
498     ElementsKind current = object->GetJSHClass()->GetElementsKind();
499     if (Elements::IsGeneric(current)) {
500         return;
501     }
502     auto newKind = Elements::ToElementsKind(value.GetTaggedValue(), kind);
503     // Merge current kind and new kind
504     newKind = Elements::MergeElementsKind(current, newKind);
505     if (newKind == current) {
506         return;
507     }
508     auto arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
509     if (arrayHClassIndexMap.find(newKind) != arrayHClassIndexMap.end()) {
510         auto index = static_cast<size_t>(thread->GetArrayHClassIndexMap().at(newKind));
511         auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
512         JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
513         object->SetClass(hclass);
514     }
515 }
516 
EnableProtoChangeMarker(const JSThread * thread,const JSHandle<JSHClass> & jshclass)517 JSHandle<JSTaggedValue> JSHClass::EnableProtoChangeMarker(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
518 {
519     JSTaggedValue proto = jshclass->GetPrototype();
520     if (!proto.IsECMAObject()) {
521         // Return JSTaggedValue directly. No proto check is needed.
522         LOG_ECMA(FATAL) << "this branch is unreachable";
523         UNREACHABLE();
524     }
525     JSHandle<JSObject> protoHandle(thread, proto);
526     JSHandle<JSHClass> protoClass(thread, protoHandle->GetJSHClass());
527     // in AOT's IC mechanism (VTable), when the prototype chain changes, it needs to notify each subclass
528     // PHC (prototype-HClass) and its IHC (instance-HClass) from the current PHC along the chain.
529     // therefore, when registering, it is also necessary to register IHC into its
530     // PHC's Listener to ensure that it can be notified.
531     if (jshclass->IsTSIHCWithInheritInfo()) {
532         RegisterOnProtoChain(thread, jshclass);
533     } else {
534         RegisterOnProtoChain(thread, protoClass);
535     }
536 
537     JSTaggedValue protoChangeMarker = protoClass->GetProtoChangeMarker();
538     if (protoChangeMarker.IsProtoChangeMarker()) {
539         JSHandle<ProtoChangeMarker> markerHandle(thread, ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject()));
540         if (!markerHandle->GetHasChanged()) {
541             return JSHandle<JSTaggedValue>(markerHandle);
542         }
543     }
544     JSHandle<ProtoChangeMarker> markerHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeMarker();
545     markerHandle->SetHasChanged(false);
546     protoClass->SetProtoChangeMarker(thread, markerHandle.GetTaggedValue());
547     return JSHandle<JSTaggedValue>(markerHandle);
548 }
549 
NotifyHclassChanged(const JSThread * thread,JSHandle<JSHClass> oldHclass,JSHandle<JSHClass> newHclass,JSTaggedValue addedKey)550 void JSHClass::NotifyHclassChanged(const JSThread *thread, JSHandle<JSHClass> oldHclass, JSHandle<JSHClass> newHclass,
551                                    JSTaggedValue addedKey)
552 {
553     if (!oldHclass->IsPrototype()) {
554         return;
555     }
556     // The old hclass is the same as new one
557     if (oldHclass.GetTaggedValue() == newHclass.GetTaggedValue()) {
558         return;
559     }
560     newHclass->SetIsPrototype(true);
561     JSHClass::NoticeThroughChain(thread, oldHclass, addedKey);
562     JSHClass::RefreshUsers(thread, oldHclass, newHclass);
563 }
564 
RegisterOnProtoChain(const JSThread * thread,const JSHandle<JSHClass> & jshclass)565 void JSHClass::RegisterOnProtoChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
566 {
567     JSHandle<JSHClass> user = jshclass;
568     JSHandle<ProtoChangeDetails> userDetails = GetProtoChangeDetails(thread, user);
569 
570     while (true) {
571         // Find the prototype chain as far as the hclass has not been registered.
572         if (userDetails->GetRegisterIndex() != static_cast<uint32_t>(ProtoChangeDetails::UNREGISTERED)) {
573             return;
574         }
575 
576         JSTaggedValue proto = user->GetPrototype();
577         if (!proto.IsHeapObject()) {
578             return;
579         }
580         if (proto.IsJSProxy()) {
581             return;
582         }
583         ASSERT(proto.IsECMAObject());
584         JSHandle<JSObject> protoHandle(thread, proto);
585         JSHandle<ProtoChangeDetails> protoDetails =
586             GetProtoChangeDetails(thread, JSHandle<JSHClass>(thread, protoHandle->GetJSHClass()));
587         JSTaggedValue listeners = protoDetails->GetChangeListener();
588         JSHandle<ChangeListener> listenersHandle;
589         if (listeners.IsUndefined()) {
590             listenersHandle = JSHandle<ChangeListener>(ChangeListener::Create(thread));
591         } else {
592             listenersHandle = JSHandle<ChangeListener>(thread, listeners);
593         }
594         uint32_t registerIndex = 0;
595         JSHandle<ChangeListener> newListeners = ChangeListener::Add(thread, listenersHandle, user, &registerIndex);
596         userDetails->SetRegisterIndex(registerIndex);
597         protoDetails->SetChangeListener(thread, newListeners.GetTaggedValue());
598         userDetails = protoDetails;
599         user = JSHandle<JSHClass>(thread, protoHandle->GetJSHClass());
600     }
601 }
602 
UnregisterOnProtoChain(const JSThread * thread,const JSHandle<JSHClass> & jshclass)603 bool JSHClass::UnregisterOnProtoChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
604 {
605     ASSERT(jshclass->IsPrototype());
606     if (!jshclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
607         return false;
608     }
609     if (!jshclass->GetPrototype().IsECMAObject()) {
610         JSTaggedValue listeners =
611             ProtoChangeDetails::Cast(jshclass->GetProtoChangeDetails().GetTaggedObject())->GetChangeListener();
612         return !listeners.IsUndefined();
613     }
614     JSHandle<ProtoChangeDetails> currentDetails = GetProtoChangeDetails(thread, jshclass);
615     uint32_t index = currentDetails->GetRegisterIndex();
616     if (index == static_cast<uint32_t>(ProtoChangeDetails::UNREGISTERED)) {
617         return false;
618     }
619     JSTaggedValue proto = jshclass->GetPrototype();
620     ASSERT(proto.IsECMAObject());
621     JSTaggedValue protoDetailsValue = JSObject::Cast(proto.GetTaggedObject())->GetJSHClass()->GetProtoChangeDetails();
622     if (protoDetailsValue.IsUndefined() || protoDetailsValue.IsNull()) {
623         return false;
624     }
625     ASSERT(protoDetailsValue.IsProtoChangeDetails());
626     JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
627     ASSERT(!listenersValue.IsUndefined());
628     JSHandle<ChangeListener> listeners(thread, listenersValue.GetTaggedObject());
629     ASSERT(listeners->Get(index) == jshclass.GetTaggedValue());
630     listeners->Delete(thread, index);
631     return true;
632 }
633 
GetProtoChangeDetails(const JSThread * thread,const JSHandle<JSHClass> & jshclass)634 JSHandle<ProtoChangeDetails> JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
635 {
636     JSTaggedValue protoDetails = jshclass->GetProtoChangeDetails();
637     if (protoDetails.IsProtoChangeDetails()) {
638         return JSHandle<ProtoChangeDetails>(thread, protoDetails);
639     }
640     JSHandle<ProtoChangeDetails> protoDetailsHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeDetails();
641     jshclass->SetProtoChangeDetails(thread, protoDetailsHandle.GetTaggedValue());
642     return protoDetailsHandle;
643 }
644 
GetProtoChangeDetails(const JSThread * thread,const JSHandle<JSObject> & obj)645 JSHandle<ProtoChangeDetails> JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle<JSObject> &obj)
646 {
647     JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
648     return GetProtoChangeDetails(thread, jshclass);
649 }
650 
MarkProtoChanged(const JSThread * thread,const JSHandle<JSHClass> & jshclass,JSTaggedValue addedKey)651 void JSHClass::MarkProtoChanged(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
652                                 JSTaggedValue addedKey)
653 {
654     DISALLOW_GARBAGE_COLLECTION;
655     ASSERT(jshclass->IsPrototype() || jshclass->HasTSSubtyping());
656     JSTaggedValue markerValue = jshclass->GetProtoChangeMarker();
657     if (markerValue.IsProtoChangeMarker()) {
658         ProtoChangeMarker *protoChangeMarker = ProtoChangeMarker::Cast(markerValue.GetTaggedObject());
659         protoChangeMarker->SetHasChanged(true);
660     }
661 
662     if (jshclass->HasTSSubtyping()) {
663         if (addedKey.IsString()) {
664             JSHandle<JSTaggedValue> key(thread, addedKey);
665             if (SubtypingOperator::TryMaintainTSSubtypingOnPrototype(thread, jshclass, key)) {
666                 return;
667             }
668         }
669         jshclass->InitTSInheritInfo(thread);
670     }
671 }
672 
NoticeThroughChain(const JSThread * thread,const JSHandle<JSHClass> & jshclass,JSTaggedValue addedKey)673 void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
674                                   JSTaggedValue addedKey)
675 {
676     DISALLOW_GARBAGE_COLLECTION;
677     MarkProtoChanged(thread, jshclass, addedKey);
678     JSTaggedValue protoDetailsValue = jshclass->GetProtoChangeDetails();
679     if (!protoDetailsValue.IsProtoChangeDetails()) {
680         return;
681     }
682     JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
683     if (!listenersValue.IsTaggedArray()) {
684         return;
685     }
686     ChangeListener *listeners = ChangeListener::Cast(listenersValue.GetTaggedObject());
687     for (uint32_t i = 0; i < listeners->GetEnd(); i++) {
688         JSTaggedValue temp = listeners->Get(i);
689         if (temp.IsJSHClass()) {
690             NoticeThroughChain(thread, JSHandle<JSHClass>(thread, listeners->Get(i).GetTaggedObject()), addedKey);
691         }
692     }
693 }
694 
RefreshUsers(const JSThread * thread,const JSHandle<JSHClass> & oldHclass,const JSHandle<JSHClass> & newHclass)695 void JSHClass::RefreshUsers(const JSThread *thread, const JSHandle<JSHClass> &oldHclass,
696                             const JSHandle<JSHClass> &newHclass)
697 {
698     ASSERT(oldHclass->IsPrototype());
699     ASSERT(newHclass->IsPrototype());
700     bool onceRegistered = UnregisterOnProtoChain(thread, oldHclass);
701 
702     // oldHclass is already marked. Only update newHclass.protoChangeDetails if it doesn't exist for further use.
703     if (!newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
704         newHclass->SetProtoChangeDetails(thread, oldHclass->GetProtoChangeDetails());
705     }
706     oldHclass->SetProtoChangeDetails(thread, JSTaggedValue::Undefined());
707     if (onceRegistered) {
708         if (newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
709             ProtoChangeDetails::Cast(newHclass->GetProtoChangeDetails().GetTaggedObject())
710                 ->SetRegisterIndex(ProtoChangeDetails::UNREGISTERED);
711         }
712         RegisterOnProtoChain(thread, newHclass);
713     }
714 }
715 
HasTSSubtyping() const716 bool JSHClass::HasTSSubtyping() const
717 {
718     // if fill TS inherit info, supers must not be empty
719     WeakVector *supers = WeakVector::Cast(GetSupers().GetTaggedObject());
720     return !(supers->Empty());
721 }
722 
IsTSIHCWithInheritInfo() const723 bool JSHClass::IsTSIHCWithInheritInfo() const
724 {
725     return IsTS() && !IsPrototype() && HasTSSubtyping();
726 }
727 
LookupPropertyInAotHClass(const JSThread * thread,JSHClass * hclass,JSTaggedValue key)728 PropertyLookupResult JSHClass::LookupPropertyInAotHClass(const JSThread *thread, JSHClass *hclass, JSTaggedValue key)
729 {
730     DISALLOW_GARBAGE_COLLECTION;
731     ASSERT(hclass->IsTS());
732 
733     PropertyLookupResult result;
734     int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
735     // found in local
736     if (entry != -1) {
737         result.SetIsFound(true);
738         result.SetIsLocal(true);
739         uint32_t offset = hclass->GetInlinedPropertiesOffset(entry);
740         result.SetOffset(offset);
741         PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry);
742         if (attr.IsNotHole()) {
743             result.SetIsNotHole(true);
744         }
745         if (attr.IsAccessor()) {
746             result.SetIsAccessor(true);
747         }
748         result.SetRepresentation(attr.GetRepresentation());
749         result.SetIsWritable(attr.IsWritable());
750         return result;
751     }
752 
753     // found in vtable
754     if (hclass->GetVTable().IsUndefined()) {
755         result.SetIsFound(false);
756         return result;
757     }
758     JSHandle<VTable> vtable(thread, hclass->GetVTable());
759     entry = vtable->GetTupleIndexByName(key);
760     if (entry != -1) {
761         result.SetIsVtable();
762         uint32_t offset = static_cast<uint32_t>(entry * VTable::TUPLE_SIZE);
763         result.SetOffset(offset);
764         if (vtable->IsAccessor(entry)) {
765             result.SetIsAccessor(true);
766         }
767         return result;
768     }
769 
770     // not fuond
771     result.SetIsFound(false);
772     return result;
773 }
774 
CopyTSInheritInfo(const JSThread * thread,const JSHandle<JSHClass> & oldHClass,JSHandle<JSHClass> & newHClass)775 void JSHClass::CopyTSInheritInfo(const JSThread *thread, const JSHandle<JSHClass> &oldHClass,
776                                  JSHandle<JSHClass> &newHClass)
777 {
778     JSHandle<WeakVector> supers(thread, oldHClass->GetSupers());
779     JSHandle<WeakVector> copySupers = WeakVector::Copy(thread, supers);
780     newHClass->SetSupers(thread, copySupers);
781 
782     uint8_t level = oldHClass->GetLevel();
783     newHClass->SetLevel(level);
784 
785     JSHandle<VTable> vtable(thread, oldHClass->GetVTable());
786     JSHandle<VTable> copyVtable = VTable::Copy(thread, vtable);
787     newHClass->SetVTable(thread, copyVtable);
788 }
789 
DumpForProfile(const JSHClass * hclass,PGOHClassLayoutDesc & desc,PGOObjKind kind)790 bool JSHClass::DumpForProfile(const JSHClass *hclass, PGOHClassLayoutDesc &desc, PGOObjKind kind)
791 {
792     DISALLOW_GARBAGE_COLLECTION;
793     if (hclass->IsDictionaryMode()) {
794         return false;
795     }
796     if (kind == PGOObjKind::ELEMENT) {
797         desc.UpdateElementKind(hclass->GetElementsKind());
798     }
799 
800     LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
801     int element = static_cast<int>(hclass->NumberOfProps());
802     for (int i = 0; i < element; i++) {
803         layout->DumpFieldIndexForProfile(i, desc, kind);
804     }
805     return true;
806 }
807 }  // namespace panda::ecmascript
808