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