1/* 2 * Copyright (c) 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 "ecmascript/shared_objects/js_shared_array.h" 17 18#include "ecmascript/interpreter/interpreter.h" 19#include "ecmascript/object_fast_operator-inl.h" 20 21namespace panda::ecmascript { 22using base::ArrayHelper; 23 24JSTaggedValue JSSharedArray::LengthGetter([[maybe_unused]] JSThread *thread, const JSHandle<JSObject> &self, 25 SCheckMode checkMode) 26{ 27 [[maybe_unused]] ConcurrentApiScope<JSSharedArray> scope(thread, JSHandle<JSTaggedValue>::Cast(self), 28 checkMode); 29 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 30 return JSTaggedValue(JSSharedArray::Cast(*self)->GetLength()); 31} 32 33bool JSSharedArray::DummyLengthSetter([[maybe_unused]] JSThread *thread, 34 [[maybe_unused]] const JSHandle<JSObject> &self, 35 [[maybe_unused]] const JSHandle<JSTaggedValue> &value, 36 [[maybe_unused]] bool mayThrow) 37{ 38 // length is the read only property for Shared Array 39 return true; 40} 41 42bool JSSharedArray::LengthSetter(JSThread *thread, const JSHandle<JSObject> &self, const JSHandle<JSTaggedValue> &value, 43 bool mayThrow) 44{ 45 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); 46 uint32_t newLen = 0; 47 if (!JSTaggedValue::ToArrayLength(thread, value, &newLen) && mayThrow) { 48 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); 49 } 50 51 uint32_t oldLen = JSSharedArray::Cast(*self)->GetArrayLength(); 52 if (oldLen == newLen) { 53 return true; 54 } 55 56 if (!IsArrayLengthWritable(thread, self)) { 57 if (mayThrow) { 58 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), false); 59 } 60 return false; 61 } 62 63 JSSharedArray::SetCapacity(thread, self, oldLen, newLen); 64 uint32_t actualLen = JSSharedArray::Cast(*self)->GetArrayLength(); 65 if (actualLen != newLen) { // LCOV_EXCL_START 66 if (mayThrow) { 67 THROW_TYPE_ERROR_AND_RETURN(thread, "Not all array elements is configurable", false); 68 } 69 return false; 70 } // LCOV_EXCL_STOP 71 72 return true; 73} 74 75JSHandle<JSTaggedValue> JSSharedArray::ArrayCreate(JSThread *thread, JSTaggedNumber length, ArrayMode mode) 76{ 77 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 78 JSHandle<JSTaggedValue> sharedArrayFunction = env->GetSharedArrayFunction(); 79 return JSSharedArray::ArrayCreate(thread, length, sharedArrayFunction, mode); 80} 81 82// 9.4.2.2 ArrayCreate(length, proto) 83JSHandle<JSTaggedValue> JSSharedArray::ArrayCreate(JSThread *thread, JSTaggedNumber length, 84 const JSHandle<JSTaggedValue> &newTarget, ArrayMode mode) 85{ 86 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 87 // Assert: length is an integer Number ≥ 0. 88 ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer"); 89 // 2. If length is −0, let length be +0. 90 double arrayLength = length.GetNumber(); 91 if (arrayLength > MAX_ARRAY_INDEX) { 92 JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception()); 93 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::TYPE_ERROR, 94 "Parameter error.Array length must less than 2^32."); 95 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, exception); 96 } 97 uint32_t normalArrayLength = length.ToUint32(); 98 99 // 8. Set the [[Prototype]] internal slot of A to proto. 100 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 101 JSHandle<JSFunction> arrayFunc(env->GetSharedArrayFunction()); 102 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc, newTarget); 103 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 104 // 9. Set the [[Extensible]] internal slot of A to true. 105 106 // 10. Perform OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: 107 // true, [[Enumerable]]: false, [[Configurable]]: false}). 108 if (mode == ArrayMode::LITERAL) { 109 JSSharedArray::Cast(*obj)->SetArrayLength(thread, normalArrayLength); 110 } else { 111 JSSharedArray::SetCapacity(thread, obj, 0, normalArrayLength, true); 112 } 113 return JSHandle<JSTaggedValue>(obj); 114} 115 116// 9.4.2.3 ArraySpeciesCreate(originalArray, length) 117JSTaggedValue JSSharedArray::ArraySpeciesCreate(JSThread *thread, const JSHandle<JSObject> &originalArray, 118 JSTaggedNumber length) 119{ 120 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 121 const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 122 // Assert: length is an integer Number ≥ 0. 123 ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer"); 124 // If length is −0, let length be +0. 125 int64_t arrayLength = length.GetNumber(); 126 if (arrayLength == -0) { 127 arrayLength = +0; 128 } 129 // Let C be undefined. 130 // Let isArray be IsArray(originalArray). 131 JSHandle<JSTaggedValue> originalValue(originalArray); 132 bool isSArray = originalValue->IsJSSharedArray(); 133 // ReturnIfAbrupt(isArray). 134 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 135 // If isArray is true, then 136 JSHandle<JSTaggedValue> constructor(thread, JSTaggedValue::Undefined()); 137 if (isSArray) { 138 // Let C be Get(originalArray, "constructor"). 139 auto *hclass = originalArray->GetJSHClass(); 140 JSTaggedValue proto = hclass->GetPrototype(); 141 if (hclass->IsJSSharedArray() && !hclass->HasConstructor() && proto.IsJSSharedArray()) { 142 return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue(); 143 } 144 JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString(); 145 constructor = JSTaggedValue::GetProperty(thread, originalValue, constructorKey).GetValue(); 146 // ReturnIfAbrupt(C). 147 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 148 // If IsConstructor(C) is true, then 149 if (constructor->IsConstructor()) { 150 // Let thisRealm be the running execution context’s Realm. 151 // Let realmC be GetFunctionRealm(C). 152 JSHandle<GlobalEnv> realmC = JSObject::GetFunctionRealm(thread, constructor); 153 // ReturnIfAbrupt(realmC). 154 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 155 // If thisRealm and realmC are not the same Realm Record, then 156 if (*realmC != *env) { 157 JSTaggedValue realmArrayConstructor = realmC->GetSharedArrayFunction().GetTaggedValue(); 158 // If SameValue(C, realmC.[[intrinsics]].[[%Array%]]) is true, let C be undefined. 159 if (JSTaggedValue::SameValue(constructor.GetTaggedValue(), realmArrayConstructor)) { 160 return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue(); 161 } 162 } 163 } 164 165 // If Type(C) is Object, then 166 if (constructor->IsECMAObject()) { 167 // Let C be Get(C, @@species). 168 JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol(); 169 constructor = JSTaggedValue::GetProperty(thread, constructor, speciesSymbol).GetValue(); 170 // ReturnIfAbrupt(C). 171 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 172 // If C is null, let C be undefined. 173 if (constructor->IsNull()) { 174 return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue(); 175 } 176 } 177 } 178 179 // If C is undefined, return ArrayCreate(length). 180 if (constructor->IsUndefined()) { 181 return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue(); 182 } 183 // If IsConstructor(C) is false, throw a TypeError exception. 184 if (!constructor->IsConstructor()) { 185 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a constructor", JSTaggedValue::Exception()); 186 } 187 // Return Construct(C, «length»). 188 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 189 EcmaRuntimeCallInfo *info = 190 EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1); 191 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 192 info->SetCallArg(JSTaggedValue(arrayLength)); 193 JSTaggedValue result = JSFunction::Construct(info); 194 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 195 196 // NOTEIf originalArray was created using the standard built-in Array constructor for 197 // a Realm that is not the Realm of the running execution context, then a new Array is 198 // created using the Realm of the running execution context. This maintains compatibility 199 // with Web browsers that have historically had that behaviour for the Array.prototype methods 200 // that now are defined using ArraySpeciesCreate. 201 return result; 202} 203 204JSHandle<TaggedArray> JSSharedArray::SetCapacity(const JSThread *thread, const JSHandle<TaggedArray> &array, 205 uint32_t capa) 206{ 207 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 208 uint32_t oldLength = array->GetLength(); 209 JSHandle<TaggedArray> newArray = factory->CopySArray(array, oldLength, capa); 210 return newArray; 211} 212 213void JSSharedArray::SetCapacity(JSThread *thread, const JSHandle<JSObject> &array, uint32_t oldLen, uint32_t newLen, 214 bool isNew) 215{ 216 TaggedArray *element = TaggedArray::Cast(array->GetElements().GetTaggedObject()); 217 218 if (element->IsDictionaryMode()) { // LCOV_EXCL_START 219 THROW_TYPE_ERROR(thread, "SendableArray don't support dictionary mode."); 220 } // LCOV_EXCL_STOP 221 uint32_t capacity = element->GetLength(); 222 if (newLen <= capacity) { 223 // judge if need to cut down the array size, else fill the unused tail with holes 224 CheckAndCopyArray(thread, JSHandle<JSSharedArray>(array)); 225 array->FillElementsWithHoles(thread, newLen, oldLen < capacity ? oldLen : capacity); 226 } 227 if (newLen > capacity) { 228 JSObject::GrowElementsCapacity(thread, array, newLen, isNew); 229 } 230 JSSharedArray::Cast(*array)->SetArrayLength(thread, newLen); 231} 232 233bool JSSharedArray::ArraySetLength(JSThread *thread, const JSHandle<JSObject> &array, const PropertyDescriptor &desc) 234{ 235 JSHandle<JSTaggedValue> lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString()); 236 237 // 1. If the [[Value]] field of Desc is absent, then 238 if (!desc.HasValue()) { 239 // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc). 240 return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, desc); 241 } 242 // 2. Let newLenDesc be a copy of Desc. 243 // (Actual copying is not necessary.) 244 PropertyDescriptor newLenDesc = desc; 245 // 3. - 7. Convert Desc.[[Value]] to newLen. 246 uint32_t newLen = 0; 247 if (!JSTaggedValue::ToArrayLength(thread, desc.GetValue(), &newLen)) { 248 THROW_RANGE_ERROR_AND_RETURN(thread, "array length must equal or less than 2^32.", false); 249 } 250 // 8. Set newLenDesc.[[Value]] to newLen. 251 // (Done below, if needed.) 252 // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). 253 PropertyDescriptor oldLenDesc(thread); 254 [[maybe_unused]] bool success = GetOwnProperty(thread, array, lengthKeyHandle, oldLenDesc); 255 // 10. (Assert) 256 ASSERT(success); 257 258 // 11. Let oldLen be oldLenDesc.[[Value]]. 259 uint32_t oldLen = 0; 260 JSTaggedValue::ToArrayLength(thread, oldLenDesc.GetValue(), &oldLen); 261 // 12. If newLen >= oldLen, then 262 if (newLen >= oldLen) { 263 // 8. Set newLenDesc.[[Value]] to newLen. 264 // 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc). 265 newLenDesc.SetValue(JSHandle<JSTaggedValue>(thread, JSTaggedValue(newLen))); 266 return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, newLenDesc); 267 } 268 // 13. If oldLenDesc.[[Writable]] is false, return false. 269 if (!oldLenDesc.IsWritable() || 270 // Also handle the {configurable: true} case since we later use 271 // JSSharedArray::SetLength instead of OrdinaryDefineOwnProperty to change 272 // the length, and it doesn't have access to the descriptor anymore. 273 newLenDesc.IsConfigurable() || 274 (newLenDesc.HasEnumerable() && (newLenDesc.IsEnumerable() != oldLenDesc.IsEnumerable()))) { 275 return false; 276 } 277 // 14. If newLenDesc.[[Writable]] is absent or has the value true, 278 // let newWritable be true. 279 bool newWritable = false; 280 if (!newLenDesc.HasWritable() || newLenDesc.IsWritable()) { 281 newWritable = true; 282 } else { 283 // 15. Else, 284 // 15a. Need to defer setting the [[Writable]] attribute to false in case 285 // any elements cannot be deleted. 286 // 15b. Let newWritable be false. (It's initialized as "false" anyway.) 287 // 15c. Set newLenDesc.[[Writable]] to true. 288 // (Not needed.) 289 } 290 291 // Most of steps 16 through 19 is implemented by JSSharedArray::SetCapacity. 292 JSSharedArray::SetCapacity(thread, array, oldLen, newLen); 293 // Steps 19d-ii, 20. 294 if (!newWritable) { // LCOV_EXCL_START 295 PropertyDescriptor readonly(thread); 296 readonly.SetWritable(false); 297 success = JSObject::DefineOwnProperty(thread, array, lengthKeyHandle, readonly); 298 ASSERT_PRINT(success, "DefineOwnProperty of length must be success here!"); 299 } // LCOV_EXCL_STOP 300 301 // Steps 19d-v, 21. Return false if there were non-deletable elements. 302 uint32_t arrayLength = JSSharedArray::Cast(*array)->GetArrayLength(); 303 return arrayLength == newLen; 304} 305 306bool JSSharedArray::PropertyKeyToArrayIndex(JSThread *thread, const JSHandle<JSTaggedValue> &key, uint32_t *output) 307{ 308 return JSTaggedValue::ToArrayLength(thread, key, output) && *output <= JSSharedArray::MAX_ARRAY_INDEX; 309} 310 311// 9.4.2.1 [[DefineOwnProperty]] ( P, Desc) 312bool JSSharedArray::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &array, 313 const JSHandle<JSTaggedValue> &key, const PropertyDescriptor &desc, 314 SCheckMode sCheckMode) 315{ 316 if (!desc.GetValue()->IsSharedType() || (desc.HasGetter() && !desc.GetGetter()->IsSharedType()) || 317 (desc.HasSetter() && !desc.GetSetter()->IsSharedType())) { 318 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::TYPE_ERROR, 319 "Parameter error. Only accept sendable value."); 320 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); 321 } 322 323 if (sCheckMode == SCheckMode::CHECK && !(JSSharedArray::Cast(*array)->IsKeyInRange(key))) { 324 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR, 325 "Key out of length."); 326 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); 327 } 328 329 // 1. Assert: IsPropertyKey(P) is true. 330 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key!"); 331 // 2. If P is "length", then 332 if (IsLengthString(thread, key)) { 333 // a. Return ArraySetLength(A, Desc). 334 return ArraySetLength(thread, array, desc); 335 } 336 337 // 3. Else if P is an array index, then 338 // already do in step 4. 339 // 4. Return OrdinaryDefineOwnProperty(A, P, Desc). 340 bool success = JSObject::OrdinaryDefineOwnProperty(thread, array, key, desc); 341 if (success) { 342 JSTaggedValue constructorKey = thread->GlobalConstants()->GetConstructorString(); 343 if (key.GetTaggedValue() == constructorKey) { 344 array->GetJSHClass()->SetHasConstructor(true); 345 return true; 346 } 347 } 348 return success; 349} 350 351bool JSSharedArray::IsLengthString(JSThread *thread, const JSHandle<JSTaggedValue> &key) 352{ 353 return key.GetTaggedValue() == thread->GlobalConstants()->GetLengthString(); 354} 355 356JSHandle<JSSharedArray> JSSharedArray::CreateArrayFromList(JSThread *thread, const JSHandle<TaggedArray> &elements) 357{ 358 // Assert: elements is a List whose elements are all ECMAScript language values. 359 // 2. Let array be ArrayCreate(0). 360 uint32_t length = elements->GetLength(); 361 362 // 4. For each element e of elements 363 auto env = thread->GetEcmaVM()->GetGlobalEnv(); 364 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 365 JSHandle<JSFunction> arrayFunc(env->GetSharedArrayFunction()); 366 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc); 367 JSSharedArray::Cast(*obj)->SetArrayLength(thread, length); 368 obj->SetElements(thread, elements); 369 obj->GetJSHClass()->SetExtensible(false); 370 JSHandle<JSSharedArray> arr(obj); 371 372 return arr; 373} 374 375JSHandle<JSTaggedValue> JSSharedArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 376 uint32_t index) 377{ 378 auto result = ObjectFastOperator::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), index); 379 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 380 return JSHandle<JSTaggedValue>(thread, result); 381} 382 383JSHandle<JSTaggedValue> JSSharedArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 384 const JSHandle<JSTaggedValue> &key, SCheckMode sCheckMode) 385{ 386 auto result = 387 ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(), sCheckMode); 388 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 389 return JSHandle<JSTaggedValue>(thread, result); 390} 391 392bool JSSharedArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index, 393 const JSHandle<JSTaggedValue> &value) 394{ 395 return ObjectFastOperator::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue()); 396} 397 398bool JSSharedArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 399 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value) 400{ 401 return ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(), 402 value.GetTaggedValue(), SCheckMode::SKIP); 403} 404 405OperationResult JSSharedArray::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 406 const JSHandle<JSTaggedValue> &key, SCheckMode sCheckMode) 407{ 408 // Add Concurrent check for shared array 409 [[maybe_unused]] ConcurrentApiScope<JSSharedArray> scope(thread, obj, 410 sCheckMode); 411 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 412 OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); 413 414 ObjectOperator op(thread, obj, key); 415 // Out of bounds check for shared array 416 if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) { 417 return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(false)); 418 } 419 return OperationResult(thread, JSObject::GetProperty(thread, &op), PropertyMetaData(op.IsFound())); 420} 421 422bool JSSharedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 423 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value, bool mayThrow, 424 SCheckMode sCheckMode) 425{ 426 // Concurrent check for shared array 427 [[maybe_unused]] ConcurrentApiScope<JSSharedArray, ModType::WRITE> scope( 428 thread, obj, sCheckMode); 429 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); 430 // 2 ~ 4 findProperty in Receiver, Obj and its parents 431 ObjectOperator op(thread, obj, key); 432 // Out of bounds check for shared array 433 if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) { 434 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR, 435 "The value of index is out of range."); 436 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); 437 } 438 return JSObject::SetProperty(&op, value, mayThrow); 439} 440 441bool JSSharedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 442 uint32_t index, const JSHandle<JSTaggedValue> &value, bool mayThrow, 443 SCheckMode sCheckMode) 444{ 445 // Concurrent check for shared array 446 [[maybe_unused]] ConcurrentApiScope<JSSharedArray, ModType::WRITE> scope( 447 thread, obj, sCheckMode); 448 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); 449 // 2 ~ 4 findProperty in Receiver, Obj and its parents 450 ObjectOperator op(thread, obj, index); 451 // Out of bounds check for shared array 452 if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) { 453 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR, 454 "The value of index is out of range."); 455 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); 456 } 457 return JSObject::SetProperty(&op, value, mayThrow); 458} 459 460// ecma2024 23.1.3.20 Array.prototype.sort(comparefn) 461JSTaggedValue JSSharedArray::Sort(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 462 const JSHandle<JSTaggedValue> &fn) 463{ 464 ASSERT(fn->IsUndefined() || fn->IsCallable()); 465 // 3. Let len be ?LengthOfArrayLike(obj). 466 int64_t len = ArrayHelper::GetArrayLength(thread, obj); 467 // ReturnIfAbrupt(len). 468 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 469 // If len is 0 or 1, no need to sort 470 if (len == 0 || len == 1) { 471 return obj.GetTaggedValue(); 472 } 473 474 // 4. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs 475 // the following steps when called: 476 // a. Return ? CompareArrayElements(x, y, comparefn). 477 // 5. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, SKIP-HOLES). 478 JSHandle<TaggedArray> sortedList = 479 ArrayHelper::SortIndexedProperties(thread, obj, len, fn, base::HolesType::SKIP_HOLES); 480 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 481 // 6. Let itemCount be the number of elements in sortedList. 482 uint32_t itemCount = sortedList->GetLength(); 483 484 // 7. Let j be 0. 485 uint32_t j = 0; 486 // 8. Repeat, while j < itemCount, 487 // a. Perform ! Set(obj, ! ToString((j)), sortedList[j], true). 488 // b. Set j to j + 1. 489 JSMutableHandle<JSTaggedValue> item(thread, JSTaggedValue::Undefined()); 490 while (j < itemCount) { 491 item.Update(sortedList->Get(j)); 492 JSSharedArray::FastSetPropertyByValue(thread, obj, j, item); 493 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 494 ++j; 495 } 496 // 9. NOTE: The call to SortIndexedProperties in step 5 uses SKIP-HOLES.The remaining indices are deleted to 497 // preserve the number of holes that were detected and excluded from the sort. 498 // 10. Repeat, while j < len, 499 // a. Perform ? DeletePropertyOrThrow(obj, ! ToString((j))). 500 // b. Set j to j + 1. 501 while (j < len) { 502 item.Update(JSTaggedValue(j)); 503 JSTaggedValue::DeletePropertyOrThrow(thread, obj, item); 504 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 505 ++j; 506 } 507 508 return obj.GetTaggedValue(); 509} 510 511bool JSSharedArray::IncludeInSortedValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 512 const JSHandle<JSTaggedValue> &value) 513{ 514 ASSERT(obj->IsJSSharedArray()); 515 JSHandle<JSSharedArray> arrayObj = JSHandle<JSSharedArray>::Cast(obj); 516 int32_t length = static_cast<int32_t>(arrayObj->GetArrayLength()); 517 if (length == 0) { 518 return false; 519 } 520 int32_t left = 0; 521 int32_t right = length - 1; 522 while (left <= right) { 523 int32_t middle = (left + right) / 2; 524 JSHandle<JSTaggedValue> vv = JSSharedArray::FastGetPropertyByValue(thread, obj, middle); 525 ComparisonResult res = JSTaggedValue::Compare(thread, vv, value); 526 if (res == ComparisonResult::EQUAL) { 527 return true; 528 } else if (res == ComparisonResult::LESS) { 529 left = middle + 1; 530 } else { 531 right = middle - 1; 532 } 533 } 534 return false; 535} 536 537void JSSharedArray::CheckAndCopyArray(const JSThread *thread, JSHandle<JSSharedArray> obj) 538{ 539 JSHandle<TaggedArray> arr(thread, obj->GetElements()); 540 // Check whether array is shared in the nonmovable space before set properties and elements. 541 // If true, then really copy array in the semi space. 542 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 543 if (arr.GetTaggedValue().IsCOWArray()) { 544 auto newArray = factory->CopyArray(arr, arr->GetLength(), arr->GetLength(), 545 JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE); 546 obj->SetElements(thread, newArray.GetTaggedValue()); 547 } 548 JSHandle<TaggedArray> prop(thread, obj->GetProperties()); 549 if (prop.GetTaggedValue().IsCOWArray()) { 550 auto newProps = factory->CopyArray(prop, prop->GetLength(), prop->GetLength(), 551 JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE); 552 obj->SetProperties(thread, newProps.GetTaggedValue()); 553 } 554} 555 556void JSSharedArray::DeleteInElementMode(const JSThread *thread, JSHandle<JSSharedArray> &obj) 557{ 558 JSHandle<TaggedArray> elements(thread, obj->GetElements()); 559 ASSERT(!obj->GetJSHClass()->IsDictionaryElement()); 560 uint32_t length = elements->GetLength(); 561 // fixme(hzzhouzebin) Optimize Delete later. 562 uint32_t size = 0; 563 for (uint32_t i = 0; i < length; i++) { 564 JSTaggedValue value = ElementAccessor::Get(JSHandle<JSObject>(obj), i); 565 if (value.IsHole()) { 566 continue; 567 } 568 ++size; 569 } 570 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 571 JSHandle<TaggedArray> newElements( 572 factory->NewTaggedArray(length, JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE)); 573 uint32_t newCurr = 0; 574 for (uint32_t i = 0; i < length; i++) { 575 JSTaggedValue value = ElementAccessor::Get(JSHandle<JSObject>(obj), i); 576 if (value.IsHole()) { 577 continue; 578 } 579 newElements->Set(thread, newCurr, value); 580 ++newCurr; 581 } 582 obj->SetElements(thread, newElements); 583} 584} // namespace panda::ecmascript 585