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#ifndef ECMASCRIPT_JS_HCLASS_INL_H 17#define ECMASCRIPT_JS_HCLASS_INL_H 18 19#include "ecmascript/js_hclass.h" 20 21#include "ecmascript/js_bigint.h" 22#include "ecmascript/layout_info.h" 23#include "ecmascript/layout_info-inl.h" 24#include "ecmascript/byte_array.h" 25#include "ecmascript/mem/assert_scope.h" 26#include "ecmascript/transitions_dictionary.h" 27 28namespace panda::ecmascript { 29 30bool JSHClass::ProtoIsFastJSArray(const JSThread *thread, const JSHandle<JSTaggedValue> proto, 31 const JSHandle<JSHClass> hclass) 32{ 33 // Since we currently only support ElementsKind for JSArray initial hclass, 34 // if an object's hclass has a non-generic ElementsKind, it must be one of the JSArray initial hclass. 35 // if an object's hclass has a Generic ElementsKind, it might be the JSArray initial generic elementskind hclass, 36 // which therefore needs further hclass comparison. 37 if (proto->IsJSArray()) { 38 JSTaggedValue genericArrayHClass = thread->GlobalConstants()->GetElementHoleTaggedClass(); 39 if (!Elements::IsGeneric(hclass->GetElementsKind()) || hclass.GetTaggedValue() == genericArrayHClass) { 40 return true; 41 } 42 } 43 return false; 44} 45 46void JSHClass::AddTransitions(const JSThread *thread, const JSHandle<JSHClass> &parent, const JSHandle<JSHClass> &child, 47 const JSHandle<JSTaggedValue> &key, PropertyAttributes attributes) 48{ 49 UpdateRootHClass(thread, parent, child); 50 JSTaggedValue transitions = parent->GetTransitions(); 51 if (transitions.IsUndefined()) { 52 JSTaggedValue weakChild = JSTaggedValue(child.GetTaggedValue().CreateAndGetWeakRef()); 53 parent->SetTransitions(thread, weakChild); 54 return; 55 } 56 JSMutableHandle<TransitionsDictionary> dict(thread, JSTaggedValue::Undefined()); 57 if (transitions.IsWeak()) { 58 auto cachedHClass = JSHClass::Cast(transitions.GetTaggedWeakRef()); 59 if (cachedHClass->HasProps()) { 60 uint32_t last = cachedHClass->LastPropIndex(); 61 LayoutInfo* layoutInfo = LayoutInfo::Cast(cachedHClass->GetLayout().GetTaggedObject()); 62 auto metaData = JSHandle<JSTaggedValue>(thread, 63 JSTaggedValue(layoutInfo->GetAttr(last).GetPropertyMetaData())); 64 auto lastKey = JSHandle<JSTaggedValue>(thread, layoutInfo->GetKey(last)); 65 auto lastHClass = JSHandle<JSTaggedValue>(thread, cachedHClass); 66 dict.Update(TransitionsDictionary::Create(thread)); 67 transitions = TransitionsDictionary::PutIfAbsent(thread, dict, lastKey, lastHClass, 68 metaData).GetTaggedValue(); 69 } 70 } 71 auto metaData = JSHandle<JSTaggedValue>(thread, JSTaggedValue(attributes.GetPropertyMetaData())); 72 dict.Update(transitions); 73 transitions = TransitionsDictionary::PutIfAbsent(thread, dict, key, JSHandle<JSTaggedValue>(child), 74 metaData).GetTaggedValue(); 75 parent->SetTransitions(thread, transitions); 76} 77 78void JSHClass::AddExtensionTransitions(const JSThread *thread, const JSHandle<JSHClass> &parent, 79 const JSHandle<JSHClass> &child, const JSHandle<JSTaggedValue> &key) 80{ 81 auto attr = JSHandle<JSTaggedValue>(thread, PropertyAttributes(0).GetTaggedValue()); 82 AddProtoTransitions(thread, parent, child, key, attr); 83} 84 85void JSHClass::AddProtoTransitions(const JSThread *thread, const JSHandle<JSHClass> &parent, 86 const JSHandle<JSHClass> &child, const JSHandle<JSTaggedValue> &key, 87 const JSHandle<JSTaggedValue> &proto) 88{ 89 ALLOW_LOCAL_TO_SHARE_WEAK_REF_HANDLE; 90 UpdateRootHClass(thread, parent, child); 91 JSTaggedValue transitions = parent->GetTransitions(); 92 JSMutableHandle<TransitionsDictionary> dict(thread, JSTaggedValue::Undefined()); 93 if (transitions.IsUndefined()) { 94 transitions = TransitionsDictionary::Create(thread).GetTaggedValue(); 95 } else if (transitions.IsWeak()) { 96 auto cachedHClass = JSHClass::Cast(transitions.GetTaggedWeakRef()); 97 if (cachedHClass->HasProps()) { 98 uint32_t last = cachedHClass->LastPropIndex(); 99 LayoutInfo* layoutInfo = LayoutInfo::Cast(cachedHClass->GetLayout().GetTaggedObject()); 100 auto metaData = JSHandle<JSTaggedValue>(thread, 101 JSTaggedValue(layoutInfo->GetAttr(last).GetPropertyMetaData())); 102 auto lastKey = JSHandle<JSTaggedValue>(thread, layoutInfo->GetKey(last)); 103 auto lastHClass = JSHandle<JSTaggedValue>(thread, cachedHClass); 104 dict.Update(TransitionsDictionary::Create(thread)); 105 transitions = TransitionsDictionary::PutIfAbsent(thread, dict, lastKey, lastHClass, 106 metaData).GetTaggedValue(); 107 } 108 } 109 dict.Update(transitions); 110 transitions = 111 TransitionsDictionary::PutIfAbsent(thread, dict, key, JSHandle<JSTaggedValue>(child), proto).GetTaggedValue(); 112 parent->SetTransitions(thread, transitions); 113} 114 115inline JSHClass *JSHClass::FindTransitions(const JSTaggedValue &key, const JSTaggedValue &metaData, 116 const Representation &rep) 117{ 118 DISALLOW_GARBAGE_COLLECTION; 119 JSTaggedValue transitions = GetTransitions(); 120 if (transitions.IsUndefined()) { 121 return nullptr; 122 } 123 if (transitions.IsWeak()) { 124 auto cachedHClass = JSHClass::Cast(transitions.GetTaggedWeakRef()); 125 if (cachedHClass->PropsIsEmpty()) { 126 return nullptr; 127 } 128 int last = static_cast<int>(cachedHClass->LastPropIndex()); 129 LayoutInfo *layoutInfo = LayoutInfo::Cast(cachedHClass->GetLayout().GetTaggedObject()); 130 auto lastMetaData = layoutInfo->GetAttr(last).GetPropertyMetaData(); 131 auto lastKey = layoutInfo->GetKey(last); 132 if (lastMetaData == metaData.GetInt() && key == lastKey) { 133 return CheckHClassForRep(cachedHClass, rep); 134 } 135 return nullptr; 136 } 137 138 ASSERT(transitions.IsTaggedArray()); 139 TransitionsDictionary *dict = TransitionsDictionary::Cast(transitions.GetTaggedObject()); 140 auto entry = dict->FindEntry(key, metaData); 141 if (entry == -1) { 142 return nullptr; 143 } 144 145 JSTaggedValue ret = dict->GetValue(entry); 146 if (ret.IsUndefined()) { 147 return nullptr; 148 } 149 150 return CheckHClassForRep(JSHClass::Cast(ret.GetTaggedWeakRef()), rep); 151} 152 153inline JSHClass *JSHClass::FindProtoTransitions(const JSTaggedValue &key, const JSTaggedValue &proto) 154{ 155 DISALLOW_GARBAGE_COLLECTION; 156 JSTaggedValue transitions = GetTransitions(); 157 if (transitions.IsWeak() || !transitions.IsTaggedArray()) { 158 ASSERT(transitions.IsUndefined() || transitions.IsWeak()); 159 return nullptr; 160 } 161 ASSERT(transitions.IsTaggedArray()); 162 TransitionsDictionary *dict = TransitionsDictionary::Cast(transitions.GetTaggedObject()); 163 auto entry = dict->FindEntry(key, proto); 164 if (entry == -1) { 165 return nullptr; 166 } 167 168 JSTaggedValue ret = dict->GetValue(entry); 169 if (ret.IsUndefined()) { 170 return nullptr; 171 } 172 173 return JSHClass::Cast(ret.GetTaggedWeakRef()); 174} 175 176inline void JSHClass::RestoreElementsKindToGeneric(JSHClass *newJsHClass) 177{ 178 newJsHClass->SetElementsKind(ElementsKind::GENERIC); 179} 180 181inline JSHClass *JSHClass::CheckHClassForRep(JSHClass *hclass, const Representation &rep) 182{ 183 if (!hclass->IsTS()) { 184 return hclass; 185 } 186 if (rep == Representation::NONE) { 187 return hclass; 188 } 189 190 int last = static_cast<int>(hclass->LastPropIndex()); 191 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); 192 auto lastRep = layoutInfo->GetAttr(last).GetRepresentation(); 193 auto result = hclass; 194 if (lastRep == Representation::INT) { 195 if (rep != Representation::INT) { 196 result = nullptr; 197 } 198 } else if (lastRep == Representation::DOUBLE) { 199 if (rep != Representation::INT && rep != Representation::DOUBLE) { 200 result = nullptr; 201 } 202 } 203 return result; 204} 205 206inline void JSHClass::UpdatePropertyMetaData(const JSThread *thread, [[maybe_unused]] const JSTaggedValue &key, 207 const PropertyAttributes &metaData) 208{ 209 DISALLOW_GARBAGE_COLLECTION; 210 ASSERT(!GetLayout().IsNull()); 211 LayoutInfo *layoutInfo = LayoutInfo::Cast(GetLayout().GetTaggedObject()); 212 ASSERT(layoutInfo->GetLength() != 0); 213 uint32_t entry = metaData.GetOffset(); 214 215 layoutInfo->SetNormalAttr(thread, entry, metaData); 216} 217 218inline bool JSHClass::HasReferenceField() 219{ 220 auto type = GetObjectType(); 221 switch (type) { 222 case JSType::LINE_STRING: 223 case JSType::CONSTANT_STRING: 224 case JSType::JS_NATIVE_POINTER: 225 return false; 226 default: 227 return true; 228 } 229} 230 231inline size_t JSHClass::SizeFromJSHClass(TaggedObject *header) 232{ 233 // CAUTION! Never use T::Cast(header) in this function 234 // it would cause issue during GC because hclass may forward to a new addres 235 // and the casting method would still use the old address. 236 auto type = GetObjectType(); 237 size_t size = 0; 238 switch (type) { 239 case JSType::TAGGED_ARRAY: 240 case JSType::TAGGED_DICTIONARY: 241 case JSType::LEXICAL_ENV: 242 case JSType::SENDABLE_ENV: 243 case JSType::CONSTANT_POOL: 244 case JSType::AOT_LITERAL_INFO: 245 case JSType::VTABLE: 246 case JSType::COW_TAGGED_ARRAY: 247 case JSType::MUTANT_TAGGED_ARRAY: 248 case JSType::COW_MUTANT_TAGGED_ARRAY: 249 case JSType::PROFILE_TYPE_INFO: 250 size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), 251 reinterpret_cast<TaggedArray *>(header)->GetLength()); 252 break; 253 case JSType::BYTE_ARRAY: 254 size = ByteArray::ComputeSize(reinterpret_cast<ByteArray *>(header)->GetByteLength(), 255 reinterpret_cast<ByteArray *>(header)->GetArrayLength()); 256 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)); 257 break; 258 case JSType::LINE_STRING: 259 size = LineEcmaString::ObjectSize(reinterpret_cast<EcmaString* >(header)); 260 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)); 261 break; 262 case JSType::CONSTANT_STRING: 263 size = ConstantString::SIZE; 264 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)); 265 break; 266 case JSType::TREE_STRING: 267 size = TreeEcmaString::SIZE; 268 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)); 269 break; 270 case JSType::SLICED_STRING: 271 size = SlicedString::SIZE; 272 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)); 273 break; 274 case JSType::MACHINE_CODE_OBJECT: 275 size = reinterpret_cast<MachineCode *>(header)->GetMachineCodeObjectSize(); 276 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)); 277 break; 278 case JSType::BIGINT: 279 size = BigInt::ComputeSize(reinterpret_cast<BigInt *>(header)->GetLength()); 280 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)); 281 break; 282 default: 283 ASSERT(GetObjectSize() != 0); 284 size = GetObjectSize(); 285 break; 286 } 287 ASSERT(AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)) == size); 288 return size; 289} 290 291inline void JSHClass::Copy(const JSThread *thread, const JSHClass *jshclass) 292{ 293 DISALLOW_GARBAGE_COLLECTION; 294 295 // copy jshclass 296 SetPrototype(thread, jshclass->GetPrototype()); 297 SetBitField(jshclass->GetBitField()); 298 SetIsAllTaggedProp(jshclass->IsAllTaggedProp()); 299 SetNumberOfProps(jshclass->NumberOfProps()); 300} 301 302inline JSHClass *JSHClass::FindRootHClass(JSHClass *hclass) 303{ 304 auto root = hclass; 305 while (!ProfileType(root->GetProfileType()).IsRootType()) { 306 auto parent = root->GetParent(); 307 if (!parent.IsJSHClass()) { 308 break; 309 } 310 root = JSHClass::Cast(parent.GetTaggedObject()); 311 } 312 return root; 313} 314 315inline JSTaggedValue JSHClass::FindProtoHClass(JSHClass *hclass) 316{ 317 auto proto = hclass->GetProto(); 318 if (proto.IsJSObject()) { 319 auto prototypeObj = JSObject::Cast(proto); 320 return JSTaggedValue(prototypeObj->GetClass()); 321 } 322 return JSTaggedValue::Undefined(); 323} 324 325inline JSTaggedValue JSHClass::FindProtoRootHClass(JSHClass *hclass) 326{ 327 auto proto = hclass->GetProto(); 328 if (proto.IsJSObject()) { 329 auto prototypeObj = JSObject::Cast(proto); 330 auto prototypeHClass = prototypeObj->GetClass(); 331 return JSTaggedValue(JSHClass::FindRootHClass(prototypeHClass)); 332 } 333 return JSTaggedValue::Undefined(); 334} 335 336inline void JSHClass::UpdateRootHClass(const JSThread *thread, const JSHandle<JSHClass> &parent, 337 const JSHandle<JSHClass> &child) 338{ 339 if (thread->GetEcmaVM()->IsEnablePGOProfiler()) { 340 child->SetParent(thread, parent); 341 } 342} 343 344inline int JSHClass::FindPropertyEntry(const JSThread *thread, JSHClass *hclass, JSTaggedValue key) 345{ 346 DISALLOW_GARBAGE_COLLECTION; 347 LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); 348 uint32_t propsNumber = hclass->NumberOfProps(); 349 int entry = layout->FindElementWithCache(thread, hclass, key, propsNumber); 350 return entry; 351} 352 353template<bool checkDuplicateKeys /* = false*/> 354void JSHClass::AddPropertyToNewHClass(const JSThread *thread, JSHandle<JSHClass> &jshclass, 355 JSHandle<JSHClass> &newJsHClass, 356 const JSHandle<JSTaggedValue> &key, 357 const PropertyAttributes &attr) 358{ 359 ASSERT(!jshclass->IsDictionaryMode()); 360 ASSERT(!newJsHClass->IsDictionaryMode()); 361 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 362 // Add Property and metaData 363 uint32_t offset = attr.GetOffset(); 364 newJsHClass->IncNumberOfProps(); 365 366 { 367 JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, newJsHClass->GetLayout()); 368 369 if (layoutInfoHandle->NumberOfElements() != static_cast<int>(offset)) { 370 layoutInfoHandle.Update(factory->CopyAndReSort(layoutInfoHandle, offset, offset + 1)); 371 } else if (layoutInfoHandle->GetPropertiesCapacity() <= static_cast<int>(offset)) { // need to Grow 372 layoutInfoHandle.Update( 373 factory->ExtendLayoutInfo(layoutInfoHandle, offset)); 374 } 375 newJsHClass->SetLayout(thread, layoutInfoHandle); 376 layoutInfoHandle->AddKey<checkDuplicateKeys>(thread, offset, key.GetTaggedValue(), attr); 377 } 378 379 // Add newClass to old hclass's transitions. 380 AddTransitions(thread, jshclass, newJsHClass, key, attr); 381} 382 383template<bool checkDuplicateKeys /* = false*/> 384JSHandle<JSHClass> JSHClass::SetPropertyOfObjHClass(const JSThread *thread, JSHandle<JSHClass> &jshclass, 385 const JSHandle<JSTaggedValue> &key, 386 const PropertyAttributes &attr, const Representation &rep) 387{ 388 JSHClass *newClass = jshclass->FindTransitions( 389 key.GetTaggedValue(), JSTaggedValue(attr.GetPropertyMetaData()), rep); 390 if (newClass != nullptr) { 391 newClass->SetPrototype(thread, jshclass->GetPrototype()); 392 return JSHandle<JSHClass>(thread, newClass); 393 } 394 395 JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass); 396 AddPropertyToNewHClass<checkDuplicateKeys>(thread, jshclass, newJsHClass, key, attr); 397 return newJsHClass; 398} 399} // namespace panda::ecmascript 400 401#endif // ECMASCRIPT_JS_HCLASS_INL_H 402