tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }