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_JSOBJECT_INL_H 17#define ECMASCRIPT_JSOBJECT_INL_H 18 19#include "ecmascript/js_object.h" 20 21#include "ecmascript/element_accessor-inl.h" 22#include "ecmascript/js_array.h" 23#include "ecmascript/js_hclass-inl.h" 24#include "ecmascript/js_tagged_value-inl.h" 25#include "ecmascript/js_typed_array.h" 26#include "ecmascript/object_operator.h" 27#include "ecmascript/tagged_array-inl.h" 28#include "ecmascript/tagged_queue.h" 29#include "ecmascript/tagged_dictionary.h" 30 31namespace panda::ecmascript { 32inline void ECMAObject::SetCallable(bool flag) 33{ 34 GetClass()->SetCallable(flag); 35} 36 37inline bool ECMAObject::IsCallable() const 38{ 39 return GetClass()->IsCallable(); 40} 41 42// JSObject 43inline bool JSObject::IsExtensible() const 44{ 45 return GetJSHClass()->IsExtensible(); 46} 47 48inline void JSObject::FillElementsWithHoles(const JSThread *thread, uint32_t start, uint32_t end) 49{ 50 if (start >= end) { 51 return; 52 } 53 54 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole()); 55 JSHandle<JSObject> thisObj(thread, this); 56 for (uint32_t i = start; i < end; i++) { 57 ElementAccessor::Set(thread, thisObj, i, holeHandle, false); 58 } 59} 60 61inline uint32_t JSObject::GetNonInlinedFastPropsCapacity() const 62{ 63 uint32_t inlineProps = GetJSHClass()->GetInlinedProperties(); 64 return PropertyAttributes::MAX_FAST_PROPS_CAPACITY - inlineProps; 65} 66 67inline bool JSObject::IsJSGlobalObject() const 68{ 69 return GetJSHClass()->IsJSGlobalObject(); 70} 71 72inline bool JSObject::IsConstructor() const 73{ 74 return GetJSHClass()->IsConstructor(); 75} 76 77inline bool JSObject::IsECMAObject() const 78{ 79 return GetJSHClass()->IsECMAObject(); 80} 81 82inline bool JSObject::IsJSError() const 83{ 84 return GetJSHClass()->IsJSError(); 85} 86 87inline bool JSObject::IsArguments() const 88{ 89 return GetJSHClass()->IsArguments(); 90} 91 92inline bool JSObject::IsDate() const 93{ 94 return GetJSHClass()->IsDate(); 95} 96 97inline bool JSObject::IsJSArray() const 98{ 99 return GetJSHClass()->IsJSArray(); 100} 101 102inline bool JSObject::IsJSSArray() const 103{ 104 return GetJSHClass()->IsJSSharedArray(); 105} 106 107inline bool JSObject::IsJSShared() const 108{ 109 return GetJSHClass()->IsJSShared(); 110} 111 112inline bool JSObject::IsJSMap() const 113{ 114 return GetJSHClass()->IsJSMap(); 115} 116 117inline bool JSObject::IsJSSet() const 118{ 119 return GetJSHClass()->IsJSSet(); 120} 121 122inline bool JSObject::IsJSRegExp() const 123{ 124 return GetJSHClass()->IsJSRegExp(); 125} 126 127inline bool JSObject::IsJSFunction() const 128{ 129 return GetJSHClass()->IsJSFunction(); 130} 131 132inline bool JSObject::IsBoundFunction() const 133{ 134 return GetJSHClass()->IsJsBoundFunction(); 135} 136 137inline bool JSObject::IsJSIntlBoundFunction() const 138{ 139 return GetJSHClass()->IsJSIntlBoundFunction(); 140} 141 142inline bool JSObject::IsProxyRevocFunction() const 143{ 144 return GetJSHClass()->IsJSProxyRevocFunction(); 145} 146 147inline bool JSObject::IsAccessorData() const 148{ 149 return GetJSHClass()->IsAccessorData(); 150} 151 152inline bool JSObject::IsJSGlobalEnv() const 153{ 154 return GetJSHClass()->IsJsGlobalEnv(); 155} 156 157inline bool JSObject::IsJSProxy() const 158{ 159 return GetJSHClass()->IsJSProxy(); 160} 161 162inline bool JSObject::IsGeneratorObject() const 163{ 164 return GetJSHClass()->IsGeneratorObject(); 165} 166 167inline bool JSObject::IsAsyncGeneratorObject() const 168{ 169 return GetJSHClass()->IsAsyncGeneratorObject(); 170} 171 172inline bool JSObject::IsForinIterator() const 173{ 174 return GetJSHClass()->IsForinIterator(); 175} 176 177inline bool JSObject::IsJSSetIterator() const 178{ 179 return GetJSHClass()->IsJSSetIterator(); 180} 181 182inline bool JSObject::IsJSRegExpIterator() const 183{ 184 return GetJSHClass()->IsJSRegExpIterator(); 185} 186 187inline bool JSObject::IsJSMapIterator() const 188{ 189 return GetJSHClass()->IsJSMapIterator(); 190} 191 192inline bool JSObject::IsJSArrayIterator() const 193{ 194 return GetJSHClass()->IsJSArrayIterator(); 195} 196 197inline bool JSObject::IsJSAPIArrayListIterator() const 198{ 199 return GetJSHClass()->IsJSAPIArrayListIterator(); 200} 201 202inline bool JSObject::IsJSAPIStackIterator() const 203{ 204 return GetJSHClass()->IsJSAPIStackIterator(); 205} 206 207inline bool JSObject::IsJSAPIVectorIterator() const 208{ 209 return GetJSHClass()->IsJSAPIVectorIterator(); 210} 211 212inline bool JSObject::IsJSAPIBitVectorIterator() const 213{ 214 return GetJSHClass()->IsJSAPIBitVectorIterator(); 215} 216 217inline bool JSObject::IsJSAPILinkedListIterator() const 218{ 219 return GetJSHClass()->IsJSAPILinkedListIterator(); 220} 221 222inline bool JSObject::IsJSAPIListIterator() const 223{ 224 return GetJSHClass()->IsJSAPIListIterator(); 225} 226 227inline bool JSObject::IsJSPrimitiveRef() const 228{ 229 return GetJSHClass()->IsJsPrimitiveRef(); 230} 231 232inline bool JSObject::IsElementDict() const 233{ 234 return TaggedArray::Cast(GetElements().GetTaggedObject())->IsDictionaryMode(); 235} 236 237inline bool JSObject::IsPropertiesDict() const 238{ 239 return TaggedArray::Cast(GetProperties().GetTaggedObject())->IsDictionaryMode(); 240} 241 242inline bool JSObject::IsTypedArray() const 243{ 244 return GetJSHClass()->IsTypedArray(); 245} 246 247std::pair<bool, JSTaggedValue> JSObject::ConvertValueWithRep(PropertyAttributes attr, JSTaggedValue value) 248{ 249 if (attr.IsDoubleRep()) { 250 if (value.IsInt()) { 251 double doubleValue = value.GetInt(); 252 return std::pair(true, JSTaggedValue(bit_cast<JSTaggedType>(doubleValue))); 253 } else if (value.IsDouble()) { 254 return std::pair(true, JSTaggedValue(bit_cast<JSTaggedType>(value.GetDouble()))); 255 } else { 256 return std::pair(false, value); 257 } 258 } else if (attr.IsIntRep()) { 259 if (value.IsInt()) { 260 int intValue = value.GetInt(); 261 return std::pair(true, JSTaggedValue(static_cast<JSTaggedType>(intValue))); 262 } else { 263 return std::pair(false, value); 264 } 265 } 266 return std::pair(true, value); 267} 268 269void JSObject::SetPropertyInlinedPropsWithRep(const JSThread *thread, uint32_t index, JSTaggedValue value) 270{ 271 auto layout = LayoutInfo::Cast(GetJSHClass()->GetLayout().GetTaggedObject()); 272 auto attr = layout->GetAttr(index); 273 if (attr.IsTaggedRep()) { 274 SetPropertyInlinedProps<true>(thread, index, value); 275 } else { 276 SetPropertyInlinedProps<false>(thread, index, value); 277 } 278} 279 280template <bool needBarrier> 281void JSObject::SetPropertyInlinedProps(const JSThread *thread, uint32_t index, JSTaggedValue value) 282{ 283 SetPropertyInlinedProps<needBarrier>(thread, GetJSHClass(), index, value); 284} 285 286JSTaggedValue JSObject::GetPropertyInlinedPropsWithRep(uint32_t index, PropertyAttributes attr) const 287{ 288 return GetPropertyInlinedPropsWithRep(GetJSHClass(), index, attr); 289} 290 291JSTaggedValue JSObject::GetPropertyInlinedPropsWithRep(const JSHClass *hclass, uint32_t index, 292 PropertyAttributes attr) const 293{ 294 auto value = GetPropertyInlinedProps(hclass, index); 295 if (attr.IsDoubleRep()) { 296 value = JSTaggedValue(bit_cast<double>(value.GetRawData())); 297 } else if (attr.IsIntRep()) { 298 value = JSTaggedValue(static_cast<int32_t>(value.GetRawData())); 299 } 300 return value; 301} 302 303JSTaggedValue JSObject::GetPropertyInlinedProps(uint32_t index) const 304{ 305 return GetPropertyInlinedProps(GetJSHClass(), index); 306} 307 308template <bool needBarrier> 309void JSObject::SetPropertyInlinedProps(const JSThread *thread, const JSHClass *hclass, uint32_t index, 310 JSTaggedValue value) 311{ 312 uint32_t offset = hclass->GetInlinedPropertiesOffset(index); 313 ASSERT(hclass->GetObjectSize() > offset); 314 if constexpr (needBarrier) { 315 SET_VALUE_WITH_BARRIER(thread, this, offset, value); 316 } else { 317 SET_VALUE_PRIMITIVE(this, offset, value); 318 } 319} 320 321JSTaggedValue JSObject::GetPropertyInlinedProps(const JSHClass *hclass, uint32_t index) const 322{ 323 uint32_t offset = hclass->GetInlinedPropertiesOffset(index); 324 return JSTaggedValue(GET_VALUE(this, offset)); 325} 326 327JSTaggedValue JSObject::GetProperty(const JSHClass *hclass, PropertyAttributes attr) const 328{ 329 if (attr.IsInlinedProps()) { 330 return GetPropertyInlinedPropsWithRep(hclass, attr.GetOffset(), attr); 331 } 332 TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); 333 return array->Get(attr.GetOffset() - hclass->GetInlinedProperties()); 334} 335 336template <bool needBarrier> 337void JSObject::SetProperty(const JSThread *thread, const JSHClass *hclass, PropertyAttributes attr, JSTaggedValue value) 338{ 339 if (attr.IsInlinedProps()) { 340 SetPropertyInlinedProps<needBarrier>(thread, hclass, attr.GetOffset(), value); 341 } else { 342 TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); 343 array->Set<needBarrier>(thread, attr.GetOffset() - hclass->GetInlinedProperties(), value); 344 } 345} 346 347inline bool JSObject::ShouldTransToDict(uint32_t capacity, uint32_t index) 348{ 349 if (index < capacity) { 350 return false; 351 } 352 353 if (index - capacity > MAX_GAP) { 354 return true; 355 } 356 357 if (index >= static_cast<uint32_t>(INT32_MAX)) { 358 return true; 359 } 360 361 if (capacity >= MIN_GAP) { 362 return index > capacity * FAST_ELEMENTS_FACTOR; 363 } 364 365 return false; 366} 367 368inline bool JSObject::ShouldTransToFastElements(JSThread *thread, TaggedArray *elements, 369 uint32_t capacity, uint32_t index) 370{ 371 JSHandle<NumberDictionary> dictionary(thread, elements); 372 if (index >= static_cast<uint32_t>(INT32_MAX)) { 373 return false; 374 } 375 // Turn fast if only saves 50% space. 376 if (static_cast<uint32_t>(dictionary->GetLength()) * SHOULD_TRANS_TO_FAST_ELEMENTS_FACTOR >= capacity || 377 static_cast<uint64_t>(dictionary->NextEnumerationIndex(thread)) > 378 PropertyAttributes::DictionaryOrderField::MaxValue()) { 379 return true; 380 } 381 return false; 382} 383 384inline uint32_t JSObject::ComputeElementCapacity(uint32_t oldCapacity, bool isNew) 385{ 386 uint32_t newCapacity = isNew ? oldCapacity : (oldCapacity + (oldCapacity >> 1U)); 387 return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH; 388} 389 390inline uint32_t JSObject::ComputeElementCapacityHighGrowth(uint32_t oldCapacity) 391{ 392 uint32_t newCapacity = oldCapacity * 2; 393 return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH; 394} 395 396inline uint32_t JSObject::ComputeElementCapacityWithHint(uint32_t oldCapacity, uint32_t hint) 397{ 398 uint32_t newCapacity = 0; 399 if ((oldCapacity >= hint) || (hint < MIN_ELEMENTS_HINT_LENGTH) || (hint >= MAX_ELEMENTS_HINT_LENGTH)) { 400 return newCapacity; 401 } 402 if ((hint / oldCapacity) <= ELEMENTS_HINT_FACTOR) { 403 newCapacity = hint; 404 } 405 return newCapacity; 406} 407 408inline uint32_t JSObject::ComputeNonInlinedFastPropsCapacity(JSThread *thread, uint32_t oldCapacity, 409 uint32_t maxNonInlinedFastPropsCapacity) 410{ 411 uint32_t newCapacity = oldCapacity + thread->GetPropertiesGrowStep(); 412 return newCapacity > maxNonInlinedFastPropsCapacity ? maxNonInlinedFastPropsCapacity : newCapacity; 413} 414 415// static 416template<ElementTypes types> 417JSHandle<JSTaggedValue> JSObject::CreateListFromArrayLike(JSThread *thread, const JSHandle<JSTaggedValue> &obj) 418{ 419 // 3. If Type(obj) is not Object, throw a TypeError exception. 420 if (!obj->IsECMAObject()) { 421 THROW_TYPE_ERROR_AND_RETURN(thread, "CreateListFromArrayLike must accept object", 422 JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception())); 423 } 424 if (obj->IsTypedArray()) { 425 uint32_t len = JSHandle<JSTypedArray>::Cast(obj)->GetArrayLength(); 426 JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len); 427 JSTypedArray::FastCopyElementToArray(thread, obj, array); 428 // c. ReturnIfAbrupt(next). 429 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 430 return JSHandle<JSTaggedValue>(array); 431 } 432 // 4. Let len be ToLength(Get(obj, "length")). 433 JSHandle<JSTaggedValue> lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); 434 435 JSHandle<JSTaggedValue> value = GetProperty(thread, obj, lengthKeyHandle).GetValue(); 436 JSTaggedNumber number = JSTaggedValue::ToLength(thread, value); 437 // 5. ReturnIfAbrupt(len). 438 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 439 if (number.GetNumber() > MAX_ELEMENT_INDEX) { 440 THROW_TYPE_ERROR_AND_RETURN(thread, "len is bigger than 2^32 - 1", 441 JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception())); 442 } 443 444 uint32_t len = number.ToUint32(); 445 // 6. Let list be an empty List. 446 JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len); 447 // 8. Repeat while index < len 448 for (uint32_t i = 0; i < len; i++) { 449 JSTaggedValue next = JSTaggedValue::GetProperty(thread, obj, i).GetValue().GetTaggedValue(); 450 // c. ReturnIfAbrupt(next). 451 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 452 453 if constexpr (types == ElementTypes::STRING_AND_SYMBOL) { 454 if (!next.IsString() && !next.IsSymbol()) { 455 THROW_TYPE_ERROR_AND_RETURN(thread, "CreateListFromArrayLike: not an element of elementTypes", 456 JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception())); 457 } 458 } 459 460 array->Set(thread, i, next); 461 } 462 return JSHandle<JSTaggedValue>(array); 463} 464 465inline JSTaggedValue JSObject::ShouldGetValueFromBox(ObjectOperator *op) 466{ 467 JSTaggedValue result = op->GetValue(); 468 if (result.IsPropertyBox()) { 469 result = PropertyBox::Cast(result.GetTaggedObject())->GetValue(); 470 } 471 return result; 472} 473 474inline bool JSObject::CheckHClassHit(const JSHandle<JSObject> &obj, const JSHandle<JSHClass> &cls) 475{ 476 return obj->GetJSHClass() == *cls; 477} 478 479inline uint32_t JSObject::SetValuesOrEntries(JSThread *thread, const JSHandle<TaggedArray> &prop, uint32_t index, 480 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value, 481 PropertyKind kind) 482{ 483 if (kind == PropertyKind::VALUE) { 484 prop->Set(thread, index++, value); 485 return index; 486 } 487 JSHandle<TaggedArray> keyValue = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2); // 2: key-value pair 488 keyValue->Set(thread, 0, key); 489 keyValue->Set(thread, 1, value); 490 JSHandle<JSArray> entry = JSArray::CreateArrayFromList(thread, keyValue); 491 prop->Set(thread, index++, entry.GetTaggedValue()); 492 return index; 493} 494 495inline void JSObject::SetEnumCacheKind(JSThread *thread, TaggedArray *array, EnumCacheKind kind) 496{ 497 array->Set(thread, EnumCache::ENUM_CACHE_KIND_OFFSET, JSTaggedValue(static_cast<uint8_t>(kind))); 498} 499 500inline EnumCacheKind JSObject::GetEnumCacheKind(JSThread *thread, TaggedArray *array) 501{ 502 return static_cast<EnumCacheKind>(array->Get(thread, EnumCache::ENUM_CACHE_KIND_OFFSET).GetInt()); 503} 504 505inline EnumCacheKind JSObject::GetEnumCacheKind(JSThread *thread, JSTaggedValue enumCache) 506{ 507 if (enumCache.IsUndefinedOrNull()) { 508 return EnumCacheKind::NONE; 509 } 510 JSTaggedValue emptyArray = thread->GlobalConstants()->GetEmptyArray(); 511 if (enumCache == emptyArray) { 512 return EnumCacheKind::SIMPLE; 513 } 514 TaggedArray *array = TaggedArray::Cast(enumCache.GetTaggedObject()); 515 return JSObject::GetEnumCacheKind(thread, array); 516} 517 518inline JSTaggedValue JSObject::GetPrototype(JSTaggedValue obj) 519{ 520 JSHClass *hclass = obj.GetTaggedObject()->GetClass(); 521 return hclass->GetPrototype(); 522} 523 524inline bool JSObject::IsDepulicateKeys(JSThread *thread, JSHandle<TaggedArray> keys, int32_t lastLength, 525 JSHandle<TaggedQueue> shadowQueue, JSHandle<JSTaggedValue> key) 526{ 527 if (lastLength < 0) { 528 return false; 529 } 530 JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined()); 531 for (int32_t i = EnumCache::ENUM_CACHE_HEADER_SIZE; i < lastLength; i++) { 532 value.Update(keys->Get(i)); 533 bool has = JSTaggedValue::Equal(thread, value, key); 534 if (has) { 535 return true; 536 } 537 } 538 539 uint32_t shadowSize = shadowQueue->Size(); 540 for (uint32_t i = 0; i < shadowSize; i++) { 541 value.Update(shadowQueue->Get(i)); 542 bool has = JSTaggedValue::Equal(thread, value, key); 543 if (has) { 544 return true; 545 } 546 } 547 return false; 548} 549 550inline void JSObject::ClearHasDeleteProperty(JSHandle<JSTaggedValue> object) 551{ 552 object->GetTaggedObject()->GetClass()->SetHasDeleteProperty(false); 553} 554 555inline std::pair<JSHandle<TaggedArray>, JSHandle<TaggedArray>> JSObject::GetOwnEnumerableNamesInFastMode( 556 JSThread *thread, const JSHandle<JSObject> &obj, uint32_t *copyLengthOfKeys, uint32_t *copyLengthOfElements) 557{ 558 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 559 std::pair<uint32_t, uint32_t> numOfKeys = obj->GetNumberOfEnumKeys(); 560 uint32_t numOfEnumKeys = numOfKeys.first; 561 uint32_t numOfElements = obj->GetNumberOfElements(); 562 JSHandle<TaggedArray> elementArray = numOfElements > 0 ? JSObject::GetEnumElementKeys( 563 thread, obj, 0, numOfElements, copyLengthOfElements) : factory->EmptyArray(); 564 JSHandle<TaggedArray> keyArray = numOfEnumKeys > 0 ? JSObject::GetAllEnumKeys( 565 thread, obj, numOfEnumKeys, copyLengthOfKeys) : factory->EmptyArray(); 566 return std::make_pair(keyArray, elementArray); 567} 568 569inline bool JSObject::HasMutantTaggedArrayElements(const JSHandle<JSObject> &obj) 570{ 571 return obj->GetElements().IsMutantTaggedArray(); 572} 573} // namespace panda::ecmascript 574#endif // ECMASCRIPT_JSOBJECT_INL_H 575