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