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