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, ®isterIndex);
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