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