1/* 2 * Copyright (c) 2023-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_OBJECT_FAST_OPERATOR_INL_H 17#define ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H 18 19#include "ecmascript/js_handle.h" 20#include "ecmascript/js_tagged_value.h" 21#include "ecmascript/jspandafile/class_info_extractor.h" 22#include "ecmascript/mem/assert_scope.h" 23#include "ecmascript/object_fast_operator.h" 24 25#include "ecmascript/base/array_helper.h" 26#include "ecmascript/ecma_string_table.h" 27#include "ecmascript/element_accessor-inl.h" 28#include "ecmascript/global_env.h" 29#include "ecmascript/js_api/js_api_arraylist.h" 30#include "ecmascript/js_api/js_api_deque.h" 31#include "ecmascript/js_api/js_api_linked_list.h" 32#include "ecmascript/js_api/js_api_list.h" 33#include "ecmascript/js_api/js_api_plain_array.h" 34#include "ecmascript/js_api/js_api_queue.h" 35#include "ecmascript/js_api/js_api_stack.h" 36#include "ecmascript/js_api/js_api_vector.h" 37#include "ecmascript/js_api/js_api_bitvector.h" 38#include "ecmascript/js_date.h" 39#include "ecmascript/js_function.h" 40#include "ecmascript/js_hclass-inl.h" 41#include "ecmascript/js_object-inl.h" 42#include "ecmascript/js_tagged_value-inl.h" 43#include "ecmascript/js_typed_array.h" 44#include "ecmascript/message_string.h" 45#include "ecmascript/property_attributes.h" 46#include "ecmascript/runtime_call_id.h" 47#include "ecmascript/shared_objects/concurrent_api_scope.h" 48#include "ecmascript/shared_objects/js_shared_array.h" 49#include "ecmascript/tagged_array.h" 50#include "ecmascript/tagged_dictionary.h" 51 52namespace panda::ecmascript { 53// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 54#define CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder) \ 55 if (UNLIKELY((receiver) != (holder))) { \ 56 return JSTaggedValue::Hole(); \ 57 } 58 59std::pair<JSTaggedValue, bool> ObjectFastOperator::HasOwnProperty(JSThread *thread, 60 JSTaggedValue receiver, JSTaggedValue key) 61{ 62 [[maybe_unused]] DisallowGarbageCollection noGc; 63 if (!receiver.IsHeapObject() || !(receiver.IsRegularObject())) { 64 return std::make_pair(JSTaggedValue::Hole(), false); 65 } 66 if (!key.IsString()) { 67 return std::make_pair(JSTaggedValue::Hole(), false); 68 } 69 70 uint32_t index = 0; 71 if (JSTaggedValue::ToElementIndex(key, &index)) { 72 ASSERT(index < JSObject::MAX_ELEMENT_INDEX); 73 JSHandle<JSObject> receiverObj(thread, receiver); 74 if (ElementAccessor::GetElementsLength(receiverObj) == 0) { 75 return std::make_pair(JSTaggedValue::Hole(), true); // Empty Array 76 } 77 78 if (!ElementAccessor::IsDictionaryMode(receiverObj)) { 79 if (ElementAccessor::GetElementsLength(receiverObj) <= index) { 80 return std::make_pair(JSTaggedValue::Hole(), true); 81 } 82 JSTaggedValue value = ElementAccessor::Get(receiverObj, index); 83 return std::make_pair(value, true); 84 } else { 85 NumberDictionary *dictionary = 86 NumberDictionary::Cast(JSObject::Cast(receiver)->GetElements().GetTaggedObject()); 87 int entry = dictionary->FindEntry(JSTaggedValue(static_cast<int>(index))); 88 if (entry == -1) { 89 return std::make_pair(JSTaggedValue::Hole(), true); 90 } 91 return std::make_pair(JSTaggedValue::Undefined(), true); 92 } 93 } 94 95 if (!EcmaStringAccessor(key).IsInternString()) { 96 JSHandle<EcmaString> keyHandle(thread, key); 97 EcmaString *str = thread->GetEcmaVM()->GetEcmaStringTable()->TryGetInternString(thread, keyHandle); 98 if (str == nullptr) { 99 return std::make_pair(JSTaggedValue::Hole(), true); 100 } 101 key = JSTaggedValue(str); 102 } 103 auto *hclass = receiver.GetTaggedObject()->GetClass(); 104 if (LIKELY(!hclass->IsDictionaryMode())) { 105 ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode()); 106 int entry = JSHClass::FindPropertyEntry(thread, hclass, key); 107 if (entry != -1) { 108 return std::make_pair(JSTaggedValue::Undefined(), true); 109 } 110 } else { 111 TaggedArray *array = TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject()); 112 ASSERT(array->IsDictionaryMode()); 113 NameDictionary *dict = NameDictionary::Cast(array); 114 int entry = dict->FindEntry(key); 115 if (entry != -1) { 116 return std::make_pair(JSTaggedValue::Undefined(), true); 117 } 118 } 119 return std::make_pair(JSTaggedValue::Hole(), true); 120} 121 122template <ObjectFastOperator::Status status> 123JSTaggedValue ObjectFastOperator::TryFastHasProperty(JSThread *thread, JSTaggedValue receiver, 124 JSMutableHandle<JSTaggedValue> keyHandle) 125{ 126 JSTaggedValue key = keyHandle.GetTaggedValue(); 127 if (UNLIKELY(!receiver.IsHeapObject() || !receiver.IsRegularObject())) { 128 return JSTaggedValue::Hole(); 129 } 130 if (UNLIKELY(!key.IsNumber() && !key.IsString())) { 131 return JSTaggedValue::Hole(); 132 } 133 134 // Elements 135 auto index = TryToElementsIndex(key); 136 if (index >= 0) { 137 ASSERT(index < JSObject::MAX_ELEMENT_INDEX); 138 JSHandle<JSObject> receiverObj(thread, receiver); 139 if (!ElementAccessor::IsDictionaryMode(receiverObj)) { 140 if (index < ElementAccessor::GetElementsLength(receiverObj)) { 141 JSTaggedValue value = ElementAccessor::Get(receiverObj, index); 142 return value.IsHole() ? JSTaggedValue::Hole() : JSTaggedValue::True(); 143 } 144 } 145 return JSTaggedValue::Hole(); 146 } 147 148 // layout cache 149 auto *hclass = receiver.GetTaggedObject()->GetClass(); 150 if (LIKELY(!hclass->IsDictionaryMode())) { 151 if (!EcmaStringAccessor(key).IsInternString()) { 152 JSHandle<JSTaggedValue> receiverHandler(thread, receiver); 153 auto string = thread->GetEcmaVM()->GetFactory()->InternString(keyHandle); 154 EcmaStringAccessor(string).SetInternString(); 155 keyHandle.Update(JSTaggedValue(string)); 156 // Maybe moved by GC 157 key = keyHandle.GetTaggedValue(); 158 receiver = receiverHandler.GetTaggedValue(); 159 } 160 ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode()); 161 int entry = JSHClass::FindPropertyEntry(thread, hclass, key); 162 if (entry != -1) { 163 return JSTaggedValue::True(); 164 } 165 } 166 return JSTaggedValue::Hole(); 167} 168 169template <ObjectFastOperator::Status status> 170JSTaggedValue ObjectFastOperator::TryFastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, 171 JSMutableHandle<JSTaggedValue> keyHandle) 172{ 173 JSTaggedValue key = keyHandle.GetTaggedValue(); 174 if (UNLIKELY(!receiver.IsHeapObject() || !receiver.IsRegularObject())) { 175 return JSTaggedValue::Hole(); 176 } 177 if (UNLIKELY(!key.IsNumber() && !key.IsString())) { 178 return JSTaggedValue::Hole(); 179 } 180 auto index = TryToElementsIndex(key); 181 if (index >= 0) { 182 return TryFastGetPropertyByIndex<status>(thread, receiver, index); 183 } 184 if (key.IsString()) { 185 if (!EcmaStringAccessor(key).IsInternString()) { 186 [[maybe_unused]] EcmaHandleScope handleScope(thread); 187 JSHandle<JSTaggedValue> receiverHandler(thread, receiver); 188 auto string = thread->GetEcmaVM()->GetFactory()->InternString(keyHandle); 189 EcmaStringAccessor(string).SetInternString(); 190 keyHandle.Update(JSTaggedValue(string)); 191 // Maybe moved by GC 192 key = keyHandle.GetTaggedValue(); 193 receiver = receiverHandler.GetTaggedValue(); 194 } 195 auto ret = TryGetPropertyByNameThroughCacheAtLocal(thread, receiver, key); 196 if (!ret.IsHole()) { 197 return ret; 198 } 199 return ObjectFastOperator::GetPropertyByName<status>(thread, receiver, key); 200 } 201 return JSTaggedValue::Hole(); 202} 203 204template<ObjectFastOperator::Status status> 205JSTaggedValue ObjectFastOperator::TryFastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index) 206{ 207 JSTaggedValue holder = receiver; 208 auto *hclass = holder.GetTaggedObject()->GetClass(); 209 JSHandle<JSObject> currentHolder(thread, holder); 210 if (!hclass->IsDictionaryElement()) { 211 ASSERT(!ElementAccessor::IsDictionaryMode(currentHolder)); 212 if (index < ElementAccessor::GetElementsLength(currentHolder)) { 213 JSTaggedValue value = ElementAccessor::Get(currentHolder, index); 214 if (!value.IsHole()) { 215 return value; 216 } 217 } 218 } 219 return JSTaggedValue::Hole(); 220} 221 222template<ObjectFastOperator::Status status> 223JSTaggedValue ObjectFastOperator::TryGetPropertyByNameThroughCacheAtLocal(JSThread *thread, JSTaggedValue receiver, 224 JSTaggedValue key) 225{ 226 auto *hclass = receiver.GetTaggedObject()->GetClass(); 227 if (LIKELY(!hclass->IsDictionaryMode())) { 228 ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode()); 229 230 int entry = JSHClass::FindPropertyEntry(thread, hclass, key); 231 if (entry != -1) { 232 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); 233 PropertyAttributes attr(layoutInfo->GetAttr(entry)); 234 ASSERT(static_cast<int>(attr.GetOffset()) == entry); 235 auto value = JSObject::Cast(receiver)->GetProperty(hclass, attr); 236 if (UNLIKELY(attr.IsAccessor())) { 237 if (GetInternal(status)) { 238 return value; 239 } 240 return CallGetter(thread, receiver, receiver, value); 241 } 242 ASSERT(!value.IsAccessor()); 243 if (!value.IsHole()) { 244 return value; 245 } 246 } 247 } 248 return JSTaggedValue::Hole(); 249} 250 251template<ObjectFastOperator::Status status> 252JSTaggedValue ObjectFastOperator::GetPropertyByName(JSThread *thread, JSTaggedValue receiver, 253 JSTaggedValue key, [[maybe_unused]]bool noAllocate, 254 [[maybe_unused]]bool *isCallGetter) 255{ 256 INTERPRETER_TRACE(thread, GetPropertyByName); 257 // no gc when return hole 258 ASSERT(key.IsStringOrSymbol()); 259 JSTaggedValue holder = receiver; 260 do { 261 auto *hclass = holder.GetTaggedObject()->GetClass(); 262 JSType jsType = hclass->GetObjectType(); 263 if (IsSpecialIndexedObj(jsType)) { 264 if (IsFastTypeArray(jsType)) { 265 JSTaggedValue res = FastGetTypeArrayProperty(thread, receiver, holder, key, jsType); 266 if (res.IsNull()) { 267 return JSTaggedValue::Hole(); 268 } else if (UNLIKELY(!res.IsHole())) { 269 return res; 270 } 271 } else if (IsString(jsType) && key.IsString()) { 272 auto vm = thread->GetEcmaVM(); 273 JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString(); 274 bool isLenKey = EcmaStringAccessor::StringsAreEqual(vm, 275 JSHandle<EcmaString>(thread, key), JSHandle<EcmaString>(thread, lenKey)); 276 if (isLenKey) { // get string length 277 return JSTaggedValue(EcmaStringAccessor(holder).GetLength()); 278 } else { // get string prototype 279 JSHandle<GlobalEnv> env = vm->GetGlobalEnv(); 280 JSHandle<JSTaggedValue> stringPrototype = env->GetStringPrototype(); 281 holder = stringPrototype.GetTaggedValue(); 282 continue; 283 } 284 } else if (!IsJSPrimitiveRef(jsType)) { // not string prototype etc. 285 return JSTaggedValue::Hole(); 286 } 287 } 288 289 if (LIKELY(!hclass->IsDictionaryMode())) { 290 ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode()); 291 292 int entry = JSHClass::FindPropertyEntry(thread, hclass, key); 293 if (entry != -1) { 294 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); 295 PropertyAttributes attr(layoutInfo->GetAttr(entry)); 296 ASSERT(static_cast<int>(attr.GetOffset()) == entry); 297 auto value = JSObject::Cast(holder)->GetProperty(hclass, attr); 298 if (UNLIKELY(attr.IsAccessor())) { 299 if (GetInternal(status)) { 300 return value; 301 } 302 if (noAllocate) { 303 *isCallGetter = true; 304 return value; 305 } 306 return CallGetter(thread, receiver, holder, value); 307 } 308 ASSERT(!value.IsAccessor()); 309 if (!value.IsHole()) { 310 return value; 311 } 312 } 313 } else { 314 TaggedArray *array = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject()); 315 ASSERT(array->IsDictionaryMode()); 316 NameDictionary *dict = NameDictionary::Cast(array); 317 int entry = dict->FindEntry(key); 318 if (entry != -1) { 319 auto value = dict->GetValue(entry); 320 auto attr = dict->GetAttributes(entry); 321 if (UNLIKELY(attr.IsAccessor())) { 322 if (GetInternal(status)) { 323 return value; 324 } 325 if (noAllocate) { 326 *isCallGetter = true; 327 return value; 328 } 329 return CallGetter(thread, receiver, holder, value); 330 } 331 ASSERT(!value.IsAccessor()); 332 return value; 333 } 334 } 335 if (UseOwn(status)) { 336 break; 337 } 338 holder = hclass->GetPrototype(); 339 } while (holder.IsHeapObject()); 340 // not found 341 return JSTaggedValue::Undefined(); 342} 343 344template<ObjectFastOperator::Status status> 345JSTaggedValue ObjectFastOperator::TrySetPropertyByNameThroughCacheAtLocal(JSThread *thread, JSTaggedValue receiver, 346 JSTaggedValue key, JSTaggedValue value) 347{ 348 bool isTagged = true; 349 JSTaggedValue originValue = value; 350 auto *hclass = receiver.GetTaggedObject()->GetClass(); 351 if (LIKELY(!hclass->IsDictionaryMode())) { 352 ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode()); 353 int entry = JSHClass::FindPropertyEntry(thread, hclass, key); 354 if (entry != -1) { 355 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); 356 PropertyAttributes attr(layoutInfo->GetAttr(entry)); 357 ASSERT(static_cast<int>(attr.GetOffset()) == entry); 358 if (UNLIKELY(attr.IsAccessor())) { 359 if (DefineSemantics(status)) { 360 return JSTaggedValue::Hole(); 361 } 362 auto accessor = JSObject::Cast(receiver)->GetProperty(hclass, attr); 363 if (ShouldCallSetter(receiver, receiver, accessor, attr)) { 364 return CallSetter(thread, receiver, value, accessor); 365 } 366 } 367 if (UNLIKELY(!attr.IsWritable())) { 368 if (DefineSemantics(status)) { 369 return JSTaggedValue::Hole(); 370 } 371 [[maybe_unused]] EcmaHandleScope handleScope(thread); 372 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), 373 JSTaggedValue::Exception()); 374 } 375 if (hclass->IsTS()) { 376 auto attrVal = JSObject::Cast(receiver)->GetProperty(hclass, attr); 377 if (attrVal.IsHole()) { 378 return JSTaggedValue::Hole(); 379 } 380 JSHandle<JSObject> objHandle(thread, receiver); 381 JSHandle<JSTaggedValue> valueHandle(thread, value); 382 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind(); 383 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, 384 JSHandle<JSTaggedValue>(thread, key), valueHandle, attr); 385 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind); 386 receiver = objHandle.GetTaggedValue(); 387 originValue = valueHandle.GetTaggedValue(); 388 value = actualValue.value; 389 isTagged = actualValue.isTagged; 390 } 391 if (receiver.IsJSShared()) { 392 if (!ClassHelper::MatchFieldType(attr.GetSharedFieldType(), originValue)) { 393 THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), 394 JSTaggedValue::Exception()); 395 } 396 } 397 if (isTagged) { 398 JSObject::Cast(receiver)->SetProperty<true>(thread, hclass, attr, value); 399 } else { 400 JSObject::Cast(receiver)->SetProperty<false>(thread, hclass, attr, value); 401 } 402 return JSTaggedValue::Undefined(); 403 } 404 } 405 return JSTaggedValue::Hole(); 406} 407 408template<ObjectFastOperator::Status status> 409JSTaggedValue ObjectFastOperator::SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, 410 JSTaggedValue value, SCheckMode sCheckMode) 411{ 412 INTERPRETER_TRACE(thread, SetPropertyByName); 413 // property 414 JSTaggedValue holder = receiver; 415 int receiverHoleEntry = -1; 416 do { 417 auto *hclass = holder.GetTaggedObject()->GetClass(); 418 JSType jsType = hclass->GetObjectType(); 419 if (IsSpecialIndexedObj(jsType)) { 420 if (IsFastTypeArray(jsType)) { 421 JSTaggedValue res = FastSetTypeArrayProperty(thread, receiver, holder, key, value, jsType); 422 if (res.IsNull()) { 423 return JSTaggedValue::Hole(); 424 } else if (UNLIKELY(!res.IsHole())) { 425 return res; 426 } 427 } else if (IsSpecialContainer(jsType)) { 428 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property on Container", JSTaggedValue::Exception()); 429 } else { 430 return JSTaggedValue::Hole(); 431 } 432 } 433 // UpdateRepresentation 434 if (LIKELY(!hclass->IsDictionaryMode())) { 435 ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode()); 436 int entry = JSHClass::FindPropertyEntry(thread, hclass, key); 437 if (entry != -1) { 438 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); 439 PropertyAttributes attr(layoutInfo->GetAttr(entry)); 440 ASSERT(static_cast<int>(attr.GetOffset()) == entry); 441 if (UNLIKELY(attr.IsAccessor())) { 442 if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) { 443 return JSTaggedValue::Hole(); 444 } 445 auto accessor = JSObject::Cast(holder)->GetProperty(hclass, attr); 446 if (ShouldCallSetter(receiver, holder, accessor, attr)) { 447 return CallSetter(thread, receiver, value, accessor); 448 } 449 } 450 if (UNLIKELY(!attr.IsWritable())) { 451 if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) { 452 return JSTaggedValue::Hole(); 453 } 454 [[maybe_unused]] EcmaHandleScope handleScope(thread); 455 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), 456 JSTaggedValue::Exception()); 457 } 458 if (hclass->IsTS()) { 459 auto attrVal = JSObject::Cast(holder)->GetProperty(hclass, attr); 460 if (attrVal.IsHole()) { 461 if (receiverHoleEntry == -1 && holder == receiver) { 462 receiverHoleEntry = entry; 463 } 464 if (UseOwn(status)) { 465 break; 466 } 467 holder = hclass->GetPrototype(); 468 continue; 469 } 470 } 471 if (UNLIKELY(holder != receiver)) { 472 break; 473 } 474 if (holder.IsJSShared() && (sCheckMode == SCheckMode::CHECK)) { 475 if (!ClassHelper::MatchFieldType(attr.GetSharedFieldType(), value)) { 476 THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), 477 JSTaggedValue::Exception()); 478 } 479 } 480 JSHandle<JSObject> objHandle(thread, receiver); 481 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind(); 482 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, 483 JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value), attr); 484 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind); 485 receiver = objHandle.GetTaggedValue(); 486 hclass = objHandle->GetClass(); 487 if (actualValue.isTagged) { 488 JSObject::Cast(receiver)->SetProperty<true>(thread, hclass, attr, actualValue.value); 489 } else { 490 JSObject::Cast(receiver)->SetProperty<false>(thread, hclass, attr, actualValue.value); 491 } 492 return JSTaggedValue::Undefined(); 493 } 494 } else { 495 TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject()); 496 ASSERT(properties->IsDictionaryMode()); 497 NameDictionary *dict = NameDictionary::Cast(properties); 498 int entry = dict->FindEntry(key); 499 if (entry != -1) { 500 auto attr = dict->GetAttributes(entry); 501 if (UNLIKELY(attr.IsAccessor())) { 502 if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) { 503 return JSTaggedValue::Hole(); 504 } 505 auto accessor = dict->GetValue(entry); 506 if (ShouldCallSetter(receiver, holder, accessor, attr)) { 507 return CallSetter(thread, receiver, value, accessor); 508 } 509 } 510 if (UNLIKELY(!attr.IsWritable())) { 511 if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) { 512 return JSTaggedValue::Hole(); 513 } 514 [[maybe_unused]] EcmaHandleScope handleScope(thread); 515 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), 516 JSTaggedValue::Exception()); 517 } 518 if (UNLIKELY(holder != receiver)) { 519 break; 520 } 521 if ((sCheckMode == SCheckMode::CHECK) && holder.IsJSShared()) { 522 if (!ClassHelper::MatchFieldType(attr.GetDictSharedFieldType(), value)) { 523 THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), 524 JSTaggedValue::Exception()); 525 } 526 } 527 dict->UpdateValue(thread, entry, value); 528 return JSTaggedValue::Undefined(); 529 } 530 } 531 if (UseOwn(status) || DefineSemantics(status)) { 532 break; 533 } 534 holder = hclass->GetPrototype(); 535 } while (holder.IsHeapObject()); 536 537 if (receiverHoleEntry != -1) { 538 auto *receiverHClass = receiver.GetTaggedObject()->GetClass(); 539 LayoutInfo *receiverLayoutInfo = LayoutInfo::Cast(receiverHClass->GetLayout().GetTaggedObject()); 540 PropertyAttributes attr(receiverLayoutInfo->GetAttr(receiverHoleEntry)); 541 JSHandle<JSObject> objHandle(thread, receiver); 542 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind(); 543 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, 544 JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value), attr); 545 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind); 546 receiver = objHandle.GetTaggedValue(); 547 receiverHClass = objHandle->GetClass(); 548 if (actualValue.isTagged) { 549 JSObject::Cast(receiver)->SetProperty<true>(thread, receiverHClass, attr, actualValue.value); 550 } else { 551 JSObject::Cast(receiver)->SetProperty<false>(thread, receiverHClass, attr, actualValue.value); 552 } 553 return JSTaggedValue::Undefined(); 554 } 555 556 [[maybe_unused]] EcmaHandleScope handleScope(thread); 557 JSHandle<JSObject> objHandle(thread, receiver); 558 JSHandle<JSTaggedValue> keyHandle(thread, key); 559 JSHandle<JSTaggedValue> valueHandle(thread, value); 560 561 if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { 562 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetPropertyWhenNotExtensible), 563 JSTaggedValue::Exception()); 564 } 565 ASSERT(!receiver.IsJSShared()); 566 PropertyAttributes attr = PropertyAttributes::Default(); 567 AddPropertyByName(thread, objHandle, keyHandle, valueHandle, attr); 568 return JSTaggedValue::Undefined(); 569} 570 571template <ObjectFastOperator::Status status> 572JSTaggedValue ObjectFastOperator::GetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index) 573{ 574 INTERPRETER_TRACE(thread, GetPropertyByIndex); 575 [[maybe_unused]] EcmaHandleScope handleScope(thread); 576 JSTaggedValue holder = receiver; 577 do { 578 auto *hclass = holder.GetTaggedObject()->GetClass(); 579 JSType jsType = hclass->GetObjectType(); 580 if (IsSpecialIndexedObj(jsType)) { 581 if (jsType == JSType::JS_TYPED_ARRAY) { 582 return JSTaggedValue::Hole(); 583 } 584 if (IsFastTypeArray(jsType)) { 585 return JSTypedArray::FastGetPropertyByIndex(thread, holder, index, jsType); 586 } 587 if (IsSpecialContainer(jsType)) { 588 return GetContainerProperty(thread, holder, index, jsType); 589 } 590 if (IsString(jsType)) { 591 if (index < EcmaStringAccessor(holder).GetLength()) { 592 EcmaString *subStr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), 593 JSHandle<EcmaString>(thread, holder), index, 1); 594 return JSTaggedValue(subStr); 595 } 596 } 597 return JSTaggedValue::Hole(); 598 } 599 JSHandle<JSObject> currentHolder(thread, holder); 600 if (!hclass->IsDictionaryElement()) { 601 ASSERT(!ElementAccessor::IsDictionaryMode(currentHolder)); 602 if (index < ElementAccessor::GetElementsLength(currentHolder)) { 603 JSTaggedValue value = ElementAccessor::Get(currentHolder, index); 604 if (!value.IsHole()) { 605 return value; 606 } 607 } 608 } else { 609 TaggedArray *elements = TaggedArray::Cast(currentHolder->GetElements().GetTaggedObject()); 610 NumberDictionary *dict = NumberDictionary::Cast(elements); 611 int entry = dict->FindEntry(JSTaggedValue(static_cast<int>(index))); 612 if (entry != -1) { 613 auto attr = dict->GetAttributes(entry); 614 auto value = dict->GetValue(entry); 615 if (UNLIKELY(attr.IsAccessor())) { 616 return CallGetter(thread, receiver, holder, value); 617 } 618 ASSERT(!value.IsAccessor()); 619 return value; 620 } 621 } 622 if (UseOwn(status)) { 623 break; 624 } 625 holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); 626 } while (holder.IsHeapObject()); 627 628 // not found 629 return JSTaggedValue::Undefined(); 630} 631 632template <ObjectFastOperator::Status status> 633JSTaggedValue ObjectFastOperator::SetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, 634 JSTaggedValue value) 635{ 636 INTERPRETER_TRACE(thread, SetPropertyByIndex); 637 JSTaggedValue holder = receiver; 638 do { 639 auto *hclass = holder.GetTaggedObject()->GetClass(); 640 JSType jsType = hclass->GetObjectType(); 641 if (IsSpecialIndexedObj(jsType)) { 642 if (jsType == JSType::JS_TYPED_ARRAY) { 643 return JSTaggedValue::Hole(); 644 } 645 if (IsFastTypeArray(jsType)) { 646 CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder); 647 return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType); 648 } 649 if (IsSpecialContainer(jsType)) { 650 if (DefineSemantics(status)) { 651 return JSTaggedValue::Hole(); 652 } 653 return SetContainerProperty(thread, holder, index, value, jsType); 654 } 655 return JSTaggedValue::Hole(); 656 } 657 JSHandle<JSObject> arrayHandler(thread, holder); 658 TaggedArray *elements = TaggedArray::Cast(arrayHandler->GetElements().GetTaggedObject()); 659 if (!hclass->IsDictionaryElement()) { 660 ASSERT(!elements->IsDictionaryMode()); 661 if (UNLIKELY(holder != receiver)) { 662 break; 663 } 664 if (index < elements->GetLength()) { 665 JSTaggedValue oldValue = ElementAccessor::Get(arrayHandler, index); 666 if (!oldValue.IsHole()) { 667 if (holder.IsJSCOWArray()) { 668 [[maybe_unused]] EcmaHandleScope handleScope(thread); 669 JSHandle<JSArray> holderHandler(thread, holder); 670 JSHandle<JSObject> obj(thread, holder); 671 JSHandle<JSTaggedValue> valueHandle(thread, value); 672 // CheckAndCopyArray may cause gc. 673 JSArray::CheckAndCopyArray(thread, holderHandler); 674 ElementAccessor::Set(thread, obj, index, valueHandle, true); 675 return JSTaggedValue::Undefined(); 676 } 677 JSHandle<JSTaggedValue> valueHandle(thread, value); 678 ElementAccessor::Set(thread, arrayHandler, index, valueHandle, true); 679 return JSTaggedValue::Undefined(); 680 } 681 } 682 } else { 683 NumberDictionary *dict = NumberDictionary::Cast(elements); 684 int entry = dict->FindEntry(JSTaggedValue(static_cast<int>(index))); 685 if (entry != -1) { 686 auto attr = dict->GetAttributes(entry); 687 if (UNLIKELY(!attr.IsWritable() || !attr.IsConfigurable())) { 688 return JSTaggedValue::Hole(); 689 } 690 if (UNLIKELY(holder != receiver)) { 691 break; 692 } 693 if (UNLIKELY(attr.IsAccessor())) { 694 if (DefineSemantics(status)) { 695 return JSTaggedValue::Hole(); 696 } 697 auto accessor = dict->GetValue(entry); 698 if (ShouldCallSetter(receiver, holder, accessor, attr)) { 699 return CallSetter(thread, receiver, value, accessor); 700 } 701 } 702 dict->UpdateValue(thread, entry, value); 703 return JSTaggedValue::Undefined(); 704 } 705 return JSTaggedValue::Hole(); 706 } 707 if (UseOwn(status) || DefineSemantics(status)) { 708 break; 709 } 710 holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); 711 } while (holder.IsHeapObject()); 712 713 return AddPropertyByIndex(thread, receiver, index, value); 714} 715 716template <ObjectFastOperator::Status status> 717JSTaggedValue ObjectFastOperator::GetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) 718{ 719 INTERPRETER_TRACE(thread, GetPropertyByValue); 720 if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) { 721 return JSTaggedValue::Hole(); 722 } 723 // fast path 724 auto index = TryToElementsIndex(key); 725 if (LIKELY(index >= 0)) { 726 return GetPropertyByIndex<status>(thread, receiver, index); 727 } 728 if (!key.IsNumber()) { 729 if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) { 730 // update string stable 731 [[maybe_unused]] EcmaHandleScope handleScope(thread); 732 JSHandle<JSTaggedValue> receiverHandler(thread, receiver); 733 key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key))); 734 // Maybe moved by GC 735 receiver = receiverHandler.GetTaggedValue(); 736 } 737 return ObjectFastOperator::GetPropertyByName<status>(thread, receiver, key); 738 } 739 return JSTaggedValue::Hole(); 740} 741 742template<ObjectFastOperator::Status status> 743JSTaggedValue ObjectFastOperator::SetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, 744 JSTaggedValue value, SCheckMode sCheckMode) 745{ 746 INTERPRETER_TRACE(thread, SetPropertyByValue); 747 if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) { 748 return JSTaggedValue::Hole(); 749 } 750 // fast path 751 auto index = TryToElementsIndex(key); 752 if (LIKELY(index >= 0)) { 753 return SetPropertyByIndex<status>(thread, receiver, index, value); 754 } 755 if (!key.IsNumber()) { 756 if (key.IsString()) { 757 if (!EcmaStringAccessor(key).IsInternString()) { 758 // update string stable 759 [[maybe_unused]] EcmaHandleScope handleScope(thread); 760 JSHandle<JSTaggedValue> receiverHandler(thread, receiver); 761 JSHandle<JSTaggedValue> valueHandler(thread, value); 762 key = JSTaggedValue( 763 thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key))); 764 // Maybe moved by GC 765 receiver = receiverHandler.GetTaggedValue(); 766 value = valueHandler.GetTaggedValue(); 767 } 768 } 769 ObjectOperator::UpdateDetector(thread, receiver, key); 770 return ObjectFastOperator::SetPropertyByName<status>(thread, receiver, key, value, sCheckMode); 771 } 772 return JSTaggedValue::Hole(); 773} 774 775bool ObjectFastOperator::FastSetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, 776 JSTaggedValue value, SCheckMode sCheckMode) 777{ 778 INTERPRETER_TRACE(thread, FastSetPropertyByValue); 779 JSTaggedValue result = ObjectFastOperator::SetPropertyByValue(thread, receiver, key, value, sCheckMode); 780 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); 781 if (!result.IsHole()) { 782 return !result.IsException(); 783 } 784 return JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver), 785 JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value), 786 true, sCheckMode); 787} 788 789bool ObjectFastOperator::FastSetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, 790 JSTaggedValue value) 791{ 792 INTERPRETER_TRACE(thread, FastSetPropertyByIndex); 793 JSTaggedValue result = ObjectFastOperator::SetPropertyByIndex(thread, receiver, index, value); 794 if (!result.IsHole()) { 795 return !result.IsException(); 796 } 797 return JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver), index, 798 JSHandle<JSTaggedValue>(thread, value), true); 799} 800 801JSTaggedValue ObjectFastOperator::FastGetPropertyByName(JSThread *thread, JSTaggedValue receiver, 802 JSTaggedValue key) 803{ 804 INTERPRETER_TRACE(thread, FastGetPropertyByName); 805 ASSERT(key.IsStringOrSymbol()); 806 if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) { 807 JSHandle<JSTaggedValue> receiverHandler(thread, receiver); 808 key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key))); 809 // Maybe moved by GC 810 receiver = receiverHandler.GetTaggedValue(); 811 } 812 JSTaggedValue result = ObjectFastOperator::GetPropertyByName<Status::GetInternal>(thread, receiver, key); 813 if (result.IsHole()) { 814 return JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver), 815 JSHandle<JSTaggedValue>(thread, key)).GetValue().GetTaggedValue(); 816 } 817 return result; 818} 819 820JSTaggedValue ObjectFastOperator::FastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, 821 SCheckMode sCheckMode) 822{ 823 INTERPRETER_TRACE(thread, FastGetPropertyByValue); 824 JSTaggedValue result = ObjectFastOperator::GetPropertyByValue(thread, receiver, key); 825 if (result.IsHole()) { 826 return JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver), 827 JSHandle<JSTaggedValue>(thread, key), sCheckMode).GetValue().GetTaggedValue(); 828 } 829 return result; 830} 831 832JSTaggedValue ObjectFastOperator::FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index) 833{ 834 INTERPRETER_TRACE(thread, FastGetPropertyByIndex); 835 JSTaggedValue result = ObjectFastOperator::GetPropertyByIndex(thread, receiver, index); 836 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 837 if (result.IsHole()) { 838 return JSTaggedValue::GetProperty(thread, 839 JSHandle<JSTaggedValue>(thread, receiver), index).GetValue().GetTaggedValue(); 840 } 841 return result; 842} 843 844JSTaggedValue ObjectFastOperator::FastParseDate(const EcmaString *str) 845{ 846 int year = 0; 847 int month = 1; 848 int date = 1; 849 int index = 0; 850 851 CVector<uint8_t> tmpBuf; 852 EcmaStringAccessor strAccessor(const_cast<EcmaString *>(str)); 853 int len = static_cast<int>(strAccessor.GetLength()); 854 auto data = reinterpret_cast<const char *>(strAccessor.GetUtf8DataFlat(str, tmpBuf)); 855 if (!GetNumFromString(data, len, &index, &year)) { 856 return JSTaggedValue::Hole(); 857 } 858 if (!GetNumFromString(data, len, &index, &month)) { 859 return JSTaggedValue::Hole(); 860 } 861 if (!GetNumFromString(data, len, &index, &date)) { 862 return JSTaggedValue::Hole(); 863 } 864 if (month < 1 || month > JSDate::MONTH_PER_YEAR) { 865 return JSTaggedValue::Hole(); 866 } 867 if (date < 1 || date > JSDate::MAX_DAYS_MONTH) { 868 return JSTaggedValue::Hole(); 869 } 870 double day = JSDate::MakeDay(year, month - 1, date); 871 double timeValue = JSDate::TimeClip(JSDate::MakeDate(day, 0)); 872 return JSTaggedValue(timeValue); 873} 874 875PropertyAttributes ObjectFastOperator::AddPropertyByName(JSThread *thread, JSHandle<JSObject> objHandle, 876 JSHandle<JSTaggedValue> keyHandle, 877 JSHandle<JSTaggedValue> valueHandle, 878 PropertyAttributes attr) 879{ 880 INTERPRETER_TRACE(thread, AddPropertyByName); 881 882 if ((objHandle->IsJSArray() || objHandle->IsTypedArray()) && 883 keyHandle.GetTaggedValue() == thread->GlobalConstants()->GetConstructorString()) { 884 objHandle->GetJSHClass()->SetHasConstructor(true); 885 } 886 int32_t nextInlinedPropsIndex = objHandle->GetJSHClass()->GetNextInlinedPropsIndex(); 887 if (nextInlinedPropsIndex >= 0) { 888 attr.SetOffset(nextInlinedPropsIndex); 889 attr.SetIsInlinedProps(true); 890 attr.SetRepresentation(Representation::TAGGED); 891 auto rep = PropertyAttributes::TranslateToRep(valueHandle.GetTaggedValue()); 892 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind(); 893 JSHClass::AddProperty(thread, objHandle, keyHandle, attr, rep); 894 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind); 895 oldKind = objHandle->GetJSHClass()->GetElementsKind(); 896 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, keyHandle, valueHandle, attr); 897 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind); 898 if (actualValue.isTagged) { 899 objHandle->SetPropertyInlinedProps<true>(thread, nextInlinedPropsIndex, valueHandle.GetTaggedValue()); 900 } else { 901 objHandle->SetPropertyInlinedProps<false>(thread, nextInlinedPropsIndex, actualValue.value); 902 } 903 return attr; 904 } 905 906 JSMutableHandle<TaggedArray> array(thread, objHandle->GetProperties()); 907 uint32_t length = array->GetLength(); 908 if (length == 0) { 909 length = JSObject::MIN_PROPERTIES_LENGTH; 910 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 911 array.Update(factory->NewTaggedArray(length).GetTaggedValue()); 912 objHandle->SetProperties(thread, array.GetTaggedValue()); 913 } 914 915 if (!array->IsDictionaryMode()) { 916 attr.SetIsInlinedProps(false); 917 uint32_t nonInlinedProps = static_cast<uint32_t>(objHandle->GetJSHClass()->GetNextNonInlinedPropsIndex()); 918 ASSERT(length >= nonInlinedProps); 919 uint32_t numberOfProps = objHandle->GetJSHClass()->NumberOfProps(); 920 if (UNLIKELY(numberOfProps >= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) { 921 // change to dictionary and add one. 922 JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread, objHandle)); 923 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, attr); 924 JSHandle<NameDictionary> newDict = 925 NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr); 926 objHandle->SetProperties(thread, newDict); 927 // index is not essential when fastMode is false; 928 return attr; 929 } 930 // if array is full, grow array or change to dictionary mode 931 if (length == nonInlinedProps) { 932 uint32_t maxNonInlinedFastPropsCapacity = objHandle->GetNonInlinedFastPropsCapacity(); 933 // Grow properties array size 934 uint32_t capacity = JSObject::ComputeNonInlinedFastPropsCapacity(thread, length, 935 maxNonInlinedFastPropsCapacity); 936 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 937 array.Update(factory->CopyArray(array, length, capacity).GetTaggedValue()); 938 objHandle->SetProperties(thread, array.GetTaggedValue()); 939 } 940 941 attr.SetOffset(nonInlinedProps + objHandle->GetJSHClass()->GetInlinedProperties()); 942 attr.SetRepresentation(Representation::TAGGED); 943 auto rep = PropertyAttributes::TranslateToRep(valueHandle.GetTaggedValue()); 944 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind(); 945 JSHClass::AddProperty(thread, objHandle, keyHandle, attr, rep); 946 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind); 947 oldKind = objHandle->GetJSHClass()->GetElementsKind(); 948 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, keyHandle, valueHandle, attr); 949 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind); 950 if (actualValue.isTagged) { 951 array->Set<true>(thread, nonInlinedProps, valueHandle.GetTaggedValue()); 952 } else { 953 array->Set<false>(thread, nonInlinedProps, actualValue.value); 954 } 955 } else { 956 JSHandle<NameDictionary> dictHandle(array); 957 JSHandle<NameDictionary> newDict = 958 NameDictionary::PutIfAbsent(thread, dictHandle, keyHandle, valueHandle, attr); 959 objHandle->SetProperties(thread, newDict); 960 } 961 return attr; 962} 963 964JSTaggedValue ObjectFastOperator::CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, 965 JSTaggedValue value) 966{ 967 INTERPRETER_TRACE(thread, CallGetter); 968 // Accessor 969 [[maybe_unused]] EcmaHandleScope handleScope(thread); 970 AccessorData *accessor = AccessorData::Cast(value.GetTaggedObject()); 971 if (UNLIKELY(accessor->IsInternal())) { 972 JSHandle<JSObject> objHandle(thread, holder); 973 return accessor->CallInternalGet(thread, objHandle); 974 } 975 JSHandle<JSTaggedValue> objHandle(thread, receiver); 976 return JSObject::CallGetter(thread, accessor, objHandle); 977} 978 979JSTaggedValue ObjectFastOperator::CallSetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value, 980 JSTaggedValue accessorValue) 981{ 982 INTERPRETER_TRACE(thread, CallSetter); 983 // Accessor 984 [[maybe_unused]] EcmaHandleScope handleScope(thread); 985 JSHandle<JSTaggedValue> objHandle(thread, receiver); 986 JSHandle<JSTaggedValue> valueHandle(thread, value); 987 988 auto accessor = AccessorData::Cast(accessorValue.GetTaggedObject()); 989 bool success = JSObject::CallSetter(thread, *accessor, objHandle, valueHandle, true); 990 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 991 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); 992} 993 994bool ObjectFastOperator::ShouldCallSetter(JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue accessorValue, 995 PropertyAttributes attr) 996{ 997 if (!AccessorData::Cast(accessorValue.GetTaggedObject())->IsInternal()) { 998 return true; 999 } 1000 if (receiver != holder) { 1001 return false; 1002 } 1003 return attr.IsWritable(); 1004} 1005 1006bool ObjectFastOperator::IsSpecialIndexedObj(JSType jsType) 1007{ 1008 return jsType > JSType::JS_ARRAY; 1009} 1010 1011bool ObjectFastOperator::IsJSSharedArray(JSType jsType) 1012{ 1013 return jsType == JSType::JS_SHARED_ARRAY; 1014} 1015 1016bool ObjectFastOperator::IsFastTypeArray(JSType jsType) 1017{ 1018 return jsType >= JSType::JS_TYPED_ARRAY_FIRST && jsType <= JSType::JS_TYPED_ARRAY_LAST; 1019} 1020 1021bool ObjectFastOperator::IsString(JSType jsType) 1022{ 1023 return JSType::STRING_FIRST <= jsType && jsType <= JSType::STRING_LAST; 1024} 1025 1026bool ObjectFastOperator::IsJSPrimitiveRef(JSType jsType) 1027{ 1028 return jsType == JSType::JS_PRIMITIVE_REF; 1029} 1030 1031JSTaggedValue ObjectFastOperator::FastGetTypeArrayProperty(JSThread *thread, JSTaggedValue receiver, 1032 JSTaggedValue holder, 1033 JSTaggedValue key, JSType jsType) 1034{ 1035 CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder); 1036 JSTaggedValue negativeZero = thread->GlobalConstants()->GetNegativeZeroString(); 1037 if (UNLIKELY(negativeZero == key)) { 1038 return JSTaggedValue::Undefined(); 1039 } 1040 uint32_t index = 0; 1041 if (TryStringOrSymbolToIndex(key, &index)) { 1042 if (UNLIKELY(index == JSObject::MAX_ELEMENT_INDEX)) { 1043 return JSTaggedValue::Null(); 1044 } 1045 return JSTypedArray::FastGetPropertyByIndex(thread, receiver, index, jsType); 1046 } 1047 return JSTaggedValue::Hole(); 1048} 1049 1050bool ObjectFastOperator::TryStringOrSymbolToIndex(JSTaggedValue key, uint32_t *output) 1051{ 1052 if (key.IsSymbol()) { 1053 return false; 1054 } 1055 auto strObj = static_cast<EcmaString *>(key.GetTaggedObject()); 1056 return EcmaStringAccessor(strObj).ToTypedArrayIndex(output); 1057} 1058 1059JSTaggedValue ObjectFastOperator::FastSetTypeArrayProperty(JSThread *thread, JSTaggedValue receiver, 1060 JSTaggedValue holder, JSTaggedValue key, 1061 JSTaggedValue value, JSType jsType) 1062{ 1063 CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder); 1064 JSTaggedValue negativeZero = thread->GlobalConstants()->GetNegativeZeroString(); 1065 if (UNLIKELY(negativeZero == key)) { 1066 if (value.IsECMAObject()) { 1067 return JSTaggedValue::Null(); 1068 } 1069 return JSTaggedValue::Undefined(); 1070 } 1071 uint32_t index = 0; 1072 if (TryStringOrSymbolToIndex(key, &index)) { 1073 if (UNLIKELY(index == JSObject::MAX_ELEMENT_INDEX)) { 1074 return JSTaggedValue::Null(); 1075 } 1076 return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType); 1077 } 1078 return JSTaggedValue::Hole(); 1079} 1080 1081bool ObjectFastOperator::IsSpecialContainer(JSType jsType) 1082{ 1083 return jsType >= JSType::JS_API_ARRAY_LIST && jsType <= JSType::JS_API_QUEUE; 1084} 1085 1086JSTaggedValue ObjectFastOperator::GetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, 1087 JSType jsType) 1088{ 1089 JSTaggedValue res = JSTaggedValue::Undefined(); 1090 switch (jsType) { 1091 case JSType::JS_API_ARRAY_LIST: 1092 res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Get(thread, index); 1093 break; 1094 case JSType::JS_API_QUEUE: 1095 res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Get(thread, index); 1096 break; 1097 case JSType::JS_API_PLAIN_ARRAY: 1098 res = JSAPIPlainArray::Cast(receiver.GetTaggedObject())->Get(JSTaggedValue(index)); 1099 break; 1100 case JSType::JS_API_DEQUE: 1101 res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Get(index); 1102 break; 1103 case JSType::JS_API_STACK: 1104 res = JSAPIStack::Cast(receiver.GetTaggedObject())->Get(index); 1105 break; 1106 case JSType::JS_API_VECTOR: { 1107 auto self = JSHandle<JSTaggedValue>(thread, receiver); 1108 res = JSAPIVector::Get(thread, JSHandle<JSAPIVector>::Cast(self), index); 1109 break; 1110 } 1111 case JSType::JS_API_LIST: { 1112 res = JSAPIList::Cast(receiver.GetTaggedObject())->Get(index); 1113 break; 1114 } 1115 case JSType::JS_API_BITVECTOR: { 1116 res = JSAPIBitVector::Cast(receiver.GetTaggedObject())->Get(thread, index); 1117 break; 1118 } 1119 case JSType::JS_API_LINKED_LIST: { 1120 res = JSAPILinkedList::Cast(receiver.GetTaggedObject())->Get(index); 1121 break; 1122 } 1123 default: 1124 break; 1125 } 1126 return res; 1127} 1128 1129JSTaggedValue ObjectFastOperator::SetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, 1130 JSTaggedValue value, JSType jsType) 1131{ 1132 JSTaggedValue res = JSTaggedValue::Undefined(); 1133 switch (jsType) { 1134 case JSType::JS_API_ARRAY_LIST: 1135 res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Set(thread, index, value); 1136 break; 1137 case JSType::JS_API_QUEUE: 1138 res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Set(thread, index, value); 1139 break; 1140 case JSType::JS_API_PLAIN_ARRAY: { 1141 JSHandle<JSAPIPlainArray> plainArray(thread, receiver); 1142 res = JSAPIPlainArray::Set(thread, plainArray, index, value); 1143 break; 1144 } 1145 case JSType::JS_API_DEQUE: 1146 res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Set(thread, index, value); 1147 break; 1148 case JSType::JS_API_STACK: 1149 res = JSAPIStack::Cast(receiver.GetTaggedObject())->Set(thread, index, value); 1150 break; 1151 case JSType::JS_API_VECTOR: 1152 res = JSAPIVector::Cast(receiver.GetTaggedObject())->Set(thread, index, value); 1153 break; 1154 case JSType::JS_API_BITVECTOR: 1155 res = JSAPIBitVector::Cast(receiver.GetTaggedObject())->Set(thread, index, value); 1156 break; 1157 case JSType::JS_API_LIST: { 1158 JSHandle<JSAPIList> singleList(thread, receiver); 1159 res = JSAPIList::Set(thread, singleList, index, JSHandle<JSTaggedValue>(thread, value)); 1160 break; 1161 } 1162 case JSType::JS_API_LINKED_LIST: { 1163 JSHandle<JSAPILinkedList> doubleList(thread, receiver); 1164 res = JSAPILinkedList::Set(thread, doubleList, index, JSHandle<JSTaggedValue>(thread, value)); 1165 break; 1166 } 1167 default: 1168 break; 1169 } 1170 return res; 1171} 1172 1173JSTaggedValue ObjectFastOperator::AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, 1174 JSTaggedValue value) 1175{ 1176 INTERPRETER_TRACE(thread, AddPropertyByIndex); 1177 [[maybe_unused]] EcmaHandleScope handleScope(thread); 1178 // fixme(hzzhouzebin) this makes SharedArray's frozen no sense. 1179 if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible()) && !receiver.IsJSSharedArray()) { 1180 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetPropertyWhenNotExtensible), 1181 JSTaggedValue::Exception()); 1182 } 1183 1184 bool success = JSObject::AddElementInternal(thread, JSHandle<JSObject>(thread, receiver), index, 1185 JSHandle<JSTaggedValue>(thread, value), PropertyAttributes::Default()); 1186 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); 1187} 1188 1189int64_t ObjectFastOperator::TryToElementsIndex(JSTaggedValue key) 1190{ 1191 if (LIKELY(key.IsInt())) { 1192 return key.GetInt(); 1193 } 1194 if (key.IsString()) { 1195 uint32_t index = 0; 1196 if (JSTaggedValue::StringToElementIndex(key, &index)) { 1197 return static_cast<int64_t>(index); 1198 } 1199 } else if (key.IsDouble()) { 1200 double number = key.GetDouble(); 1201 auto integer = static_cast<int32_t>(number); 1202 if (number == integer) { 1203 return integer; 1204 } 1205 } 1206 return -1; 1207} 1208 1209bool ObjectFastOperator::GetNumFromString(const char *str, int len, int *index, int *num) 1210{ 1211 int indexStr = *index; 1212 char oneByte = 0; 1213 oneByte = str[indexStr]; 1214 if (oneByte < '0' || oneByte > '9') { 1215 return false; 1216 } 1217 if (indexStr >= len) { 1218 return false; 1219 } 1220 int value = 0; 1221 while (indexStr < len) { 1222 oneByte = str[indexStr]; 1223 int val = static_cast<int>(oneByte - '0'); 1224 if (val >= 0 && val <= JSDate::NUM_NINE) { 1225 value = value * JSDate::TEN + val; 1226 indexStr++; 1227 } else if (oneByte != '-') { 1228 return false; 1229 } else { 1230 indexStr++; 1231 break; 1232 } 1233 } 1234 *num = value; 1235 *index = indexStr; 1236 return true; 1237} 1238 1239JSTaggedValue ObjectFastOperator::FastGetPropertyByPorpsIndex(JSThread *thread, 1240 JSTaggedValue receiver, uint32_t index) 1241{ 1242 JSTaggedValue value = JSTaggedValue::Hole(); 1243 JSObject *obj = JSObject::Cast(receiver); 1244 TaggedArray *properties = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); 1245 if (!properties->IsDictionaryMode()) { 1246 JSHClass *jsHclass = obj->GetJSHClass(); 1247 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()); 1248 PropertyAttributes attr = layoutInfo->GetAttr(index); 1249 value = obj->GetProperty(jsHclass, attr); 1250 } else { 1251 NameDictionary *dict = NameDictionary::Cast(properties); 1252 value = dict->GetValue(index); 1253 } 1254 if (UNLIKELY(value.IsAccessor())) { 1255 return CallGetter(thread, JSTaggedValue(obj), JSTaggedValue(obj), value); 1256 } 1257 ASSERT(!value.IsAccessor()); 1258 return value; 1259} 1260} 1261#endif // ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H 1262