Array.cpp (167880B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "builtin/Array-inl.h" 8 9 #include "mozilla/CheckedInt.h" 10 #include "mozilla/DebugOnly.h" 11 #include "mozilla/MathAlgorithms.h" 12 #include "mozilla/Maybe.h" 13 #include "mozilla/ScopeExit.h" 14 #include "mozilla/SIMD.h" 15 #include "mozilla/TextUtils.h" 16 17 #include <algorithm> 18 #include <cmath> 19 20 #include "jsfriendapi.h" 21 #include "jsnum.h" 22 #include "jstypes.h" 23 24 #include "builtin/SelfHostingDefines.h" 25 #include "ds/Sort.h" 26 #include "jit/InlinableNatives.h" 27 #include "jit/TrampolineNatives.h" 28 #include "js/Class.h" 29 #include "js/Conversions.h" 30 #include "js/experimental/JitInfo.h" // JSJitGetterOp, JSJitInfo 31 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 32 #include "js/PropertySpec.h" 33 #include "util/Poison.h" 34 #include "util/StringBuilder.h" 35 #include "util/Text.h" 36 #include "vm/ArgumentsObject.h" 37 #include "vm/EqualityOperations.h" 38 #include "vm/Interpreter.h" 39 #include "vm/Iteration.h" 40 #include "vm/JSContext.h" 41 #include "vm/JSFunction.h" 42 #include "vm/JSObject.h" 43 #include "vm/PlainObject.h" // js::PlainObject 44 #include "vm/Probes.h" 45 #include "vm/SelfHosting.h" 46 #include "vm/Shape.h" 47 #include "vm/StringType.h" 48 #include "vm/ToSource.h" // js::ValueToSource 49 #include "vm/TypedArrayObject.h" 50 #include "vm/WrapperObject.h" 51 #include "builtin/Sorting-inl.h" 52 #include "vm/ArgumentsObject-inl.h" 53 #include "vm/ArrayObject-inl.h" 54 #include "vm/GeckoProfiler-inl.h" 55 #include "vm/IsGivenTypeObject-inl.h" 56 #include "vm/JSAtomUtils-inl.h" // PrimitiveValueToId, IndexToId 57 #include "vm/NativeObject-inl.h" 58 59 using namespace js; 60 61 using mozilla::Abs; 62 using mozilla::CeilingLog2; 63 using mozilla::CheckedInt; 64 using mozilla::DebugOnly; 65 using mozilla::Maybe; 66 using mozilla::SIMD; 67 68 using JS::AutoCheckCannotGC; 69 using JS::IsArrayAnswer; 70 using JS::ToUint32; 71 72 bool js::ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj) { 73 if (!obj->is<NativeObject>()) { 74 return true; 75 } 76 77 if (obj->as<NativeObject>().isIndexed()) { 78 return true; 79 } 80 81 if (obj->is<TypedArrayObject>()) { 82 return true; 83 } 84 85 return ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames, 86 obj->getClass(), PropertyKey::Int(0), obj); 87 } 88 89 bool js::PrototypeMayHaveIndexedProperties(NativeObject* obj) { 90 do { 91 MOZ_ASSERT(obj->hasStaticPrototype(), 92 "dynamic-prototype objects must be non-native"); 93 94 JSObject* proto = obj->staticPrototype(); 95 if (!proto) { 96 return false; // no extra indexed properties found 97 } 98 99 if (ObjectMayHaveExtraIndexedOwnProperties(proto)) { 100 return true; 101 } 102 obj = &proto->as<NativeObject>(); 103 if (obj->getDenseInitializedLength() != 0) { 104 return true; 105 } 106 } while (true); 107 } 108 109 /* 110 * Whether obj may have indexed properties anywhere besides its dense 111 * elements. This includes other indexed properties in its shape hierarchy, and 112 * indexed properties or elements along its prototype chain. 113 */ 114 bool js::ObjectMayHaveExtraIndexedProperties(JSObject* obj) { 115 MOZ_ASSERT_IF(obj->hasDynamicPrototype(), !obj->is<NativeObject>()); 116 117 if (ObjectMayHaveExtraIndexedOwnProperties(obj)) { 118 return true; 119 } 120 121 return PrototypeMayHaveIndexedProperties(&obj->as<NativeObject>()); 122 } 123 124 bool JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer) { 125 if (obj->is<ArrayObject>()) { 126 *answer = IsArrayAnswer::Array; 127 return true; 128 } 129 130 if (obj->is<ProxyObject>()) { 131 return Proxy::isArray(cx, obj, answer); 132 } 133 134 *answer = IsArrayAnswer::NotArray; 135 return true; 136 } 137 138 bool JS::IsArray(JSContext* cx, HandleObject obj, bool* isArray) { 139 IsArrayAnswer answer; 140 if (!IsArray(cx, obj, &answer)) { 141 return false; 142 } 143 144 if (answer == IsArrayAnswer::RevokedProxy) { 145 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 146 JSMSG_PROXY_REVOKED); 147 return false; 148 } 149 150 *isArray = answer == IsArrayAnswer::Array; 151 return true; 152 } 153 154 bool js::IsArrayFromJit(JSContext* cx, HandleObject obj, bool* isArray) { 155 return JS::IsArray(cx, obj, isArray); 156 } 157 158 // ES2017 7.1.15 ToLength. 159 bool js::ToLength(JSContext* cx, HandleValue v, uint64_t* out) { 160 if (v.isInt32()) { 161 int32_t i = v.toInt32(); 162 *out = i < 0 ? 0 : i; 163 return true; 164 } 165 166 double d; 167 if (v.isDouble()) { 168 d = v.toDouble(); 169 } else { 170 if (!ToNumber(cx, v, &d)) { 171 return false; 172 } 173 } 174 175 d = JS::ToInteger(d); 176 if (d <= 0.0) { 177 *out = 0; 178 } else { 179 *out = uint64_t(std::min(d, DOUBLE_INTEGRAL_PRECISION_LIMIT - 1)); 180 } 181 return true; 182 } 183 184 bool js::GetLengthProperty(JSContext* cx, HandleObject obj, uint64_t* lengthp) { 185 if (obj->is<ArrayObject>()) { 186 *lengthp = obj->as<ArrayObject>().length(); 187 return true; 188 } 189 190 if (obj->is<ArgumentsObject>()) { 191 ArgumentsObject& argsobj = obj->as<ArgumentsObject>(); 192 if (!argsobj.hasOverriddenLength()) { 193 *lengthp = argsobj.initialLength(); 194 return true; 195 } 196 } 197 198 RootedValue value(cx); 199 if (!GetProperty(cx, obj, obj, cx->names().length, &value)) { 200 return false; 201 } 202 203 return ToLength(cx, value, lengthp); 204 } 205 206 // Fast path for array functions where the object is expected to be an array. 207 static MOZ_ALWAYS_INLINE bool GetLengthPropertyInlined(JSContext* cx, 208 HandleObject obj, 209 uint64_t* lengthp) { 210 if (obj->is<ArrayObject>()) { 211 *lengthp = obj->as<ArrayObject>().length(); 212 return true; 213 } 214 215 return GetLengthProperty(cx, obj, lengthp); 216 } 217 218 /* 219 * Determine if the id represents an array index. 220 * 221 * An id is an array index according to ECMA by (15.4): 222 * 223 * "Array objects give special treatment to a certain class of property names. 224 * A property name P (in the form of a string value) is an array index if and 225 * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal 226 * to 2^32-1." 227 * 228 * This means the largest allowed index is actually 2^32-2 (4294967294). 229 * 230 * In our implementation, it would be sufficient to check for id.isInt32() 231 * except that by using signed 31-bit integers we miss the top half of the 232 * valid range. This function checks the string representation itself; note 233 * that calling a standard conversion routine might allow strings such as 234 * "08" or "4.0" as array indices, which they are not. 235 * 236 */ 237 JS_PUBLIC_API bool js::StringIsArrayIndex(const JSLinearString* str, 238 uint32_t* indexp) { 239 if (!str->isIndex(indexp)) { 240 return false; 241 } 242 MOZ_ASSERT(*indexp <= MAX_ARRAY_INDEX); 243 return true; 244 } 245 246 JS_PUBLIC_API bool js::StringIsArrayIndex(const char16_t* str, uint32_t length, 247 uint32_t* indexp) { 248 if (length == 0 || length > UINT32_CHAR_BUFFER_LENGTH) { 249 return false; 250 } 251 if (!mozilla::IsAsciiDigit(str[0])) { 252 return false; 253 } 254 if (!CheckStringIsIndex(str, length, indexp)) { 255 return false; 256 } 257 MOZ_ASSERT(*indexp <= MAX_ARRAY_INDEX); 258 return true; 259 } 260 261 /* 262 * If the property at the given index exists, get its value into |vp| and set 263 * |*hole| to false. Otherwise set |*hole| to true and |vp| to Undefined. 264 */ 265 template <typename T> 266 static bool HasAndGetElement(JSContext* cx, HandleObject obj, 267 HandleObject receiver, T index, bool* hole, 268 MutableHandleValue vp) { 269 if (obj->is<NativeObject>()) { 270 NativeObject* nobj = &obj->as<NativeObject>(); 271 if (index < nobj->getDenseInitializedLength()) { 272 vp.set(nobj->getDenseElement(size_t(index))); 273 if (!vp.isMagic(JS_ELEMENTS_HOLE)) { 274 *hole = false; 275 return true; 276 } 277 } 278 if (nobj->is<ArgumentsObject>() && index <= UINT32_MAX) { 279 if (nobj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) { 280 *hole = false; 281 return true; 282 } 283 } 284 } 285 286 RootedId id(cx); 287 if (!IndexToId(cx, index, &id)) { 288 return false; 289 } 290 291 bool found; 292 if (!HasProperty(cx, obj, id, &found)) { 293 return false; 294 } 295 296 if (found) { 297 if (!GetProperty(cx, obj, receiver, id, vp)) { 298 return false; 299 } 300 } else { 301 vp.setUndefined(); 302 } 303 *hole = !found; 304 return true; 305 } 306 307 template <typename T> 308 static inline bool HasAndGetElement(JSContext* cx, HandleObject obj, T index, 309 bool* hole, MutableHandleValue vp) { 310 return HasAndGetElement(cx, obj, obj, index, hole, vp); 311 } 312 313 bool js::HasAndGetElement(JSContext* cx, HandleObject obj, uint64_t index, 314 bool* hole, MutableHandleValue vp) { 315 return HasAndGetElement(cx, obj, obj, index, hole, vp); 316 } 317 318 bool ElementAdder::append(JSContext* cx, HandleValue v) { 319 MOZ_ASSERT(index_ < length_); 320 if (resObj_) { 321 NativeObject* resObj = &resObj_->as<NativeObject>(); 322 DenseElementResult result = 323 resObj->setOrExtendDenseElements(cx, index_, v.address(), 1); 324 if (result == DenseElementResult::Failure) { 325 return false; 326 } 327 if (result == DenseElementResult::Incomplete) { 328 if (!DefineDataElement(cx, resObj_, index_, v)) { 329 return false; 330 } 331 } 332 } else { 333 vp_[index_] = v; 334 } 335 index_++; 336 return true; 337 } 338 339 void ElementAdder::appendHole() { 340 MOZ_ASSERT(getBehavior_ == ElementAdder::CheckHasElemPreserveHoles); 341 MOZ_ASSERT(index_ < length_); 342 if (!resObj_) { 343 vp_[index_].setMagic(JS_ELEMENTS_HOLE); 344 } 345 index_++; 346 } 347 348 bool js::GetElementsWithAdder(JSContext* cx, HandleObject obj, 349 HandleObject receiver, uint32_t begin, 350 uint32_t end, ElementAdder* adder) { 351 MOZ_ASSERT(begin <= end); 352 353 RootedValue val(cx); 354 for (uint32_t i = begin; i < end; i++) { 355 if (adder->getBehavior() == ElementAdder::CheckHasElemPreserveHoles) { 356 bool hole; 357 if (!HasAndGetElement(cx, obj, receiver, i, &hole, &val)) { 358 return false; 359 } 360 if (hole) { 361 adder->appendHole(); 362 continue; 363 } 364 } else { 365 MOZ_ASSERT(adder->getBehavior() == ElementAdder::GetElement); 366 if (!GetElement(cx, obj, receiver, i, &val)) { 367 return false; 368 } 369 } 370 if (!adder->append(cx, val)) { 371 return false; 372 } 373 } 374 375 return true; 376 } 377 378 static inline bool IsPackedArrayOrNoExtraIndexedProperties(JSObject* obj, 379 uint64_t length) { 380 return (IsPackedArray(obj) && obj->as<ArrayObject>().length() == length) || 381 !ObjectMayHaveExtraIndexedProperties(obj); 382 } 383 384 static bool GetDenseElements(NativeObject* aobj, uint32_t length, Value* vp) { 385 MOZ_ASSERT(IsPackedArrayOrNoExtraIndexedProperties(aobj, length)); 386 387 if (length > aobj->getDenseInitializedLength()) { 388 return false; 389 } 390 391 for (size_t i = 0; i < length; i++) { 392 vp[i] = aobj->getDenseElement(i); 393 394 // No other indexed properties so hole => undefined. 395 if (vp[i].isMagic(JS_ELEMENTS_HOLE)) { 396 vp[i] = UndefinedValue(); 397 } 398 } 399 400 return true; 401 } 402 403 bool js::GetElements(JSContext* cx, HandleObject aobj, uint32_t length, 404 Value* vp) { 405 if (IsPackedArrayOrNoExtraIndexedProperties(aobj, length)) { 406 if (GetDenseElements(&aobj->as<NativeObject>(), length, vp)) { 407 return true; 408 } 409 } 410 411 if (aobj->is<ArgumentsObject>()) { 412 ArgumentsObject& argsobj = aobj->as<ArgumentsObject>(); 413 if (!argsobj.hasOverriddenLength()) { 414 if (argsobj.maybeGetElements(0, length, vp)) { 415 return true; 416 } 417 } 418 } 419 420 if (aobj->is<TypedArrayObject>()) { 421 Handle<TypedArrayObject*> typedArray = aobj.as<TypedArrayObject>(); 422 if (typedArray->length().valueOr(0) == length) { 423 return TypedArrayObject::getElements(cx, typedArray, length, vp); 424 } 425 } 426 427 if (js::GetElementsOp op = aobj->getOpsGetElements()) { 428 ElementAdder adder(cx, vp, length, ElementAdder::GetElement); 429 return op(cx, aobj, 0, length, &adder); 430 } 431 432 for (uint32_t i = 0; i < length; i++) { 433 if (!GetElement(cx, aobj, aobj, i, 434 MutableHandleValue::fromMarkedLocation(&vp[i]))) { 435 return false; 436 } 437 } 438 439 return true; 440 } 441 442 static inline bool GetArrayElement(JSContext* cx, HandleObject obj, 443 uint64_t index, MutableHandleValue vp) { 444 if (obj->is<NativeObject>()) { 445 NativeObject* nobj = &obj->as<NativeObject>(); 446 if (index < nobj->getDenseInitializedLength()) { 447 vp.set(nobj->getDenseElement(size_t(index))); 448 if (!vp.isMagic(JS_ELEMENTS_HOLE)) { 449 return true; 450 } 451 } 452 453 if (nobj->is<ArgumentsObject>() && index <= UINT32_MAX) { 454 if (nobj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) { 455 return true; 456 } 457 } 458 } 459 460 RootedId id(cx); 461 if (!IndexToId(cx, index, &id)) { 462 return false; 463 } 464 return GetProperty(cx, obj, obj, id, vp); 465 } 466 467 static inline bool DefineArrayElement(JSContext* cx, HandleObject obj, 468 uint64_t index, HandleValue value) { 469 RootedId id(cx); 470 if (!IndexToId(cx, index, &id)) { 471 return false; 472 } 473 return DefineDataProperty(cx, obj, id, value); 474 } 475 476 // Set the value of the property at the given index to v. 477 static inline bool SetArrayElement(JSContext* cx, HandleObject obj, 478 uint64_t index, HandleValue v) { 479 RootedId id(cx); 480 if (!IndexToId(cx, index, &id)) { 481 return false; 482 } 483 return SetProperty(cx, obj, id, v); 484 } 485 486 /* 487 * Attempt to delete the element |index| from |obj| as if by 488 * |obj.[[Delete]](index)|. 489 * 490 * If an error occurs while attempting to delete the element (that is, the call 491 * to [[Delete]] threw), return false. 492 * 493 * Otherwise call result.succeed() or result.fail() to indicate whether the 494 * deletion attempt succeeded (that is, whether the call to [[Delete]] returned 495 * true or false). (Deletes generally fail only when the property is 496 * non-configurable, but proxies may implement different semantics.) 497 */ 498 static bool DeleteArrayElement(JSContext* cx, HandleObject obj, uint64_t index, 499 ObjectOpResult& result) { 500 if (obj->is<ArrayObject>() && !obj->as<NativeObject>().isIndexed() && 501 !obj->as<NativeObject>().denseElementsAreSealed()) { 502 ArrayObject* aobj = &obj->as<ArrayObject>(); 503 if (index <= UINT32_MAX) { 504 uint32_t idx = uint32_t(index); 505 if (idx < aobj->getDenseInitializedLength()) { 506 if (idx + 1 == aobj->getDenseInitializedLength()) { 507 aobj->setDenseInitializedLengthMaybeNonExtensible(cx, idx); 508 } else { 509 aobj->setDenseElementHole(idx); 510 } 511 if (!SuppressDeletedElement(cx, obj, idx)) { 512 return false; 513 } 514 } 515 } 516 517 return result.succeed(); 518 } 519 520 RootedId id(cx); 521 if (!IndexToId(cx, index, &id)) { 522 return false; 523 } 524 return DeleteProperty(cx, obj, id, result); 525 } 526 527 /* ES6 draft rev 32 (2 Febr 2015) 7.3.7 */ 528 static bool DeletePropertyOrThrow(JSContext* cx, HandleObject obj, 529 uint64_t index) { 530 ObjectOpResult success; 531 if (!DeleteArrayElement(cx, obj, index, success)) { 532 return false; 533 } 534 if (!success) { 535 RootedId id(cx); 536 if (!IndexToId(cx, index, &id)) { 537 return false; 538 } 539 return success.reportError(cx, obj, id); 540 } 541 return true; 542 } 543 544 static bool DeletePropertiesOrThrow(JSContext* cx, HandleObject obj, 545 uint64_t len, uint64_t finalLength) { 546 if (obj->is<ArrayObject>() && !obj->as<NativeObject>().isIndexed() && 547 !obj->as<NativeObject>().denseElementsAreSealed()) { 548 if (len <= UINT32_MAX) { 549 // Skip forward to the initialized elements of this array. 550 len = std::min(uint32_t(len), 551 obj->as<ArrayObject>().getDenseInitializedLength()); 552 } 553 } 554 555 for (uint64_t k = len; k > finalLength; k--) { 556 if (!CheckForInterrupt(cx)) { 557 return false; 558 } 559 560 if (!DeletePropertyOrThrow(cx, obj, k - 1)) { 561 return false; 562 } 563 } 564 return true; 565 } 566 567 static bool SetArrayLengthProperty(JSContext* cx, Handle<ArrayObject*> obj, 568 HandleValue value) { 569 RootedId id(cx, NameToId(cx->names().length)); 570 ObjectOpResult result; 571 if (obj->lengthIsWritable()) { 572 Rooted<PropertyDescriptor> desc( 573 cx, PropertyDescriptor::Data(value, JS::PropertyAttribute::Writable)); 574 if (!ArraySetLength(cx, obj, id, desc, result)) { 575 return false; 576 } 577 } else { 578 MOZ_ALWAYS_TRUE(result.fail(JSMSG_READ_ONLY)); 579 } 580 return result.checkStrict(cx, obj, id); 581 } 582 583 static bool SetLengthProperty(JSContext* cx, HandleObject obj, 584 uint64_t length) { 585 MOZ_ASSERT(length < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)); 586 587 RootedValue v(cx, NumberValue(length)); 588 if (obj->is<ArrayObject>()) { 589 return SetArrayLengthProperty(cx, obj.as<ArrayObject>(), v); 590 } 591 return SetProperty(cx, obj, cx->names().length, v); 592 } 593 594 bool js::SetLengthProperty(JSContext* cx, HandleObject obj, uint32_t length) { 595 RootedValue v(cx, NumberValue(length)); 596 if (obj->is<ArrayObject>()) { 597 return SetArrayLengthProperty(cx, obj.as<ArrayObject>(), v); 598 } 599 return SetProperty(cx, obj, cx->names().length, v); 600 } 601 602 bool js::ArrayLengthGetter(JSContext* cx, HandleObject obj, HandleId id, 603 MutableHandleValue vp) { 604 MOZ_ASSERT(id == NameToId(cx->names().length)); 605 606 vp.setNumber(obj->as<ArrayObject>().length()); 607 return true; 608 } 609 610 bool js::ArrayLengthSetter(JSContext* cx, HandleObject obj, HandleId id, 611 HandleValue v, ObjectOpResult& result) { 612 MOZ_ASSERT(id == NameToId(cx->names().length)); 613 614 Handle<ArrayObject*> arr = obj.as<ArrayObject>(); 615 MOZ_ASSERT(arr->lengthIsWritable(), 616 "setter shouldn't be called if property is non-writable"); 617 618 Rooted<PropertyDescriptor> desc( 619 cx, PropertyDescriptor::Data(v, JS::PropertyAttribute::Writable)); 620 return ArraySetLength(cx, arr, id, desc, result); 621 } 622 623 struct ReverseIndexComparator { 624 bool operator()(const uint32_t& a, const uint32_t& b, bool* lessOrEqualp) { 625 MOZ_ASSERT(a != b, "how'd we get duplicate indexes?"); 626 *lessOrEqualp = b <= a; 627 return true; 628 } 629 }; 630 631 // Fast path to remove all elements with index >= newLen when setting the 632 // .length property of an array to a smaller value. 633 static bool TryFastDeleteElementsForNewLength(JSContext* cx, 634 Handle<ArrayObject*> arr, 635 uint32_t newLen, bool* success) { 636 MOZ_ASSERT(newLen < arr->length()); 637 638 // If there might be an active for-in iterator for this array we have to use 639 // the generic code path because it supports suppressing deleted properties. 640 // Keys deleted before being reached during the iteration must not be visited. 641 if (arr->denseElementsMaybeInIteration()) { 642 *success = false; 643 return true; 644 } 645 646 // Sealed elements are non-configurable and shouldn't be removed. 647 if (arr->denseElementsAreSealed()) { 648 *success = false; 649 return true; 650 } 651 652 if (arr->isIndexed()) { 653 // This fast path doesn't suppress deleted properties from active iterators. 654 if (arr->compartment()->objectMaybeInIteration(arr)) { 655 *success = false; 656 return true; 657 } 658 659 // First add all sparse indexes we want to remove to a vector and check for 660 // non-configurable elements. 661 JS::RootedVector<PropertyKey> keys(cx); 662 for (ShapePropertyIter<NoGC> iter(arr->shape()); !iter.done(); iter++) { 663 uint32_t index; 664 if (!IdIsIndex(iter->key(), &index)) { 665 continue; 666 } 667 if (index < newLen) { 668 continue; 669 } 670 // Non-configurable elements shouldn't be removed. 671 if (!iter->configurable()) { 672 *success = false; 673 return true; 674 } 675 if (!keys.append(iter->key())) { 676 return false; 677 } 678 } 679 680 // Remove the sparse elements. Note that this starts at the most recently 681 // added property because this is most efficient when removing many 682 // elements. 683 // 684 // The rest of this function must be infallible (other than OOM). 685 for (size_t i = 0, len = keys.length(); i < len; i++) { 686 MOZ_ASSERT(arr->containsPure(keys[i]), "must still be a sparse element"); 687 if (!NativeObject::removeProperty(cx, arr, keys[i])) { 688 MOZ_ASSERT(cx->isThrowingOutOfMemory()); 689 return false; 690 } 691 } 692 } 693 694 // Remove dense elements. 695 uint32_t oldCapacity = arr->getDenseCapacity(); 696 uint32_t oldInitializedLength = arr->getDenseInitializedLength(); 697 MOZ_ASSERT(oldCapacity >= oldInitializedLength); 698 if (oldInitializedLength > newLen) { 699 arr->setDenseInitializedLengthMaybeNonExtensible(cx, newLen); 700 } 701 if (oldCapacity > newLen) { 702 if (arr->isExtensible()) { 703 arr->shrinkElements(cx, newLen); 704 } else { 705 MOZ_ASSERT(arr->getDenseInitializedLength() == arr->getDenseCapacity()); 706 } 707 } 708 709 *success = true; 710 return true; 711 } 712 713 /* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */ 714 bool js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id, 715 Handle<PropertyDescriptor> desc, 716 ObjectOpResult& result) { 717 MOZ_ASSERT(id == NameToId(cx->names().length)); 718 MOZ_ASSERT(desc.isDataDescriptor() || desc.isGenericDescriptor()); 719 720 // Step 1. 721 uint32_t newLen; 722 if (!desc.hasValue()) { 723 // The spec has us calling OrdinaryDefineOwnProperty if 724 // Desc.[[Value]] is absent, but our implementation is so different that 725 // this is impossible. Instead, set newLen to the current length and 726 // proceed to step 9. 727 newLen = arr->length(); 728 } else { 729 // Step 2 is irrelevant in our implementation. 730 731 // Step 3. 732 if (!ToUint32(cx, desc.value(), &newLen)) { 733 return false; 734 } 735 736 // Step 4. 737 double d; 738 if (!ToNumber(cx, desc.value(), &d)) { 739 return false; 740 } 741 742 // Step 5. 743 if (d != newLen) { 744 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 745 JSMSG_BAD_ARRAY_LENGTH); 746 return false; 747 } 748 749 // Steps 6-8 are irrelevant in our implementation. 750 } 751 752 // Steps 9-11. 753 bool lengthIsWritable = arr->lengthIsWritable(); 754 #ifdef DEBUG 755 { 756 mozilla::Maybe<PropertyInfo> lengthProp = arr->lookupPure(id); 757 MOZ_ASSERT(lengthProp.isSome()); 758 MOZ_ASSERT(lengthProp->writable() == lengthIsWritable); 759 } 760 #endif 761 uint32_t oldLen = arr->length(); 762 763 // Part of steps 1.a, 12.a, and 16: Fail if we're being asked to change 764 // enumerability or configurability, or otherwise break the object 765 // invariants. (ES6 checks these by calling OrdinaryDefineOwnProperty, but 766 // in SM, the array length property is hardly ordinary.) 767 if ((desc.hasConfigurable() && desc.configurable()) || 768 (desc.hasEnumerable() && desc.enumerable()) || 769 (!lengthIsWritable && desc.hasWritable() && desc.writable())) { 770 return result.fail(JSMSG_CANT_REDEFINE_PROP); 771 } 772 773 // Steps 12-13 for arrays with non-writable length. 774 if (!lengthIsWritable) { 775 if (newLen == oldLen) { 776 return result.succeed(); 777 } 778 779 return result.fail(JSMSG_CANT_REDEFINE_ARRAY_LENGTH); 780 } 781 782 // Step 19. 783 bool succeeded = true; 784 do { 785 // The initialized length and capacity of an array only need updating 786 // when non-hole elements are added or removed, which doesn't happen 787 // when array length stays the same or increases. 788 if (newLen >= oldLen) { 789 break; 790 } 791 792 bool success; 793 if (!TryFastDeleteElementsForNewLength(cx, arr, newLen, &success)) { 794 return false; 795 } 796 797 if (success) { 798 // We've done the work of deleting any elements needing deletion. 799 // Thus we can skip straight to defining the length. 800 break; 801 } 802 803 // Step 15. 804 // 805 // Attempt to delete all elements above the new length, from greatest 806 // to least. If any of these deletions fails, we're supposed to define 807 // the length to one greater than the index that couldn't be deleted, 808 // *with the property attributes specified*. This might convert the 809 // length to be not the value specified, yet non-writable. (You may be 810 // forgiven for thinking these are interesting semantics.) Example: 811 // 812 // var arr = 813 // Object.defineProperty([0, 1, 2, 3], 1, { writable: false }); 814 // Object.defineProperty(arr, "length", 815 // { value: 0, writable: false }); 816 // 817 // will convert |arr| to an array of non-writable length two, then 818 // throw a TypeError. 819 // 820 // We implement this behavior, in the relevant lops below, by setting 821 // |succeeded| to false. Then we exit the loop, define the length 822 // appropriately, and only then throw a TypeError, if necessary. 823 uint32_t gap = oldLen - newLen; 824 const uint32_t RemoveElementsFastLimit = 1 << 24; 825 if (gap < RemoveElementsFastLimit) { 826 // If we're removing a relatively small number of elements, just do 827 // it exactly by the spec. 828 while (newLen < oldLen) { 829 // Step 15a. 830 oldLen--; 831 832 // Steps 15b-d. 833 ObjectOpResult deleteSucceeded; 834 if (!DeleteElement(cx, arr, oldLen, deleteSucceeded)) { 835 return false; 836 } 837 if (!deleteSucceeded) { 838 newLen = oldLen + 1; 839 succeeded = false; 840 break; 841 } 842 } 843 } else { 844 // If we're removing a large number of elements from an array 845 // that's probably sparse, try a different tack. Get all the own 846 // property names, sift out the indexes in the deletion range into 847 // a vector, sort the vector greatest to least, then delete the 848 // indexes greatest to least using that vector. See bug 322135. 849 // 850 // This heuristic's kind of a huge guess -- "large number of 851 // elements" and "probably sparse" are completely unprincipled 852 // predictions. In the long run, bug 586842 will support the right 853 // fix: store sparse elements in a sorted data structure that 854 // permits fast in-reverse-order traversal and concurrent removals. 855 856 Vector<uint32_t> indexes(cx); 857 { 858 RootedIdVector props(cx); 859 if (!GetPropertyKeys(cx, arr, JSITER_OWNONLY | JSITER_HIDDEN, &props)) { 860 return false; 861 } 862 863 for (size_t i = 0; i < props.length(); i++) { 864 if (!CheckForInterrupt(cx)) { 865 return false; 866 } 867 868 uint32_t index; 869 if (!IdIsIndex(props[i], &index)) { 870 continue; 871 } 872 873 if (index >= newLen && index < oldLen) { 874 if (!indexes.append(index)) { 875 return false; 876 } 877 } 878 } 879 } 880 881 uint32_t count = indexes.length(); 882 { 883 // We should use radix sort to be O(n), but this is uncommon 884 // enough that we'll punt til someone complains. 885 Vector<uint32_t> scratch(cx); 886 if (!scratch.resize(count)) { 887 return false; 888 } 889 MOZ_ALWAYS_TRUE(MergeSort(indexes.begin(), count, scratch.begin(), 890 ReverseIndexComparator())); 891 } 892 893 uint32_t index = UINT32_MAX; 894 for (uint32_t i = 0; i < count; i++) { 895 MOZ_ASSERT(indexes[i] < index, "indexes should never repeat"); 896 index = indexes[i]; 897 898 // Steps 15b-d. 899 ObjectOpResult deleteSucceeded; 900 if (!DeleteElement(cx, arr, index, deleteSucceeded)) { 901 return false; 902 } 903 if (!deleteSucceeded) { 904 newLen = index + 1; 905 succeeded = false; 906 break; 907 } 908 } 909 } 910 } while (false); 911 912 // Update array length. Technically we should have been doing this 913 // throughout the loop, in step 19.d.iii. 914 arr->setLength(cx, newLen); 915 916 // Step 20. 917 if (desc.hasWritable() && !desc.writable()) { 918 Maybe<PropertyInfo> lengthProp = arr->lookup(cx, id); 919 MOZ_ASSERT(lengthProp.isSome()); 920 MOZ_ASSERT(lengthProp->isCustomDataProperty()); 921 PropertyFlags flags = lengthProp->flags(); 922 flags.clearFlag(PropertyFlag::Writable); 923 if (!NativeObject::changeCustomDataPropAttributes(cx, arr, id, flags)) { 924 return false; 925 } 926 } 927 928 // All operations past here until the |!succeeded| code must be infallible, 929 // so that all element fields remain properly synchronized. 930 931 // Trim the initialized length, if needed, to preserve the <= length 932 // invariant. (Capacity was already reduced during element deletion, if 933 // necessary.) 934 ObjectElements* header = arr->getElementsHeader(); 935 header->initializedLength = std::min(header->initializedLength, newLen); 936 937 if (!arr->isExtensible()) { 938 arr->shrinkCapacityToInitializedLength(cx); 939 } 940 941 if (desc.hasWritable() && !desc.writable()) { 942 arr->setNonWritableLength(cx); 943 } 944 945 if (!succeeded) { 946 return result.fail(JSMSG_CANT_TRUNCATE_ARRAY); 947 } 948 949 return result.succeed(); 950 } 951 952 static bool array_addProperty(JSContext* cx, HandleObject obj, HandleId id, 953 HandleValue v) { 954 ArrayObject* arr = &obj->as<ArrayObject>(); 955 956 uint32_t index; 957 if (!IdIsIndex(id, &index)) { 958 return true; 959 } 960 961 uint32_t length = arr->length(); 962 if (index >= length) { 963 MOZ_ASSERT(arr->lengthIsWritable(), 964 "how'd this element get added if length is non-writable?"); 965 arr->setLength(cx, index + 1); 966 } 967 return true; 968 } 969 970 static SharedShape* AddLengthProperty(JSContext* cx, 971 Handle<SharedShape*> shape) { 972 // Add the 'length' property for a newly created array shape. 973 974 MOZ_ASSERT(shape->propMapLength() == 0); 975 MOZ_ASSERT(shape->getObjectClass() == &ArrayObject::class_); 976 977 RootedId lengthId(cx, NameToId(cx->names().length)); 978 constexpr PropertyFlags flags = {PropertyFlag::CustomDataProperty, 979 PropertyFlag::Writable}; 980 981 Rooted<SharedPropMap*> map(cx, shape->propMap()); 982 uint32_t mapLength = shape->propMapLength(); 983 ObjectFlags objectFlags = shape->objectFlags(); 984 985 if (!SharedPropMap::addCustomDataProperty(cx, &ArrayObject::class_, &map, 986 &mapLength, lengthId, flags, 987 &objectFlags)) { 988 return nullptr; 989 } 990 991 return SharedShape::getPropMapShape(cx, shape->base(), shape->numFixedSlots(), 992 map, mapLength, objectFlags); 993 } 994 995 bool js::IsArrayConstructor(const JSObject* obj) { 996 // Note: this also returns true for cross-realm Array constructors in the 997 // same compartment. 998 return IsNativeFunction(obj, ArrayConstructor); 999 } 1000 1001 static bool IsArrayConstructor(const Value& v) { 1002 return v.isObject() && IsArrayConstructor(&v.toObject()); 1003 } 1004 1005 bool js::IsCrossRealmArrayConstructor(JSContext* cx, JSObject* obj, 1006 bool* result) { 1007 if (obj->is<WrapperObject>()) { 1008 obj = CheckedUnwrapDynamic(obj, cx); 1009 if (!obj) { 1010 ReportAccessDenied(cx); 1011 return false; 1012 } 1013 } 1014 1015 *result = 1016 IsArrayConstructor(obj) && obj->as<JSFunction>().realm() != cx->realm(); 1017 return true; 1018 } 1019 1020 static MOZ_ALWAYS_INLINE bool HasBuiltinArraySpecies(ArrayObject* arr, 1021 JSContext* cx) { 1022 // Ensure `Array.prototype.constructor` and `Array[@@species]` haven't been 1023 // mutated. 1024 if (!cx->realm()->realmFuses.optimizeArraySpeciesFuse.intact()) { 1025 return false; 1026 } 1027 1028 // Ensure the array has `Array.prototype` as prototype and doesn't have an own 1029 // `constructor` property. 1030 // 1031 // Most arrays have the default array shape so we have a fast path for this 1032 // case. 1033 GlobalObject* global = cx->global(); 1034 if (arr->shape() == global->maybeArrayShapeWithDefaultProto()) { 1035 return true; 1036 } 1037 1038 // Ensure the array's prototype is the actual Array.prototype. 1039 NativeObject* arrayProto = global->maybeGetArrayPrototype(); 1040 if (!arrayProto || arr->staticPrototype() != arrayProto) { 1041 return false; 1042 } 1043 1044 // Fail if the array has an own `constructor` property. 1045 if (arr->containsPure(NameToId(cx->names().constructor))) { 1046 return false; 1047 } 1048 1049 return true; 1050 } 1051 1052 // Returns true iff we know for -sure- that it is definitely safe to use the 1053 // realm's array constructor. 1054 // 1055 // This function is conservative as it may return false for cases which 1056 // ultimately do use the array constructor. 1057 static MOZ_ALWAYS_INLINE bool IsArraySpecies(JSContext* cx, 1058 HandleObject origArray) { 1059 if (MOZ_UNLIKELY(origArray->is<ProxyObject>())) { 1060 if (origArray->getClass()->isDOMClass()) { 1061 #ifdef DEBUG 1062 // We assume DOM proxies never return true for IsArray. 1063 IsArrayAnswer answer; 1064 MOZ_ASSERT(Proxy::isArray(cx, origArray, &answer)); 1065 MOZ_ASSERT(answer == IsArrayAnswer::NotArray); 1066 #endif 1067 return true; 1068 } 1069 return false; 1070 } 1071 1072 // 9.4.2.3 Step 4. Non-array objects always use the default constructor. 1073 if (!origArray->is<ArrayObject>()) { 1074 return true; 1075 } 1076 1077 if (HasBuiltinArraySpecies(&origArray->as<ArrayObject>(), cx)) { 1078 return true; 1079 } 1080 1081 Value ctor; 1082 if (!GetPropertyPure(cx, origArray, NameToId(cx->names().constructor), 1083 &ctor)) { 1084 return false; 1085 } 1086 1087 if (!IsArrayConstructor(ctor)) { 1088 return ctor.isUndefined(); 1089 } 1090 1091 // 9.4.2.3 Step 6.c. Use the current realm's constructor if |ctor| is a 1092 // cross-realm Array constructor. 1093 if (cx->realm() != ctor.toObject().as<JSFunction>().realm()) { 1094 return true; 1095 } 1096 1097 jsid speciesId = PropertyKey::Symbol(cx->wellKnownSymbols().species); 1098 JSFunction* getter; 1099 if (!GetGetterPure(cx, &ctor.toObject(), speciesId, &getter)) { 1100 return false; 1101 } 1102 1103 if (!getter) { 1104 return false; 1105 } 1106 1107 return IsSelfHostedFunctionWithName(getter, cx->names().dollar_ArraySpecies_); 1108 } 1109 1110 bool js::intrinsic_CanOptimizeArraySpecies(JSContext* cx, unsigned argc, 1111 Value* vp) { 1112 CallArgs args = CallArgsFromVp(argc, vp); 1113 MOZ_ASSERT(args.length() == 1); 1114 1115 JSObject* obj = &args[0].toObject(); 1116 1117 // Return `true` if this is a plain array with the original array shape and 1118 // an intact array-species fuse. This is the case for at least 98% of all 1119 // calls on Speedometer 3 and JetStream 2. This condition is also simple 1120 // enough to inline efficiently in JIT code. 1121 // 1122 // The shape check implies: 1123 // - The object is an array object. 1124 // - The array belongs to the current realm. 1125 // - The array has (the current realm's) `Array.prototype` as prototype. 1126 // - The array does not define an own `constructor` property. 1127 bool optimizable = 1128 obj->shape() == cx->global()->maybeArrayShapeWithDefaultProto() && 1129 cx->realm()->realmFuses.optimizeArraySpeciesFuse.intact(); 1130 args.rval().setBoolean(optimizable); 1131 return true; 1132 } 1133 1134 static bool ArraySpeciesCreate(JSContext* cx, HandleObject origArray, 1135 uint64_t length, MutableHandleObject arr) { 1136 MOZ_ASSERT(length < DOUBLE_INTEGRAL_PRECISION_LIMIT); 1137 1138 FixedInvokeArgs<2> args(cx); 1139 1140 args[0].setObject(*origArray); 1141 args[1].set(NumberValue(length)); 1142 1143 RootedValue rval(cx); 1144 if (!CallSelfHostedFunction(cx, cx->names().ArraySpeciesCreate, 1145 UndefinedHandleValue, args, &rval)) { 1146 return false; 1147 } 1148 1149 MOZ_ASSERT(rval.isObject()); 1150 arr.set(&rval.toObject()); 1151 return true; 1152 } 1153 1154 JSString* js::ArrayToSource(JSContext* cx, HandleObject obj) { 1155 AutoCycleDetector detector(cx, obj); 1156 if (!detector.init()) { 1157 return nullptr; 1158 } 1159 1160 JSStringBuilder sb(cx); 1161 1162 if (detector.foundCycle()) { 1163 if (!sb.append("[]")) { 1164 return nullptr; 1165 } 1166 return sb.finishString(); 1167 } 1168 1169 if (!sb.append('[')) { 1170 return nullptr; 1171 } 1172 1173 uint64_t length; 1174 if (!GetLengthPropertyInlined(cx, obj, &length)) { 1175 return nullptr; 1176 } 1177 1178 RootedValue elt(cx); 1179 for (uint64_t index = 0; index < length; index++) { 1180 bool hole; 1181 if (!CheckForInterrupt(cx) || 1182 !::HasAndGetElement(cx, obj, index, &hole, &elt)) { 1183 return nullptr; 1184 } 1185 1186 /* Get element's character string. */ 1187 JSString* str; 1188 if (hole) { 1189 str = cx->runtime()->emptyString; 1190 } else { 1191 str = ValueToSource(cx, elt); 1192 if (!str) { 1193 return nullptr; 1194 } 1195 } 1196 1197 /* Append element to buffer. */ 1198 if (!sb.append(str)) { 1199 return nullptr; 1200 } 1201 if (index + 1 != length) { 1202 if (!sb.append(", ")) { 1203 return nullptr; 1204 } 1205 } else if (hole) { 1206 if (!sb.append(',')) { 1207 return nullptr; 1208 } 1209 } 1210 } 1211 1212 /* Finalize the buffer. */ 1213 if (!sb.append(']')) { 1214 return nullptr; 1215 } 1216 1217 return sb.finishString(); 1218 } 1219 1220 static bool array_toSource(JSContext* cx, unsigned argc, Value* vp) { 1221 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "toSource"); 1222 CallArgs args = CallArgsFromVp(argc, vp); 1223 1224 if (!args.thisv().isObject()) { 1225 ReportIncompatible(cx, args); 1226 return false; 1227 } 1228 1229 Rooted<JSObject*> obj(cx, &args.thisv().toObject()); 1230 1231 JSString* str = ArrayToSource(cx, obj); 1232 if (!str) { 1233 return false; 1234 } 1235 1236 args.rval().setString(str); 1237 return true; 1238 } 1239 1240 template <typename SeparatorOp> 1241 static bool ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, 1242 Handle<NativeObject*> obj, uint64_t length, 1243 StringBuilder& sb, uint32_t* numProcessed) { 1244 // This loop handles all elements up to initializedLength. If 1245 // length > initLength we rely on the second loop to add the 1246 // other elements. 1247 MOZ_ASSERT(*numProcessed == 0); 1248 uint64_t initLength = 1249 std::min<uint64_t>(obj->getDenseInitializedLength(), length); 1250 MOZ_ASSERT(initLength <= UINT32_MAX, 1251 "initialized length shouldn't exceed UINT32_MAX"); 1252 uint32_t initLengthClamped = uint32_t(initLength); 1253 while (*numProcessed < initLengthClamped) { 1254 if (!CheckForInterrupt(cx)) { 1255 return false; 1256 } 1257 1258 // Step 7.b. 1259 Value elem = obj->getDenseElement(*numProcessed); 1260 1261 // Steps 7.c-d. 1262 if (elem.isString()) { 1263 if (!sb.append(elem.toString())) { 1264 return false; 1265 } 1266 } else if (elem.isNumber()) { 1267 if (!NumberValueToStringBuilder(elem, sb)) { 1268 return false; 1269 } 1270 } else if (elem.isBoolean()) { 1271 if (!BooleanToStringBuilder(elem.toBoolean(), sb)) { 1272 return false; 1273 } 1274 } else if (elem.isObject() || elem.isSymbol()) { 1275 /* 1276 * Object stringifying could modify the initialized length or make 1277 * the array sparse. Delegate it to a separate loop to keep this 1278 * one tight. 1279 * 1280 * Symbol stringifying is a TypeError, so into the slow path 1281 * with those as well. 1282 */ 1283 break; 1284 } else if (elem.isBigInt()) { 1285 // ToString(bigint) doesn't access bigint.toString or 1286 // anything like that, so it can't mutate the array we're 1287 // walking through, so it *could* be handled here. We don't 1288 // do so yet for reasons of initial-implementation economy. 1289 break; 1290 } else { 1291 MOZ_ASSERT(elem.isMagic(JS_ELEMENTS_HOLE) || elem.isNullOrUndefined()); 1292 } 1293 1294 // Steps 7.a, 7.e. 1295 if (++(*numProcessed) != length && !sepOp(sb)) { 1296 return false; 1297 } 1298 } 1299 1300 // If we processed all dense elements and there are no other extra indexed 1301 // properties, all remaining GetElement operations would return undefined. 1302 // This is used to optimize str.repeat() like uses: 1303 // new Array(1e5).join("foo"). 1304 if (*numProcessed == initLength && initLength < length && 1305 length < UINT32_MAX) { 1306 // initLength < length, so this can't be packed. 1307 MOZ_ASSERT(!ObjectMayHaveExtraIndexedProperties(obj)); 1308 while (*numProcessed < length) { 1309 if (!CheckForInterrupt(cx)) { 1310 return false; 1311 } 1312 1313 #ifdef DEBUG 1314 RootedValue v(cx); 1315 if (!GetArrayElement(cx, obj, *numProcessed, &v)) { 1316 return false; 1317 } 1318 MOZ_ASSERT(v.isUndefined()); 1319 #endif 1320 1321 if (++(*numProcessed) != length && !sepOp(sb)) { 1322 return false; 1323 } 1324 } 1325 } 1326 1327 return true; 1328 } 1329 1330 template <typename SeparatorOp> 1331 static bool ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, 1332 uint64_t length, StringBuilder& sb) { 1333 // Step 6. 1334 uint32_t numProcessed = 0; 1335 1336 if (IsPackedArrayOrNoExtraIndexedProperties(obj, length)) { 1337 if (!ArrayJoinDenseKernel<SeparatorOp>(cx, sepOp, obj.as<NativeObject>(), 1338 length, sb, &numProcessed)) { 1339 return false; 1340 } 1341 } 1342 1343 // Step 7. 1344 if (numProcessed != length) { 1345 RootedValue v(cx); 1346 for (uint64_t i = numProcessed; i < length;) { 1347 if (!CheckForInterrupt(cx)) { 1348 return false; 1349 } 1350 1351 // Step 7.b. 1352 if (!GetArrayElement(cx, obj, i, &v)) { 1353 return false; 1354 } 1355 1356 // Steps 7.c-d. 1357 if (!v.isNullOrUndefined()) { 1358 if (!ValueToStringBuilder(cx, v, sb)) { 1359 return false; 1360 } 1361 } 1362 1363 // Steps 7.a, 7.e. 1364 if (++i != length && !sepOp(sb)) { 1365 return false; 1366 } 1367 } 1368 } 1369 1370 return true; 1371 } 1372 1373 // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce 1374 // 22.1.3.13 Array.prototype.join ( separator ) 1375 bool js::array_join(JSContext* cx, unsigned argc, Value* vp) { 1376 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "join"); 1377 CallArgs args = CallArgsFromVp(argc, vp); 1378 1379 // Step 1. 1380 RootedObject obj(cx, ToObject(cx, args.thisv())); 1381 if (!obj) { 1382 return false; 1383 } 1384 1385 AutoCycleDetector detector(cx, obj); 1386 if (!detector.init()) { 1387 return false; 1388 } 1389 1390 if (detector.foundCycle()) { 1391 args.rval().setString(cx->names().empty_); 1392 return true; 1393 } 1394 1395 // Step 2. 1396 uint64_t length; 1397 if (!GetLengthPropertyInlined(cx, obj, &length)) { 1398 return false; 1399 } 1400 1401 // Steps 3-4. 1402 Rooted<JSLinearString*> sepstr(cx); 1403 if (args.hasDefined(0)) { 1404 JSString* s = ToString<CanGC>(cx, args[0]); 1405 if (!s) { 1406 return false; 1407 } 1408 sepstr = s->ensureLinear(cx); 1409 if (!sepstr) { 1410 return false; 1411 } 1412 } else { 1413 sepstr = cx->names().comma_; 1414 } 1415 1416 // Steps 5-8 (When the length is zero, directly return the empty string). 1417 if (length == 0) { 1418 args.rval().setString(cx->emptyString()); 1419 return true; 1420 } 1421 1422 // An optimized version of a special case of steps 5-8: when length==1 and 1423 // the 0th element is a string, ToString() of that element is a no-op and 1424 // so it can be immediately returned as the result. 1425 if (length == 1 && obj->is<NativeObject>()) { 1426 NativeObject* nobj = &obj->as<NativeObject>(); 1427 if (nobj->getDenseInitializedLength() == 1) { 1428 Value elem0 = nobj->getDenseElement(0); 1429 if (elem0.isString()) { 1430 args.rval().set(elem0); 1431 return true; 1432 } 1433 } 1434 } 1435 1436 // Step 5. 1437 JSStringBuilder sb(cx); 1438 if (sepstr->hasTwoByteChars() && !sb.ensureTwoByteChars()) { 1439 return false; 1440 } 1441 1442 // The separator will be added |length - 1| times, reserve space for that 1443 // so that we don't have to unnecessarily grow the buffer. 1444 size_t seplen = sepstr->length(); 1445 if (seplen > 0) { 1446 if (length > UINT32_MAX) { 1447 ReportAllocationOverflow(cx); 1448 return false; 1449 } 1450 CheckedInt<uint32_t> res = 1451 CheckedInt<uint32_t>(seplen) * (uint32_t(length) - 1); 1452 if (!res.isValid()) { 1453 ReportAllocationOverflow(cx); 1454 return false; 1455 } 1456 1457 if (!sb.reserve(res.value())) { 1458 return false; 1459 } 1460 } 1461 1462 // Various optimized versions of steps 6-7. 1463 if (seplen == 0) { 1464 auto sepOp = [](StringBuilder&) { return true; }; 1465 if (!ArrayJoinKernel(cx, sepOp, obj, length, sb)) { 1466 return false; 1467 } 1468 } else if (seplen == 1) { 1469 char16_t c = sepstr->latin1OrTwoByteChar(0); 1470 if (c <= JSString::MAX_LATIN1_CHAR) { 1471 Latin1Char l1char = Latin1Char(c); 1472 auto sepOp = [l1char](StringBuilder& sb) { return sb.append(l1char); }; 1473 if (!ArrayJoinKernel(cx, sepOp, obj, length, sb)) { 1474 return false; 1475 } 1476 } else { 1477 auto sepOp = [c](StringBuilder& sb) { return sb.append(c); }; 1478 if (!ArrayJoinKernel(cx, sepOp, obj, length, sb)) { 1479 return false; 1480 } 1481 } 1482 } else { 1483 Handle<JSLinearString*> sepHandle = sepstr; 1484 auto sepOp = [sepHandle](StringBuilder& sb) { 1485 return sb.append(sepHandle); 1486 }; 1487 if (!ArrayJoinKernel(cx, sepOp, obj, length, sb)) { 1488 return false; 1489 } 1490 } 1491 1492 // Step 8. 1493 JSString* str = sb.finishString(); 1494 if (!str) { 1495 return false; 1496 } 1497 1498 args.rval().setString(str); 1499 return true; 1500 } 1501 1502 // ES2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f 1503 // 22.1.3.27 Array.prototype.toLocaleString ([ reserved1 [ , reserved2 ] ]) 1504 // ES2017 Intl draft rev 78bbe7d1095f5ff3760ac4017ed366026e4cb276 1505 // 13.4.1 Array.prototype.toLocaleString ([ locales [ , options ]]) 1506 static bool array_toLocaleString(JSContext* cx, unsigned argc, Value* vp) { 1507 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", 1508 "toLocaleString"); 1509 1510 CallArgs args = CallArgsFromVp(argc, vp); 1511 1512 // Step 1 1513 RootedObject obj(cx, ToObject(cx, args.thisv())); 1514 if (!obj) { 1515 return false; 1516 } 1517 1518 // Avoid calling into self-hosted code if the array is empty. 1519 if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() == 0) { 1520 args.rval().setString(cx->names().empty_); 1521 return true; 1522 } 1523 1524 AutoCycleDetector detector(cx, obj); 1525 if (!detector.init()) { 1526 return false; 1527 } 1528 1529 if (detector.foundCycle()) { 1530 args.rval().setString(cx->names().empty_); 1531 return true; 1532 } 1533 1534 FixedInvokeArgs<2> args2(cx); 1535 1536 args2[0].set(args.get(0)); 1537 args2[1].set(args.get(1)); 1538 1539 // Steps 2-10. 1540 RootedValue thisv(cx, ObjectValue(*obj)); 1541 return CallSelfHostedFunction(cx, cx->names().ArrayToLocaleString, thisv, 1542 args2, args.rval()); 1543 } 1544 1545 /* vector must point to rooted memory. */ 1546 static bool SetArrayElements(JSContext* cx, HandleObject obj, uint64_t start, 1547 uint32_t count, const Value* vector) { 1548 MOZ_ASSERT(count <= MAX_ARRAY_INDEX); 1549 MOZ_ASSERT(start + count < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)); 1550 1551 if (count == 0) { 1552 return true; 1553 } 1554 1555 if (!ObjectMayHaveExtraIndexedProperties(obj) && start <= UINT32_MAX) { 1556 NativeObject* nobj = &obj->as<NativeObject>(); 1557 DenseElementResult result = 1558 nobj->setOrExtendDenseElements(cx, uint32_t(start), vector, count); 1559 if (result != DenseElementResult::Incomplete) { 1560 return result == DenseElementResult::Success; 1561 } 1562 } 1563 1564 RootedId id(cx); 1565 const Value* end = vector + count; 1566 while (vector < end) { 1567 if (!CheckForInterrupt(cx)) { 1568 return false; 1569 } 1570 1571 if (!IndexToId(cx, start++, &id)) { 1572 return false; 1573 } 1574 1575 if (!SetProperty(cx, obj, id, HandleValue::fromMarkedLocation(vector++))) { 1576 return false; 1577 } 1578 } 1579 1580 return true; 1581 } 1582 1583 static DenseElementResult ArrayReverseDenseKernel(JSContext* cx, 1584 Handle<NativeObject*> obj, 1585 uint32_t length) { 1586 MOZ_ASSERT(length > 1); 1587 1588 // If there are no elements, we're done. 1589 if (obj->getDenseInitializedLength() == 0) { 1590 return DenseElementResult::Success; 1591 } 1592 1593 if (!obj->isExtensible()) { 1594 return DenseElementResult::Incomplete; 1595 } 1596 1597 if (!IsPackedArray(obj)) { 1598 /* 1599 * It's actually surprisingly complicated to reverse an array due 1600 * to the orthogonality of array length and array capacity while 1601 * handling leading and trailing holes correctly. Reversing seems 1602 * less likely to be a common operation than other array 1603 * mass-mutation methods, so for now just take a probably-small 1604 * memory hit (in the absence of too many holes in the array at 1605 * its start) and ensure that the capacity is sufficient to hold 1606 * all the elements in the array if it were full. 1607 */ 1608 DenseElementResult result = obj->ensureDenseElements(cx, length, 0); 1609 if (result != DenseElementResult::Success) { 1610 return result; 1611 } 1612 1613 /* Fill out the array's initialized length to its proper length. */ 1614 obj->ensureDenseInitializedLength(length, 0); 1615 } 1616 1617 if (!obj->denseElementsMaybeInIteration() && 1618 !cx->zone()->needsIncrementalBarrier()) { 1619 obj->reverseDenseElementsNoPreBarrier(length); 1620 return DenseElementResult::Success; 1621 } 1622 1623 auto setElementMaybeHole = [](JSContext* cx, Handle<NativeObject*> obj, 1624 uint32_t index, const Value& val) { 1625 if (MOZ_LIKELY(!val.isMagic(JS_ELEMENTS_HOLE))) { 1626 obj->setDenseElement(index, val); 1627 return true; 1628 } 1629 1630 obj->setDenseElementHole(index); 1631 return SuppressDeletedProperty(cx, obj, PropertyKey::Int(index)); 1632 }; 1633 1634 RootedValue origlo(cx), orighi(cx); 1635 1636 uint32_t lo = 0, hi = length - 1; 1637 for (; lo < hi; lo++, hi--) { 1638 origlo = obj->getDenseElement(lo); 1639 orighi = obj->getDenseElement(hi); 1640 if (!setElementMaybeHole(cx, obj, lo, orighi)) { 1641 return DenseElementResult::Failure; 1642 } 1643 if (!setElementMaybeHole(cx, obj, hi, origlo)) { 1644 return DenseElementResult::Failure; 1645 } 1646 } 1647 1648 return DenseElementResult::Success; 1649 } 1650 1651 // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce 1652 // 22.1.3.21 Array.prototype.reverse ( ) 1653 static bool array_reverse(JSContext* cx, unsigned argc, Value* vp) { 1654 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "reverse"); 1655 CallArgs args = CallArgsFromVp(argc, vp); 1656 1657 // Step 1. 1658 RootedObject obj(cx, ToObject(cx, args.thisv())); 1659 if (!obj) { 1660 return false; 1661 } 1662 1663 // Step 2. 1664 uint64_t len; 1665 if (!GetLengthPropertyInlined(cx, obj, &len)) { 1666 return false; 1667 } 1668 1669 // An empty array or an array with length 1 is already reversed. 1670 if (len <= 1) { 1671 args.rval().setObject(*obj); 1672 return true; 1673 } 1674 1675 if (IsPackedArrayOrNoExtraIndexedProperties(obj, len) && len <= UINT32_MAX) { 1676 DenseElementResult result = 1677 ArrayReverseDenseKernel(cx, obj.as<NativeObject>(), uint32_t(len)); 1678 if (result != DenseElementResult::Incomplete) { 1679 /* 1680 * Per ECMA-262, don't update the length of the array, even if the new 1681 * array has trailing holes (and thus the original array began with 1682 * holes). 1683 */ 1684 args.rval().setObject(*obj); 1685 return result == DenseElementResult::Success; 1686 } 1687 } 1688 1689 // Steps 3-5. 1690 RootedValue lowval(cx), hival(cx); 1691 for (uint64_t i = 0, half = len / 2; i < half; i++) { 1692 bool hole, hole2; 1693 if (!CheckForInterrupt(cx) || 1694 !::HasAndGetElement(cx, obj, i, &hole, &lowval) || 1695 !::HasAndGetElement(cx, obj, len - i - 1, &hole2, &hival)) { 1696 return false; 1697 } 1698 1699 if (!hole && !hole2) { 1700 if (!SetArrayElement(cx, obj, i, hival)) { 1701 return false; 1702 } 1703 if (!SetArrayElement(cx, obj, len - i - 1, lowval)) { 1704 return false; 1705 } 1706 } else if (hole && !hole2) { 1707 if (!SetArrayElement(cx, obj, i, hival)) { 1708 return false; 1709 } 1710 if (!DeletePropertyOrThrow(cx, obj, len - i - 1)) { 1711 return false; 1712 } 1713 } else if (!hole && hole2) { 1714 if (!DeletePropertyOrThrow(cx, obj, i)) { 1715 return false; 1716 } 1717 if (!SetArrayElement(cx, obj, len - i - 1, lowval)) { 1718 return false; 1719 } 1720 } else { 1721 // No action required. 1722 } 1723 } 1724 1725 // Step 6. 1726 args.rval().setObject(*obj); 1727 return true; 1728 } 1729 1730 static inline bool CompareStringValues(JSContext* cx, const Value& a, 1731 const Value& b, bool* lessOrEqualp) { 1732 if (!CheckForInterrupt(cx)) { 1733 return false; 1734 } 1735 1736 JSString* astr = a.toString(); 1737 JSString* bstr = b.toString(); 1738 int32_t result; 1739 if (!CompareStrings(cx, astr, bstr, &result)) { 1740 return false; 1741 } 1742 1743 *lessOrEqualp = (result <= 0); 1744 return true; 1745 } 1746 1747 static const uint64_t powersOf10[] = { 1748 1, 10, 100, 1000, 10000, 100000, 1749 1000000, 10000000, 100000000, 1000000000, 1000000000000ULL}; 1750 1751 static inline unsigned NumDigitsBase10(uint32_t n) { 1752 /* 1753 * This is just floor_log10(n) + 1 1754 * Algorithm taken from 1755 * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 1756 */ 1757 uint32_t log2 = CeilingLog2(n); 1758 uint32_t t = log2 * 1233 >> 12; 1759 return t - (n < powersOf10[t]) + 1; 1760 } 1761 1762 static inline bool CompareLexicographicInt32(const Value& a, const Value& b, 1763 bool* lessOrEqualp) { 1764 int32_t aint = a.toInt32(); 1765 int32_t bint = b.toInt32(); 1766 1767 /* 1768 * If both numbers are equal ... trivial 1769 * If only one of both is negative --> arithmetic comparison as char code 1770 * of '-' is always less than any other digit 1771 * If both numbers are negative convert them to positive and continue 1772 * handling ... 1773 */ 1774 if (aint == bint) { 1775 *lessOrEqualp = true; 1776 } else if ((aint < 0) && (bint >= 0)) { 1777 *lessOrEqualp = true; 1778 } else if ((aint >= 0) && (bint < 0)) { 1779 *lessOrEqualp = false; 1780 } else { 1781 uint32_t auint = Abs(aint); 1782 uint32_t buint = Abs(bint); 1783 1784 /* 1785 * ... get number of digits of both integers. 1786 * If they have the same number of digits --> arithmetic comparison. 1787 * If digits_a > digits_b: a < b*10e(digits_a - digits_b). 1788 * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b. 1789 */ 1790 unsigned digitsa = NumDigitsBase10(auint); 1791 unsigned digitsb = NumDigitsBase10(buint); 1792 if (digitsa == digitsb) { 1793 *lessOrEqualp = (auint <= buint); 1794 } else if (digitsa > digitsb) { 1795 MOZ_ASSERT((digitsa - digitsb) < std::size(powersOf10)); 1796 *lessOrEqualp = 1797 (uint64_t(auint) < uint64_t(buint) * powersOf10[digitsa - digitsb]); 1798 } else { /* if (digitsb > digitsa) */ 1799 MOZ_ASSERT((digitsb - digitsa) < std::size(powersOf10)); 1800 *lessOrEqualp = 1801 (uint64_t(auint) * powersOf10[digitsb - digitsa] <= uint64_t(buint)); 1802 } 1803 } 1804 1805 return true; 1806 } 1807 1808 template <typename Char1, typename Char2> 1809 static inline bool CompareSubStringValues(JSContext* cx, const Char1* s1, 1810 size_t len1, const Char2* s2, 1811 size_t len2, bool* lessOrEqualp) { 1812 if (!CheckForInterrupt(cx)) { 1813 return false; 1814 } 1815 1816 if (!s1 || !s2) { 1817 return false; 1818 } 1819 1820 int32_t result = CompareChars(s1, len1, s2, len2); 1821 *lessOrEqualp = (result <= 0); 1822 return true; 1823 } 1824 1825 namespace { 1826 1827 struct SortComparatorStrings { 1828 JSContext* const cx; 1829 1830 explicit SortComparatorStrings(JSContext* cx) : cx(cx) {} 1831 1832 bool operator()(const Value& a, const Value& b, bool* lessOrEqualp) { 1833 return CompareStringValues(cx, a, b, lessOrEqualp); 1834 } 1835 }; 1836 1837 struct SortComparatorLexicographicInt32 { 1838 bool operator()(const Value& a, const Value& b, bool* lessOrEqualp) { 1839 return CompareLexicographicInt32(a, b, lessOrEqualp); 1840 } 1841 }; 1842 1843 struct StringifiedElement { 1844 size_t charsBegin; 1845 size_t charsEnd; 1846 size_t elementIndex; 1847 }; 1848 1849 struct SortComparatorStringifiedElements { 1850 JSContext* const cx; 1851 const StringBuilder& sb; 1852 1853 SortComparatorStringifiedElements(JSContext* cx, const StringBuilder& sb) 1854 : cx(cx), sb(sb) {} 1855 1856 bool operator()(const StringifiedElement& a, const StringifiedElement& b, 1857 bool* lessOrEqualp) { 1858 size_t lenA = a.charsEnd - a.charsBegin; 1859 size_t lenB = b.charsEnd - b.charsBegin; 1860 1861 if (sb.isUnderlyingBufferLatin1()) { 1862 return CompareSubStringValues(cx, sb.rawLatin1Begin() + a.charsBegin, 1863 lenA, sb.rawLatin1Begin() + b.charsBegin, 1864 lenB, lessOrEqualp); 1865 } 1866 1867 return CompareSubStringValues(cx, sb.rawTwoByteBegin() + a.charsBegin, lenA, 1868 sb.rawTwoByteBegin() + b.charsBegin, lenB, 1869 lessOrEqualp); 1870 } 1871 }; 1872 1873 struct NumericElement { 1874 double dv; 1875 size_t elementIndex; 1876 }; 1877 1878 static bool ComparatorNumericLeftMinusRight(const NumericElement& a, 1879 const NumericElement& b, 1880 bool* lessOrEqualp) { 1881 *lessOrEqualp = std::isunordered(a.dv, b.dv) || (a.dv <= b.dv); 1882 return true; 1883 } 1884 1885 static bool ComparatorNumericRightMinusLeft(const NumericElement& a, 1886 const NumericElement& b, 1887 bool* lessOrEqualp) { 1888 *lessOrEqualp = std::isunordered(a.dv, b.dv) || (b.dv <= a.dv); 1889 return true; 1890 } 1891 1892 using ComparatorNumeric = bool (*)(const NumericElement&, const NumericElement&, 1893 bool*); 1894 1895 static const ComparatorNumeric SortComparatorNumerics[] = { 1896 nullptr, nullptr, ComparatorNumericLeftMinusRight, 1897 ComparatorNumericRightMinusLeft}; 1898 1899 static bool ComparatorInt32LeftMinusRight(const Value& a, const Value& b, 1900 bool* lessOrEqualp) { 1901 *lessOrEqualp = (a.toInt32() <= b.toInt32()); 1902 return true; 1903 } 1904 1905 static bool ComparatorInt32RightMinusLeft(const Value& a, const Value& b, 1906 bool* lessOrEqualp) { 1907 *lessOrEqualp = (b.toInt32() <= a.toInt32()); 1908 return true; 1909 } 1910 1911 using ComparatorInt32 = bool (*)(const Value&, const Value&, bool*); 1912 1913 static const ComparatorInt32 SortComparatorInt32s[] = { 1914 nullptr, nullptr, ComparatorInt32LeftMinusRight, 1915 ComparatorInt32RightMinusLeft}; 1916 1917 // Note: Values for this enum must match up with SortComparatorNumerics 1918 // and SortComparatorInt32s. 1919 enum ComparatorMatchResult { 1920 Match_Failure = 0, 1921 Match_None, 1922 Match_LeftMinusRight, 1923 Match_RightMinusLeft 1924 }; 1925 1926 } // namespace 1927 1928 /* 1929 * Specialize behavior for comparator functions with particular common bytecode 1930 * patterns: namely, |return x - y| and |return y - x|. 1931 */ 1932 static ComparatorMatchResult MatchNumericComparator(JSContext* cx, 1933 JSObject* obj) { 1934 if (!obj->is<JSFunction>()) { 1935 return Match_None; 1936 } 1937 1938 RootedFunction fun(cx, &obj->as<JSFunction>()); 1939 if (!fun->isInterpreted() || fun->isClassConstructor()) { 1940 return Match_None; 1941 } 1942 1943 JSScript* script = JSFunction::getOrCreateScript(cx, fun); 1944 if (!script) { 1945 return Match_Failure; 1946 } 1947 1948 jsbytecode* pc = script->code(); 1949 1950 uint16_t arg0, arg1; 1951 if (JSOp(*pc) != JSOp::GetArg) { 1952 return Match_None; 1953 } 1954 arg0 = GET_ARGNO(pc); 1955 pc += JSOpLength_GetArg; 1956 1957 if (JSOp(*pc) != JSOp::GetArg) { 1958 return Match_None; 1959 } 1960 arg1 = GET_ARGNO(pc); 1961 pc += JSOpLength_GetArg; 1962 1963 if (JSOp(*pc) != JSOp::Sub) { 1964 return Match_None; 1965 } 1966 pc += JSOpLength_Sub; 1967 1968 if (JSOp(*pc) != JSOp::Return) { 1969 return Match_None; 1970 } 1971 1972 if (arg0 == 0 && arg1 == 1) { 1973 return Match_LeftMinusRight; 1974 } 1975 1976 if (arg0 == 1 && arg1 == 0) { 1977 return Match_RightMinusLeft; 1978 } 1979 1980 return Match_None; 1981 } 1982 1983 template <typename K, typename C> 1984 static inline bool MergeSortByKey(K keys, size_t len, K scratch, C comparator, 1985 MutableHandle<GCVector<Value>> vec) { 1986 MOZ_ASSERT(vec.length() >= len); 1987 1988 /* Sort keys. */ 1989 if (!MergeSort(keys, len, scratch, comparator)) { 1990 return false; 1991 } 1992 1993 /* 1994 * Reorder vec by keys in-place, going element by element. When an out-of- 1995 * place element is encountered, move that element to its proper position, 1996 * displacing whatever element was at *that* point to its proper position, 1997 * and so on until an element must be moved to the current position. 1998 * 1999 * At each outer iteration all elements up to |i| are sorted. If 2000 * necessary each inner iteration moves some number of unsorted elements 2001 * (including |i|) directly to sorted position. Thus on completion |*vec| 2002 * is sorted, and out-of-position elements have moved once. Complexity is 2003 * Θ(len) + O(len) == O(2*len), with each element visited at most twice. 2004 */ 2005 for (size_t i = 0; i < len; i++) { 2006 size_t j = keys[i].elementIndex; 2007 if (i == j) { 2008 continue; // fixed point 2009 } 2010 2011 MOZ_ASSERT(j > i, "Everything less than |i| should be in the right place!"); 2012 Value tv = vec[j]; 2013 do { 2014 size_t k = keys[j].elementIndex; 2015 keys[j].elementIndex = j; 2016 vec[j].set(vec[k]); 2017 j = k; 2018 } while (j != i); 2019 2020 // We could assert the loop invariant that |i == keys[i].elementIndex| 2021 // here if we synced |keys[i].elementIndex|. But doing so would render 2022 // the assertion vacuous, so don't bother, even in debug builds. 2023 vec[i].set(tv); 2024 } 2025 2026 return true; 2027 } 2028 2029 /* 2030 * Sort Values as strings. 2031 * 2032 * To minimize #conversions, SortLexicographically() first converts all Values 2033 * to strings at once, then sorts the elements by these cached strings. 2034 */ 2035 static bool SortLexicographically(JSContext* cx, 2036 MutableHandle<GCVector<Value>> vec, 2037 size_t len) { 2038 MOZ_ASSERT(vec.length() >= len); 2039 2040 StringBuilder sb(cx); 2041 Vector<StringifiedElement, 0, TempAllocPolicy> strElements(cx); 2042 2043 /* MergeSort uses the upper half as scratch space. */ 2044 if (!strElements.resize(2 * len)) { 2045 return false; 2046 } 2047 2048 /* Convert Values to strings. */ 2049 size_t cursor = 0; 2050 for (size_t i = 0; i < len; i++) { 2051 if (!CheckForInterrupt(cx)) { 2052 return false; 2053 } 2054 2055 if (!ValueToStringBuilder(cx, vec[i], sb)) { 2056 return false; 2057 } 2058 2059 strElements[i] = {cursor, sb.length(), i}; 2060 cursor = sb.length(); 2061 } 2062 2063 /* Sort Values in vec alphabetically. */ 2064 return MergeSortByKey(strElements.begin(), len, strElements.begin() + len, 2065 SortComparatorStringifiedElements(cx, sb), vec); 2066 } 2067 2068 /* 2069 * Sort Values as numbers. 2070 * 2071 * To minimize #conversions, SortNumerically first converts all Values to 2072 * numerics at once, then sorts the elements by these cached numerics. 2073 */ 2074 static bool SortNumerically(JSContext* cx, MutableHandle<GCVector<Value>> vec, 2075 size_t len, ComparatorMatchResult comp) { 2076 MOZ_ASSERT(vec.length() >= len); 2077 2078 Vector<NumericElement, 0, TempAllocPolicy> numElements(cx); 2079 2080 /* MergeSort uses the upper half as scratch space. */ 2081 if (!numElements.resize(2 * len)) { 2082 return false; 2083 } 2084 2085 /* Convert Values to numerics. */ 2086 for (size_t i = 0; i < len; i++) { 2087 if (!CheckForInterrupt(cx)) { 2088 return false; 2089 } 2090 2091 double dv; 2092 if (!ToNumber(cx, vec[i], &dv)) { 2093 return false; 2094 } 2095 2096 numElements[i] = {dv, i}; 2097 } 2098 2099 /* Sort Values in vec numerically. */ 2100 return MergeSortByKey(numElements.begin(), len, numElements.begin() + len, 2101 SortComparatorNumerics[comp], vec); 2102 } 2103 2104 static bool FillWithUndefined(JSContext* cx, HandleObject obj, uint32_t start, 2105 uint32_t count) { 2106 MOZ_ASSERT(start < start + count, 2107 "count > 0 and start + count doesn't overflow"); 2108 2109 do { 2110 if (ObjectMayHaveExtraIndexedProperties(obj)) { 2111 break; 2112 } 2113 2114 NativeObject* nobj = &obj->as<NativeObject>(); 2115 if (!nobj->isExtensible()) { 2116 break; 2117 } 2118 2119 if (obj->is<ArrayObject>() && !obj->as<ArrayObject>().lengthIsWritable() && 2120 start + count >= obj->as<ArrayObject>().length()) { 2121 break; 2122 } 2123 2124 DenseElementResult result = nobj->ensureDenseElements(cx, start, count); 2125 if (result != DenseElementResult::Success) { 2126 if (result == DenseElementResult::Failure) { 2127 return false; 2128 } 2129 MOZ_ASSERT(result == DenseElementResult::Incomplete); 2130 break; 2131 } 2132 2133 if (obj->is<ArrayObject>() && 2134 start + count >= obj->as<ArrayObject>().length()) { 2135 obj->as<ArrayObject>().setLengthToInitializedLength(); 2136 } 2137 2138 for (uint32_t i = 0; i < count; i++) { 2139 nobj->setDenseElement(start + i, UndefinedHandleValue); 2140 } 2141 2142 return true; 2143 } while (false); 2144 2145 for (uint32_t i = 0; i < count; i++) { 2146 if (!CheckForInterrupt(cx) || 2147 !SetArrayElement(cx, obj, start + i, UndefinedHandleValue)) { 2148 return false; 2149 } 2150 } 2151 2152 return true; 2153 } 2154 2155 static bool ArraySortWithoutComparator(JSContext* cx, Handle<JSObject*> obj, 2156 uint64_t length, 2157 ComparatorMatchResult comp) { 2158 MOZ_ASSERT(length > 1); 2159 2160 if (length > UINT32_MAX) { 2161 ReportAllocationOverflow(cx); 2162 return false; 2163 } 2164 uint32_t len = uint32_t(length); 2165 2166 /* 2167 * We need a temporary array of 2 * len Value to hold the array elements 2168 * and the scratch space for merge sort. Check that its size does not 2169 * overflow size_t, which would allow for indexing beyond the end of the 2170 * malloc'd vector. 2171 */ 2172 #if JS_BITS_PER_WORD == 32 2173 if (size_t(len) > size_t(-1) / (2 * sizeof(Value))) { 2174 ReportAllocationOverflow(cx); 2175 return false; 2176 } 2177 #endif 2178 2179 size_t n, undefs; 2180 { 2181 Rooted<GCVector<Value>> vec(cx, GCVector<Value>(cx)); 2182 if (!vec.reserve(2 * size_t(len))) { 2183 return false; 2184 } 2185 2186 /* 2187 * By ECMA 262, 15.4.4.11, a property that does not exist (which we 2188 * call a "hole") is always greater than an existing property with 2189 * value undefined and that is always greater than any other property. 2190 * Thus to sort holes and undefs we simply count them, sort the rest 2191 * of elements, append undefs after them and then make holes after 2192 * undefs. 2193 */ 2194 undefs = 0; 2195 bool allStrings = true; 2196 bool allInts = true; 2197 RootedValue v(cx); 2198 if (IsPackedArray(obj)) { 2199 Handle<ArrayObject*> array = obj.as<ArrayObject>(); 2200 2201 for (uint32_t i = 0; i < len; i++) { 2202 if (!CheckForInterrupt(cx)) { 2203 return false; 2204 } 2205 2206 v.set(array->getDenseElement(i)); 2207 MOZ_ASSERT(!v.isMagic(JS_ELEMENTS_HOLE)); 2208 if (v.isUndefined()) { 2209 ++undefs; 2210 continue; 2211 } 2212 vec.infallibleAppend(v); 2213 allStrings = allStrings && v.isString(); 2214 allInts = allInts && v.isInt32(); 2215 } 2216 } else { 2217 for (uint32_t i = 0; i < len; i++) { 2218 if (!CheckForInterrupt(cx)) { 2219 return false; 2220 } 2221 2222 bool hole; 2223 if (!::HasAndGetElement(cx, obj, i, &hole, &v)) { 2224 return false; 2225 } 2226 if (hole) { 2227 continue; 2228 } 2229 if (v.isUndefined()) { 2230 ++undefs; 2231 continue; 2232 } 2233 vec.infallibleAppend(v); 2234 allStrings = allStrings && v.isString(); 2235 allInts = allInts && v.isInt32(); 2236 } 2237 } 2238 2239 /* 2240 * If the array only contains holes, we're done. But if it contains 2241 * undefs, those must be sorted to the front of the array. 2242 */ 2243 n = vec.length(); 2244 if (n == 0 && undefs == 0) { 2245 return true; 2246 } 2247 2248 /* Here len == n + undefs + number_of_holes. */ 2249 if (comp == Match_None) { 2250 /* 2251 * Sort using the default comparator converting all elements to 2252 * strings. 2253 */ 2254 if (allStrings) { 2255 MOZ_ALWAYS_TRUE(vec.resize(n * 2)); 2256 if (!MergeSort(vec.begin(), n, vec.begin() + n, 2257 SortComparatorStrings(cx))) { 2258 return false; 2259 } 2260 } else if (allInts) { 2261 MOZ_ALWAYS_TRUE(vec.resize(n * 2)); 2262 if (!MergeSort(vec.begin(), n, vec.begin() + n, 2263 SortComparatorLexicographicInt32())) { 2264 return false; 2265 } 2266 } else { 2267 if (!SortLexicographically(cx, &vec, n)) { 2268 return false; 2269 } 2270 } 2271 } else { 2272 if (allInts) { 2273 MOZ_ALWAYS_TRUE(vec.resize(n * 2)); 2274 if (!MergeSort(vec.begin(), n, vec.begin() + n, 2275 SortComparatorInt32s[comp])) { 2276 return false; 2277 } 2278 } else { 2279 if (!SortNumerically(cx, &vec, n, comp)) { 2280 return false; 2281 } 2282 } 2283 } 2284 2285 if (!SetArrayElements(cx, obj, 0, uint32_t(n), vec.begin())) { 2286 return false; 2287 } 2288 } 2289 2290 /* Set undefs that sorted after the rest of elements. */ 2291 if (undefs > 0) { 2292 if (!FillWithUndefined(cx, obj, n, undefs)) { 2293 return false; 2294 } 2295 n += undefs; 2296 } 2297 2298 /* Re-create any holes that sorted to the end of the array. */ 2299 for (uint32_t i = n; i < len; i++) { 2300 if (!CheckForInterrupt(cx) || !DeletePropertyOrThrow(cx, obj, i)) { 2301 return false; 2302 } 2303 } 2304 return true; 2305 } 2306 2307 // This function handles sorting without a comparator function (or with a 2308 // trivial comparator function that we can pattern match) by calling 2309 // ArraySortWithoutComparator. 2310 // 2311 // If there's a non-trivial comparator function, it initializes the 2312 // ArraySortData struct for ArraySortData::sortArrayWithComparator. This 2313 // function must be called next to perform the sorting. 2314 // 2315 // This is separate from ArraySortData::sortArrayWithComparator because it lets 2316 // the compiler generate better code for ArraySortData::sortArrayWithComparator. 2317 // 2318 // https://tc39.es/ecma262/#sec-array.prototype.sort 2319 // 23.1.3.30 Array.prototype.sort ( comparefn ) 2320 static MOZ_ALWAYS_INLINE bool ArraySortPrologue(JSContext* cx, 2321 Handle<Value> thisv, 2322 Handle<Value> comparefn, 2323 ArraySortData* d, bool* done) { 2324 // Step 1. 2325 if (MOZ_UNLIKELY(!comparefn.isUndefined() && !IsCallable(comparefn))) { 2326 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_SORT_ARG); 2327 return false; 2328 } 2329 2330 // Step 2. 2331 Rooted<JSObject*> obj(cx, ToObject(cx, thisv)); 2332 if (!obj) { 2333 return false; 2334 } 2335 2336 // Step 3. 2337 uint64_t length; 2338 if (MOZ_UNLIKELY(!GetLengthPropertyInlined(cx, obj, &length))) { 2339 return false; 2340 } 2341 2342 // Arrays with less than two elements remain unchanged when sorted. 2343 if (length <= 1) { 2344 d->setReturnValue(obj); 2345 *done = true; 2346 return true; 2347 } 2348 2349 // Use a fast path if there's no comparator or if the comparator is a function 2350 // that we can pattern match. 2351 do { 2352 ComparatorMatchResult comp = Match_None; 2353 if (comparefn.isObject()) { 2354 comp = MatchNumericComparator(cx, &comparefn.toObject()); 2355 if (comp == Match_Failure) { 2356 return false; 2357 } 2358 if (comp == Match_None) { 2359 // Pattern matching failed. 2360 break; 2361 } 2362 } 2363 if (!ArraySortWithoutComparator(cx, obj, length, comp)) { 2364 return false; 2365 } 2366 d->setReturnValue(obj); 2367 *done = true; 2368 return true; 2369 } while (false); 2370 2371 // Ensure length * 2 (used below) doesn't overflow UINT32_MAX. 2372 if (MOZ_UNLIKELY(length > UINT32_MAX / 2)) { 2373 ReportAllocationOverflow(cx); 2374 return false; 2375 } 2376 uint32_t len = uint32_t(length); 2377 2378 // Merge sort requires extra scratch space. 2379 bool needsScratchSpace = len > ArraySortData::InsertionSortMaxLength; 2380 2381 Rooted<ArraySortData::ValueVector> vec(cx); 2382 if (MOZ_UNLIKELY(!vec.reserve(needsScratchSpace ? (2 * len) : len))) { 2383 ReportOutOfMemory(cx); 2384 return false; 2385 } 2386 2387 // Append elements to the vector. Skip holes. 2388 if (IsPackedArray(obj)) { 2389 Handle<ArrayObject*> array = obj.as<ArrayObject>(); 2390 const Value* elements = array->getDenseElements(); 2391 vec.infallibleAppend(elements, len); 2392 } else { 2393 RootedValue v(cx); 2394 for (uint32_t i = 0; i < len; i++) { 2395 if (!CheckForInterrupt(cx)) { 2396 return false; 2397 } 2398 2399 bool hole; 2400 if (!::HasAndGetElement(cx, obj, i, &hole, &v)) { 2401 return false; 2402 } 2403 if (hole) { 2404 continue; 2405 } 2406 vec.infallibleAppend(v); 2407 } 2408 // If there are only holes, the object is already sorted. 2409 if (vec.empty()) { 2410 d->setReturnValue(obj); 2411 *done = true; 2412 return true; 2413 } 2414 } 2415 2416 uint32_t denseLen = vec.length(); 2417 if (needsScratchSpace) { 2418 MOZ_ALWAYS_TRUE(vec.resize(denseLen * 2)); 2419 } 2420 d->init(obj, &comparefn.toObject(), std::move(vec.get()), len, denseLen); 2421 2422 // Continue in ArraySortData::sortArrayWithComparator. 2423 MOZ_ASSERT(!*done); 2424 return true; 2425 } 2426 2427 ArraySortResult js::CallComparatorSlow(ArraySortData* d, const Value& x, 2428 const Value& y) { 2429 JSContext* cx = d->cx(); 2430 FixedInvokeArgs<2> callArgs(cx); 2431 callArgs[0].set(x); 2432 callArgs[1].set(y); 2433 Rooted<Value> comparefn(cx, ObjectValue(*d->comparator())); 2434 Rooted<Value> rval(cx); 2435 if (!js::Call(cx, comparefn, UndefinedHandleValue, callArgs, &rval)) { 2436 return ArraySortResult::Failure; 2437 } 2438 d->setComparatorReturnValue(rval); 2439 return ArraySortResult::Done; 2440 } 2441 2442 // static 2443 ArraySortResult ArraySortData::sortArrayWithComparator(ArraySortData* d) { 2444 ArraySortResult result = sortWithComparatorShared<ArraySortKind::Array>(d); 2445 if (result != ArraySortResult::Done) { 2446 return result; 2447 } 2448 2449 // Copy elements to the array. 2450 JSContext* cx = d->cx(); 2451 Rooted<JSObject*> obj(cx, d->obj_); 2452 if (!SetArrayElements(cx, obj, 0, d->denseLen, d->list)) { 2453 return ArraySortResult::Failure; 2454 } 2455 2456 // Re-create any holes that sorted to the end of the array. 2457 for (uint32_t i = d->denseLen; i < d->length; i++) { 2458 if (!CheckForInterrupt(cx) || !DeletePropertyOrThrow(cx, obj, i)) { 2459 return ArraySortResult::Failure; 2460 } 2461 } 2462 2463 d->freeMallocData(); 2464 d->setReturnValue(obj); 2465 return ArraySortResult::Done; 2466 } 2467 2468 // https://tc39.es/ecma262/#sec-array.prototype.sort 2469 // 23.1.3.30 Array.prototype.sort ( comparefn ) 2470 bool js::array_sort(JSContext* cx, unsigned argc, Value* vp) { 2471 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "sort"); 2472 CallArgs args = CallArgsFromVp(argc, vp); 2473 2474 // If we have a comparator argument, use the JIT trampoline implementation 2475 // instead. This avoids a performance cliff (especially with large arrays) 2476 // because C++ => JIT calls are much slower than Trampoline => JIT calls. 2477 if (args.hasDefined(0) && jit::IsBaselineInterpreterEnabled()) { 2478 return CallTrampolineNativeJitCode(cx, jit::TrampolineNative::ArraySort, 2479 args); 2480 } 2481 2482 Rooted<ArraySortData> data(cx, cx); 2483 2484 // On all return paths other than ArraySortData::sortArrayWithComparator 2485 // returning Done, we call freeMallocData to not fail debug assertions. This 2486 // matches the JIT trampoline where we can't rely on C++ destructors. 2487 auto freeData = 2488 mozilla::MakeScopeExit([&]() { data.get().freeMallocData(); }); 2489 2490 bool done = false; 2491 if (!ArraySortPrologue(cx, args.thisv(), args.get(0), data.address(), 2492 &done)) { 2493 return false; 2494 } 2495 if (done) { 2496 args.rval().set(data.get().returnValue()); 2497 return true; 2498 } 2499 2500 FixedInvokeArgs<2> callArgs(cx); 2501 Rooted<Value> rval(cx); 2502 2503 while (true) { 2504 ArraySortResult res = 2505 ArraySortData::sortArrayWithComparator(data.address()); 2506 switch (res) { 2507 case ArraySortResult::Failure: 2508 return false; 2509 2510 case ArraySortResult::Done: 2511 freeData.release(); 2512 args.rval().set(data.get().returnValue()); 2513 return true; 2514 2515 case ArraySortResult::CallJS: 2516 case ArraySortResult::CallJSSameRealmNoUnderflow: 2517 MOZ_ASSERT(data.get().comparatorThisValue().isUndefined()); 2518 MOZ_ASSERT(&args[0].toObject() == data.get().comparator()); 2519 callArgs[0].set(data.get().comparatorArg(0)); 2520 callArgs[1].set(data.get().comparatorArg(1)); 2521 if (!js::Call(cx, args[0], UndefinedHandleValue, callArgs, &rval)) { 2522 return false; 2523 } 2524 data.get().setComparatorReturnValue(rval); 2525 break; 2526 } 2527 } 2528 } 2529 2530 ArraySortResult js::ArraySortFromJit(JSContext* cx, 2531 jit::TrampolineNativeFrameLayout* frame) { 2532 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "sort"); 2533 // Initialize the ArraySortData class stored in the trampoline frame. 2534 void* dataUninit = frame->getFrameData<ArraySortData>(); 2535 auto* data = new (dataUninit) ArraySortData(cx); 2536 2537 Rooted<Value> thisv(cx, frame->thisv()); 2538 Rooted<Value> comparefn(cx); 2539 if (frame->numActualArgs() > 0) { 2540 comparefn = frame->actualArgs()[0]; 2541 } 2542 2543 bool done = false; 2544 if (!ArraySortPrologue(cx, thisv, comparefn, data, &done)) { 2545 return ArraySortResult::Failure; 2546 } 2547 if (done) { 2548 data->freeMallocData(); 2549 return ArraySortResult::Done; 2550 } 2551 2552 return ArraySortData::sortArrayWithComparator(data); 2553 } 2554 2555 void ArraySortData::trace(JSTracer* trc) { 2556 TraceNullableRoot(trc, &comparator_, "comparator_"); 2557 TraceRoot(trc, &thisv, "thisv"); 2558 TraceRoot(trc, &callArgs[0], "callArgs0"); 2559 TraceRoot(trc, &callArgs[1], "callArgs1"); 2560 vec.trace(trc); 2561 TraceRoot(trc, &item, "item"); 2562 TraceNullableRoot(trc, &obj_, "obj"); 2563 } 2564 2565 bool js::NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v) { 2566 Handle<ArrayObject*> arr = obj.as<ArrayObject>(); 2567 2568 MOZ_ASSERT(!v.isMagic()); 2569 MOZ_ASSERT(arr->lengthIsWritable()); 2570 2571 uint32_t length = arr->length(); 2572 MOZ_ASSERT(length <= arr->getDenseCapacity()); 2573 2574 if (!arr->ensureElements(cx, length + 1)) { 2575 return false; 2576 } 2577 2578 arr->setDenseInitializedLength(length + 1); 2579 arr->setLengthToInitializedLength(); 2580 arr->initDenseElement(length, v); 2581 return true; 2582 } 2583 2584 // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce 2585 // 22.1.3.18 Array.prototype.push ( ...items ) 2586 static bool array_push(JSContext* cx, unsigned argc, Value* vp) { 2587 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "push"); 2588 CallArgs args = CallArgsFromVp(argc, vp); 2589 2590 // Step 1. 2591 RootedObject obj(cx, ToObject(cx, args.thisv())); 2592 if (!obj) { 2593 return false; 2594 } 2595 2596 // Step 2. 2597 uint64_t length; 2598 if (!GetLengthPropertyInlined(cx, obj, &length)) { 2599 return false; 2600 } 2601 2602 if (!ObjectMayHaveExtraIndexedProperties(obj) && length <= UINT32_MAX) { 2603 DenseElementResult result = 2604 obj->as<NativeObject>().setOrExtendDenseElements( 2605 cx, uint32_t(length), args.array(), args.length()); 2606 if (result != DenseElementResult::Incomplete) { 2607 if (result == DenseElementResult::Failure) { 2608 return false; 2609 } 2610 2611 uint32_t newlength = uint32_t(length) + args.length(); 2612 args.rval().setNumber(newlength); 2613 2614 // setOrExtendDenseElements takes care of updating the length for 2615 // arrays. Handle updates to the length of non-arrays here. 2616 if (!obj->is<ArrayObject>()) { 2617 MOZ_ASSERT(obj->is<NativeObject>()); 2618 return SetLengthProperty(cx, obj, newlength); 2619 } 2620 2621 return true; 2622 } 2623 } 2624 2625 // Step 5. 2626 uint64_t newlength = length + args.length(); 2627 if (newlength >= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)) { 2628 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2629 JSMSG_TOO_LONG_ARRAY); 2630 return false; 2631 } 2632 2633 // Steps 3-6. 2634 if (!SetArrayElements(cx, obj, length, args.length(), args.array())) { 2635 return false; 2636 } 2637 2638 // Steps 7-8. 2639 args.rval().setNumber(double(newlength)); 2640 return SetLengthProperty(cx, obj, newlength); 2641 } 2642 2643 // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce 2644 // 22.1.3.17 Array.prototype.pop ( ) 2645 bool js::array_pop(JSContext* cx, unsigned argc, Value* vp) { 2646 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "pop"); 2647 CallArgs args = CallArgsFromVp(argc, vp); 2648 2649 // Step 1. 2650 RootedObject obj(cx, ToObject(cx, args.thisv())); 2651 if (!obj) { 2652 return false; 2653 } 2654 2655 // Step 2. 2656 uint64_t index; 2657 if (!GetLengthPropertyInlined(cx, obj, &index)) { 2658 return false; 2659 } 2660 2661 // Steps 3-4. 2662 if (index == 0) { 2663 // Step 3.b. 2664 args.rval().setUndefined(); 2665 } else { 2666 // Steps 4.a-b. 2667 index--; 2668 2669 // Steps 4.c, 4.f. 2670 if (!GetArrayElement(cx, obj, index, args.rval())) { 2671 return false; 2672 } 2673 2674 // Steps 4.d. 2675 if (!DeletePropertyOrThrow(cx, obj, index)) { 2676 return false; 2677 } 2678 } 2679 2680 // Steps 3.a, 4.e. 2681 return SetLengthProperty(cx, obj, index); 2682 } 2683 2684 void js::ArrayShiftMoveElements(ArrayObject* arr) { 2685 AutoUnsafeCallWithABI unsafe; 2686 MOZ_ASSERT(arr->isExtensible()); 2687 MOZ_ASSERT(arr->lengthIsWritable()); 2688 MOZ_ASSERT(IsPackedArray(arr)); 2689 MOZ_ASSERT(!arr->denseElementsHaveMaybeInIterationFlag()); 2690 2691 size_t initlen = arr->getDenseInitializedLength(); 2692 MOZ_ASSERT(initlen > 0); 2693 2694 if (!arr->tryShiftDenseElements(1)) { 2695 arr->moveDenseElements(0, 1, initlen - 1); 2696 arr->setDenseInitializedLength(initlen - 1); 2697 } 2698 2699 MOZ_ASSERT(arr->getDenseInitializedLength() == initlen - 1); 2700 arr->setLengthToInitializedLength(); 2701 } 2702 2703 static inline void SetInitializedLength(JSContext* cx, NativeObject* obj, 2704 size_t initlen) { 2705 MOZ_ASSERT(obj->isExtensible()); 2706 2707 size_t oldInitlen = obj->getDenseInitializedLength(); 2708 obj->setDenseInitializedLength(initlen); 2709 if (initlen < oldInitlen) { 2710 obj->shrinkElements(cx, initlen); 2711 } 2712 } 2713 2714 static DenseElementResult ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, 2715 MutableHandleValue rval) { 2716 if (!IsPackedArray(obj) && ObjectMayHaveExtraIndexedProperties(obj)) { 2717 return DenseElementResult::Incomplete; 2718 } 2719 2720 Handle<NativeObject*> nobj = obj.as<NativeObject>(); 2721 if (nobj->denseElementsMaybeInIteration()) { 2722 return DenseElementResult::Incomplete; 2723 } 2724 2725 if (!nobj->isExtensible()) { 2726 return DenseElementResult::Incomplete; 2727 } 2728 2729 size_t initlen = nobj->getDenseInitializedLength(); 2730 if (initlen == 0) { 2731 return DenseElementResult::Incomplete; 2732 } 2733 2734 rval.set(nobj->getDenseElement(0)); 2735 if (rval.isMagic(JS_ELEMENTS_HOLE)) { 2736 rval.setUndefined(); 2737 } 2738 2739 if (nobj->tryShiftDenseElements(1)) { 2740 return DenseElementResult::Success; 2741 } 2742 2743 nobj->moveDenseElements(0, 1, initlen - 1); 2744 2745 SetInitializedLength(cx, nobj, initlen - 1); 2746 return DenseElementResult::Success; 2747 } 2748 2749 // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce 2750 // 22.1.3.22 Array.prototype.shift ( ) 2751 static bool array_shift(JSContext* cx, unsigned argc, Value* vp) { 2752 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "shift"); 2753 CallArgs args = CallArgsFromVp(argc, vp); 2754 2755 // Step 1. 2756 RootedObject obj(cx, ToObject(cx, args.thisv())); 2757 if (!obj) { 2758 return false; 2759 } 2760 2761 // Step 2. 2762 uint64_t len; 2763 if (!GetLengthPropertyInlined(cx, obj, &len)) { 2764 return false; 2765 } 2766 2767 // Step 3. 2768 if (len == 0) { 2769 // Step 3.a. 2770 if (!SetLengthProperty(cx, obj, uint32_t(0))) { 2771 return false; 2772 } 2773 2774 // Step 3.b. 2775 args.rval().setUndefined(); 2776 return true; 2777 } 2778 2779 uint64_t newlen = len - 1; 2780 2781 /* Fast paths. */ 2782 uint64_t startIndex; 2783 DenseElementResult result = ArrayShiftDenseKernel(cx, obj, args.rval()); 2784 if (result != DenseElementResult::Incomplete) { 2785 if (result == DenseElementResult::Failure) { 2786 return false; 2787 } 2788 2789 if (len <= UINT32_MAX) { 2790 return SetLengthProperty(cx, obj, newlen); 2791 } 2792 2793 startIndex = UINT32_MAX - 1; 2794 } else { 2795 // Steps 4, 9. 2796 if (!GetElement(cx, obj, 0, args.rval())) { 2797 return false; 2798 } 2799 2800 startIndex = 0; 2801 } 2802 2803 // Steps 5-6. 2804 RootedValue value(cx); 2805 for (uint64_t i = startIndex; i < newlen; i++) { 2806 if (!CheckForInterrupt(cx)) { 2807 return false; 2808 } 2809 bool hole; 2810 if (!::HasAndGetElement(cx, obj, i + 1, &hole, &value)) { 2811 return false; 2812 } 2813 if (hole) { 2814 if (!DeletePropertyOrThrow(cx, obj, i)) { 2815 return false; 2816 } 2817 } else { 2818 if (!SetArrayElement(cx, obj, i, value)) { 2819 return false; 2820 } 2821 } 2822 } 2823 2824 // Step 7. 2825 if (!DeletePropertyOrThrow(cx, obj, newlen)) { 2826 return false; 2827 } 2828 2829 // Step 8. 2830 return SetLengthProperty(cx, obj, newlen); 2831 } 2832 2833 // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce 2834 // 22.1.3.29 Array.prototype.unshift ( ...items ) 2835 static bool array_unshift(JSContext* cx, unsigned argc, Value* vp) { 2836 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "unshift"); 2837 CallArgs args = CallArgsFromVp(argc, vp); 2838 2839 // Step 1. 2840 RootedObject obj(cx, ToObject(cx, args.thisv())); 2841 if (!obj) { 2842 return false; 2843 } 2844 2845 // Step 2. 2846 uint64_t length; 2847 if (!GetLengthPropertyInlined(cx, obj, &length)) { 2848 return false; 2849 } 2850 2851 // Steps 3-4. 2852 if (args.length() > 0) { 2853 bool optimized = false; 2854 do { 2855 if (length > UINT32_MAX) { 2856 break; 2857 } 2858 if (ObjectMayHaveExtraIndexedProperties(obj)) { 2859 break; 2860 } 2861 NativeObject* nobj = &obj->as<NativeObject>(); 2862 if (nobj->denseElementsMaybeInIteration()) { 2863 break; 2864 } 2865 if (!nobj->isExtensible()) { 2866 break; 2867 } 2868 if (nobj->is<ArrayObject>() && 2869 !nobj->as<ArrayObject>().lengthIsWritable()) { 2870 break; 2871 } 2872 if (!nobj->tryUnshiftDenseElements(args.length())) { 2873 DenseElementResult result = 2874 nobj->ensureDenseElements(cx, uint32_t(length), args.length()); 2875 if (result != DenseElementResult::Success) { 2876 if (result == DenseElementResult::Failure) { 2877 return false; 2878 } 2879 MOZ_ASSERT(result == DenseElementResult::Incomplete); 2880 break; 2881 } 2882 if (length > 0) { 2883 nobj->moveDenseElements(args.length(), 0, uint32_t(length)); 2884 } 2885 } 2886 for (uint32_t i = 0; i < args.length(); i++) { 2887 nobj->setDenseElement(i, args[i]); 2888 } 2889 optimized = true; 2890 } while (false); 2891 2892 if (!optimized) { 2893 if (length > 0) { 2894 uint64_t last = length; 2895 uint64_t upperIndex = last + args.length(); 2896 2897 // Step 4.a. 2898 if (upperIndex >= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)) { 2899 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2900 JSMSG_TOO_LONG_ARRAY); 2901 return false; 2902 } 2903 2904 // Steps 4.b-c. 2905 RootedValue value(cx); 2906 do { 2907 --last; 2908 --upperIndex; 2909 if (!CheckForInterrupt(cx)) { 2910 return false; 2911 } 2912 bool hole; 2913 if (!::HasAndGetElement(cx, obj, last, &hole, &value)) { 2914 return false; 2915 } 2916 if (hole) { 2917 if (!DeletePropertyOrThrow(cx, obj, upperIndex)) { 2918 return false; 2919 } 2920 } else { 2921 if (!SetArrayElement(cx, obj, upperIndex, value)) { 2922 return false; 2923 } 2924 } 2925 } while (last != 0); 2926 } 2927 2928 // Steps 4.d-f. 2929 /* Copy from args to the bottom of the array. */ 2930 if (!SetArrayElements(cx, obj, 0, args.length(), args.array())) { 2931 return false; 2932 } 2933 } 2934 } 2935 2936 // Step 5. 2937 uint64_t newlength = length + args.length(); 2938 if (!SetLengthProperty(cx, obj, newlength)) { 2939 return false; 2940 } 2941 2942 // Step 6. 2943 /* Follow Perl by returning the new array length. */ 2944 args.rval().setNumber(double(newlength)); 2945 return true; 2946 } 2947 2948 enum class ArrayAccess { Read, Write }; 2949 2950 /* 2951 * Returns true if this is a dense array whose properties ending at |endIndex| 2952 * (exclusive) may be accessed (get, set, delete) directly through its 2953 * contiguous vector of elements without fear of getters, setters, etc. along 2954 * the prototype chain, or of enumerators requiring notification of 2955 * modifications. 2956 */ 2957 template <ArrayAccess Access> 2958 static bool CanOptimizeForDenseStorage(HandleObject arr, uint64_t endIndex) { 2959 /* If the desired properties overflow dense storage, we can't optimize. */ 2960 if (endIndex > UINT32_MAX) { 2961 return false; 2962 } 2963 2964 if (Access == ArrayAccess::Read) { 2965 /* 2966 * Dense storage read access is possible for any packed array as long 2967 * as we only access properties within the initialized length. In all 2968 * other cases we need to ensure there are no other indexed properties 2969 * on this object or on the prototype chain. Callers are required to 2970 * clamp the read length, so it doesn't exceed the initialized length. 2971 */ 2972 if (IsPackedArray(arr) && 2973 endIndex <= arr->as<ArrayObject>().getDenseInitializedLength()) { 2974 return true; 2975 } 2976 return !ObjectMayHaveExtraIndexedProperties(arr); 2977 } 2978 2979 /* There's no optimizing possible if it's not an array. */ 2980 if (!arr->is<ArrayObject>()) { 2981 return false; 2982 } 2983 2984 /* If the length is non-writable, always pick the slow path */ 2985 if (!arr->as<ArrayObject>().lengthIsWritable()) { 2986 return false; 2987 } 2988 2989 /* Also pick the slow path if the object is non-extensible. */ 2990 if (!arr->as<ArrayObject>().isExtensible()) { 2991 return false; 2992 } 2993 2994 /* Also pick the slow path if the object is being iterated over. */ 2995 if (arr->as<ArrayObject>().denseElementsMaybeInIteration()) { 2996 return false; 2997 } 2998 2999 /* Or we attempt to write to indices outside the initialized length. */ 3000 if (endIndex > arr->as<ArrayObject>().getDenseInitializedLength()) { 3001 return false; 3002 } 3003 3004 /* 3005 * Now watch out for getters and setters along the prototype chain or in 3006 * other indexed properties on the object. Packed arrays don't have any 3007 * other indexed properties by definition. 3008 */ 3009 return IsPackedArray(arr) || !ObjectMayHaveExtraIndexedProperties(arr); 3010 } 3011 3012 static ArrayObject* CopyDenseArrayElements(JSContext* cx, 3013 Handle<NativeObject*> obj, 3014 uint32_t begin, uint32_t count) { 3015 size_t initlen = obj->getDenseInitializedLength(); 3016 MOZ_ASSERT(initlen <= UINT32_MAX, 3017 "initialized length shouldn't exceed UINT32_MAX"); 3018 uint32_t newlength = 0; 3019 if (initlen > begin) { 3020 newlength = std::min<uint32_t>(initlen - begin, count); 3021 } 3022 3023 ArrayObject* narr = NewDenseFullyAllocatedArray(cx, newlength); 3024 if (!narr) { 3025 return nullptr; 3026 } 3027 3028 MOZ_ASSERT(count >= narr->length()); 3029 narr->setLength(cx, count); 3030 3031 if (newlength > 0) { 3032 narr->initDenseElements(obj, begin, newlength); 3033 } 3034 3035 return narr; 3036 } 3037 3038 static bool CopyArrayElements(JSContext* cx, HandleObject obj, uint64_t begin, 3039 uint64_t count, Handle<ArrayObject*> result) { 3040 MOZ_ASSERT(result->length() == count); 3041 3042 uint64_t startIndex = 0; 3043 RootedValue value(cx); 3044 3045 // Use dense storage for new indexed properties where possible. 3046 { 3047 uint32_t index = 0; 3048 uint32_t limit = std::min<uint32_t>(count, PropertyKey::IntMax); 3049 for (; index < limit; index++) { 3050 bool hole; 3051 if (!CheckForInterrupt(cx) || 3052 !::HasAndGetElement(cx, obj, begin + index, &hole, &value)) { 3053 return false; 3054 } 3055 3056 if (!hole) { 3057 DenseElementResult edResult = result->ensureDenseElements(cx, index, 1); 3058 if (edResult != DenseElementResult::Success) { 3059 if (edResult == DenseElementResult::Failure) { 3060 return false; 3061 } 3062 3063 MOZ_ASSERT(edResult == DenseElementResult::Incomplete); 3064 if (!DefineDataElement(cx, result, index, value)) { 3065 return false; 3066 } 3067 3068 break; 3069 } 3070 result->setDenseElement(index, value); 3071 } 3072 } 3073 startIndex = index + 1; 3074 } 3075 3076 // Copy any remaining elements. 3077 for (uint64_t i = startIndex; i < count; i++) { 3078 bool hole; 3079 if (!CheckForInterrupt(cx) || 3080 !::HasAndGetElement(cx, obj, begin + i, &hole, &value)) { 3081 return false; 3082 } 3083 3084 if (!hole && !DefineArrayElement(cx, result, i, value)) { 3085 return false; 3086 } 3087 } 3088 return true; 3089 } 3090 3091 // Helpers for array_splice_impl() and array_to_spliced() 3092 // 3093 // Initialize variables common to splice() and toSpliced(): 3094 // - GetItemCount() returns the number of new elements being added. 3095 // - GetActualDeleteCount() returns the number of elements being deleted. 3096 3097 static uint32_t GetItemCount(const CallArgs& args) { 3098 if (args.length() < 2) { 3099 return 0; 3100 } 3101 return (args.length() - 2); 3102 } 3103 3104 static bool GetActualDeleteCount(JSContext* cx, const CallArgs& args, 3105 HandleObject obj, uint64_t len, 3106 uint64_t actualStart, uint32_t insertCount, 3107 uint64_t* actualDeleteCount) { 3108 MOZ_ASSERT(len < DOUBLE_INTEGRAL_PRECISION_LIMIT); 3109 MOZ_ASSERT(actualStart <= len); 3110 MOZ_ASSERT(insertCount == GetItemCount(args)); 3111 3112 if (args.length() < 1) { 3113 // Step 8. If start is not present, then let actualDeleteCount be 0. 3114 *actualDeleteCount = 0; 3115 } else if (args.length() < 2) { 3116 // Step 9. Else if deleteCount is not present, then let actualDeleteCount be 3117 // len - actualStart. 3118 *actualDeleteCount = len - actualStart; 3119 } else { 3120 // Step 10.a. Else, let dc be toIntegerOrInfinity(deleteCount). 3121 double deleteCount; 3122 if (!ToInteger(cx, args.get(1), &deleteCount)) { 3123 return false; 3124 } 3125 3126 // Step 10.b. Let actualDeleteCount be the result of clamping dc between 0 3127 // and len - actualStart. 3128 *actualDeleteCount = 3129 uint64_t(std::clamp(deleteCount, 0.0, double(len - actualStart))); 3130 MOZ_ASSERT(*actualDeleteCount <= len); 3131 3132 // Step 11. Let newLen be len + insertCount - actualDeleteCount. 3133 // Step 12. If newLen > 2^53 - 1, throw a TypeError exception. 3134 if (len + uint64_t(insertCount) - *actualDeleteCount >= 3135 uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)) { 3136 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 3137 JSMSG_TOO_LONG_ARRAY); 3138 return false; 3139 } 3140 } 3141 MOZ_ASSERT(actualStart + *actualDeleteCount <= len); 3142 3143 return true; 3144 } 3145 3146 static bool array_splice_impl(JSContext* cx, unsigned argc, Value* vp, 3147 bool returnValueIsUsed) { 3148 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "splice"); 3149 CallArgs args = CallArgsFromVp(argc, vp); 3150 3151 /* Step 1. */ 3152 RootedObject obj(cx, ToObject(cx, args.thisv())); 3153 if (!obj) { 3154 return false; 3155 } 3156 3157 /* Step 2. */ 3158 uint64_t len; 3159 if (!GetLengthPropertyInlined(cx, obj, &len)) { 3160 return false; 3161 } 3162 3163 /* Steps 3-6. */ 3164 /* actualStart is the index after which elements will be 3165 deleted and/or new elements will be added */ 3166 uint64_t actualStart = 0; 3167 if (args.hasDefined(0)) { 3168 if (!ToIntegerIndex(cx, args[0], len, &actualStart)) { 3169 return false; 3170 } 3171 } 3172 MOZ_ASSERT(actualStart <= len); 3173 3174 /* Steps 7-10.*/ 3175 /* itemCount is the number of elements being added */ 3176 uint32_t itemCount = GetItemCount(args); 3177 3178 /* actualDeleteCount is the number of elements being deleted */ 3179 uint64_t actualDeleteCount; 3180 if (!GetActualDeleteCount(cx, args, obj, len, actualStart, itemCount, 3181 &actualDeleteCount)) { 3182 return false; 3183 } 3184 3185 RootedObject arr(cx); 3186 if (IsArraySpecies(cx, obj)) { 3187 if (actualDeleteCount > UINT32_MAX) { 3188 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 3189 JSMSG_BAD_ARRAY_LENGTH); 3190 return false; 3191 } 3192 uint32_t count = uint32_t(actualDeleteCount); 3193 3194 if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, 3195 actualStart + count)) { 3196 MOZ_ASSERT(actualStart <= UINT32_MAX, 3197 "if actualStart + count <= UINT32_MAX, then actualStart <= " 3198 "UINT32_MAX"); 3199 if (returnValueIsUsed) { 3200 /* Steps 11-13. */ 3201 arr = CopyDenseArrayElements(cx, obj.as<NativeObject>(), 3202 uint32_t(actualStart), count); 3203 if (!arr) { 3204 return false; 3205 } 3206 } 3207 } else { 3208 /* Step 11. */ 3209 arr = NewDenseFullyAllocatedArray(cx, count); 3210 if (!arr) { 3211 return false; 3212 } 3213 3214 /* Steps 12-13. */ 3215 if (!CopyArrayElements(cx, obj, actualStart, count, 3216 arr.as<ArrayObject>())) { 3217 return false; 3218 } 3219 } 3220 } else { 3221 /* Step 11. */ 3222 if (!ArraySpeciesCreate(cx, obj, actualDeleteCount, &arr)) { 3223 return false; 3224 } 3225 3226 /* Steps 12-13. */ 3227 RootedValue fromValue(cx); 3228 for (uint64_t k = 0; k < actualDeleteCount; k++) { 3229 if (!CheckForInterrupt(cx)) { 3230 return false; 3231 } 3232 3233 /* Steps 13.b, 13.c.i. */ 3234 bool hole; 3235 if (!::HasAndGetElement(cx, obj, actualStart + k, &hole, &fromValue)) { 3236 return false; 3237 } 3238 3239 /* Step 13.c. */ 3240 if (!hole) { 3241 /* Step 13.c.ii. */ 3242 if (!DefineArrayElement(cx, arr, k, fromValue)) { 3243 return false; 3244 } 3245 } 3246 } 3247 3248 /* Step 14. */ 3249 if (!SetLengthProperty(cx, arr, actualDeleteCount)) { 3250 return false; 3251 } 3252 } 3253 3254 /* Step 15. */ 3255 uint64_t finalLength = len - actualDeleteCount + itemCount; 3256 3257 if (itemCount < actualDeleteCount) { 3258 /* Step 16: the array is being shrunk. */ 3259 uint64_t sourceIndex = actualStart + actualDeleteCount; 3260 uint64_t targetIndex = actualStart + itemCount; 3261 3262 if (CanOptimizeForDenseStorage<ArrayAccess::Write>(obj, len)) { 3263 MOZ_ASSERT(sourceIndex <= len && targetIndex <= len && len <= UINT32_MAX, 3264 "sourceIndex and targetIndex are uint32 array indices"); 3265 MOZ_ASSERT(finalLength < len, "finalLength is strictly less than len"); 3266 MOZ_ASSERT(obj->is<NativeObject>()); 3267 3268 /* Step 16.b. */ 3269 Handle<ArrayObject*> arr = obj.as<ArrayObject>(); 3270 if (targetIndex != 0 || !arr->tryShiftDenseElements(sourceIndex)) { 3271 arr->moveDenseElements(uint32_t(targetIndex), uint32_t(sourceIndex), 3272 uint32_t(len - sourceIndex)); 3273 } 3274 3275 /* Steps 20. */ 3276 SetInitializedLength(cx, arr, finalLength); 3277 } else { 3278 /* 3279 * This is all very slow if the length is very large. We don't yet 3280 * have the ability to iterate in sorted order, so we just do the 3281 * pessimistic thing and let CheckForInterrupt handle the 3282 * fallout. 3283 */ 3284 3285 /* Step 16. */ 3286 RootedValue fromValue(cx); 3287 for (uint64_t from = sourceIndex, to = targetIndex; from < len; 3288 from++, to++) { 3289 /* Steps 15.b.i-ii (implicit). */ 3290 3291 if (!CheckForInterrupt(cx)) { 3292 return false; 3293 } 3294 3295 /* Steps 16.b.iii-v */ 3296 bool hole; 3297 if (!::HasAndGetElement(cx, obj, from, &hole, &fromValue)) { 3298 return false; 3299 } 3300 3301 if (hole) { 3302 if (!DeletePropertyOrThrow(cx, obj, to)) { 3303 return false; 3304 } 3305 } else { 3306 if (!SetArrayElement(cx, obj, to, fromValue)) { 3307 return false; 3308 } 3309 } 3310 } 3311 3312 /* Step 16d. */ 3313 if (!DeletePropertiesOrThrow(cx, obj, len, finalLength)) { 3314 return false; 3315 } 3316 } 3317 } else if (itemCount > actualDeleteCount) { 3318 MOZ_ASSERT(actualDeleteCount <= UINT32_MAX); 3319 uint32_t deleteCount = uint32_t(actualDeleteCount); 3320 3321 /* Step 17. */ 3322 3323 // Fast path for when we can simply extend and move the dense elements. 3324 auto extendElements = [len, itemCount, deleteCount](JSContext* cx, 3325 HandleObject obj) { 3326 if (!obj->is<ArrayObject>()) { 3327 return DenseElementResult::Incomplete; 3328 } 3329 if (len > UINT32_MAX) { 3330 return DenseElementResult::Incomplete; 3331 } 3332 3333 // Ensure there are no getters/setters or other extra indexed properties. 3334 if (ObjectMayHaveExtraIndexedProperties(obj)) { 3335 return DenseElementResult::Incomplete; 3336 } 3337 3338 // Watch out for arrays with non-writable length or non-extensible arrays. 3339 // In these cases `splice` may have to throw an exception so we let the 3340 // slow path handle it. We also have to ensure we maintain the 3341 // |capacity <= initializedLength| invariant for such objects. See 3342 // NativeObject::shrinkCapacityToInitializedLength. 3343 Handle<ArrayObject*> arr = obj.as<ArrayObject>(); 3344 if (!arr->lengthIsWritable() || !arr->isExtensible()) { 3345 return DenseElementResult::Incomplete; 3346 } 3347 3348 // Also use the slow path if there might be an active for-in iterator so 3349 // that we don't have to worry about suppressing deleted properties. 3350 if (arr->denseElementsMaybeInIteration()) { 3351 return DenseElementResult::Incomplete; 3352 } 3353 3354 return arr->ensureDenseElements(cx, uint32_t(len), 3355 itemCount - deleteCount); 3356 }; 3357 3358 DenseElementResult res = extendElements(cx, obj); 3359 if (res == DenseElementResult::Failure) { 3360 return false; 3361 } 3362 if (res == DenseElementResult::Success) { 3363 MOZ_ASSERT(finalLength <= UINT32_MAX); 3364 MOZ_ASSERT((actualStart + actualDeleteCount) <= len && len <= UINT32_MAX, 3365 "start and deleteCount are uint32 array indices"); 3366 MOZ_ASSERT(actualStart + itemCount <= UINT32_MAX, 3367 "can't overflow because |len - actualDeleteCount + itemCount " 3368 "<= UINT32_MAX| " 3369 "and |actualStart <= len - actualDeleteCount| are both true"); 3370 uint32_t start = uint32_t(actualStart); 3371 uint32_t length = uint32_t(len); 3372 3373 Handle<ArrayObject*> arr = obj.as<ArrayObject>(); 3374 arr->moveDenseElements(start + itemCount, start + deleteCount, 3375 length - (start + deleteCount)); 3376 3377 /* Step 20. */ 3378 SetInitializedLength(cx, arr, finalLength); 3379 } else { 3380 MOZ_ASSERT(res == DenseElementResult::Incomplete); 3381 3382 RootedValue fromValue(cx); 3383 for (uint64_t k = len - actualDeleteCount; k > actualStart; k--) { 3384 if (!CheckForInterrupt(cx)) { 3385 return false; 3386 } 3387 3388 /* Step 17.b.i. */ 3389 uint64_t from = k + actualDeleteCount - 1; 3390 3391 /* Step 17.b.ii. */ 3392 uint64_t to = k + itemCount - 1; 3393 3394 /* Steps 17.b.iii, 17.b.iv.1. */ 3395 bool hole; 3396 if (!::HasAndGetElement(cx, obj, from, &hole, &fromValue)) { 3397 return false; 3398 } 3399 3400 /* Steps 17.b.iv. */ 3401 if (hole) { 3402 /* Step 17.b.v.1. */ 3403 if (!DeletePropertyOrThrow(cx, obj, to)) { 3404 return false; 3405 } 3406 } else { 3407 /* Step 17.b.iv.2. */ 3408 if (!SetArrayElement(cx, obj, to, fromValue)) { 3409 return false; 3410 } 3411 } 3412 } 3413 } 3414 } 3415 3416 Value* items = args.array() + 2; 3417 3418 /* Steps 18-19. */ 3419 if (!SetArrayElements(cx, obj, actualStart, itemCount, items)) { 3420 return false; 3421 } 3422 3423 /* Step 20. */ 3424 if (!SetLengthProperty(cx, obj, finalLength)) { 3425 return false; 3426 } 3427 3428 /* Step 21. */ 3429 if (returnValueIsUsed) { 3430 args.rval().setObject(*arr); 3431 } 3432 3433 return true; 3434 } 3435 3436 /* ES 2016 draft Mar 25, 2016 22.1.3.26. */ 3437 static bool array_splice(JSContext* cx, unsigned argc, Value* vp) { 3438 return array_splice_impl(cx, argc, vp, true); 3439 } 3440 3441 static bool array_splice_noRetVal(JSContext* cx, unsigned argc, Value* vp) { 3442 return array_splice_impl(cx, argc, vp, false); 3443 } 3444 3445 static void CopyDenseElementsFillHoles(ArrayObject* arr, NativeObject* nobj, 3446 uint32_t length) { 3447 // Ensure |arr| is an empty array with sufficient capacity. 3448 MOZ_ASSERT(arr->getDenseInitializedLength() == 0); 3449 MOZ_ASSERT(arr->getDenseCapacity() >= length); 3450 MOZ_ASSERT(length > 0); 3451 3452 uint32_t count = std::min(nobj->getDenseInitializedLength(), length); 3453 3454 if (count > 0) { 3455 if (nobj->denseElementsArePacked()) { 3456 // Copy all dense elements when no holes are present. 3457 arr->initDenseElements(nobj, 0, count); 3458 } else { 3459 arr->setDenseInitializedLength(count); 3460 3461 // Handle each element separately to filter out holes. 3462 for (uint32_t i = 0; i < count; i++) { 3463 Value val = nobj->getDenseElement(i); 3464 if (val.isMagic(JS_ELEMENTS_HOLE)) { 3465 val = UndefinedValue(); 3466 } 3467 arr->initDenseElement(i, val); 3468 } 3469 } 3470 } 3471 3472 // Fill trailing holes with undefined. 3473 if (count < length) { 3474 arr->setDenseInitializedLength(length); 3475 3476 for (uint32_t i = count; i < length; i++) { 3477 arr->initDenseElement(i, UndefinedValue()); 3478 } 3479 } 3480 3481 // Ensure |length| elements have been copied and no holes are present. 3482 MOZ_ASSERT(arr->getDenseInitializedLength() == length); 3483 MOZ_ASSERT(arr->denseElementsArePacked()); 3484 } 3485 3486 // ES2026 draft rev a562082b031d89d00ee667181ce8a6158656bd4b 3487 // 23.1.3.35 Array.prototype.toSpliced ( start, skipCount, ...items ) 3488 static bool array_toSpliced(JSContext* cx, unsigned argc, Value* vp) { 3489 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "toSpliced"); 3490 CallArgs args = CallArgsFromVp(argc, vp); 3491 3492 // Step 1. Let O be ? ToObject(this value). 3493 RootedObject obj(cx, ToObject(cx, args.thisv())); 3494 if (!obj) { 3495 return false; 3496 } 3497 3498 // Step 2. Let len be ? LengthOfArrayLike(O). 3499 uint64_t len; 3500 if (!GetLengthPropertyInlined(cx, obj, &len)) { 3501 return false; 3502 } 3503 3504 // Steps 3-6. 3505 // |actualStart| is the index after which elements will be deleted and/or 3506 // new elements will be added 3507 uint64_t actualStart = 0; 3508 if (args.hasDefined(0)) { 3509 if (!ToIntegerIndex(cx, args[0], len, &actualStart)) { 3510 return false; 3511 } 3512 } 3513 MOZ_ASSERT(actualStart <= len); 3514 3515 // Step 7. Let insertCount be the number of elements in items. 3516 uint32_t insertCount = GetItemCount(args); 3517 3518 // Steps 8-10. 3519 // actualDeleteCount is the number of elements being deleted 3520 uint64_t actualDeleteCount; 3521 if (!GetActualDeleteCount(cx, args, obj, len, actualStart, insertCount, 3522 &actualDeleteCount)) { 3523 return false; 3524 } 3525 MOZ_ASSERT(actualStart + actualDeleteCount <= len); 3526 3527 // Step 11. Let newLen be len + insertCount - actualDeleteCount. 3528 uint64_t newLen = len + insertCount - actualDeleteCount; 3529 3530 // Step 12 handled by GetActualDeleteCount(). 3531 MOZ_ASSERT(newLen < DOUBLE_INTEGRAL_PRECISION_LIMIT); 3532 MOZ_ASSERT(actualStart <= newLen, 3533 "if |actualStart + actualDeleteCount <= len| and " 3534 "|newLen = len + insertCount - actualDeleteCount|, then " 3535 "|actualStart <= newLen|"); 3536 3537 // ArrayCreate, step 1. 3538 if (newLen > UINT32_MAX) { 3539 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 3540 JSMSG_BAD_ARRAY_LENGTH); 3541 return false; 3542 } 3543 3544 // Step 13. Let A be ? ArrayCreate(𝔽(newLen)). 3545 Rooted<ArrayObject*> arr(cx, 3546 NewDensePartlyAllocatedArray(cx, uint32_t(newLen))); 3547 if (!arr) { 3548 return false; 3549 } 3550 3551 // Steps 14-19 optimized for dense elements. 3552 if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, len)) { 3553 MOZ_ASSERT(len <= UINT32_MAX); 3554 MOZ_ASSERT(actualDeleteCount <= UINT32_MAX, 3555 "if |actualStart + actualDeleteCount <= len| and " 3556 "|len <= UINT32_MAX|, then |actualDeleteCount <= UINT32_MAX|"); 3557 3558 uint32_t length = uint32_t(len); 3559 uint32_t newLength = uint32_t(newLen); 3560 uint32_t start = uint32_t(actualStart); 3561 uint32_t deleteCount = uint32_t(actualDeleteCount); 3562 3563 auto nobj = obj.as<NativeObject>(); 3564 3565 ArrayObject* arr = NewDenseFullyAllocatedArray(cx, newLength); 3566 if (!arr) { 3567 return false; 3568 } 3569 3570 // Below code doesn't handle the case when the storage has to grow, 3571 // therefore the capacity must fit for at least |newLength| elements. 3572 MOZ_ASSERT(arr->getDenseCapacity() >= newLength); 3573 3574 if (deleteCount == 0 && insertCount == 0) { 3575 // Copy the array when we don't have to remove or insert any elements. 3576 if (newLength > 0) { 3577 CopyDenseElementsFillHoles(arr, nobj, newLength); 3578 } 3579 } else { 3580 // Copy nobj[0..start] to arr[0..start]. 3581 if (start > 0) { 3582 CopyDenseElementsFillHoles(arr, nobj, start); 3583 } 3584 3585 // Insert |items| into arr[start..(start + insertCount)]. 3586 if (insertCount > 0) { 3587 auto items = HandleValueArray::subarray(args, 2, insertCount); 3588 3589 // Prefer |initDenseElements| because it's faster. 3590 if (arr->getDenseInitializedLength() == 0) { 3591 arr->initDenseElements(items.begin(), items.length()); 3592 } else { 3593 arr->ensureDenseInitializedLength(start, items.length()); 3594 arr->copyDenseElements(start, items.begin(), items.length()); 3595 } 3596 } 3597 3598 uint32_t fromIndex = start + deleteCount; 3599 uint32_t toIndex = start + insertCount; 3600 MOZ_ASSERT((length - fromIndex) == (newLength - toIndex), 3601 "Copies all remaining elements to the end"); 3602 3603 // Copy nobj[(start + deleteCount)..length] to 3604 // arr[(start + insertCount)..newLength]. 3605 if (fromIndex < length) { 3606 uint32_t end = std::min(length, nobj->getDenseInitializedLength()); 3607 if (fromIndex < end) { 3608 uint32_t count = end - fromIndex; 3609 if (nobj->denseElementsArePacked()) { 3610 // Copy all dense elements when no holes are present. 3611 const Value* src = nobj->getDenseElements() + fromIndex; 3612 arr->ensureDenseInitializedLength(toIndex, count); 3613 arr->copyDenseElements(toIndex, src, count); 3614 fromIndex += count; 3615 toIndex += count; 3616 } else { 3617 arr->setDenseInitializedLength(toIndex + count); 3618 3619 // Handle each element separately to filter out holes. 3620 for (uint32_t i = 0; i < count; i++) { 3621 Value val = nobj->getDenseElement(fromIndex++); 3622 if (val.isMagic(JS_ELEMENTS_HOLE)) { 3623 val = UndefinedValue(); 3624 } 3625 arr->initDenseElement(toIndex++, val); 3626 } 3627 } 3628 } 3629 3630 arr->setDenseInitializedLength(newLength); 3631 3632 // Fill trailing holes with undefined. 3633 while (fromIndex < length) { 3634 arr->initDenseElement(toIndex++, UndefinedValue()); 3635 fromIndex++; 3636 } 3637 } 3638 3639 MOZ_ASSERT(fromIndex == length); 3640 MOZ_ASSERT(toIndex == newLength); 3641 } 3642 3643 // Ensure the result array is packed and has the correct length. 3644 MOZ_ASSERT(IsPackedArray(arr)); 3645 MOZ_ASSERT(arr->length() == newLength); 3646 3647 args.rval().setObject(*arr); 3648 return true; 3649 } 3650 3651 // Copy everything before start 3652 3653 // Step 14. Let i be 0. 3654 uint32_t i = 0; 3655 3656 // Step 15. Let r be actualStart + actualDeleteCount. 3657 uint64_t r = actualStart + actualDeleteCount; 3658 3659 // Step 16. Repeat while i < actualStart, 3660 RootedValue iValue(cx); 3661 while (i < uint32_t(actualStart)) { 3662 if (!CheckForInterrupt(cx)) { 3663 return false; 3664 } 3665 3666 // Skip Step 16.a. Let Pi be ! ToString(𝔽(i)). 3667 3668 // Step 16.b. Let iValue be ? Get(O, Pi). 3669 if (!GetArrayElement(cx, obj, i, &iValue)) { 3670 return false; 3671 } 3672 3673 // Step 16.c. Perform ! CreateDataPropertyOrThrow(A, Pi, iValue). 3674 if (!DefineArrayElement(cx, arr, i, iValue)) { 3675 return false; 3676 } 3677 3678 // Step 16.d. Set i to i + 1. 3679 i++; 3680 } 3681 3682 // Result array now contains all elements before start. 3683 3684 // Copy new items 3685 if (insertCount > 0) { 3686 HandleValueArray items = HandleValueArray::subarray(args, 2, insertCount); 3687 3688 // Fast-path to copy all items in one go. 3689 DenseElementResult result = 3690 arr->setOrExtendDenseElements(cx, i, items.begin(), items.length()); 3691 if (result == DenseElementResult::Failure) { 3692 return false; 3693 } 3694 3695 if (result == DenseElementResult::Success) { 3696 i += items.length(); 3697 } else { 3698 MOZ_ASSERT(result == DenseElementResult::Incomplete); 3699 3700 // Step 17. For each element E of items, do 3701 for (size_t j = 0; j < items.length(); j++) { 3702 if (!CheckForInterrupt(cx)) { 3703 return false; 3704 } 3705 3706 // Skip Step 17.a. Let Pi be ! ToString(𝔽(i)). 3707 3708 // Step 17.b. Perform ! CreateDataPropertyOrThrow(A, Pi, E). 3709 if (!DefineArrayElement(cx, arr, i, items[j])) { 3710 return false; 3711 } 3712 3713 // Step 17.c. Set i to i + 1. 3714 i++; 3715 } 3716 } 3717 } 3718 3719 // Copy items after new items 3720 // Step 18. Repeat, while i < newLen, 3721 RootedValue fromValue(cx); 3722 while (i < uint32_t(newLen)) { 3723 if (!CheckForInterrupt(cx)) { 3724 return false; 3725 } 3726 3727 // Skip Step 18.a. Let Pi be ! ToString(𝔽(i)). 3728 // Skip Step 18.b. Let from be ! ToString(𝔽(r)). 3729 3730 // Step 18.c. Let fromValue be ? Get(O, from). */ 3731 if (!GetArrayElement(cx, obj, r, &fromValue)) { 3732 return false; 3733 } 3734 3735 // Step 18.d. Perform ! CreateDataPropertyOrThrow(A, Pi, fromValue). 3736 if (!DefineArrayElement(cx, arr, i, fromValue)) { 3737 return false; 3738 } 3739 3740 // Step 18.e. Set i to i + 1. 3741 i++; 3742 3743 // Step 18.f. Set r to r + 1. 3744 r++; 3745 } 3746 3747 // Step 19. Return A. 3748 args.rval().setObject(*arr); 3749 return true; 3750 } 3751 3752 // ES2026 draft rev a562082b031d89d00ee667181ce8a6158656bd4b 3753 // Array.prototype.with ( index, value ) 3754 static bool array_with(JSContext* cx, unsigned argc, Value* vp) { 3755 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "with"); 3756 CallArgs args = CallArgsFromVp(argc, vp); 3757 3758 // Step 1. Let O be ? ToObject(this value). 3759 RootedObject obj(cx, ToObject(cx, args.thisv())); 3760 if (!obj) { 3761 return false; 3762 } 3763 3764 // Step 2. Let len be ? LengthOfArrayLike(O). 3765 uint64_t len; 3766 if (!GetLengthPropertyInlined(cx, obj, &len)) { 3767 return false; 3768 } 3769 3770 // Step 3. Let relativeIndex be ? ToIntegerOrInfinity(index). 3771 double relativeIndex; 3772 if (!ToInteger(cx, args.get(0), &relativeIndex)) { 3773 return false; 3774 } 3775 3776 // Step 4. If relativeIndex >= 0, let actualIndex be relativeIndex. 3777 double actualIndex = relativeIndex; 3778 if (actualIndex < 0) { 3779 // Step 5. Else, let actualIndex be len + relativeIndex. 3780 actualIndex = double(len) + actualIndex; 3781 } 3782 3783 // Step 6. If actualIndex >= len or actualIndex < 0, throw a RangeError 3784 // exception. 3785 if (actualIndex < 0 || actualIndex >= double(len)) { 3786 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); 3787 return false; 3788 } 3789 3790 // ArrayCreate, step 1. 3791 if (len > UINT32_MAX) { 3792 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 3793 JSMSG_BAD_ARRAY_LENGTH); 3794 return false; 3795 } 3796 uint32_t length = uint32_t(len); 3797 3798 MOZ_ASSERT(length > 0); 3799 MOZ_ASSERT(0 <= actualIndex && actualIndex < UINT32_MAX); 3800 3801 // Steps 7-10 optimized for dense elements. 3802 if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, length)) { 3803 auto nobj = obj.as<NativeObject>(); 3804 3805 ArrayObject* arr = NewDenseFullyAllocatedArray(cx, length); 3806 if (!arr) { 3807 return false; 3808 } 3809 3810 CopyDenseElementsFillHoles(arr, nobj, length); 3811 3812 // Replace the value at |actualIndex|. 3813 arr->setDenseElement(uint32_t(actualIndex), args.get(1)); 3814 3815 // Ensure the result array is packed and has the correct length. 3816 MOZ_ASSERT(IsPackedArray(arr)); 3817 MOZ_ASSERT(arr->length() == length); 3818 3819 args.rval().setObject(*arr); 3820 return true; 3821 } 3822 3823 // Step 7. Let A be ? ArrayCreate(𝔽(len)). 3824 RootedObject arr(cx, NewDensePartlyAllocatedArray(cx, length)); 3825 if (!arr) { 3826 return false; 3827 } 3828 3829 // Steps 8-9. Let k be 0; Repeat, while k < len, 3830 RootedValue fromValue(cx); 3831 for (uint32_t k = 0; k < length; k++) { 3832 if (!CheckForInterrupt(cx)) { 3833 return false; 3834 } 3835 3836 // Skip Step 9.a. Let Pk be ! ToString(𝔽(k)). 3837 3838 // Step 9.b. If k is actualIndex, let fromValue be value. 3839 if (k == uint32_t(actualIndex)) { 3840 fromValue = args.get(1); 3841 } else { 3842 // Step 9.c. Else, let fromValue be ? Get(O, 𝔽(k)). 3843 if (!GetArrayElement(cx, obj, k, &fromValue)) { 3844 return false; 3845 } 3846 } 3847 3848 // Step 9.d. Perform ! CreateDataPropertyOrThrow(A, 𝔽(k), fromValue). 3849 if (!DefineArrayElement(cx, arr, k, fromValue)) { 3850 return false; 3851 } 3852 } 3853 3854 // Step 10. Return A. 3855 args.rval().setObject(*arr); 3856 return true; 3857 } 3858 3859 struct SortComparatorIndexes { 3860 bool operator()(uint32_t a, uint32_t b, bool* lessOrEqualp) { 3861 *lessOrEqualp = (a <= b); 3862 return true; 3863 } 3864 }; 3865 3866 // Returns all indexed properties in the range [begin, end) found on |obj| or 3867 // its proto chain. This function does not handle proxies, objects with 3868 // resolve/lookupProperty hooks or indexed getters, as those can introduce 3869 // new properties. In those cases, *success is set to |false|. 3870 static bool GetIndexedPropertiesInRange(JSContext* cx, HandleObject obj, 3871 uint64_t begin, uint64_t end, 3872 Vector<uint32_t>& indexes, 3873 bool* success) { 3874 *success = false; 3875 3876 // TODO: Add IdIsIndex with support for large indices. 3877 if (end > UINT32_MAX) { 3878 return true; 3879 } 3880 MOZ_ASSERT(begin <= UINT32_MAX); 3881 3882 // First, look for proxies or class hooks that can introduce extra 3883 // properties. 3884 JSObject* pobj = obj; 3885 do { 3886 if (!pobj->is<NativeObject>() || pobj->getClass()->getResolve() || 3887 pobj->getOpsLookupProperty()) { 3888 return true; 3889 } 3890 } while ((pobj = pobj->staticPrototype())); 3891 3892 // Collect indexed property names. 3893 pobj = obj; 3894 do { 3895 // Append dense elements. 3896 NativeObject* nativeObj = &pobj->as<NativeObject>(); 3897 uint32_t initLen = nativeObj->getDenseInitializedLength(); 3898 for (uint32_t i = begin; i < initLen && i < end; i++) { 3899 if (nativeObj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) { 3900 continue; 3901 } 3902 if (!indexes.append(i)) { 3903 return false; 3904 } 3905 } 3906 3907 // Append typed array elements. 3908 if (nativeObj->is<TypedArrayObject>()) { 3909 size_t len = nativeObj->as<TypedArrayObject>().length().valueOr(0); 3910 for (uint32_t i = begin; i < len && i < end; i++) { 3911 if (!indexes.append(i)) { 3912 return false; 3913 } 3914 } 3915 } 3916 3917 // Append sparse elements. 3918 if (nativeObj->isIndexed()) { 3919 ShapePropertyIter<NoGC> iter(nativeObj->shape()); 3920 for (; !iter.done(); iter++) { 3921 jsid id = iter->key(); 3922 uint32_t i; 3923 if (!IdIsIndex(id, &i)) { 3924 continue; 3925 } 3926 3927 if (!(begin <= i && i < end)) { 3928 continue; 3929 } 3930 3931 // Watch out for getters, they can add new properties. 3932 if (!iter->isDataProperty()) { 3933 return true; 3934 } 3935 3936 if (!indexes.append(i)) { 3937 return false; 3938 } 3939 } 3940 } 3941 } while ((pobj = pobj->staticPrototype())); 3942 3943 // Sort the indexes. 3944 Vector<uint32_t> tmp(cx); 3945 size_t n = indexes.length(); 3946 if (!tmp.resize(n)) { 3947 return false; 3948 } 3949 if (!MergeSort(indexes.begin(), n, tmp.begin(), SortComparatorIndexes())) { 3950 return false; 3951 } 3952 3953 // Remove duplicates. 3954 if (!indexes.empty()) { 3955 uint32_t last = 0; 3956 for (size_t i = 1, len = indexes.length(); i < len; i++) { 3957 uint32_t elem = indexes[i]; 3958 if (indexes[last] != elem) { 3959 last++; 3960 indexes[last] = elem; 3961 } 3962 } 3963 if (!indexes.resize(last + 1)) { 3964 return false; 3965 } 3966 } 3967 3968 *success = true; 3969 return true; 3970 } 3971 3972 static bool SliceSparse(JSContext* cx, HandleObject obj, uint64_t begin, 3973 uint64_t end, Handle<ArrayObject*> result) { 3974 MOZ_ASSERT(begin <= end); 3975 3976 Vector<uint32_t> indexes(cx); 3977 bool success; 3978 if (!GetIndexedPropertiesInRange(cx, obj, begin, end, indexes, &success)) { 3979 return false; 3980 } 3981 3982 if (!success) { 3983 return CopyArrayElements(cx, obj, begin, end - begin, result); 3984 } 3985 3986 MOZ_ASSERT(end <= UINT32_MAX, 3987 "indices larger than UINT32_MAX should be rejected by " 3988 "GetIndexedPropertiesInRange"); 3989 3990 RootedValue value(cx); 3991 for (uint32_t index : indexes) { 3992 MOZ_ASSERT(begin <= index && index < end); 3993 3994 bool hole; 3995 if (!::HasAndGetElement(cx, obj, index, &hole, &value)) { 3996 return false; 3997 } 3998 3999 if (!hole && 4000 !DefineDataElement(cx, result, index - uint32_t(begin), value)) { 4001 return false; 4002 } 4003 } 4004 4005 return true; 4006 } 4007 4008 static JSObject* SliceArguments(JSContext* cx, Handle<ArgumentsObject*> argsobj, 4009 uint32_t begin, uint32_t count) { 4010 MOZ_ASSERT(!argsobj->hasOverriddenLength() && 4011 !argsobj->hasOverriddenElement()); 4012 MOZ_ASSERT(begin + count <= argsobj->initialLength()); 4013 4014 ArrayObject* result = NewDenseFullyAllocatedArray(cx, count); 4015 if (!result) { 4016 return nullptr; 4017 } 4018 result->setDenseInitializedLength(count); 4019 4020 for (uint32_t index = 0; index < count; index++) { 4021 const Value& v = argsobj->element(begin + index); 4022 result->initDenseElement(index, v); 4023 } 4024 return result; 4025 } 4026 4027 static bool ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint64_t begin, 4028 uint64_t end, MutableHandleValue rval) { 4029 if (begin > end) { 4030 begin = end; 4031 } 4032 4033 if ((end - begin) > UINT32_MAX) { 4034 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 4035 JSMSG_BAD_ARRAY_LENGTH); 4036 return false; 4037 } 4038 uint32_t count = uint32_t(end - begin); 4039 4040 if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, end)) { 4041 MOZ_ASSERT(begin <= UINT32_MAX, 4042 "if end <= UINT32_MAX, then begin <= UINT32_MAX"); 4043 JSObject* narr = CopyDenseArrayElements(cx, obj.as<NativeObject>(), 4044 uint32_t(begin), count); 4045 if (!narr) { 4046 return false; 4047 } 4048 4049 rval.setObject(*narr); 4050 return true; 4051 } 4052 4053 if (obj->is<ArgumentsObject>()) { 4054 Handle<ArgumentsObject*> argsobj = obj.as<ArgumentsObject>(); 4055 if (!argsobj->hasOverriddenLength() && !argsobj->hasOverriddenElement()) { 4056 MOZ_ASSERT(begin <= UINT32_MAX, "begin is limited by |argsobj|'s length"); 4057 JSObject* narr = SliceArguments(cx, argsobj, uint32_t(begin), count); 4058 if (!narr) { 4059 return false; 4060 } 4061 4062 rval.setObject(*narr); 4063 return true; 4064 } 4065 } 4066 4067 Rooted<ArrayObject*> narr(cx, NewDensePartlyAllocatedArray(cx, count)); 4068 if (!narr) { 4069 return false; 4070 } 4071 4072 if (end <= UINT32_MAX) { 4073 if (js::GetElementsOp op = obj->getOpsGetElements()) { 4074 ElementAdder adder(cx, narr, count, 4075 ElementAdder::CheckHasElemPreserveHoles); 4076 if (!op(cx, obj, uint32_t(begin), uint32_t(end), &adder)) { 4077 return false; 4078 } 4079 4080 rval.setObject(*narr); 4081 return true; 4082 } 4083 } 4084 4085 if (obj->is<NativeObject>() && obj->as<NativeObject>().isIndexed() && 4086 count > 1000) { 4087 if (!SliceSparse(cx, obj, begin, end, narr)) { 4088 return false; 4089 } 4090 } else { 4091 if (!CopyArrayElements(cx, obj, begin, count, narr)) { 4092 return false; 4093 } 4094 } 4095 4096 rval.setObject(*narr); 4097 return true; 4098 } 4099 4100 /* ES 2016 draft Mar 25, 2016 22.1.3.23. */ 4101 static bool array_slice(JSContext* cx, unsigned argc, Value* vp) { 4102 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "slice"); 4103 CallArgs args = CallArgsFromVp(argc, vp); 4104 4105 /* Step 1. */ 4106 RootedObject obj(cx, ToObject(cx, args.thisv())); 4107 if (!obj) { 4108 return false; 4109 } 4110 4111 /* Step 2. */ 4112 uint64_t length; 4113 if (!GetLengthPropertyInlined(cx, obj, &length)) { 4114 return false; 4115 } 4116 4117 /* Steps 3-4. */ 4118 uint64_t k = 0; 4119 if (args.hasDefined(0)) { 4120 if (!ToIntegerIndex(cx, args[0], length, &k)) { 4121 return false; 4122 } 4123 } 4124 4125 /* Steps 5-6. */ 4126 uint64_t final = length; 4127 if (args.hasDefined(1)) { 4128 if (!ToIntegerIndex(cx, args[1], length, &final)) { 4129 return false; 4130 } 4131 } 4132 4133 if (IsArraySpecies(cx, obj)) { 4134 /* Steps 7-12: Optimized for ordinary array. */ 4135 return ArraySliceOrdinary(cx, obj, k, final, args.rval()); 4136 } 4137 4138 /* Step 7. */ 4139 uint64_t count = final > k ? final - k : 0; 4140 4141 /* Step 8. */ 4142 RootedObject arr(cx); 4143 if (!ArraySpeciesCreate(cx, obj, count, &arr)) { 4144 return false; 4145 } 4146 4147 /* Step 9. */ 4148 uint64_t n = 0; 4149 4150 /* Step 10. */ 4151 RootedValue kValue(cx); 4152 while (k < final) { 4153 if (!CheckForInterrupt(cx)) { 4154 return false; 4155 } 4156 4157 /* Steps 10.a-b, and 10.c.i. */ 4158 bool kNotPresent; 4159 if (!::HasAndGetElement(cx, obj, k, &kNotPresent, &kValue)) { 4160 return false; 4161 } 4162 4163 /* Step 10.c. */ 4164 if (!kNotPresent) { 4165 /* Steps 10.c.ii. */ 4166 if (!DefineArrayElement(cx, arr, n, kValue)) { 4167 return false; 4168 } 4169 } 4170 /* Step 10.d. */ 4171 k++; 4172 4173 /* Step 10.e. */ 4174 n++; 4175 } 4176 4177 /* Step 11. */ 4178 if (!SetLengthProperty(cx, arr, n)) { 4179 return false; 4180 } 4181 4182 /* Step 12. */ 4183 args.rval().setObject(*arr); 4184 return true; 4185 } 4186 4187 static inline uint32_t NormalizeSliceTerm(int32_t value, uint32_t length) { 4188 if (value >= 0) { 4189 return std::min(uint32_t(value), length); 4190 } 4191 return uint32_t(std::max(int32_t(uint32_t(value) + length), 0)); 4192 } 4193 4194 static bool ArraySliceDenseKernel(JSContext* cx, ArrayObject* arr, 4195 int32_t beginArg, int32_t endArg, 4196 ArrayObject* result) { 4197 uint32_t length = arr->length(); 4198 4199 uint32_t begin = NormalizeSliceTerm(beginArg, length); 4200 uint32_t end = NormalizeSliceTerm(endArg, length); 4201 4202 if (begin > end) { 4203 begin = end; 4204 } 4205 4206 uint32_t count = end - begin; 4207 size_t initlen = arr->getDenseInitializedLength(); 4208 if (initlen > begin) { 4209 uint32_t newlength = std::min<uint32_t>(initlen - begin, count); 4210 if (newlength > 0) { 4211 if (!result->ensureElements(cx, newlength)) { 4212 return false; 4213 } 4214 result->initDenseElements(arr, begin, newlength); 4215 } 4216 } 4217 4218 MOZ_ASSERT(count >= result->length()); 4219 result->setLength(cx, count); 4220 4221 return true; 4222 } 4223 4224 JSObject* js::ArraySliceDense(JSContext* cx, HandleObject obj, int32_t begin, 4225 int32_t end, HandleObject result) { 4226 MOZ_ASSERT(IsPackedArray(obj)); 4227 4228 if (result && IsArraySpecies(cx, obj)) { 4229 if (!ArraySliceDenseKernel(cx, &obj->as<ArrayObject>(), begin, end, 4230 &result->as<ArrayObject>())) { 4231 return nullptr; 4232 } 4233 return result; 4234 } 4235 4236 // Slower path if the JIT wasn't able to allocate an object inline. 4237 JS::RootedValueArray<4> argv(cx); 4238 argv[0].setUndefined(); 4239 argv[1].setObject(*obj); 4240 argv[2].setInt32(begin); 4241 argv[3].setInt32(end); 4242 if (!array_slice(cx, 2, argv.begin())) { 4243 return nullptr; 4244 } 4245 return &argv[0].toObject(); 4246 } 4247 4248 JSObject* js::ArgumentsSliceDense(JSContext* cx, HandleObject obj, 4249 int32_t begin, int32_t end, 4250 HandleObject result) { 4251 MOZ_ASSERT(obj->is<ArgumentsObject>()); 4252 MOZ_ASSERT(IsArraySpecies(cx, obj)); 4253 4254 Handle<ArgumentsObject*> argsobj = obj.as<ArgumentsObject>(); 4255 MOZ_ASSERT(!argsobj->hasOverriddenLength()); 4256 MOZ_ASSERT(!argsobj->hasOverriddenElement()); 4257 4258 uint32_t length = argsobj->initialLength(); 4259 uint32_t actualBegin = NormalizeSliceTerm(begin, length); 4260 uint32_t actualEnd = NormalizeSliceTerm(end, length); 4261 4262 if (actualBegin > actualEnd) { 4263 actualBegin = actualEnd; 4264 } 4265 uint32_t count = actualEnd - actualBegin; 4266 4267 if (result) { 4268 Handle<ArrayObject*> resArray = result.as<ArrayObject>(); 4269 MOZ_ASSERT(resArray->getDenseInitializedLength() == 0); 4270 MOZ_ASSERT(resArray->length() == 0); 4271 4272 if (count > 0) { 4273 if (!resArray->ensureElements(cx, count)) { 4274 return nullptr; 4275 } 4276 resArray->setDenseInitializedLength(count); 4277 resArray->setLengthToInitializedLength(); 4278 4279 for (uint32_t index = 0; index < count; index++) { 4280 const Value& v = argsobj->element(actualBegin + index); 4281 resArray->initDenseElement(index, v); 4282 } 4283 } 4284 4285 return resArray; 4286 } 4287 4288 // Slower path if the JIT wasn't able to allocate an object inline. 4289 return SliceArguments(cx, argsobj, actualBegin, count); 4290 } 4291 4292 static bool array_isArray(JSContext* cx, unsigned argc, Value* vp) { 4293 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array", "isArray"); 4294 CallArgs args = CallArgsFromVp(argc, vp); 4295 4296 bool isArray = false; 4297 if (args.get(0).isObject()) { 4298 RootedObject obj(cx, &args[0].toObject()); 4299 if (!IsArray(cx, obj, &isArray)) { 4300 return false; 4301 } 4302 } 4303 args.rval().setBoolean(isArray); 4304 return true; 4305 } 4306 4307 static bool ArrayFromCallArgs(JSContext* cx, CallArgs& args, 4308 HandleObject proto = nullptr) { 4309 ArrayObject* obj = 4310 NewDenseCopiedArrayWithProto(cx, args.length(), args.array(), proto); 4311 if (!obj) { 4312 return false; 4313 } 4314 4315 args.rval().setObject(*obj); 4316 return true; 4317 } 4318 4319 static bool array_of(JSContext* cx, unsigned argc, Value* vp) { 4320 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array", "of"); 4321 CallArgs args = CallArgsFromVp(argc, vp); 4322 4323 bool isArrayConstructor = 4324 IsArrayConstructor(args.thisv()) && 4325 args.thisv().toObject().nonCCWRealm() == cx->realm(); 4326 4327 if (isArrayConstructor || !IsConstructor(args.thisv())) { 4328 // isArrayConstructor will usually be true in practice. This is the most 4329 // common path. 4330 return ArrayFromCallArgs(cx, args); 4331 } 4332 4333 // Step 4. 4334 RootedObject obj(cx); 4335 { 4336 FixedConstructArgs<1> cargs(cx); 4337 4338 cargs[0].setNumber(args.length()); 4339 4340 if (!Construct(cx, args.thisv(), cargs, args.thisv(), &obj)) { 4341 return false; 4342 } 4343 } 4344 4345 // Step 8. 4346 for (unsigned k = 0; k < args.length(); k++) { 4347 if (!DefineDataElement(cx, obj, k, args[k])) { 4348 return false; 4349 } 4350 } 4351 4352 // Steps 9-10. 4353 if (!SetLengthProperty(cx, obj, args.length())) { 4354 return false; 4355 } 4356 4357 // Step 11. 4358 args.rval().setObject(*obj); 4359 return true; 4360 } 4361 4362 static const JSJitInfo array_splice_info = { 4363 {(JSJitGetterOp)array_splice_noRetVal}, 4364 {0}, /* unused */ 4365 {0}, /* unused */ 4366 JSJitInfo::IgnoresReturnValueNative, 4367 JSJitInfo::AliasEverything, 4368 JSVAL_TYPE_UNDEFINED, 4369 }; 4370 4371 enum class SearchKind { 4372 // Specializes SearchElementDense for Array.prototype.indexOf/lastIndexOf. 4373 // This means hole values are ignored and StrictlyEqual semantics are used. 4374 IndexOf, 4375 // Specializes SearchElementDense for Array.prototype.includes. 4376 // This means hole values are treated as |undefined| and SameValueZero 4377 // semantics are used. 4378 Includes, 4379 }; 4380 4381 template <SearchKind Kind, typename Iter> 4382 static bool SearchElementDense(JSContext* cx, HandleValue val, Iter iterator, 4383 MutableHandleValue rval) { 4384 // We assume here and in the iterator lambdas that nothing can trigger GC or 4385 // move dense elements. 4386 AutoCheckCannotGC nogc; 4387 4388 // Fast path for string values. 4389 if (val.isString()) { 4390 JSLinearString* str = val.toString()->ensureLinear(cx); 4391 if (!str) { 4392 return false; 4393 } 4394 const uint32_t strLen = str->length(); 4395 auto cmp = [str, strLen](JSContext* cx, const Value& element, bool* equal) { 4396 if (!element.isString() || element.toString()->length() != strLen) { 4397 *equal = false; 4398 return true; 4399 } 4400 JSLinearString* s = element.toString()->ensureLinear(cx); 4401 if (!s) { 4402 return false; 4403 } 4404 *equal = EqualStrings(str, s); 4405 return true; 4406 }; 4407 return iterator(cx, cmp, rval); 4408 } 4409 4410 // Fast path for numbers. 4411 if (val.isNumber()) { 4412 double dval = val.toNumber(); 4413 // For |includes|, two NaN values are considered equal, so we use a 4414 // different implementation for NaN. 4415 if (Kind == SearchKind::Includes && std::isnan(dval)) { 4416 auto cmp = [](JSContext*, const Value& element, bool* equal) { 4417 *equal = (element.isDouble() && std::isnan(element.toDouble())); 4418 return true; 4419 }; 4420 return iterator(cx, cmp, rval); 4421 } 4422 auto cmp = [dval](JSContext*, const Value& element, bool* equal) { 4423 *equal = (element.isNumber() && element.toNumber() == dval); 4424 return true; 4425 }; 4426 return iterator(cx, cmp, rval); 4427 } 4428 4429 // Fast path for values where we can use a simple bitwise comparison. 4430 if (CanUseBitwiseCompareForStrictlyEqual(val)) { 4431 // For |includes| we need to treat hole values as |undefined| so we use a 4432 // different path if searching for |undefined|. 4433 if (Kind == SearchKind::Includes && val.isUndefined()) { 4434 auto cmp = [](JSContext*, const Value& element, bool* equal) { 4435 *equal = (element.isUndefined() || element.isMagic(JS_ELEMENTS_HOLE)); 4436 return true; 4437 }; 4438 return iterator(cx, cmp, rval); 4439 } 4440 uint64_t bits = val.asRawBits(); 4441 auto cmp = [bits](JSContext*, const Value& element, bool* equal) { 4442 *equal = (bits == element.asRawBits()); 4443 return true; 4444 }; 4445 return iterator(cx, cmp, rval); 4446 } 4447 4448 MOZ_ASSERT(val.isBigInt()); 4449 4450 // Generic implementation for the remaining types. 4451 auto cmp = [val](JSContext* cx, const Value& element, bool* equal) { 4452 if (MOZ_UNLIKELY(element.isMagic(JS_ELEMENTS_HOLE))) { 4453 // |includes| treats holes as |undefined|, but |undefined| is already 4454 // handled above. For |indexOf| we have to ignore holes. 4455 *equal = false; 4456 return true; 4457 } 4458 // Note: |includes| uses SameValueZero, but that checks for NaN and then 4459 // calls StrictlyEqual. Since we already handled NaN above, we can call 4460 // StrictlyEqual directly. 4461 MOZ_ASSERT(!val.isNumber()); 4462 return StrictlyEqual(cx, val, element, equal); 4463 }; 4464 return iterator(cx, cmp, rval); 4465 } 4466 4467 // ES2026 draft rev a562082b031d89d00ee667181ce8a6158656bd4b 4468 // 23.1.3.17 Array.prototype.indexOf ( searchElement [ , fromIndex ] ) 4469 bool js::array_indexOf(JSContext* cx, unsigned argc, Value* vp) { 4470 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "indexOf"); 4471 CallArgs args = CallArgsFromVp(argc, vp); 4472 4473 // Step 1. 4474 RootedObject obj(cx, ToObject(cx, args.thisv())); 4475 if (!obj) { 4476 return false; 4477 } 4478 4479 // Step 2. 4480 uint64_t len; 4481 if (!GetLengthPropertyInlined(cx, obj, &len)) { 4482 return false; 4483 } 4484 4485 // Step 3. 4486 if (len == 0) { 4487 args.rval().setInt32(-1); 4488 return true; 4489 } 4490 4491 // Steps 4-9. 4492 uint64_t k = 0; 4493 if (args.hasDefined(1)) { 4494 if (!ToIntegerIndex(cx, args[1], len, &k)) { 4495 return false; 4496 } 4497 4498 // Return early if |k| exceeds the current length. 4499 if (k >= len) { 4500 args.rval().setInt32(-1); 4501 return true; 4502 } 4503 } 4504 4505 MOZ_ASSERT(k < len); 4506 4507 HandleValue searchElement = args.get(0); 4508 4509 // Step 10 optimized for dense elements. 4510 if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, len)) { 4511 MOZ_ASSERT(len <= UINT32_MAX); 4512 4513 NativeObject* nobj = &obj->as<NativeObject>(); 4514 uint32_t start = uint32_t(k); 4515 uint32_t length = 4516 std::min(nobj->getDenseInitializedLength(), uint32_t(len)); 4517 const Value* elements = nobj->getDenseElements(); 4518 4519 if (CanUseBitwiseCompareForStrictlyEqual(searchElement) && length > start) { 4520 const uint64_t* elementsAsBits = 4521 reinterpret_cast<const uint64_t*>(elements); 4522 const uint64_t* res = SIMD::memchr64( 4523 elementsAsBits + start, searchElement.asRawBits(), length - start); 4524 if (res) { 4525 args.rval().setInt32(static_cast<int32_t>(res - elementsAsBits)); 4526 } else { 4527 args.rval().setInt32(-1); 4528 } 4529 return true; 4530 } 4531 4532 auto iterator = [elements, start, length](JSContext* cx, auto cmp, 4533 MutableHandleValue rval) { 4534 static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX, 4535 "code assumes dense index fits in Int32Value"); 4536 for (uint32_t i = start; i < length; i++) { 4537 bool equal; 4538 if (MOZ_UNLIKELY(!cmp(cx, elements[i], &equal))) { 4539 return false; 4540 } 4541 if (equal) { 4542 rval.setInt32(int32_t(i)); 4543 return true; 4544 } 4545 } 4546 rval.setInt32(-1); 4547 return true; 4548 }; 4549 return SearchElementDense<SearchKind::IndexOf>(cx, searchElement, iterator, 4550 args.rval()); 4551 } 4552 4553 // Step 10. 4554 RootedValue v(cx); 4555 for (; k < len; k++) { 4556 if (!CheckForInterrupt(cx)) { 4557 return false; 4558 } 4559 4560 bool hole; 4561 if (!::HasAndGetElement(cx, obj, k, &hole, &v)) { 4562 return false; 4563 } 4564 if (hole) { 4565 continue; 4566 } 4567 4568 bool equal; 4569 if (!StrictlyEqual(cx, v, searchElement, &equal)) { 4570 return false; 4571 } 4572 if (equal) { 4573 args.rval().setNumber(k); 4574 return true; 4575 } 4576 } 4577 4578 // Step 11. 4579 args.rval().setInt32(-1); 4580 return true; 4581 } 4582 4583 // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9 4584 // 22.1.3.17 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] ) 4585 bool js::array_lastIndexOf(JSContext* cx, unsigned argc, Value* vp) { 4586 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "lastIndexOf"); 4587 CallArgs args = CallArgsFromVp(argc, vp); 4588 4589 // Step 1. 4590 RootedObject obj(cx, ToObject(cx, args.thisv())); 4591 if (!obj) { 4592 return false; 4593 } 4594 4595 // Step 2. 4596 uint64_t len; 4597 if (!GetLengthPropertyInlined(cx, obj, &len)) { 4598 return false; 4599 } 4600 4601 // Step 3. 4602 if (len == 0) { 4603 args.rval().setInt32(-1); 4604 return true; 4605 } 4606 4607 // Steps 4-6. 4608 uint64_t k = len - 1; 4609 if (args.length() > 1) { 4610 double n; 4611 if (!ToInteger(cx, args[1], &n)) { 4612 return false; 4613 } 4614 4615 // Steps 5-6. 4616 if (n < 0) { 4617 double d = double(len) + n; 4618 if (d < 0) { 4619 args.rval().setInt32(-1); 4620 return true; 4621 } 4622 k = uint64_t(d); 4623 } else if (n < double(k)) { 4624 k = uint64_t(n); 4625 } 4626 } 4627 4628 MOZ_ASSERT(k < len); 4629 4630 HandleValue searchElement = args.get(0); 4631 4632 // Steps 7 and 8 optimized for dense elements. 4633 if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, k + 1)) { 4634 MOZ_ASSERT(k <= UINT32_MAX); 4635 4636 NativeObject* nobj = &obj->as<NativeObject>(); 4637 uint32_t initLen = nobj->getDenseInitializedLength(); 4638 if (initLen == 0) { 4639 args.rval().setInt32(-1); 4640 return true; 4641 } 4642 4643 uint32_t end = std::min(uint32_t(k), initLen - 1); 4644 const Value* elements = nobj->getDenseElements(); 4645 4646 auto iterator = [elements, end](JSContext* cx, auto cmp, 4647 MutableHandleValue rval) { 4648 static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX, 4649 "code assumes dense index fits in int32_t"); 4650 for (int32_t i = int32_t(end); i >= 0; i--) { 4651 bool equal; 4652 if (MOZ_UNLIKELY(!cmp(cx, elements[i], &equal))) { 4653 return false; 4654 } 4655 if (equal) { 4656 rval.setInt32(int32_t(i)); 4657 return true; 4658 } 4659 } 4660 rval.setInt32(-1); 4661 return true; 4662 }; 4663 return SearchElementDense<SearchKind::IndexOf>(cx, searchElement, iterator, 4664 args.rval()); 4665 } 4666 4667 // Step 7. 4668 RootedValue v(cx); 4669 for (int64_t i = int64_t(k); i >= 0; i--) { 4670 if (!CheckForInterrupt(cx)) { 4671 return false; 4672 } 4673 4674 bool hole; 4675 if (!::HasAndGetElement(cx, obj, uint64_t(i), &hole, &v)) { 4676 return false; 4677 } 4678 if (hole) { 4679 continue; 4680 } 4681 4682 bool equal; 4683 if (!StrictlyEqual(cx, v, searchElement, &equal)) { 4684 return false; 4685 } 4686 if (equal) { 4687 args.rval().setNumber(uint64_t(i)); 4688 return true; 4689 } 4690 } 4691 4692 // Step 8. 4693 args.rval().setInt32(-1); 4694 return true; 4695 } 4696 4697 // ES2026 draft rev a562082b031d89d00ee667181ce8a6158656bd4b 4698 // 23.1.3.16 Array.prototype.includes ( searchElement [ , fromIndex ] ) 4699 bool js::array_includes(JSContext* cx, unsigned argc, Value* vp) { 4700 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "includes"); 4701 CallArgs args = CallArgsFromVp(argc, vp); 4702 4703 // Step 1. 4704 RootedObject obj(cx, ToObject(cx, args.thisv())); 4705 if (!obj) { 4706 return false; 4707 } 4708 4709 // Step 2. 4710 uint64_t len; 4711 if (!GetLengthPropertyInlined(cx, obj, &len)) { 4712 return false; 4713 } 4714 4715 // Step 3. 4716 if (len == 0) { 4717 args.rval().setBoolean(false); 4718 return true; 4719 } 4720 4721 // Steps 4-9. 4722 uint64_t k = 0; 4723 if (args.hasDefined(1)) { 4724 if (!ToIntegerIndex(cx, args[1], len, &k)) { 4725 return false; 4726 } 4727 4728 // Return early if |k| exceeds the current length. 4729 if (k >= len) { 4730 args.rval().setBoolean(false); 4731 return true; 4732 } 4733 } 4734 4735 MOZ_ASSERT(k < len); 4736 4737 HandleValue searchElement = args.get(0); 4738 4739 // Step 10 optimized for dense elements. 4740 if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, len)) { 4741 MOZ_ASSERT(len <= UINT32_MAX); 4742 4743 NativeObject* nobj = &obj->as<NativeObject>(); 4744 uint32_t start = uint32_t(k); 4745 uint32_t length = 4746 std::min(nobj->getDenseInitializedLength(), uint32_t(len)); 4747 const Value* elements = nobj->getDenseElements(); 4748 4749 // Trailing holes are treated as |undefined|. 4750 if (uint32_t(len) > length && searchElement.isUndefined()) { 4751 // |undefined| is strictly equal only to |undefined|. 4752 args.rval().setBoolean(true); 4753 return true; 4754 } 4755 4756 // For |includes| we need to treat hole values as |undefined| so we use a 4757 // different path if searching for |undefined|. 4758 if (CanUseBitwiseCompareForStrictlyEqual(searchElement) && 4759 !searchElement.isUndefined() && length > start) { 4760 if (SIMD::memchr64(reinterpret_cast<const uint64_t*>(elements) + start, 4761 searchElement.asRawBits(), length - start)) { 4762 args.rval().setBoolean(true); 4763 } else { 4764 args.rval().setBoolean(false); 4765 } 4766 return true; 4767 } 4768 4769 auto iterator = [elements, start, length](JSContext* cx, auto cmp, 4770 MutableHandleValue rval) { 4771 for (uint32_t i = start; i < length; i++) { 4772 bool equal; 4773 if (MOZ_UNLIKELY(!cmp(cx, elements[i], &equal))) { 4774 return false; 4775 } 4776 if (equal) { 4777 rval.setBoolean(true); 4778 return true; 4779 } 4780 } 4781 rval.setBoolean(false); 4782 return true; 4783 }; 4784 return SearchElementDense<SearchKind::Includes>(cx, searchElement, iterator, 4785 args.rval()); 4786 } 4787 4788 // Step 10. 4789 RootedValue v(cx); 4790 for (; k < len; k++) { 4791 if (!CheckForInterrupt(cx)) { 4792 return false; 4793 } 4794 4795 if (!GetArrayElement(cx, obj, k, &v)) { 4796 return false; 4797 } 4798 4799 bool equal; 4800 if (!SameValueZero(cx, v, searchElement, &equal)) { 4801 return false; 4802 } 4803 if (equal) { 4804 args.rval().setBoolean(true); 4805 return true; 4806 } 4807 } 4808 4809 // Step 11. 4810 args.rval().setBoolean(false); 4811 return true; 4812 } 4813 4814 // ES2024 draft 23.1.3.2.1 IsConcatSpreadable 4815 static bool IsConcatSpreadable(JSContext* cx, HandleValue v, bool* spreadable) { 4816 // Step 1. 4817 if (!v.isObject()) { 4818 *spreadable = false; 4819 return true; 4820 } 4821 4822 // Step 2. 4823 JS::Symbol* sym = cx->wellKnownSymbols().isConcatSpreadable; 4824 JSObject* holder; 4825 if (MOZ_UNLIKELY( 4826 MaybeHasInterestingSymbolProperty(cx, &v.toObject(), sym, &holder))) { 4827 RootedValue res(cx); 4828 RootedObject obj(cx, holder); 4829 Rooted<PropertyKey> key(cx, PropertyKey::Symbol(sym)); 4830 if (!GetProperty(cx, obj, v, key, &res)) { 4831 return false; 4832 } 4833 // Step 3. 4834 if (!res.isUndefined()) { 4835 *spreadable = ToBoolean(res); 4836 return true; 4837 } 4838 } 4839 4840 // Step 4. 4841 if (MOZ_LIKELY(v.toObject().is<ArrayObject>())) { 4842 *spreadable = true; 4843 return true; 4844 } 4845 RootedObject obj(cx, &v.toObject()); 4846 bool isArray; 4847 if (!JS::IsArray(cx, obj, &isArray)) { 4848 return false; 4849 } 4850 *spreadable = isArray; 4851 return true; 4852 } 4853 4854 // Returns true if the object may have an @@isConcatSpreadable property. 4855 static bool MaybeHasIsConcatSpreadable(JSContext* cx, JSObject* obj) { 4856 JS::Symbol* sym = cx->wellKnownSymbols().isConcatSpreadable; 4857 JSObject* holder; 4858 return MaybeHasInterestingSymbolProperty(cx, obj, sym, &holder); 4859 } 4860 4861 static bool TryOptimizePackedArrayConcat(JSContext* cx, CallArgs& args, 4862 Handle<JSObject*> obj, 4863 bool* optimized) { 4864 // Fast path for the following cases: 4865 // 4866 // (1) packedArray.concat(): copy the array's elements. 4867 // (2) packedArray.concat(packedArray): concatenate two packed arrays. 4868 // (3) packedArray.concat(value): copy and append a single non-array value. 4869 // 4870 // These cases account for almost all calls to Array.prototype.concat in 4871 // Speedometer 3. 4872 4873 *optimized = false; 4874 4875 if (args.length() > 1) { 4876 return true; 4877 } 4878 4879 // The `this` object must be a packed array without @@isConcatSpreadable. 4880 // @@isConcatSpreadable is uncommon and requires a property lookup and more 4881 // complicated code, so we let the slow path handle it. 4882 if (!IsPackedArray(obj)) { 4883 return true; 4884 } 4885 if (MaybeHasIsConcatSpreadable(cx, obj)) { 4886 return true; 4887 } 4888 4889 Handle<ArrayObject*> thisArr = obj.as<ArrayObject>(); 4890 uint32_t thisLen = thisArr->length(); 4891 4892 if (args.length() == 0) { 4893 // Case (1). Copy the packed array. 4894 ArrayObject* arr = NewDenseFullyAllocatedArray(cx, thisLen); 4895 if (!arr) { 4896 return false; 4897 } 4898 arr->initDenseElements(thisArr->getDenseElements(), thisLen); 4899 args.rval().setObject(*arr); 4900 *optimized = true; 4901 return true; 4902 } 4903 4904 MOZ_ASSERT(args.length() == 1); 4905 4906 // If the argument is an object, it must not have an @@isConcatSpreadable 4907 // property. 4908 if (args[0].isObject() && 4909 MaybeHasIsConcatSpreadable(cx, &args[0].toObject())) { 4910 return true; 4911 } 4912 4913 MOZ_ASSERT_IF(args[0].isObject(), args[0].toObject().is<NativeObject>()); 4914 4915 // Case (3). Copy and append a single value if the argument is not an array. 4916 if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>()) { 4917 ArrayObject* arr = NewDenseFullyAllocatedArray(cx, thisLen + 1); 4918 if (!arr) { 4919 return false; 4920 } 4921 arr->initDenseElements(thisArr->getDenseElements(), thisLen); 4922 4923 arr->ensureDenseInitializedLength(thisLen, 1); 4924 arr->initDenseElement(thisLen, args[0]); 4925 4926 args.rval().setObject(*arr); 4927 *optimized = true; 4928 return true; 4929 } 4930 4931 // Case (2). Concatenate two packed arrays. 4932 if (!IsPackedArray(&args[0].toObject())) { 4933 return true; 4934 } 4935 4936 uint32_t argLen = args[0].toObject().as<ArrayObject>().length(); 4937 4938 // Compute the array length. This can't overflow because both arrays are 4939 // packed. 4940 static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < INT32_MAX); 4941 MOZ_ASSERT(thisLen <= NativeObject::MAX_DENSE_ELEMENTS_COUNT); 4942 MOZ_ASSERT(argLen <= NativeObject::MAX_DENSE_ELEMENTS_COUNT); 4943 uint32_t totalLen = thisLen + argLen; 4944 4945 ArrayObject* arr = NewDenseFullyAllocatedArray(cx, totalLen); 4946 if (!arr) { 4947 return false; 4948 } 4949 arr->initDenseElements(thisArr->getDenseElements(), thisLen); 4950 4951 ArrayObject* argArr = &args[0].toObject().as<ArrayObject>(); 4952 arr->ensureDenseInitializedLength(thisLen, argLen); 4953 arr->initDenseElementRange(thisLen, argArr, argLen); 4954 4955 args.rval().setObject(*arr); 4956 *optimized = true; 4957 return true; 4958 } 4959 4960 static bool array_concat(JSContext* cx, unsigned argc, Value* vp) { 4961 AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "concat"); 4962 CallArgs args = CallArgsFromVp(argc, vp); 4963 4964 // Step 1. 4965 RootedObject obj(cx, ToObject(cx, args.thisv())); 4966 if (!obj) { 4967 return false; 4968 } 4969 4970 bool isArraySpecies = IsArraySpecies(cx, obj); 4971 4972 // Fast path for the most common cases. 4973 if (isArraySpecies) { 4974 bool optimized; 4975 if (!TryOptimizePackedArrayConcat(cx, args, obj, &optimized)) { 4976 return false; 4977 } 4978 if (optimized) { 4979 return true; 4980 } 4981 } 4982 4983 // Step 2. 4984 RootedObject arr(cx); 4985 if (isArraySpecies) { 4986 arr = NewDenseEmptyArray(cx); 4987 if (!arr) { 4988 return false; 4989 } 4990 } else { 4991 if (!ArraySpeciesCreate(cx, obj, 0, &arr)) { 4992 return false; 4993 } 4994 } 4995 4996 // Step 3. 4997 uint64_t n = 0; 4998 4999 // Step 4 (handled implicitly with nextArg and CallArgs). 5000 uint32_t nextArg = 0; 5001 5002 // Step 5. 5003 RootedValue v(cx, ObjectValue(*obj)); 5004 while (true) { 5005 // Step 5.a. 5006 bool spreadable; 5007 if (!IsConcatSpreadable(cx, v, &spreadable)) { 5008 return false; 5009 } 5010 // Step 5.b. 5011 if (spreadable) { 5012 // Step 5.b.i. 5013 obj = &v.toObject(); 5014 uint64_t len; 5015 if (!GetLengthPropertyInlined(cx, obj, &len)) { 5016 return false; 5017 } 5018 5019 // Step 5.b.ii. 5020 if (n + len > uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT) - 1) { 5021 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 5022 JSMSG_TOO_LONG_ARRAY); 5023 return false; 5024 } 5025 5026 // Step 5.b.iii. 5027 uint64_t k = 0; 5028 5029 // Step 5.b.iv. 5030 5031 // Try a fast path for copying dense elements directly. 5032 bool optimized = false; 5033 if (len > 0 && isArraySpecies && 5034 CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, len) && 5035 n + len <= NativeObject::MAX_DENSE_ELEMENTS_COUNT) { 5036 NativeObject* nobj = &obj->as<NativeObject>(); 5037 ArrayObject* resArr = &arr->as<ArrayObject>(); 5038 uint32_t count = 5039 std::min(uint32_t(len), nobj->getDenseInitializedLength()); 5040 5041 DenseElementResult res = resArr->ensureDenseElements(cx, n, count); 5042 if (res == DenseElementResult::Failure) { 5043 return false; 5044 } 5045 if (res == DenseElementResult::Success) { 5046 resArr->initDenseElementRange(n, nobj, count); 5047 n += len; 5048 optimized = true; 5049 } else { 5050 MOZ_ASSERT(res == DenseElementResult::Incomplete); 5051 } 5052 } 5053 5054 if (!optimized) { 5055 // Step 5.b.iv. 5056 while (k < len) { 5057 if (!CheckForInterrupt(cx)) { 5058 return false; 5059 } 5060 5061 // Step 5.b.iv.2. 5062 bool hole; 5063 if (!::HasAndGetElement(cx, obj, k, &hole, &v)) { 5064 return false; 5065 } 5066 if (!hole) { 5067 // Step 5.b.iv.3. 5068 if (!DefineArrayElement(cx, arr, n, v)) { 5069 return false; 5070 } 5071 } 5072 5073 // Step 5.b.iv.4. 5074 n++; 5075 5076 // Step 5.b.iv.5. 5077 k++; 5078 } 5079 } 5080 } else { 5081 // Step 5.c.ii. 5082 if (n >= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT) - 1) { 5083 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 5084 JSMSG_TOO_LONG_ARRAY); 5085 return false; 5086 } 5087 5088 // Step 5.c.iii. 5089 if (!DefineArrayElement(cx, arr, n, v)) { 5090 return false; 5091 } 5092 5093 // Step 5.c.iv. 5094 n++; 5095 } 5096 5097 // Move on to the next argument. 5098 if (nextArg == args.length()) { 5099 break; 5100 } 5101 v = args[nextArg]; 5102 nextArg++; 5103 } 5104 5105 // Step 6. 5106 if (!SetLengthProperty(cx, arr, n)) { 5107 return false; 5108 } 5109 5110 // Step 7. 5111 args.rval().setObject(*arr); 5112 return true; 5113 } 5114 5115 static const JSFunctionSpec array_methods[] = { 5116 JS_FN("toSource", array_toSource, 0, 0), 5117 JS_SELF_HOSTED_FN("toString", "ArrayToString", 0, 0), 5118 JS_FN("toLocaleString", array_toLocaleString, 0, 0), 5119 5120 /* Perl-ish methods. */ 5121 JS_INLINABLE_FN("join", array_join, 1, 0, ArrayJoin), 5122 JS_FN("reverse", array_reverse, 0, 0), 5123 JS_TRAMPOLINE_FN("sort", array_sort, 1, 0, ArraySort), 5124 JS_INLINABLE_FN("push", array_push, 1, 0, ArrayPush), 5125 JS_INLINABLE_FN("pop", array_pop, 0, 0, ArrayPop), 5126 JS_INLINABLE_FN("shift", array_shift, 0, 0, ArrayShift), 5127 JS_FN("unshift", array_unshift, 1, 0), 5128 JS_FNINFO("splice", array_splice, &array_splice_info, 2, 0), 5129 5130 /* Pythonic sequence methods. */ 5131 JS_FN("concat", array_concat, 1, 0), 5132 JS_INLINABLE_FN("slice", array_slice, 2, 0, ArraySlice), 5133 5134 JS_FN("lastIndexOf", array_lastIndexOf, 1, 0), 5135 JS_FN("indexOf", array_indexOf, 1, 0), 5136 JS_SELF_HOSTED_FN("forEach", "ArrayForEach", 1, 0), 5137 JS_SELF_HOSTED_FN("map", "ArrayMap", 1, 0), 5138 JS_SELF_HOSTED_FN("filter", "ArrayFilter", 1, 0), 5139 JS_SELF_HOSTED_FN("reduce", "ArrayReduce", 1, 0), 5140 JS_SELF_HOSTED_FN("reduceRight", "ArrayReduceRight", 1, 0), 5141 JS_SELF_HOSTED_FN("some", "ArraySome", 1, 0), 5142 JS_SELF_HOSTED_FN("every", "ArrayEvery", 1, 0), 5143 5144 /* ES6 additions */ 5145 JS_SELF_HOSTED_FN("find", "ArrayFind", 1, 0), 5146 JS_SELF_HOSTED_FN("findIndex", "ArrayFindIndex", 1, 0), 5147 JS_SELF_HOSTED_FN("copyWithin", "ArrayCopyWithin", 3, 0), 5148 5149 JS_SELF_HOSTED_FN("fill", "ArrayFill", 3, 0), 5150 5151 JS_SELF_HOSTED_SYM_FN(iterator, "$ArrayValues", 0, 0), 5152 JS_SELF_HOSTED_FN("entries", "ArrayEntries", 0, 0), 5153 JS_SELF_HOSTED_FN("keys", "ArrayKeys", 0, 0), 5154 JS_SELF_HOSTED_FN("values", "$ArrayValues", 0, 0), 5155 5156 /* ES7 additions */ 5157 JS_FN("includes", array_includes, 1, 0), 5158 5159 /* ES2020 */ 5160 JS_SELF_HOSTED_FN("flatMap", "ArrayFlatMap", 1, 0), 5161 JS_SELF_HOSTED_FN("flat", "ArrayFlat", 0, 0), 5162 5163 JS_SELF_HOSTED_FN("at", "ArrayAt", 1, 0), 5164 JS_SELF_HOSTED_FN("findLast", "ArrayFindLast", 1, 0), 5165 JS_SELF_HOSTED_FN("findLastIndex", "ArrayFindLastIndex", 1, 0), 5166 5167 JS_SELF_HOSTED_FN("toReversed", "ArrayToReversed", 0, 0), 5168 JS_SELF_HOSTED_FN("toSorted", "ArrayToSorted", 1, 0), 5169 JS_FN("toSpliced", array_toSpliced, 2, 0), 5170 JS_FN("with", array_with, 2, 0), 5171 5172 JS_FS_END, 5173 }; 5174 5175 static const JSFunctionSpec array_static_methods[] = { 5176 JS_INLINABLE_FN("isArray", array_isArray, 1, 0, ArrayIsArray), 5177 JS_SELF_HOSTED_FN("from", "ArrayFrom", 3, 0), 5178 JS_SELF_HOSTED_FN("fromAsync", "ArrayFromAsync", 3, 0), 5179 JS_FN("of", array_of, 0, 0), 5180 5181 JS_FS_END, 5182 }; 5183 5184 const JSPropertySpec array_static_props[] = { 5185 JS_SELF_HOSTED_SYM_GET(species, "$ArraySpecies", 0), 5186 JS_PS_END, 5187 }; 5188 5189 static inline bool ArrayConstructorImpl(JSContext* cx, CallArgs& args, 5190 bool isConstructor) { 5191 RootedObject proto(cx); 5192 if (isConstructor) { 5193 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Array, &proto)) { 5194 return false; 5195 } 5196 } 5197 5198 if (args.length() != 1 || !args[0].isNumber()) { 5199 return ArrayFromCallArgs(cx, args, proto); 5200 } 5201 5202 uint32_t length; 5203 if (args[0].isInt32()) { 5204 int32_t i = args[0].toInt32(); 5205 if (i < 0) { 5206 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 5207 JSMSG_BAD_ARRAY_LENGTH); 5208 return false; 5209 } 5210 length = uint32_t(i); 5211 } else { 5212 double d = args[0].toDouble(); 5213 length = ToUint32(d); 5214 if (d != double(length)) { 5215 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 5216 JSMSG_BAD_ARRAY_LENGTH); 5217 return false; 5218 } 5219 } 5220 5221 ArrayObject* obj = NewDensePartlyAllocatedArrayWithProto(cx, length, proto); 5222 if (!obj) { 5223 return false; 5224 } 5225 5226 args.rval().setObject(*obj); 5227 return true; 5228 } 5229 5230 /* ES5 15.4.2 */ 5231 bool js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp) { 5232 AutoJSConstructorProfilerEntry pseudoFrame(cx, "Array"); 5233 CallArgs args = CallArgsFromVp(argc, vp); 5234 return ArrayConstructorImpl(cx, args, /* isConstructor = */ true); 5235 } 5236 5237 bool js::array_construct(JSContext* cx, unsigned argc, Value* vp) { 5238 AutoJSConstructorProfilerEntry pseudoFrame(cx, "Array"); 5239 CallArgs args = CallArgsFromVp(argc, vp); 5240 MOZ_ASSERT(!args.isConstructing()); 5241 MOZ_ASSERT(args.length() == 1); 5242 MOZ_ASSERT(args[0].isNumber()); 5243 return ArrayConstructorImpl(cx, args, /* isConstructor = */ false); 5244 } 5245 5246 ArrayObject* js::ArrayConstructorOneArg(JSContext* cx, 5247 Handle<ArrayObject*> templateObject, 5248 int32_t lengthInt, 5249 gc::AllocSite* site) { 5250 // JIT code can call this with a template object from a different realm when 5251 // calling another realm's Array constructor. 5252 Maybe<AutoRealm> ar; 5253 if (cx->realm() != templateObject->realm()) { 5254 MOZ_ASSERT(cx->compartment() == templateObject->compartment()); 5255 ar.emplace(cx, templateObject); 5256 } 5257 5258 if (lengthInt < 0) { 5259 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 5260 JSMSG_BAD_ARRAY_LENGTH); 5261 return nullptr; 5262 } 5263 5264 uint32_t length = uint32_t(lengthInt); 5265 ArrayObject* res = 5266 NewDensePartlyAllocatedArray(cx, length, GenericObject, site); 5267 MOZ_ASSERT_IF(res, res->realm() == templateObject->realm()); 5268 return res; 5269 } 5270 5271 /* 5272 * Array allocation functions. 5273 */ 5274 5275 static inline bool EnsureNewArrayElements(JSContext* cx, ArrayObject* obj, 5276 uint32_t length) { 5277 /* 5278 * If ensureElements creates dynamically allocated slots, then having 5279 * fixedSlots is a waste. 5280 */ 5281 DebugOnly<uint32_t> cap = obj->getDenseCapacity(); 5282 5283 if (!obj->ensureElements(cx, length)) { 5284 return false; 5285 } 5286 5287 MOZ_ASSERT_IF(cap, !obj->hasDynamicElements()); 5288 5289 return true; 5290 } 5291 5292 template <uint32_t maxLength> 5293 static MOZ_ALWAYS_INLINE ArrayObject* NewArrayWithShape( 5294 JSContext* cx, Handle<SharedShape*> shape, uint32_t length, 5295 NewObjectKind newKind, gc::AllocSite* site = nullptr) { 5296 // The shape must already have the |length| property defined on it. 5297 MOZ_ASSERT(shape->propMapLength() == 1); 5298 MOZ_ASSERT(shape->lastProperty().key() == NameToId(cx->names().length)); 5299 5300 gc::AllocKind allocKind = GuessArrayGCKind(length); 5301 MOZ_ASSERT(gc::GetObjectFinalizeKind(&ArrayObject::class_) == 5302 gc::FinalizeKind::None); 5303 MOZ_ASSERT(!IsFinalizedKind(allocKind)); 5304 5305 MOZ_ASSERT(shape->slotSpan() == 0); 5306 constexpr uint32_t slotSpan = 0; 5307 5308 AutoSetNewObjectMetadata metadata(cx); 5309 ArrayObject* arr = ArrayObject::create( 5310 cx, allocKind, GetInitialHeap(newKind, &ArrayObject::class_, site), shape, 5311 length, slotSpan, metadata, site); 5312 if (!arr) { 5313 return nullptr; 5314 } 5315 5316 if (maxLength > 0 && 5317 !EnsureNewArrayElements(cx, arr, std::min(maxLength, length))) { 5318 return nullptr; 5319 } 5320 5321 probes::CreateObject(cx, arr); 5322 return arr; 5323 } 5324 5325 static SharedShape* GetArrayShapeWithProto(JSContext* cx, HandleObject proto) { 5326 // Get a shape with zero fixed slots, because arrays store the ObjectElements 5327 // header inline. 5328 Rooted<SharedShape*> shape( 5329 cx, SharedShape::getInitialShape(cx, &ArrayObject::class_, cx->realm(), 5330 TaggedProto(proto), /* nfixed = */ 0)); 5331 if (!shape) { 5332 return nullptr; 5333 } 5334 5335 // Add the |length| property and use the new shape as initial shape for new 5336 // arrays. 5337 if (shape->propMapLength() == 0) { 5338 shape = AddLengthProperty(cx, shape); 5339 if (!shape) { 5340 return nullptr; 5341 } 5342 SharedShape::insertInitialShape(cx, shape); 5343 } else { 5344 MOZ_ASSERT(shape->propMapLength() == 1); 5345 MOZ_ASSERT(shape->lastProperty().key() == NameToId(cx->names().length)); 5346 } 5347 5348 return shape; 5349 } 5350 5351 SharedShape* GlobalObject::createArrayShapeWithDefaultProto(JSContext* cx) { 5352 MOZ_ASSERT(!cx->global()->data().arrayShapeWithDefaultProto); 5353 5354 RootedObject proto(cx, 5355 GlobalObject::getOrCreateArrayPrototype(cx, cx->global())); 5356 if (!proto) { 5357 return nullptr; 5358 } 5359 5360 SharedShape* shape = GetArrayShapeWithProto(cx, proto); 5361 if (!shape) { 5362 return nullptr; 5363 } 5364 5365 cx->global()->data().arrayShapeWithDefaultProto.init(shape); 5366 return shape; 5367 } 5368 5369 template <uint32_t maxLength> 5370 static MOZ_ALWAYS_INLINE ArrayObject* NewArray(JSContext* cx, uint32_t length, 5371 NewObjectKind newKind, 5372 gc::AllocSite* site = nullptr) { 5373 Rooted<SharedShape*> shape(cx, 5374 GlobalObject::getArrayShapeWithDefaultProto(cx)); 5375 if (!shape) { 5376 return nullptr; 5377 } 5378 5379 return NewArrayWithShape<maxLength>(cx, shape, length, newKind, site); 5380 } 5381 5382 template <uint32_t maxLength> 5383 static MOZ_ALWAYS_INLINE ArrayObject* NewArrayWithProto(JSContext* cx, 5384 uint32_t length, 5385 HandleObject proto, 5386 NewObjectKind newKind) { 5387 Rooted<SharedShape*> shape(cx); 5388 if (!proto || proto == cx->global()->maybeGetArrayPrototype()) { 5389 shape = GlobalObject::getArrayShapeWithDefaultProto(cx); 5390 } else { 5391 shape = GetArrayShapeWithProto(cx, proto); 5392 } 5393 if (!shape) { 5394 return nullptr; 5395 } 5396 5397 return NewArrayWithShape<maxLength>(cx, shape, length, newKind, nullptr); 5398 } 5399 5400 static JSObject* CreateArrayConstructor(JSContext* cx, JSProtoKey key) { 5401 MOZ_ASSERT(key == JSProto_Array); 5402 Rooted<JSObject*> ctor(cx, GlobalObject::createConstructor( 5403 cx, ArrayConstructor, cx->names().Array, 1, 5404 gc::AllocKind::FUNCTION, &jit::JitInfo_Array)); 5405 if (!ctor) { 5406 return nullptr; 5407 } 5408 if (!JSObject::setHasRealmFuseProperty(cx, ctor)) { 5409 return nullptr; 5410 } 5411 return ctor; 5412 } 5413 5414 static JSObject* CreateArrayPrototype(JSContext* cx, JSProtoKey key) { 5415 MOZ_ASSERT(key == JSProto_Array); 5416 RootedObject proto(cx, &cx->global()->getObjectPrototype()); 5417 return NewArrayWithProto<0>(cx, 0, proto, TenuredObject); 5418 } 5419 5420 static bool array_proto_finish(JSContext* cx, JS::HandleObject ctor, 5421 JS::HandleObject proto) { 5422 // Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32. 5423 RootedObject unscopables(cx, 5424 NewPlainObjectWithProto(cx, nullptr, TenuredObject)); 5425 if (!unscopables) { 5426 return false; 5427 } 5428 5429 RootedValue value(cx, BooleanValue(true)); 5430 if (!DefineDataProperty(cx, unscopables, cx->names().at, value) || 5431 !DefineDataProperty(cx, unscopables, cx->names().copyWithin, value) || 5432 !DefineDataProperty(cx, unscopables, cx->names().entries, value) || 5433 !DefineDataProperty(cx, unscopables, cx->names().fill, value) || 5434 !DefineDataProperty(cx, unscopables, cx->names().find, value) || 5435 !DefineDataProperty(cx, unscopables, cx->names().findIndex, value) || 5436 !DefineDataProperty(cx, unscopables, cx->names().findLast, value) || 5437 !DefineDataProperty(cx, unscopables, cx->names().findLastIndex, value) || 5438 !DefineDataProperty(cx, unscopables, cx->names().flat, value) || 5439 !DefineDataProperty(cx, unscopables, cx->names().flatMap, value) || 5440 !DefineDataProperty(cx, unscopables, cx->names().includes, value) || 5441 !DefineDataProperty(cx, unscopables, cx->names().keys, value) || 5442 !DefineDataProperty(cx, unscopables, cx->names().toReversed, value) || 5443 !DefineDataProperty(cx, unscopables, cx->names().toSorted, value) || 5444 !DefineDataProperty(cx, unscopables, cx->names().toSpliced, value) || 5445 !DefineDataProperty(cx, unscopables, cx->names().values, value)) { 5446 return false; 5447 } 5448 5449 RootedId id(cx, PropertyKey::Symbol(cx->wellKnownSymbols().unscopables)); 5450 value.setObject(*unscopables); 5451 if (!DefineDataProperty(cx, proto, id, value, JSPROP_READONLY)) { 5452 return false; 5453 } 5454 5455 // Mark Array prototype as having a RealmFuse property (@iterator for 5456 // example). 5457 return JSObject::setHasRealmFuseProperty(cx, proto); 5458 } 5459 5460 static const JSClassOps ArrayObjectClassOps = { 5461 array_addProperty, // addProperty 5462 nullptr, // delProperty 5463 nullptr, // enumerate 5464 nullptr, // newEnumerate 5465 nullptr, // resolve 5466 nullptr, // mayResolve 5467 nullptr, // finalize 5468 nullptr, // call 5469 nullptr, // construct 5470 nullptr, // trace 5471 }; 5472 5473 static const ClassSpec ArrayObjectClassSpec = { 5474 CreateArrayConstructor, CreateArrayPrototype, array_static_methods, 5475 array_static_props, array_methods, nullptr, 5476 array_proto_finish, 5477 }; 5478 5479 const JSClass ArrayObject::class_ = { 5480 "Array", 5481 JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_DELAY_METADATA_BUILDER, 5482 &ArrayObjectClassOps, 5483 &ArrayObjectClassSpec, 5484 }; 5485 5486 ArrayObject* js::NewDenseEmptyArray(JSContext* cx) { 5487 return NewArray<0>(cx, 0, GenericObject); 5488 } 5489 5490 ArrayObject* js::NewTenuredDenseEmptyArray(JSContext* cx) { 5491 return NewArray<0>(cx, 0, TenuredObject); 5492 } 5493 5494 ArrayObject* js::NewDenseFullyAllocatedArray( 5495 JSContext* cx, uint32_t length, NewObjectKind newKind /* = GenericObject */, 5496 gc::AllocSite* site /* = nullptr */) { 5497 return NewArray<UINT32_MAX>(cx, length, newKind, site); 5498 } 5499 5500 ArrayObject* js::NewDensePartlyAllocatedArray( 5501 JSContext* cx, uint32_t length, NewObjectKind newKind /* = GenericObject */, 5502 gc::AllocSite* site /* = nullptr */) { 5503 return NewArray<ArrayObject::EagerAllocationMaxLength>(cx, length, newKind, 5504 site); 5505 } 5506 5507 ArrayObject* js::NewDensePartlyAllocatedArrayWithProto(JSContext* cx, 5508 uint32_t length, 5509 HandleObject proto) { 5510 return NewArrayWithProto<ArrayObject::EagerAllocationMaxLength>( 5511 cx, length, proto, GenericObject); 5512 } 5513 5514 ArrayObject* js::NewDenseUnallocatedArray( 5515 JSContext* cx, uint32_t length, 5516 NewObjectKind newKind /* = GenericObject */) { 5517 return NewArray<0>(cx, length, newKind); 5518 } 5519 5520 // values must point at already-rooted Value objects 5521 ArrayObject* js::NewDenseCopiedArray( 5522 JSContext* cx, uint32_t length, const Value* values, 5523 NewObjectKind newKind /* = GenericObject */) { 5524 ArrayObject* arr = NewArray<UINT32_MAX>(cx, length, newKind); 5525 if (!arr) { 5526 return nullptr; 5527 } 5528 5529 arr->initDenseElements(values, length); 5530 return arr; 5531 } 5532 5533 // strings in props must point at already-rooted strings 5534 ArrayObject* js::NewDenseCopiedArray( 5535 JSContext* cx, uint32_t length, IteratorProperty* props, 5536 NewObjectKind newKind /* = GenericObject */) { 5537 ArrayObject* arr = NewArray<UINT32_MAX>(cx, length, newKind); 5538 if (!arr) { 5539 return nullptr; 5540 } 5541 5542 arr->initDenseElements(props, length); 5543 return arr; 5544 } 5545 5546 ArrayObject* js::NewDenseCopiedArrayWithProto(JSContext* cx, uint32_t length, 5547 const Value* values, 5548 HandleObject proto) { 5549 ArrayObject* arr = 5550 NewArrayWithProto<UINT32_MAX>(cx, length, proto, GenericObject); 5551 if (!arr) { 5552 return nullptr; 5553 } 5554 5555 arr->initDenseElements(values, length); 5556 return arr; 5557 } 5558 5559 ArrayObject* js::NewDenseFullyAllocatedArrayWithShape( 5560 JSContext* cx, uint32_t length, Handle<SharedShape*> shape) { 5561 AutoSetNewObjectMetadata metadata(cx); 5562 gc::AllocKind allocKind = GuessArrayGCKind(length); 5563 MOZ_ASSERT(gc::GetObjectFinalizeKind(&ArrayObject::class_) == 5564 gc::FinalizeKind::None); 5565 MOZ_ASSERT(!IsFinalizedKind(allocKind)); 5566 5567 gc::Heap heap = GetInitialHeap(GenericObject, &ArrayObject::class_); 5568 ArrayObject* arr = ArrayObject::create(cx, allocKind, heap, shape, length, 5569 shape->slotSpan(), metadata); 5570 if (!arr) { 5571 return nullptr; 5572 } 5573 5574 if (!EnsureNewArrayElements(cx, arr, length)) { 5575 return nullptr; 5576 } 5577 5578 probes::CreateObject(cx, arr); 5579 5580 return arr; 5581 } 5582 5583 // TODO(no-TI): clean up. 5584 ArrayObject* js::NewArrayWithShape(JSContext* cx, uint32_t length, 5585 Handle<Shape*> shape) { 5586 // Ion can call this with a shape from a different realm when calling 5587 // another realm's Array constructor. 5588 Maybe<AutoRealm> ar; 5589 if (cx->realm() != shape->realm()) { 5590 MOZ_ASSERT(cx->compartment() == shape->compartment()); 5591 ar.emplace(cx, shape); 5592 } 5593 5594 return NewDenseFullyAllocatedArray(cx, length); 5595 } 5596 5597 #ifdef DEBUG 5598 bool js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp) { 5599 CallArgs args = CallArgsFromVp(argc, vp); 5600 RootedObject obj(cx); 5601 5602 for (unsigned i = 0; i < args.length(); i++) { 5603 HandleValue arg = args[i]; 5604 5605 UniqueChars bytes = 5606 DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, nullptr); 5607 if (!bytes) { 5608 return false; 5609 } 5610 if (arg.isPrimitive() || !(obj = arg.toObjectOrNull())->is<ArrayObject>()) { 5611 fprintf(stderr, "%s: not array\n", bytes.get()); 5612 continue; 5613 } 5614 fprintf(stderr, "%s: (len %u", bytes.get(), 5615 obj->as<ArrayObject>().length()); 5616 fprintf(stderr, ", capacity %u", obj->as<ArrayObject>().getDenseCapacity()); 5617 fputs(")\n", stderr); 5618 } 5619 5620 args.rval().setUndefined(); 5621 return true; 5622 } 5623 #endif 5624 5625 JS_PUBLIC_API JSObject* JS::NewArrayObject(JSContext* cx, 5626 const HandleValueArray& contents) { 5627 MOZ_ASSERT(!cx->zone()->isAtomsZone()); 5628 AssertHeapIsIdle(); 5629 CHECK_THREAD(cx); 5630 cx->check(contents); 5631 5632 return NewDenseCopiedArray(cx, contents.length(), contents.begin()); 5633 } 5634 5635 JS_PUBLIC_API JSObject* JS::NewArrayObject(JSContext* cx, size_t length) { 5636 MOZ_ASSERT(!cx->zone()->isAtomsZone()); 5637 AssertHeapIsIdle(); 5638 CHECK_THREAD(cx); 5639 5640 return NewDenseFullyAllocatedArray(cx, length); 5641 } 5642 5643 JS_PUBLIC_API bool JS::IsArrayObject(JSContext* cx, Handle<JSObject*> obj, 5644 bool* isArray) { 5645 return IsGivenTypeObject(cx, obj, ESClass::Array, isArray); 5646 } 5647 5648 JS_PUBLIC_API bool JS::IsArrayObject(JSContext* cx, Handle<Value> value, 5649 bool* isArray) { 5650 if (!value.isObject()) { 5651 *isArray = false; 5652 return true; 5653 } 5654 5655 Rooted<JSObject*> obj(cx, &value.toObject()); 5656 return IsArrayObject(cx, obj, isArray); 5657 } 5658 5659 JS_PUBLIC_API bool JS::GetArrayLength(JSContext* cx, Handle<JSObject*> obj, 5660 uint32_t* lengthp) { 5661 AssertHeapIsIdle(); 5662 CHECK_THREAD(cx); 5663 cx->check(obj); 5664 5665 uint64_t len = 0; 5666 if (!GetLengthProperty(cx, obj, &len)) { 5667 return false; 5668 } 5669 5670 if (len > UINT32_MAX) { 5671 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 5672 JSMSG_BAD_ARRAY_LENGTH); 5673 return false; 5674 } 5675 5676 *lengthp = uint32_t(len); 5677 return true; 5678 } 5679 5680 JS_PUBLIC_API bool JS::SetArrayLength(JSContext* cx, Handle<JSObject*> obj, 5681 uint32_t length) { 5682 AssertHeapIsIdle(); 5683 CHECK_THREAD(cx); 5684 cx->check(obj); 5685 5686 return SetLengthProperty(cx, obj, length); 5687 } 5688 5689 ArrayObject* js::NewArrayWithNullProto(JSContext* cx) { 5690 Rooted<SharedShape*> shape(cx, GetArrayShapeWithProto(cx, nullptr)); 5691 if (!shape) { 5692 return nullptr; 5693 } 5694 5695 uint32_t length = 0; 5696 return ::NewArrayWithShape<0>(cx, shape, length, GenericObject); 5697 }