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