tor-browser

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

TypedArrayObject.cpp (246839B)


      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 "vm/TypedArrayObject-inl.h"
      8 #include "vm/TypedArrayObject.h"
      9 
     10 #include "mozilla/Casting.h"
     11 #include "mozilla/CheckedInt.h"
     12 #include "mozilla/EndianUtils.h"
     13 #include "mozilla/FloatingPoint.h"
     14 #include "mozilla/IntegerTypeTraits.h"
     15 #include "mozilla/Likely.h"
     16 #include "mozilla/PodOperations.h"
     17 #include "mozilla/ScopeExit.h"
     18 #include "mozilla/SIMD.h"
     19 #include "mozilla/TextUtils.h"
     20 
     21 #include <algorithm>
     22 #include <charconv>
     23 #include <iterator>
     24 #include <limits>
     25 #include <numeric>
     26 #include <string.h>
     27 #include <string_view>
     28 #if !defined(XP_WIN) && !defined(__wasi__)
     29 #  include <sys/mman.h>
     30 #endif
     31 #include <type_traits>
     32 
     33 #include "jsnum.h"
     34 #include "jstypes.h"
     35 
     36 #include "builtin/Array.h"
     37 #include "builtin/DataViewObject.h"
     38 #include "gc/Barrier.h"
     39 #include "gc/MaybeRooted.h"
     40 #include "jit/InlinableNatives.h"
     41 #include "jit/TrampolineNatives.h"
     42 #include "js/Conversions.h"
     43 #include "js/experimental/TypedData.h"  // JS_GetArrayBufferViewType, JS_GetTypedArray{Length,ByteOffset,ByteLength}, JS_IsTypedArrayObject
     44 #include "js/friend/ErrorMessages.h"    // js::GetErrorMessage, JSMSG_*
     45 #include "js/PropertySpec.h"
     46 #include "js/ScalarType.h"  // JS::Scalar::Type
     47 #include "js/UniquePtr.h"
     48 #include "js/Wrapper.h"
     49 #include "util/DifferentialTesting.h"
     50 #include "util/StringBuilder.h"
     51 #include "util/Text.h"
     52 #include "util/WindowsWrapper.h"
     53 #include "vm/ArrayBufferObject.h"
     54 #include "vm/EqualityOperations.h"
     55 #include "vm/Float16.h"
     56 #include "vm/FunctionFlags.h"  // js::FunctionFlags
     57 #include "vm/GlobalObject.h"
     58 #include "vm/Interpreter.h"
     59 #include "vm/JSContext.h"
     60 #include "vm/JSObject.h"
     61 #include "vm/SelfHosting.h"
     62 #include "vm/SharedMem.h"
     63 #include "vm/Uint8Clamped.h"
     64 #include "vm/WrapperObject.h"
     65 
     66 #include "builtin/Sorting-inl.h"
     67 #include "gc/Nursery-inl.h"
     68 #include "vm/ArrayBufferObject-inl.h"
     69 #include "vm/Compartment-inl.h"
     70 #include "vm/GeckoProfiler-inl.h"
     71 #include "vm/NativeObject-inl.h"
     72 
     73 using namespace js;
     74 
     75 using mozilla::IsAsciiDigit;
     76 
     77 /*
     78 * TypedArrayObject
     79 *
     80 * The non-templated base class for the specific typed implementations.
     81 * This class holds all the member variables that are used by
     82 * the subclasses.
     83 */
     84 
     85 bool TypedArrayObject::convertValue(JSContext* cx, HandleValue v,
     86                                    MutableHandleValue result) const {
     87  switch (type()) {
     88    case Scalar::BigInt64:
     89    case Scalar::BigUint64: {
     90      BigInt* bi = ToBigInt(cx, v);
     91      if (!bi) {
     92        return false;
     93      }
     94      result.setBigInt(bi);
     95      return true;
     96    }
     97    case Scalar::Int8:
     98    case Scalar::Uint8:
     99    case Scalar::Int16:
    100    case Scalar::Uint16:
    101    case Scalar::Int32:
    102    case Scalar::Uint32:
    103    case Scalar::Float16:
    104    case Scalar::Float32:
    105    case Scalar::Float64:
    106    case Scalar::Uint8Clamped: {
    107      double num;
    108      if (!ToNumber(cx, v, &num)) {
    109        return false;
    110      }
    111      result.setNumber(num);
    112      return true;
    113    }
    114    case Scalar::MaxTypedArrayViewType:
    115    case Scalar::Int64:
    116    case Scalar::Simd128:
    117      MOZ_CRASH("Unsupported TypedArray type");
    118  }
    119  MOZ_ASSERT_UNREACHABLE("Invalid scalar type");
    120  return false;
    121 }
    122 
    123 static bool IsTypedArrayObject(HandleValue v) {
    124  return v.isObject() && v.toObject().is<TypedArrayObject>();
    125 }
    126 
    127 static bool IsUint8ArrayObject(HandleValue v) {
    128  return IsTypedArrayObject(v) &&
    129         v.toObject().as<TypedArrayObject>().type() == Scalar::Uint8;
    130 }
    131 
    132 /* static */
    133 bool TypedArrayObject::ensureHasBuffer(JSContext* cx,
    134                                       Handle<TypedArrayObject*> typedArray) {
    135  if (typedArray->hasBuffer()) {
    136    return true;
    137  }
    138 
    139  MOZ_ASSERT(typedArray->is<FixedLengthTypedArrayObject>(),
    140             "Resizable and immutable TypedArrays always use an ArrayBuffer");
    141 
    142  auto tarray = HandleObject(typedArray).as<FixedLengthTypedArrayObject>();
    143 
    144  size_t byteLength = tarray->byteLength();
    145 
    146  AutoRealm ar(cx, tarray);
    147 
    148  ArrayBufferObject* buffer;
    149  if (tarray->hasMallocedElements(cx)) {
    150    // Allocate a new array buffer and transfer our malloced buffer to it
    151    // without copying.
    152    buffer =
    153        ArrayBufferObject::createFromTypedArrayMallocedElements(cx, tarray);
    154    if (!buffer) {
    155      return false;
    156    }
    157  } else {
    158    buffer = ArrayBufferObject::createZeroed(cx, byteLength);
    159    if (!buffer) {
    160      return false;
    161    }
    162 
    163    // tarray is not shared, because if it were it would have a buffer.
    164    memcpy(buffer->dataPointer(), tarray->dataPointerUnshared(), byteLength);
    165 
    166    // If the object is in the nursery, the buffer will be freed by the next
    167    // nursery GC. Free the data slot pointer if the object has no inline data.
    168    //
    169    // Note: we checked hasMallocedElements above, but allocating the array
    170    // buffer object might have triggered a GC and this can malloc typed array
    171    // elements if the typed array was in the nursery.
    172    if (tarray->isTenured() && tarray->hasMallocedElements(cx)) {
    173      size_t nbytes = RoundUp(byteLength, sizeof(Value));
    174      js_free(tarray->elements());
    175      RemoveCellMemory(tarray, nbytes, MemoryUse::TypedArrayElements);
    176    }
    177 
    178    tarray->setFixedSlot(TypedArrayObject::DATA_SLOT,
    179                         PrivateValue(buffer->dataPointer()));
    180  }
    181 
    182  MOZ_ASSERT(tarray->elements() == buffer->dataPointer());
    183 
    184  buffer->pinLength(tarray->isLengthPinned());
    185 
    186  // Attaching the first view to an array buffer is infallible.
    187  MOZ_ALWAYS_TRUE(buffer->addView(cx, tarray));
    188 
    189  tarray->setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*buffer));
    190 
    191  return true;
    192 }
    193 
    194 #ifdef DEBUG
    195 void FixedLengthTypedArrayObject::assertZeroLengthArrayData() const {
    196  if (length() == 0 && !hasBuffer()) {
    197    uint8_t* end = fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START);
    198    MOZ_ASSERT(end[0] == ZeroLengthArrayData);
    199  }
    200 }
    201 #endif
    202 
    203 void FixedLengthTypedArrayObject::finalize(JS::GCContext* gcx, JSObject* obj) {
    204  MOZ_ASSERT(!IsInsideNursery(obj));
    205  auto* curObj = &obj->as<FixedLengthTypedArrayObject>();
    206 
    207  // Template objects or discarded objects (which didn't have enough room
    208  // for inner elements) don't have anything to free.
    209  if (!curObj->elementsRaw()) {
    210    return;
    211  }
    212 
    213  curObj->assertZeroLengthArrayData();
    214 
    215  // Typed arrays with a buffer object do not need to be free'd
    216  if (curObj->hasBuffer()) {
    217    return;
    218  }
    219 
    220  // Free the data slot pointer if it does not point into the old JSObject.
    221  if (!curObj->hasInlineElements()) {
    222    size_t nbytes = RoundUp(curObj->byteLength(), sizeof(Value));
    223    gcx->free_(obj, curObj->elements(), nbytes, MemoryUse::TypedArrayElements);
    224  }
    225 }
    226 
    227 /* static */
    228 size_t FixedLengthTypedArrayObject::objectMoved(JSObject* obj, JSObject* old) {
    229  auto* newObj = &obj->as<FixedLengthTypedArrayObject>();
    230  const auto* oldObj = &old->as<FixedLengthTypedArrayObject>();
    231  MOZ_ASSERT(newObj->elementsRaw() == oldObj->elementsRaw());
    232 
    233  // Typed arrays with a buffer object do not need an update.
    234  if (oldObj->hasBuffer()) {
    235    return 0;
    236  }
    237 
    238  if (!IsInsideNursery(old)) {
    239    // Update the data slot pointer if it points to the old JSObject.
    240    if (oldObj->hasInlineElements()) {
    241      newObj->setInlineElements();
    242    }
    243 
    244    return 0;
    245  }
    246 
    247  void* buf = oldObj->elements();
    248 
    249  // Discarded objects (which didn't have enough room for inner elements) don't
    250  // have any data to move.
    251  if (!buf) {
    252    return 0;
    253  }
    254 
    255  Nursery& nursery = obj->runtimeFromMainThread()->gc.nursery();
    256 
    257  // Determine if we can use inline data for the target array. If this is
    258  // possible, the nursery will have picked an allocation size that is large
    259  // enough.
    260  size_t nbytes = oldObj->byteLength();
    261  bool canUseDirectForward = nbytes >= sizeof(uintptr_t);
    262 
    263  constexpr size_t headerSize = dataOffset() + sizeof(HeapSlot);
    264 
    265  gc::AllocKind allocKind = oldObj->allocKindForTenure();
    266  MOZ_ASSERT_IF(obj->isTenured(), obj->asTenured().getAllocKind() == allocKind);
    267  MOZ_ASSERT_IF(nbytes == 0,
    268                headerSize + sizeof(uint8_t) <= GetGCKindBytes(allocKind));
    269 
    270  if (nursery.isInside(buf) &&
    271      headerSize + nbytes <= GetGCKindBytes(allocKind)) {
    272    MOZ_ASSERT(oldObj->hasInlineElements());
    273 #ifdef DEBUG
    274    if (nbytes == 0) {
    275      uint8_t* output =
    276          newObj->fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START);
    277      output[0] = ZeroLengthArrayData;
    278    }
    279 #endif
    280    newObj->setInlineElements();
    281    mozilla::PodCopy(newObj->elements(), oldObj->elements(), nbytes);
    282 
    283    // Set a forwarding pointer for the element buffers in case they were
    284    // preserved on the stack by Ion.
    285    nursery.setForwardingPointerWhileTenuring(
    286        oldObj->elements(), newObj->elements(), canUseDirectForward);
    287 
    288    return 0;
    289  }
    290 
    291  // Non-inline allocations are rounded up.
    292  nbytes = RoundUp(nbytes, sizeof(Value));
    293 
    294  Nursery::WasBufferMoved result =
    295      nursery.maybeMoveNurseryOrMallocBufferOnPromotion(
    296          &buf, newObj, nbytes, nbytes, MemoryUse::TypedArrayElements,
    297          ArrayBufferContentsArena);
    298  if (result == Nursery::BufferMoved) {
    299    newObj->setReservedSlot(DATA_SLOT, PrivateValue(buf));
    300 
    301    // Set a forwarding pointer for the element buffers in case they were
    302    // preserved on the stack by Ion.
    303    nursery.setForwardingPointerWhileTenuring(
    304        oldObj->elements(), newObj->elements(), canUseDirectForward);
    305 
    306    return nbytes;
    307  }
    308 
    309  return 0;
    310 }
    311 
    312 bool FixedLengthTypedArrayObject::hasInlineElements() const {
    313  return elements() ==
    314             this->fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START) &&
    315         byteLength() <= FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT;
    316 }
    317 
    318 void FixedLengthTypedArrayObject::setInlineElements() {
    319  char* dataSlot = reinterpret_cast<char*>(this) + dataOffset();
    320  *reinterpret_cast<void**>(dataSlot) =
    321      this->fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START);
    322 }
    323 
    324 bool FixedLengthTypedArrayObject::hasMallocedElements(JSContext* cx) const {
    325  return !hasInlineElements() && !cx->nursery().isInside(elements());
    326 }
    327 
    328 /* Helper clamped uint8_t type */
    329 
    330 uint32_t js::ClampDoubleToUint8(const double x) {
    331  // Not < so that NaN coerces to 0
    332  if (!(x > 0)) {
    333    return 0;
    334  }
    335 
    336  if (x >= 255) {
    337    return 255;
    338  }
    339 
    340  // Convert with truncation.
    341  uint8_t y = uint8_t(x);
    342 
    343  // Now |y| is rounded toward zero. We want rounded to nearest, ties to even.
    344  double r = x - double(y);
    345 
    346  // It was a tie (since the difference is exactly 0.5). Round up if the number
    347  // is odd.
    348  if (r == 0.5) {
    349    return y + (y & 1);
    350  }
    351 
    352  // Round up if truncation incorrectly rounded down.
    353  return y + (r > 0.5);
    354 }
    355 
    356 static void ReportOutOfBounds(JSContext* cx, TypedArrayObject* typedArray) {
    357  if (typedArray->hasDetachedBuffer()) {
    358    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    359                              JSMSG_TYPED_ARRAY_DETACHED);
    360  } else {
    361    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    362                              JSMSG_TYPED_ARRAY_RESIZED_BOUNDS);
    363  }
    364 }
    365 
    366 namespace {
    367 
    368 template <class TypedArrayType>
    369 static TypedArrayType* NewTypedArrayObject(JSContext* cx, const JSClass* clasp,
    370                                           HandleObject proto,
    371                                           gc::AllocKind allocKind,
    372                                           gc::Heap heap) {
    373  MOZ_ASSERT(proto);
    374  allocKind = gc::GetFinalizedAllocKindForClass(allocKind, clasp);
    375 
    376  static_assert(std::is_same_v<TypedArrayType, FixedLengthTypedArrayObject> ||
    377                std::is_same_v<TypedArrayType, ResizableTypedArrayObject> ||
    378                std::is_same_v<TypedArrayType, ImmutableTypedArrayObject>);
    379 
    380  // Fixed length typed arrays can store data inline so we only use fixed slots
    381  // to cover the reserved slots, ignoring the AllocKind.
    382  MOZ_ASSERT(ClassCanHaveFixedData(clasp));
    383  constexpr size_t nfixed = TypedArrayType::RESERVED_SLOTS;
    384  static_assert(nfixed <= NativeObject::MAX_FIXED_SLOTS);
    385  static_assert(!std::is_same_v<TypedArrayType, FixedLengthTypedArrayObject> ||
    386                nfixed == FixedLengthTypedArrayObject::FIXED_DATA_START);
    387 
    388  Rooted<SharedShape*> shape(
    389      cx,
    390      SharedShape::getInitialShape(cx, clasp, cx->realm(), AsTaggedProto(proto),
    391                                   nfixed, ObjectFlags()));
    392  if (!shape) {
    393    return nullptr;
    394  }
    395 
    396  return NativeObject::create<TypedArrayType>(cx, allocKind, heap, shape);
    397 }
    398 
    399 template <typename NativeType>
    400 class FixedLengthTypedArrayObjectTemplate;
    401 
    402 template <typename NativeType>
    403 class ResizableTypedArrayObjectTemplate;
    404 
    405 template <typename NativeType>
    406 class ImmutableTypedArrayObjectTemplate;
    407 
    408 template <typename NativeType>
    409 class TypedArrayObjectTemplate {
    410  friend class js::TypedArrayObject;
    411 
    412  using FixedLengthTypedArray = FixedLengthTypedArrayObjectTemplate<NativeType>;
    413  using ResizableTypedArray = ResizableTypedArrayObjectTemplate<NativeType>;
    414  using ImmutableTypedArray = ImmutableTypedArrayObjectTemplate<NativeType>;
    415  using AutoLength = ArrayBufferViewObject::AutoLength;
    416 
    417  static constexpr auto ByteLengthLimit = TypedArrayObject::ByteLengthLimit;
    418  static constexpr auto INLINE_BUFFER_LIMIT =
    419      FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT;
    420 
    421 public:
    422  static constexpr Scalar::Type ArrayTypeID() {
    423    return TypeIDOfType<NativeType>::id;
    424  }
    425  static constexpr JSProtoKey protoKey() {
    426    return TypeIDOfType<NativeType>::protoKey;
    427  }
    428 
    429  static constexpr size_t BYTES_PER_ELEMENT = sizeof(NativeType);
    430 
    431  static JSObject* createPrototype(JSContext* cx, JSProtoKey key) {
    432    Handle<GlobalObject*> global = cx->global();
    433    RootedObject typedArrayProto(
    434        cx, GlobalObject::getOrCreateTypedArrayPrototype(cx, global));
    435    if (!typedArrayProto) {
    436      return nullptr;
    437    }
    438 
    439    const JSClass* clasp = TypedArrayObject::protoClassForType(ArrayTypeID());
    440    return GlobalObject::createBlankPrototypeInheriting(cx, clasp,
    441                                                        typedArrayProto);
    442  }
    443 
    444  static JSObject* createConstructor(JSContext* cx, JSProtoKey key) {
    445    Handle<GlobalObject*> global = cx->global();
    446    RootedFunction ctorProto(
    447        cx, GlobalObject::getOrCreateTypedArrayConstructor(cx, global));
    448    if (!ctorProto) {
    449      return nullptr;
    450    }
    451 
    452    JSFunction* fun = NewFunctionWithProto(
    453        cx, class_constructor, 3, FunctionFlags::NATIVE_CTOR, nullptr,
    454        ClassName(key, cx), ctorProto, gc::AllocKind::FUNCTION, TenuredObject);
    455 
    456    if (fun) {
    457      fun->setJitInfo(&jit::JitInfo_TypedArrayConstructor);
    458    }
    459 
    460    return fun;
    461  }
    462 
    463  static bool convertValue(JSContext* cx, HandleValue v, NativeType* result);
    464 
    465  static TypedArrayObject* makeTypedArrayWithTemplate(
    466      JSContext* cx, TypedArrayObject* templateObj, HandleObject array) {
    467    MOZ_ASSERT(!IsWrapper(array));
    468    MOZ_ASSERT(!array->is<ArrayBufferObjectMaybeShared>());
    469 
    470    return fromArray(cx, array);
    471  }
    472 
    473  static TypedArrayObject* makeTypedArrayWithTemplate(
    474      JSContext* cx, TypedArrayObject* templateObj, HandleObject arrayBuffer,
    475      HandleValue byteOffsetValue, HandleValue lengthValue) {
    476    MOZ_ASSERT(!IsWrapper(arrayBuffer));
    477    MOZ_ASSERT(arrayBuffer->is<ArrayBufferObjectMaybeShared>());
    478 
    479    uint64_t byteOffset, length;
    480    if (!byteOffsetAndLength(cx, byteOffsetValue, lengthValue, &byteOffset,
    481                             &length)) {
    482      return nullptr;
    483    }
    484 
    485    return fromBufferSameCompartment(
    486        cx, arrayBuffer.as<ArrayBufferObjectMaybeShared>(), byteOffset, length,
    487        nullptr);
    488  }
    489 
    490  // ES2026 draft rev 6d71ca0e2dbf1c0cfb87b5eb7a83cf7c76591561
    491  // 23.2.5.1 TypedArray ( ...args )
    492  static bool class_constructor(JSContext* cx, unsigned argc, Value* vp) {
    493    AutoJSConstructorProfilerEntry pseudoFrame(cx, "[TypedArray]");
    494    CallArgs args = CallArgsFromVp(argc, vp);
    495 
    496    // Step 1.
    497    if (!ThrowIfNotConstructing(cx, args, "typed array")) {
    498      return false;
    499    }
    500 
    501    // Steps 2-6.
    502    JSObject* obj = create(cx, args);
    503    if (!obj) {
    504      return false;
    505    }
    506    args.rval().setObject(*obj);
    507    return true;
    508  }
    509 
    510 private:
    511  static JSObject* create(JSContext* cx, const CallArgs& args) {
    512    MOZ_ASSERT(args.isConstructing());
    513 
    514    // Steps 5 and 6.c.
    515    if (args.length() == 0 || !args[0].isObject()) {
    516      // Step 6.c.ii.
    517      uint64_t len;
    518      if (!ToIndex(cx, args.get(0), JSMSG_BAD_ARRAY_LENGTH, &len)) {
    519        return nullptr;
    520      }
    521 
    522      // Steps 5.a and 6.c.iii.
    523      RootedObject proto(cx);
    524      if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey(), &proto)) {
    525        return nullptr;
    526      }
    527 
    528      return fromLength(cx, len, proto);
    529    }
    530 
    531    RootedObject dataObj(cx, &args[0].toObject());
    532 
    533    // Step 6.b.i.
    534    // 23.2.5.1.1 AllocateTypedArray, step 1.
    535    RootedObject proto(cx);
    536    if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey(), &proto)) {
    537      return nullptr;
    538    }
    539 
    540    // Steps 6.b.ii and 6.b.iv.
    541    if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObjectMaybeShared>()) {
    542      return fromArray(cx, dataObj, proto);
    543    }
    544 
    545    // Steps 6.b.iii.1-2.
    546    // 23.2.5.1.3 InitializeTypedArrayFromArrayBuffer, steps 2 and 4.
    547    uint64_t byteOffset, length;
    548    if (!byteOffsetAndLength(cx, args.get(1), args.get(2), &byteOffset,
    549                             &length)) {
    550      return nullptr;
    551    }
    552 
    553    // Step 6.b.iii.3.
    554    if (dataObj->is<ArrayBufferObjectMaybeShared>()) {
    555      auto buffer = dataObj.as<ArrayBufferObjectMaybeShared>();
    556      return fromBufferSameCompartment(cx, buffer, byteOffset, length, proto);
    557    }
    558    return fromBufferWrapped(cx, dataObj, byteOffset, length, proto);
    559  }
    560 
    561  // ES2026 draft rev 6d71ca0e2dbf1c0cfb87b5eb7a83cf7c76591561
    562  // 23.2.5.1.3 InitializeTypedArrayFromArrayBuffer ( O, buffer, byteOffset,
    563  // length ) Steps 2-3 and 5.
    564  static bool byteOffsetAndLength(JSContext* cx, HandleValue byteOffsetValue,
    565                                  HandleValue lengthValue, uint64_t* byteOffset,
    566                                  uint64_t* length) {
    567    // Steps 2-3.
    568    *byteOffset = 0;
    569    if (!byteOffsetValue.isUndefined()) {
    570      // Step 2.
    571      if (!ToIndex(cx, byteOffsetValue, byteOffset)) {
    572        return false;
    573      }
    574 
    575      // Step 3.
    576      if (*byteOffset % BYTES_PER_ELEMENT != 0) {
    577        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    578                                  JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_BOUNDS,
    579                                  Scalar::name(ArrayTypeID()),
    580                                  Scalar::byteSizeString(ArrayTypeID()));
    581        return false;
    582      }
    583    }
    584 
    585    // Step 5.
    586    *length = UINT64_MAX;
    587    if (!lengthValue.isUndefined()) {
    588      if (!ToIndex(cx, lengthValue, length)) {
    589        return false;
    590      }
    591    }
    592 
    593    return true;
    594  }
    595 
    596  // ES2026 draft rev 6d71ca0e2dbf1c0cfb87b5eb7a83cf7c76591561
    597  // 23.2.5.1.3 InitializeTypedArrayFromArrayBuffer ( O, buffer, byteOffset,
    598  // length ) Steps 6-9.
    599  static bool computeAndCheckLength(
    600      JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> bufferMaybeUnwrapped,
    601      uint64_t byteOffset, uint64_t lengthIndex, size_t* length,
    602      AutoLength* autoLength) {
    603    MOZ_ASSERT(byteOffset % BYTES_PER_ELEMENT == 0);
    604    MOZ_ASSERT(byteOffset < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
    605    MOZ_ASSERT_IF(lengthIndex != UINT64_MAX,
    606                  lengthIndex < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
    607 
    608    // Step 6.
    609    if (bufferMaybeUnwrapped->isDetached()) {
    610      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    611                                JSMSG_TYPED_ARRAY_DETACHED);
    612      return false;
    613    }
    614 
    615    // Step 7.
    616    size_t bufferByteLength = bufferMaybeUnwrapped->byteLength();
    617    MOZ_ASSERT(bufferByteLength <= ByteLengthLimit);
    618 
    619    // Steps 8-9.
    620    size_t len;
    621    if (lengthIndex == UINT64_MAX) {
    622      // Steps 8.a and 9.a.iii.
    623      //
    624      // Check if |byteOffset| valid.
    625      if (byteOffset > bufferByteLength) {
    626        JS_ReportErrorNumberASCII(
    627            cx, GetErrorMessage, nullptr,
    628            JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_LENGTH_BOUNDS,
    629            Scalar::name(ArrayTypeID()));
    630        return false;
    631      }
    632 
    633      // Steps 8.b-c.
    634      //
    635      // Resizable buffers without an explicit length are auto-length.
    636      if (bufferMaybeUnwrapped->isResizable()) {
    637        *length = 0;
    638        *autoLength = AutoLength::Yes;
    639        return true;
    640      }
    641 
    642      // Step 9.a.i.
    643      if (bufferByteLength % BYTES_PER_ELEMENT != 0) {
    644        // The given byte array doesn't map exactly to
    645        // |BYTES_PER_ELEMENT * N|
    646        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    647                                  JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_MISALIGNED,
    648                                  Scalar::name(ArrayTypeID()),
    649                                  Scalar::byteSizeString(ArrayTypeID()));
    650        return false;
    651      }
    652 
    653      // Step 9.a.ii.
    654      size_t newByteLength = bufferByteLength - size_t(byteOffset);
    655      len = newByteLength / BYTES_PER_ELEMENT;
    656    } else {
    657      // Step 9.b.i.
    658      uint64_t newByteLength = lengthIndex * BYTES_PER_ELEMENT;
    659 
    660      // Step 9.b.ii.
    661      if (byteOffset + newByteLength > bufferByteLength) {
    662        // |byteOffset + newByteLength| is too big for the arraybuffer
    663        JS_ReportErrorNumberASCII(
    664            cx, GetErrorMessage, nullptr,
    665            JSMSG_TYPED_ARRAY_CONSTRUCT_ARRAY_LENGTH_BOUNDS,
    666            Scalar::name(ArrayTypeID()));
    667        return false;
    668      }
    669 
    670      len = size_t(lengthIndex);
    671    }
    672 
    673    // Steps 9.c-d.
    674    MOZ_ASSERT(len <= ByteLengthLimit / BYTES_PER_ELEMENT);
    675    *length = len;
    676    *autoLength = AutoLength::No;
    677    return true;
    678  }
    679 
    680  // ES2026 draft rev 6d71ca0e2dbf1c0cfb87b5eb7a83cf7c76591561
    681  // 23.2.5.1.3 InitializeTypedArrayFromArrayBuffer ( O, buffer, byteOffset,
    682  // length ) Steps 6-12.
    683  static TypedArrayObject* fromBufferSameCompartment(
    684      JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
    685      uint64_t byteOffset, uint64_t lengthIndex, HandleObject proto) {
    686    // Steps 6-9.
    687    size_t length = 0;
    688    auto autoLength = AutoLength::No;
    689    if (!computeAndCheckLength(cx, buffer, byteOffset, lengthIndex, &length,
    690                               &autoLength)) {
    691      return nullptr;
    692    }
    693 
    694    // Steps 10-12.
    695    if (buffer->isResizable()) {
    696      return ResizableTypedArray::makeInstance(cx, buffer, byteOffset, length,
    697                                               autoLength, proto);
    698    }
    699    if (buffer->isImmutable()) {
    700      return ImmutableTypedArray::makeInstance(cx, buffer, byteOffset, length,
    701                                               proto);
    702    }
    703    return FixedLengthTypedArray::makeInstance(cx, buffer, byteOffset, length,
    704                                               proto);
    705  }
    706 
    707  // Create a TypedArray object in another compartment.
    708  //
    709  // ES6 supports creating a TypedArray in global A (using global A's
    710  // TypedArray constructor) backed by an ArrayBuffer created in global B.
    711  //
    712  // Our TypedArrayObject implementation doesn't support a TypedArray in
    713  // compartment A backed by an ArrayBuffer in compartment B. So in this
    714  // case, we create the TypedArray in B (!) and return a cross-compartment
    715  // wrapper.
    716  //
    717  // Extra twist: the spec says the new TypedArray's [[Prototype]] must be
    718  // A's TypedArray.prototype. So even though we're creating the TypedArray
    719  // in B, its [[Prototype]] must be (a cross-compartment wrapper for) the
    720  // TypedArray.prototype in A.
    721  static JSObject* fromBufferWrapped(JSContext* cx, HandleObject bufobj,
    722                                     uint64_t byteOffset, uint64_t lengthIndex,
    723                                     HandleObject proto) {
    724    JSObject* unwrapped = CheckedUnwrapStatic(bufobj);
    725    if (!unwrapped) {
    726      ReportAccessDenied(cx);
    727      return nullptr;
    728    }
    729 
    730    if (!unwrapped->is<ArrayBufferObjectMaybeShared>()) {
    731      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    732                                JSMSG_TYPED_ARRAY_BAD_ARGS);
    733      return nullptr;
    734    }
    735 
    736    Rooted<ArrayBufferObjectMaybeShared*> unwrappedBuffer(cx);
    737    unwrappedBuffer = &unwrapped->as<ArrayBufferObjectMaybeShared>();
    738 
    739    size_t length = 0;
    740    auto autoLength = AutoLength::No;
    741    if (!computeAndCheckLength(cx, unwrappedBuffer, byteOffset, lengthIndex,
    742                               &length, &autoLength)) {
    743      return nullptr;
    744    }
    745 
    746    // Make sure to get the [[Prototype]] for the created typed array from
    747    // this compartment.
    748    RootedObject protoRoot(cx, proto);
    749    if (!protoRoot) {
    750      protoRoot = GlobalObject::getOrCreatePrototype(cx, protoKey());
    751      if (!protoRoot) {
    752        return nullptr;
    753      }
    754    }
    755 
    756    RootedObject typedArray(cx);
    757    {
    758      JSAutoRealm ar(cx, unwrappedBuffer);
    759 
    760      RootedObject wrappedProto(cx, protoRoot);
    761      if (!cx->compartment()->wrap(cx, &wrappedProto)) {
    762        return nullptr;
    763      }
    764 
    765      if (unwrappedBuffer->isResizable()) {
    766        typedArray = ResizableTypedArray::makeInstance(
    767            cx, unwrappedBuffer, byteOffset, length, autoLength, wrappedProto);
    768      } else if (unwrappedBuffer->isImmutable()) {
    769        typedArray = ImmutableTypedArray::makeInstance(
    770            cx, unwrappedBuffer, byteOffset, length, wrappedProto);
    771      } else {
    772        typedArray = FixedLengthTypedArray::makeInstance(
    773            cx, unwrappedBuffer, byteOffset, length, wrappedProto);
    774      }
    775      if (!typedArray) {
    776        return nullptr;
    777      }
    778    }
    779 
    780    if (!cx->compartment()->wrap(cx, &typedArray)) {
    781      return nullptr;
    782    }
    783 
    784    return typedArray;
    785  }
    786 
    787 public:
    788  static JSObject* fromBuffer(JSContext* cx, HandleObject bufobj,
    789                              size_t byteOffset, int64_t lengthInt) {
    790    if (byteOffset % BYTES_PER_ELEMENT != 0) {
    791      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    792                                JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_BOUNDS,
    793                                Scalar::name(ArrayTypeID()),
    794                                Scalar::byteSizeString(ArrayTypeID()));
    795      return nullptr;  // invalid byteOffset
    796    }
    797 
    798    uint64_t lengthIndex = lengthInt >= 0 ? uint64_t(lengthInt) : UINT64_MAX;
    799    if (bufobj->is<ArrayBufferObjectMaybeShared>()) {
    800      auto buffer = bufobj.as<ArrayBufferObjectMaybeShared>();
    801      return fromBufferSameCompartment(cx, buffer, byteOffset, lengthIndex,
    802                                       nullptr);
    803    }
    804    return fromBufferWrapped(cx, bufobj, byteOffset, lengthIndex, nullptr);
    805  }
    806 
    807  static TypedArrayObject* fromBuffer(
    808      JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
    809      size_t byteOffset) {
    810    MOZ_ASSERT(byteOffset % BYTES_PER_ELEMENT == 0);
    811    return fromBufferSameCompartment(cx, buffer, byteOffset, UINT64_MAX,
    812                                     nullptr);
    813  }
    814 
    815  static TypedArrayObject* fromBuffer(
    816      JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
    817      size_t byteOffset, size_t length) {
    818    MOZ_ASSERT(byteOffset % BYTES_PER_ELEMENT == 0);
    819    return fromBufferSameCompartment(cx, buffer, byteOffset, length, nullptr);
    820  }
    821 
    822  static bool maybeCreateArrayBuffer(JSContext* cx, uint64_t count,
    823                                     MutableHandle<ArrayBufferObject*> buffer) {
    824    if (count > ByteLengthLimit / BYTES_PER_ELEMENT) {
    825      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    826                                JSMSG_BAD_ARRAY_LENGTH);
    827      return false;
    828    }
    829    size_t byteLength = count * BYTES_PER_ELEMENT;
    830 
    831    MOZ_ASSERT(byteLength <= ByteLengthLimit);
    832    static_assert(INLINE_BUFFER_LIMIT % BYTES_PER_ELEMENT == 0,
    833                  "ArrayBuffer inline storage shouldn't waste any space");
    834 
    835    if (byteLength <= INLINE_BUFFER_LIMIT) {
    836      // The array's data can be inline, and the buffer created lazily.
    837      return true;
    838    }
    839 
    840    ArrayBufferObject* buf = ArrayBufferObject::createZeroed(cx, byteLength);
    841    if (!buf) {
    842      return false;
    843    }
    844 
    845    buffer.set(buf);
    846    return true;
    847  }
    848 
    849  // ES2023 draft rev cf86f1cdc28e809170733d74ea64fd0f3dd79f78
    850  // 23.2.5.1.1 AllocateTypedArray ( constructorName, newTarget, defaultProto [
    851  // , length ] )
    852  static TypedArrayObject* fromLength(JSContext* cx, uint64_t nelements,
    853                                      HandleObject proto = nullptr,
    854                                      gc::Heap heap = gc::Heap::Default) {
    855    Rooted<ArrayBufferObject*> buffer(cx);
    856    if (!maybeCreateArrayBuffer(cx, nelements, &buffer)) {
    857      return nullptr;
    858    }
    859 
    860    return FixedLengthTypedArray::makeInstance(cx, buffer, 0, nelements, proto,
    861                                               heap);
    862  }
    863 
    864  static TypedArrayObject* fromArray(JSContext* cx, HandleObject other,
    865                                     HandleObject proto = nullptr);
    866 
    867  static TypedArrayObject* fromTypedArray(JSContext* cx, HandleObject other,
    868                                          bool isWrapped, HandleObject proto);
    869 
    870  static TypedArrayObject* fromObject(JSContext* cx, HandleObject other,
    871                                      HandleObject proto);
    872 
    873  static const NativeType getIndex(TypedArrayObject* tarray, size_t index) {
    874    MOZ_ASSERT(index < tarray->length().valueOr(0));
    875    return jit::AtomicOperations::loadSafeWhenRacy(
    876        tarray->dataPointerEither().cast<NativeType*>() + index);
    877  }
    878 
    879  static void setIndex(TypedArrayObject& tarray, size_t index, NativeType val) {
    880    MOZ_ASSERT(index < tarray.length().valueOr(0));
    881    jit::AtomicOperations::storeSafeWhenRacy(
    882        tarray.dataPointerEither().cast<NativeType*>() + index, val);
    883  }
    884 
    885  static bool getElement(JSContext* cx, TypedArrayObject* tarray, size_t index,
    886                         MutableHandleValue val);
    887  static bool getElementPure(TypedArrayObject* tarray, size_t index, Value* vp);
    888 
    889  static bool setElement(JSContext* cx, Handle<TypedArrayObject*> obj,
    890                         uint64_t index, HandleValue v, ObjectOpResult& result);
    891 };
    892 
    893 template <typename NativeType>
    894 class FixedLengthTypedArrayObjectTemplate
    895    : public FixedLengthTypedArrayObject,
    896      public TypedArrayObjectTemplate<NativeType> {
    897  friend class js::TypedArrayObject;
    898 
    899  using TypedArrayTemplate = TypedArrayObjectTemplate<NativeType>;
    900 
    901 public:
    902  using TypedArrayTemplate::ArrayTypeID;
    903  using TypedArrayTemplate::BYTES_PER_ELEMENT;
    904  using TypedArrayTemplate::protoKey;
    905 
    906  static inline const JSClass* instanceClass() {
    907    static_assert(ArrayTypeID() <
    908                  std::size(TypedArrayObject::fixedLengthClasses));
    909    return &TypedArrayObject::fixedLengthClasses[ArrayTypeID()];
    910  }
    911 
    912  static FixedLengthTypedArrayObject* newBuiltinClassInstance(
    913      JSContext* cx, gc::AllocKind allocKind, gc::Heap heap) {
    914    RootedObject proto(cx, GlobalObject::getOrCreatePrototype(cx, protoKey()));
    915    if (!proto) {
    916      return nullptr;
    917    }
    918    return NewTypedArrayObject<FixedLengthTypedArrayObject>(
    919        cx, instanceClass(), proto, allocKind, heap);
    920  }
    921 
    922  static FixedLengthTypedArrayObject* makeProtoInstance(
    923      JSContext* cx, HandleObject proto, gc::AllocKind allocKind) {
    924    MOZ_ASSERT(proto);
    925    return NewTypedArrayObject<FixedLengthTypedArrayObject>(
    926        cx, instanceClass(), proto, allocKind, gc::Heap::Default);
    927  }
    928 
    929  static FixedLengthTypedArrayObject* makeInstance(
    930      JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
    931      size_t byteOffset, size_t len, HandleObject proto,
    932      gc::Heap heap = gc::Heap::Default) {
    933    MOZ_ASSERT(len <= ByteLengthLimit / BYTES_PER_ELEMENT);
    934 
    935    gc::AllocKind allocKind =
    936        buffer ? gc::GetGCObjectKind(instanceClass())
    937               : AllocKindForLazyBuffer(len * BYTES_PER_ELEMENT);
    938 
    939    AutoSetNewObjectMetadata metadata(cx);
    940    FixedLengthTypedArrayObject* obj;
    941    if (proto) {
    942      obj = makeProtoInstance(cx, proto, allocKind);
    943    } else {
    944      obj = newBuiltinClassInstance(cx, allocKind, heap);
    945    }
    946    if (!obj || !obj->init(cx, buffer, byteOffset, len, BYTES_PER_ELEMENT)) {
    947      return nullptr;
    948    }
    949 
    950    return obj;
    951  }
    952 
    953  static FixedLengthTypedArrayObject* makeTemplateObject(JSContext* cx,
    954                                                         int32_t len) {
    955    MOZ_ASSERT(len >= 0);
    956    size_t nbytes;
    957    MOZ_ALWAYS_TRUE(CalculateAllocSize<NativeType>(len, &nbytes));
    958    bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
    959    gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(instanceClass())
    960                                          : AllocKindForLazyBuffer(nbytes);
    961    MOZ_ASSERT(allocKind >= gc::GetGCObjectKind(instanceClass()));
    962 
    963    AutoSetNewObjectMetadata metadata(cx);
    964 
    965    auto* tarray = newBuiltinClassInstance(cx, allocKind, gc::Heap::Tenured);
    966    if (!tarray) {
    967      return nullptr;
    968    }
    969 
    970    initTypedArraySlots(tarray, len);
    971 
    972    // Template objects don't need memory for their elements, since there
    973    // won't be any elements to store.
    974    MOZ_ASSERT(tarray->getReservedSlot(DATA_SLOT).isUndefined());
    975 
    976    return tarray;
    977  }
    978 
    979  static FixedLengthTypedArrayObject* fromDetachedBuffer(
    980      JSContext* cx, Handle<ArrayBufferObject*> buffer,
    981      gc::Heap heap = gc::Heap::Default) {
    982    MOZ_ASSERT(buffer->isDetached());
    983 
    984    gc::AllocKind allocKind = gc::GetGCObjectKind(instanceClass());
    985 
    986    AutoSetNewObjectMetadata metadata(cx);
    987    auto* obj = newBuiltinClassInstance(cx, allocKind, heap);
    988    if (!obj) {
    989      return nullptr;
    990    }
    991 
    992    // Normal construction doesn't allow creating a new TypedArray with an
    993    // already detached ArrayBuffer. Initialize all slots as if a TypedArrray
    994    // had been created with a non-detached buffer and the buffer was detached
    995    // later.
    996    obj->initFixedSlot(BUFFER_SLOT, ObjectValue(*buffer));
    997    obj->initFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0)));
    998    obj->initFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0)));
    999    obj->initFixedSlot(DATA_SLOT, UndefinedValue());
   1000 
   1001    return obj;
   1002  }
   1003 
   1004  static void initTypedArraySlots(FixedLengthTypedArrayObject* tarray,
   1005                                  int32_t len) {
   1006    MOZ_ASSERT(len >= 0);
   1007    tarray->initFixedSlot(TypedArrayObject::BUFFER_SLOT, JS::FalseValue());
   1008    tarray->initFixedSlot(TypedArrayObject::LENGTH_SLOT, PrivateValue(len));
   1009    tarray->initFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT,
   1010                          PrivateValue(size_t(0)));
   1011 
   1012 #ifdef DEBUG
   1013    if (len == 0) {
   1014      uint8_t* output =
   1015          tarray->fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START);
   1016      output[0] = TypedArrayObject::ZeroLengthArrayData;
   1017    }
   1018 #endif
   1019  }
   1020 
   1021  static void initTypedArrayData(FixedLengthTypedArrayObject* tarray, void* buf,
   1022                                 size_t nbytes, gc::AllocKind allocKind) {
   1023    if (buf) {
   1024      InitReservedSlot(tarray, TypedArrayObject::DATA_SLOT, buf, nbytes,
   1025                       MemoryUse::TypedArrayElements);
   1026    } else {
   1027 #ifdef DEBUG
   1028      constexpr size_t dataOffset = ArrayBufferViewObject::dataOffset();
   1029      constexpr size_t offset = dataOffset + sizeof(HeapSlot);
   1030      MOZ_ASSERT(offset + nbytes <= GetGCKindBytes(allocKind));
   1031 #endif
   1032 
   1033      void* data = tarray->fixedData(FIXED_DATA_START);
   1034      tarray->initReservedSlot(DATA_SLOT, PrivateValue(data));
   1035      memset(data, 0, nbytes);
   1036    }
   1037  }
   1038 
   1039  static FixedLengthTypedArrayObject* makeTypedArrayWithTemplate(
   1040      JSContext* cx, TypedArrayObject* templateObj, int32_t len) {
   1041    if (len < 0 || size_t(len) > ByteLengthLimit / BYTES_PER_ELEMENT) {
   1042      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1043                                JSMSG_BAD_ARRAY_LENGTH);
   1044      return nullptr;
   1045    }
   1046 
   1047    size_t nbytes = size_t(len) * BYTES_PER_ELEMENT;
   1048    MOZ_ASSERT(nbytes <= ByteLengthLimit);
   1049 
   1050    bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
   1051 
   1052    AutoSetNewObjectMetadata metadata(cx);
   1053 
   1054    gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(instanceClass())
   1055                                          : AllocKindForLazyBuffer(nbytes);
   1056    MOZ_ASSERT(templateObj->getClass() == instanceClass());
   1057 
   1058    RootedObject proto(cx, templateObj->staticPrototype());
   1059    auto* obj = makeProtoInstance(cx, proto, allocKind);
   1060    if (!obj) {
   1061      return nullptr;
   1062    }
   1063 
   1064    initTypedArraySlots(obj, len);
   1065 
   1066    void* buf = nullptr;
   1067    if (!fitsInline) {
   1068      MOZ_ASSERT(len > 0);
   1069 
   1070      nbytes = RoundUp(nbytes, sizeof(Value));
   1071      buf = cx->nursery().allocateZeroedBuffer(obj, nbytes,
   1072                                               js::ArrayBufferContentsArena);
   1073      if (!buf) {
   1074        ReportOutOfMemory(cx);
   1075        return nullptr;
   1076      }
   1077    }
   1078 
   1079    initTypedArrayData(obj, buf, nbytes, allocKind);
   1080 
   1081    return obj;
   1082  }
   1083 };
   1084 
   1085 template <typename NativeType>
   1086 class ResizableTypedArrayObjectTemplate
   1087    : public ResizableTypedArrayObject,
   1088      public TypedArrayObjectTemplate<NativeType> {
   1089  friend class js::TypedArrayObject;
   1090 
   1091  using TypedArrayTemplate = TypedArrayObjectTemplate<NativeType>;
   1092 
   1093 public:
   1094  using TypedArrayTemplate::ArrayTypeID;
   1095  using TypedArrayTemplate::BYTES_PER_ELEMENT;
   1096  using TypedArrayTemplate::protoKey;
   1097 
   1098  static inline const JSClass* instanceClass() {
   1099    static_assert(ArrayTypeID() <
   1100                  std::size(TypedArrayObject::resizableClasses));
   1101    return &TypedArrayObject::resizableClasses[ArrayTypeID()];
   1102  }
   1103 
   1104  static ResizableTypedArrayObject* newBuiltinClassInstance(
   1105      JSContext* cx, gc::AllocKind allocKind, gc::Heap heap) {
   1106    RootedObject proto(cx, GlobalObject::getOrCreatePrototype(cx, protoKey()));
   1107    if (!proto) {
   1108      return nullptr;
   1109    }
   1110    return NewTypedArrayObject<ResizableTypedArrayObject>(
   1111        cx, instanceClass(), proto, allocKind, heap);
   1112  }
   1113 
   1114  static ResizableTypedArrayObject* makeProtoInstance(JSContext* cx,
   1115                                                      HandleObject proto,
   1116                                                      gc::AllocKind allocKind) {
   1117    MOZ_ASSERT(proto);
   1118    return NewTypedArrayObject<ResizableTypedArrayObject>(
   1119        cx, instanceClass(), proto, allocKind, gc::Heap::Default);
   1120  }
   1121 
   1122  static ResizableTypedArrayObject* makeInstance(
   1123      JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
   1124      size_t byteOffset, size_t len, AutoLength autoLength,
   1125      HandleObject proto) {
   1126    MOZ_ASSERT(buffer);
   1127    MOZ_ASSERT(buffer->isResizable());
   1128    MOZ_ASSERT(!buffer->isDetached());
   1129    MOZ_ASSERT(autoLength == AutoLength::No || len == 0,
   1130               "length is zero for 'auto' length views");
   1131    MOZ_ASSERT(len <= ByteLengthLimit / BYTES_PER_ELEMENT);
   1132 
   1133    gc::AllocKind allocKind = gc::GetGCObjectKind(instanceClass());
   1134 
   1135    AutoSetNewObjectMetadata metadata(cx);
   1136    ResizableTypedArrayObject* obj;
   1137    if (proto) {
   1138      obj = makeProtoInstance(cx, proto, allocKind);
   1139    } else {
   1140      obj = newBuiltinClassInstance(cx, allocKind, gc::Heap::Default);
   1141    }
   1142    if (!obj || !obj->initResizable(cx, buffer, byteOffset, len,
   1143                                    BYTES_PER_ELEMENT, autoLength)) {
   1144      return nullptr;
   1145    }
   1146 
   1147    return obj;
   1148  }
   1149 
   1150  static ResizableTypedArrayObject* makeTemplateObject(JSContext* cx) {
   1151    gc::AllocKind allocKind = gc::GetGCObjectKind(instanceClass());
   1152 
   1153    AutoSetNewObjectMetadata metadata(cx);
   1154 
   1155    auto* tarray = newBuiltinClassInstance(cx, allocKind, gc::Heap::Tenured);
   1156    if (!tarray) {
   1157      return nullptr;
   1158    }
   1159 
   1160    tarray->initFixedSlot(TypedArrayObject::BUFFER_SLOT, JS::FalseValue());
   1161    tarray->initFixedSlot(TypedArrayObject::LENGTH_SLOT,
   1162                          PrivateValue(size_t(0)));
   1163    tarray->initFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT,
   1164                          PrivateValue(size_t(0)));
   1165    tarray->initFixedSlot(AUTO_LENGTH_SLOT, BooleanValue(false));
   1166    tarray->initFixedSlot(ResizableTypedArrayObject::INITIAL_LENGTH_SLOT,
   1167                          PrivateValue(size_t(0)));
   1168    tarray->initFixedSlot(ResizableTypedArrayObject::INITIAL_BYTE_OFFSET_SLOT,
   1169                          PrivateValue(size_t(0)));
   1170 
   1171    // Template objects don't need memory for their elements, since there
   1172    // won't be any elements to store.
   1173    MOZ_ASSERT(tarray->getReservedSlot(DATA_SLOT).isUndefined());
   1174 
   1175    return tarray;
   1176  }
   1177 };
   1178 
   1179 template <typename NativeType>
   1180 class ImmutableTypedArrayObjectTemplate
   1181    : public ImmutableTypedArrayObject,
   1182      public TypedArrayObjectTemplate<NativeType> {
   1183  friend class js::TypedArrayObject;
   1184 
   1185  using TypedArrayTemplate = TypedArrayObjectTemplate<NativeType>;
   1186 
   1187 public:
   1188  using TypedArrayTemplate::ArrayTypeID;
   1189  using TypedArrayTemplate::BYTES_PER_ELEMENT;
   1190  using TypedArrayTemplate::protoKey;
   1191 
   1192  static inline const JSClass* instanceClass() {
   1193    static_assert(ArrayTypeID() <
   1194                  std::size(TypedArrayObject::immutableClasses));
   1195    return &TypedArrayObject::immutableClasses[ArrayTypeID()];
   1196  }
   1197 
   1198  static ImmutableTypedArrayObject* newBuiltinClassInstance(
   1199      JSContext* cx, gc::AllocKind allocKind, gc::Heap heap) {
   1200    RootedObject proto(cx, GlobalObject::getOrCreatePrototype(cx, protoKey()));
   1201    if (!proto) {
   1202      return nullptr;
   1203    }
   1204    return NewTypedArrayObject<ImmutableTypedArrayObject>(
   1205        cx, instanceClass(), proto, allocKind, heap);
   1206  }
   1207 
   1208  static ImmutableTypedArrayObject* makeProtoInstance(JSContext* cx,
   1209                                                      HandleObject proto,
   1210                                                      gc::AllocKind allocKind) {
   1211    MOZ_ASSERT(proto);
   1212    return NewTypedArrayObject<ImmutableTypedArrayObject>(
   1213        cx, instanceClass(), proto, allocKind, gc::Heap::Default);
   1214  }
   1215 
   1216  static ImmutableTypedArrayObject* makeInstance(
   1217      JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
   1218      size_t byteOffset, size_t len, HandleObject proto) {
   1219    MOZ_ASSERT(buffer);
   1220    MOZ_ASSERT(buffer->isImmutable());
   1221    MOZ_ASSERT(!buffer->isDetached());
   1222    MOZ_ASSERT(len <= ByteLengthLimit / BYTES_PER_ELEMENT);
   1223 
   1224    gc::AllocKind allocKind = gc::GetGCObjectKind(instanceClass());
   1225 
   1226    AutoSetNewObjectMetadata metadata(cx);
   1227    ImmutableTypedArrayObject* obj;
   1228    if (proto) {
   1229      obj = makeProtoInstance(cx, proto, allocKind);
   1230    } else {
   1231      obj = newBuiltinClassInstance(cx, allocKind, gc::Heap::Default);
   1232    }
   1233    if (!obj || !obj->init(cx, buffer, byteOffset, len, BYTES_PER_ELEMENT)) {
   1234      return nullptr;
   1235    }
   1236 
   1237    return obj;
   1238  }
   1239 
   1240  static ImmutableTypedArrayObject* makeTemplateObject(JSContext* cx) {
   1241    gc::AllocKind allocKind = gc::GetGCObjectKind(instanceClass());
   1242 
   1243    AutoSetNewObjectMetadata metadata(cx);
   1244 
   1245    auto* tarray = newBuiltinClassInstance(cx, allocKind, gc::Heap::Tenured);
   1246    if (!tarray) {
   1247      return nullptr;
   1248    }
   1249 
   1250    tarray->initFixedSlot(TypedArrayObject::BUFFER_SLOT, JS::FalseValue());
   1251    tarray->initFixedSlot(TypedArrayObject::LENGTH_SLOT,
   1252                          PrivateValue(size_t(0)));
   1253    tarray->initFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT,
   1254                          PrivateValue(size_t(0)));
   1255 
   1256    // Template objects don't need memory for their elements, since there
   1257    // won't be any elements to store.
   1258    MOZ_ASSERT(tarray->getReservedSlot(DATA_SLOT).isUndefined());
   1259 
   1260    return tarray;
   1261  }
   1262 };
   1263 
   1264 template <typename NativeType>
   1265 bool TypedArrayObjectTemplate<NativeType>::convertValue(JSContext* cx,
   1266                                                        HandleValue v,
   1267                                                        NativeType* result) {
   1268  double d;
   1269  if (!ToNumber(cx, v, &d)) {
   1270    return false;
   1271  }
   1272 
   1273  if constexpr (!std::numeric_limits<NativeType>::is_integer) {
   1274    if (js::SupportDifferentialTesting()) {
   1275      // See the comment in ElementSpecific::doubleToNative.
   1276      d = JS::CanonicalizeNaN(d);
   1277    }
   1278  }
   1279 
   1280  // Assign based on characteristics of the destination type
   1281  *result = ConvertNumber<NativeType>(d);
   1282  return true;
   1283 }
   1284 
   1285 template <>
   1286 bool TypedArrayObjectTemplate<int64_t>::convertValue(JSContext* cx,
   1287                                                     HandleValue v,
   1288                                                     int64_t* result) {
   1289  JS_TRY_VAR_OR_RETURN_FALSE(cx, *result, ToBigInt64(cx, v));
   1290  return true;
   1291 }
   1292 
   1293 template <>
   1294 bool TypedArrayObjectTemplate<uint64_t>::convertValue(JSContext* cx,
   1295                                                      HandleValue v,
   1296                                                      uint64_t* result) {
   1297  JS_TRY_VAR_OR_RETURN_FALSE(cx, *result, ToBigUint64(cx, v));
   1298  return true;
   1299 }
   1300 
   1301 /**
   1302 * 10.4.5.16 TypedArraySetElement ( O, index, value )
   1303 *
   1304 * ES2025 draft rev ac21460fedf4b926520b06c9820bdbebad596a8b
   1305 */
   1306 template <typename NativeType>
   1307 /* static */ bool TypedArrayObjectTemplate<NativeType>::setElement(
   1308    JSContext* cx, Handle<TypedArrayObject*> obj, uint64_t index, HandleValue v,
   1309    ObjectOpResult& result) {
   1310  MOZ_ASSERT(!obj->is<ImmutableTypedArrayObject>());
   1311 
   1312  // Steps 1-2.
   1313  NativeType nativeValue;
   1314  if (!convertValue(cx, v, &nativeValue)) {
   1315    return false;
   1316  }
   1317 
   1318  // Step 3.
   1319  if (index < obj->length().valueOr(0)) {
   1320    MOZ_ASSERT(!obj->hasDetachedBuffer(),
   1321               "detaching an array buffer sets the length to zero");
   1322    TypedArrayObjectTemplate<NativeType>::setIndex(*obj, index, nativeValue);
   1323  }
   1324 
   1325  // Step 4.
   1326  return result.succeed();
   1327 }
   1328 
   1329 } /* anonymous namespace */
   1330 
   1331 TypedArrayObject* js::NewTypedArrayWithTemplateAndLength(
   1332    JSContext* cx, HandleObject templateObj, int32_t len) {
   1333  MOZ_ASSERT(templateObj->is<TypedArrayObject>());
   1334  TypedArrayObject* tobj = &templateObj->as<TypedArrayObject>();
   1335 
   1336  switch (tobj->type()) {
   1337 #define CREATE_TYPED_ARRAY(_, T, N)                                            \
   1338  case Scalar::N:                                                              \
   1339    return FixedLengthTypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate( \
   1340        cx, tobj, len);
   1341    JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
   1342 #undef CREATE_TYPED_ARRAY
   1343    default:
   1344      MOZ_CRASH("Unsupported TypedArray type");
   1345  }
   1346 }
   1347 
   1348 TypedArrayObject* js::NewTypedArrayWithTemplateAndArray(
   1349    JSContext* cx, HandleObject templateObj, HandleObject array) {
   1350  MOZ_ASSERT(templateObj->is<TypedArrayObject>());
   1351  TypedArrayObject* tobj = &templateObj->as<TypedArrayObject>();
   1352 
   1353  switch (tobj->type()) {
   1354 #define CREATE_TYPED_ARRAY(_, T, N)                                          \
   1355  case Scalar::N:                                                            \
   1356    return TypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate(cx, tobj, \
   1357                                                                   array);
   1358    JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
   1359 #undef CREATE_TYPED_ARRAY
   1360    default:
   1361      MOZ_CRASH("Unsupported TypedArray type");
   1362  }
   1363 }
   1364 
   1365 TypedArrayObject* js::NewTypedArrayWithTemplateAndBuffer(
   1366    JSContext* cx, HandleObject templateObj, HandleObject arrayBuffer,
   1367    HandleValue byteOffset, HandleValue length) {
   1368  MOZ_ASSERT(templateObj->is<TypedArrayObject>());
   1369  TypedArrayObject* tobj = &templateObj->as<TypedArrayObject>();
   1370 
   1371  switch (tobj->type()) {
   1372 #define CREATE_TYPED_ARRAY(_, T, N)                                 \
   1373  case Scalar::N:                                                   \
   1374    return TypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate( \
   1375        cx, tobj, arrayBuffer, byteOffset, length);
   1376    JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
   1377 #undef CREATE_TYPED_ARRAY
   1378    default:
   1379      MOZ_CRASH("Unsupported TypedArray type");
   1380  }
   1381 }
   1382 
   1383 TypedArrayObject* js::NewUint8ArrayWithLength(JSContext* cx, int32_t len,
   1384                                              gc::Heap heap) {
   1385  return TypedArrayObjectTemplate<uint8_t>::fromLength(cx, len, nullptr, heap);
   1386 }
   1387 
   1388 template <typename T>
   1389 /* static */ TypedArrayObject* TypedArrayObjectTemplate<T>::fromArray(
   1390    JSContext* cx, HandleObject other, HandleObject proto /* = nullptr */) {
   1391  // Allow nullptr proto for FriendAPI methods, which don't care about
   1392  // subclassing.
   1393  if (other->is<TypedArrayObject>()) {
   1394    return fromTypedArray(cx, other, /* wrapped= */ false, proto);
   1395  }
   1396 
   1397  if (other->is<WrapperObject>() &&
   1398      UncheckedUnwrap(other)->is<TypedArrayObject>()) {
   1399    return fromTypedArray(cx, other, /* wrapped= */ true, proto);
   1400  }
   1401 
   1402  return fromObject(cx, other, proto);
   1403 }
   1404 
   1405 // ES2023 draft rev cf86f1cdc28e809170733d74ea64fd0f3dd79f78
   1406 // 23.2.5.1 TypedArray ( ...args )
   1407 // 23.2.5.1.2 InitializeTypedArrayFromTypedArray ( O, srcArray )
   1408 template <typename T>
   1409 /* static */ TypedArrayObject* TypedArrayObjectTemplate<T>::fromTypedArray(
   1410    JSContext* cx, HandleObject other, bool isWrapped, HandleObject proto) {
   1411  MOZ_ASSERT_IF(!isWrapped, other->is<TypedArrayObject>());
   1412  MOZ_ASSERT_IF(isWrapped, other->is<WrapperObject>() &&
   1413                               UncheckedUnwrap(other)->is<TypedArrayObject>());
   1414 
   1415  Rooted<TypedArrayObject*> srcArray(cx);
   1416  if (!isWrapped) {
   1417    srcArray = &other->as<TypedArrayObject>();
   1418  } else {
   1419    srcArray = other->maybeUnwrapAs<TypedArrayObject>();
   1420    if (!srcArray) {
   1421      ReportAccessDenied(cx);
   1422      return nullptr;
   1423    }
   1424  }
   1425 
   1426  // InitializeTypedArrayFromTypedArray, step 1. (Skipped)
   1427 
   1428  // InitializeTypedArrayFromTypedArray, step 2.
   1429  auto srcLength = srcArray->length();
   1430  if (!srcLength) {
   1431    ReportOutOfBounds(cx, srcArray);
   1432    return nullptr;
   1433  }
   1434 
   1435  // InitializeTypedArrayFromTypedArray, steps 3-7. (Skipped)
   1436 
   1437  // InitializeTypedArrayFromTypedArray, step 8.
   1438  size_t elementLength = *srcLength;
   1439 
   1440  // InitializeTypedArrayFromTypedArray, step 9. (Skipped)
   1441 
   1442  // InitializeTypedArrayFromTypedArray, step 10.a. (Partial)
   1443  // InitializeTypedArrayFromTypedArray, step 11.a.
   1444  Rooted<ArrayBufferObject*> buffer(cx);
   1445  if (!maybeCreateArrayBuffer(cx, elementLength, &buffer)) {
   1446    return nullptr;
   1447  }
   1448 
   1449  // InitializeTypedArrayFromTypedArray, step 11.b.
   1450  if (Scalar::isBigIntType(ArrayTypeID()) !=
   1451      Scalar::isBigIntType(srcArray->type())) {
   1452    JS_ReportErrorNumberASCII(
   1453        cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_NOT_COMPATIBLE,
   1454        srcArray->getClass()->name,
   1455        TypedArrayObject::fixedLengthClasses[ArrayTypeID()].name);
   1456    return nullptr;
   1457  }
   1458 
   1459  // Step 6.b.i.
   1460  // InitializeTypedArrayFromTypedArray, steps 12-15.
   1461  Rooted<TypedArrayObject*> obj(cx, FixedLengthTypedArray::makeInstance(
   1462                                        cx, buffer, 0, elementLength, proto));
   1463  if (!obj) {
   1464    return nullptr;
   1465  }
   1466 
   1467  MOZ_RELEASE_ASSERT(!srcArray->hasDetachedBuffer());
   1468 
   1469  // InitializeTypedArrayFromTypedArray, steps 10.a. (Remaining parts)
   1470  // InitializeTypedArrayFromTypedArray, steps 11.c-f.
   1471  MOZ_ASSERT(!obj->isSharedMemory());
   1472  if (srcArray->isSharedMemory()) {
   1473    if (!ElementSpecific<T, SharedOps>::setFromTypedArray(
   1474            obj, elementLength, srcArray, elementLength, 0)) {
   1475      MOZ_ASSERT_UNREACHABLE(
   1476          "setFromTypedArray can only fail for overlapping buffers");
   1477      return nullptr;
   1478    }
   1479  } else {
   1480    if (!ElementSpecific<T, UnsharedOps>::setFromTypedArray(
   1481            obj, elementLength, srcArray, elementLength, 0)) {
   1482      MOZ_ASSERT_UNREACHABLE(
   1483          "setFromTypedArray can only fail for overlapping buffers");
   1484      return nullptr;
   1485    }
   1486  }
   1487 
   1488  // Step 6.b.v.
   1489  return obj;
   1490 }
   1491 
   1492 // ES2023 draft rev cf86f1cdc28e809170733d74ea64fd0f3dd79f78
   1493 // 23.2.5.1 TypedArray ( ...args )
   1494 // 23.2.5.1.4 InitializeTypedArrayFromList ( O, values )
   1495 // 23.2.5.1.5 InitializeTypedArrayFromArrayLike ( O, arrayLike )
   1496 template <typename T>
   1497 /* static */ TypedArrayObject* TypedArrayObjectTemplate<T>::fromObject(
   1498    JSContext* cx, HandleObject other, HandleObject proto) {
   1499  // Steps 1-4 and 6.a (Already performed in caller).
   1500 
   1501  // Steps 6.b.i (Allocation deferred until later).
   1502 
   1503  // Steps 6.b.ii-iii. (Not applicable)
   1504 
   1505  // Step 6.b.iv.
   1506 
   1507  // Fast path when iterable is a packed array using the default iterator.
   1508  if (IsArrayWithDefaultIterator<MustBePacked::Yes>(other, cx)) {
   1509    // Steps 6.b.iv.2-3. (We don't need to call IterableToList for the fast
   1510    // path).
   1511    Handle<ArrayObject*> array = other.as<ArrayObject>();
   1512 
   1513    // InitializeTypedArrayFromList, step 1.
   1514    size_t len = array->getDenseInitializedLength();
   1515 
   1516    // InitializeTypedArrayFromList, step 2.
   1517    Rooted<ArrayBufferObject*> buffer(cx);
   1518    if (!maybeCreateArrayBuffer(cx, len, &buffer)) {
   1519      return nullptr;
   1520    }
   1521 
   1522    // Steps 6.b.i.
   1523    Rooted<FixedLengthTypedArrayObject*> obj(
   1524        cx, FixedLengthTypedArray::makeInstance(cx, buffer, 0, len, proto));
   1525    if (!obj) {
   1526      return nullptr;
   1527    }
   1528 
   1529    // InitializeTypedArrayFromList, steps 3-4.
   1530    MOZ_ASSERT(!obj->isSharedMemory());
   1531    if (!ElementSpecific<T, UnsharedOps>::initFromIterablePackedArray(cx, obj,
   1532                                                                      array)) {
   1533      return nullptr;
   1534    }
   1535 
   1536    // InitializeTypedArrayFromList, step 5. (The assertion isn't applicable for
   1537    // the fast path).
   1538 
   1539    // Step 6.b.v.
   1540    return obj;
   1541  }
   1542 
   1543  // Step 6.b.iv.1 (Assertion; implicit in our implementation).
   1544 
   1545  // Step 6.b.iv.2.
   1546  RootedValue callee(cx);
   1547  RootedId iteratorId(cx, PropertyKey::Symbol(cx->wellKnownSymbols().iterator));
   1548  if (!GetProperty(cx, other, other, iteratorId, &callee)) {
   1549    return nullptr;
   1550  }
   1551 
   1552  // Steps 6.b.iv.3-4.
   1553  RootedObject arrayLike(cx);
   1554  if (!callee.isNullOrUndefined()) {
   1555    // Throw if other[Symbol.iterator] isn't callable.
   1556    if (!callee.isObject() || !callee.toObject().isCallable()) {
   1557      RootedValue otherVal(cx, ObjectValue(*other));
   1558      UniqueChars bytes =
   1559          DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, otherVal, nullptr);
   1560      if (!bytes) {
   1561        return nullptr;
   1562      }
   1563      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
   1564                               bytes.get());
   1565      return nullptr;
   1566    }
   1567 
   1568    FixedInvokeArgs<2> args2(cx);
   1569    args2[0].setObject(*other);
   1570    args2[1].set(callee);
   1571 
   1572    // Step 6.b.iv.3.a.
   1573    RootedValue rval(cx);
   1574    if (!CallSelfHostedFunction(cx, cx->names().IterableToList,
   1575                                UndefinedHandleValue, args2, &rval)) {
   1576      return nullptr;
   1577    }
   1578 
   1579    // Step 6.b.iv.3.b (Implemented below).
   1580    arrayLike = &rval.toObject();
   1581  } else {
   1582    // Step 4.a is an assertion: object is not an Iterator. Testing this is
   1583    // literally the very last thing we did, so we don't assert here.
   1584 
   1585    // Step 4.b (Implemented below).
   1586    arrayLike = other;
   1587  }
   1588 
   1589  // We implement InitializeTypedArrayFromList in terms of
   1590  // InitializeTypedArrayFromArrayLike.
   1591 
   1592  // InitializeTypedArrayFromArrayLike, step 1.
   1593  uint64_t len;
   1594  if (!GetLengthProperty(cx, arrayLike, &len)) {
   1595    return nullptr;
   1596  }
   1597 
   1598  // InitializeTypedArrayFromArrayLike, step 2.
   1599  Rooted<ArrayBufferObject*> buffer(cx);
   1600  if (!maybeCreateArrayBuffer(cx, len, &buffer)) {
   1601    return nullptr;
   1602  }
   1603 
   1604  MOZ_ASSERT(len <= ByteLengthLimit / BYTES_PER_ELEMENT);
   1605 
   1606  // Steps 6.b.i.
   1607  Rooted<TypedArrayObject*> obj(
   1608      cx, FixedLengthTypedArray::makeInstance(cx, buffer, 0, len, proto));
   1609  if (!obj) {
   1610    return nullptr;
   1611  }
   1612 
   1613  // InitializeTypedArrayFromArrayLike, steps 3-4.
   1614  MOZ_ASSERT(!obj->isSharedMemory());
   1615  if (!ElementSpecific<T, UnsharedOps>::setFromNonTypedArray(cx, obj, arrayLike,
   1616                                                             len)) {
   1617    return nullptr;
   1618  }
   1619 
   1620  // Step 6.b.v.
   1621  return obj;
   1622 }
   1623 
   1624 static bool TypedArrayConstructor(JSContext* cx, unsigned argc, Value* vp) {
   1625  CallArgs args = CallArgsFromVp(argc, vp);
   1626  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1627                            JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT,
   1628                            args.isConstructing() ? "construct" : "call");
   1629  return false;
   1630 }
   1631 
   1632 template <typename T>
   1633 static bool GetTemplateObjectForLength(JSContext* cx, int32_t length,
   1634                                       MutableHandle<TypedArrayObject*> res) {
   1635  size_t len = size_t(std::max(length, 0));
   1636 
   1637  size_t nbytes;
   1638  if (!js::CalculateAllocSize<T>(len, &nbytes) ||
   1639      nbytes > TypedArrayObject::ByteLengthLimit) {
   1640    return true;
   1641  }
   1642 
   1643  res.set(FixedLengthTypedArrayObjectTemplate<T>::makeTemplateObject(cx, len));
   1644  return !!res;
   1645 }
   1646 
   1647 template <typename T>
   1648 static TypedArrayObject* GetTemplateObjectForBuffer(
   1649    JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer) {
   1650  if (buffer->isResizable()) {
   1651    return ResizableTypedArrayObjectTemplate<T>::makeTemplateObject(cx);
   1652  }
   1653 
   1654  if (buffer->isImmutable()) {
   1655    return ImmutableTypedArrayObjectTemplate<T>::makeTemplateObject(cx);
   1656  }
   1657 
   1658  // We don't use the template's length in the object case, so we can create
   1659  // the template typed array with an initial length of zero.
   1660  uint32_t len = 0;
   1661 
   1662  return FixedLengthTypedArrayObjectTemplate<T>::makeTemplateObject(cx, len);
   1663 }
   1664 
   1665 template <typename T>
   1666 static TypedArrayObject* GetTemplateObjectForBufferView(
   1667    JSContext* cx, Handle<TypedArrayObject*> bufferView) {
   1668  if (bufferView->is<ResizableTypedArrayObject>()) {
   1669    return ResizableTypedArrayObjectTemplate<T>::makeTemplateObject(cx);
   1670  }
   1671 
   1672  if (bufferView->is<ImmutableTypedArrayObject>()) {
   1673    return ImmutableTypedArrayObjectTemplate<T>::makeTemplateObject(cx);
   1674  }
   1675 
   1676  // We don't use the template's length in the object case, so we can create
   1677  // the template typed array with an initial length of zero.
   1678  uint32_t len = 0;
   1679 
   1680  return FixedLengthTypedArrayObjectTemplate<T>::makeTemplateObject(cx, len);
   1681 }
   1682 
   1683 template <typename T>
   1684 static TypedArrayObject* GetTemplateObjectForArrayLike(
   1685    JSContext* cx, Handle<JSObject*> arrayLike) {
   1686  MOZ_ASSERT(!arrayLike->is<ArrayBufferObjectMaybeShared>(),
   1687             "Use GetTemplateObjectForBuffer for array buffer objects");
   1688  MOZ_ASSERT(!IsWrapper(arrayLike), "Wrappers not supported");
   1689 
   1690  // We don't use the template's length in the object case, so we can create
   1691  // the template typed array with an initial length of zero.
   1692  uint32_t len = 0;
   1693 
   1694  return FixedLengthTypedArrayObjectTemplate<T>::makeTemplateObject(cx, len);
   1695 }
   1696 
   1697 /* static */ bool TypedArrayObject::GetTemplateObjectForLength(
   1698    JSContext* cx, Scalar::Type type, int32_t length,
   1699    MutableHandle<TypedArrayObject*> res) {
   1700  MOZ_ASSERT(!res);
   1701 
   1702  switch (type) {
   1703 #define CREATE_TYPED_ARRAY_TEMPLATE(_, T, N) \
   1704  case Scalar::N:                            \
   1705    return ::GetTemplateObjectForLength<T>(cx, length, res);
   1706    JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY_TEMPLATE)
   1707 #undef CREATE_TYPED_ARRAY_TEMPLATE
   1708    default:
   1709      MOZ_CRASH("Unsupported TypedArray type");
   1710  }
   1711 }
   1712 
   1713 /* static */ TypedArrayObject* TypedArrayObject::GetTemplateObjectForBuffer(
   1714    JSContext* cx, Scalar::Type type,
   1715    Handle<ArrayBufferObjectMaybeShared*> buffer) {
   1716  switch (type) {
   1717 #define CREATE_TYPED_ARRAY_TEMPLATE(_, T, N) \
   1718  case Scalar::N:                            \
   1719    return ::GetTemplateObjectForBuffer<T>(cx, buffer);
   1720    JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY_TEMPLATE)
   1721 #undef CREATE_TYPED_ARRAY_TEMPLATE
   1722    default:
   1723      MOZ_CRASH("Unsupported TypedArray type");
   1724  }
   1725 }
   1726 
   1727 /* static */ TypedArrayObject* TypedArrayObject::GetTemplateObjectForBufferView(
   1728    JSContext* cx, Handle<TypedArrayObject*> bufferView) {
   1729  switch (bufferView->type()) {
   1730 #define CREATE_TYPED_ARRAY_TEMPLATE(_, T, N) \
   1731  case Scalar::N:                            \
   1732    return ::GetTemplateObjectForBufferView<T>(cx, bufferView);
   1733    JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY_TEMPLATE)
   1734 #undef CREATE_TYPED_ARRAY_TEMPLATE
   1735    default:
   1736      MOZ_CRASH("Unsupported TypedArray type");
   1737  }
   1738 }
   1739 
   1740 /* static */ TypedArrayObject* TypedArrayObject::GetTemplateObjectForArrayLike(
   1741    JSContext* cx, Scalar::Type type, Handle<JSObject*> arrayLike) {
   1742  // We don't support wrappers, because of the complicated interaction between
   1743  // wrapped ArrayBuffers and TypedArrays, see |fromBufferWrapped()|.
   1744  MOZ_ASSERT(!IsWrapper(arrayLike));
   1745 
   1746  switch (type) {
   1747 #define CREATE_TYPED_ARRAY_TEMPLATE(_, T, N) \
   1748  case Scalar::N:                            \
   1749    return ::GetTemplateObjectForArrayLike<T>(cx, arrayLike);
   1750    JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY_TEMPLATE)
   1751 #undef CREATE_TYPED_ARRAY_TEMPLATE
   1752    default:
   1753      MOZ_CRASH("Unsupported TypedArray type");
   1754  }
   1755 }
   1756 
   1757 static bool LengthGetterImpl(JSContext* cx, const CallArgs& args) {
   1758  auto* tarr = &args.thisv().toObject().as<TypedArrayObject>();
   1759  args.rval().setNumber(tarr->length().valueOr(0));
   1760  return true;
   1761 }
   1762 
   1763 static bool TypedArray_lengthGetter(JSContext* cx, unsigned argc, Value* vp) {
   1764  CallArgs args = CallArgsFromVp(argc, vp);
   1765  return CallNonGenericMethod<IsTypedArrayObject, LengthGetterImpl>(cx, args);
   1766 }
   1767 
   1768 static bool ByteOffsetGetterImpl(JSContext* cx, const CallArgs& args) {
   1769  auto* tarr = &args.thisv().toObject().as<TypedArrayObject>();
   1770  args.rval().setNumber(tarr->byteOffset().valueOr(0));
   1771  return true;
   1772 }
   1773 
   1774 static bool TypedArray_byteOffsetGetter(JSContext* cx, unsigned argc,
   1775                                        Value* vp) {
   1776  CallArgs args = CallArgsFromVp(argc, vp);
   1777  return CallNonGenericMethod<IsTypedArrayObject, ByteOffsetGetterImpl>(cx,
   1778                                                                        args);
   1779 }
   1780 
   1781 static bool ByteLengthGetterImpl(JSContext* cx, const CallArgs& args) {
   1782  auto* tarr = &args.thisv().toObject().as<TypedArrayObject>();
   1783  args.rval().setNumber(tarr->byteLength().valueOr(0));
   1784  return true;
   1785 }
   1786 
   1787 static bool TypedArray_byteLengthGetter(JSContext* cx, unsigned argc,
   1788                                        Value* vp) {
   1789  CallArgs args = CallArgsFromVp(argc, vp);
   1790  return CallNonGenericMethod<IsTypedArrayObject, ByteLengthGetterImpl>(cx,
   1791                                                                        args);
   1792 }
   1793 
   1794 static bool BufferGetterImpl(JSContext* cx, const CallArgs& args) {
   1795  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   1796  Rooted<TypedArrayObject*> tarray(
   1797      cx, &args.thisv().toObject().as<TypedArrayObject>());
   1798  if (!TypedArrayObject::ensureHasBuffer(cx, tarray)) {
   1799    return false;
   1800  }
   1801  args.rval().set(tarray->bufferValue());
   1802  return true;
   1803 }
   1804 
   1805 static bool TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp) {
   1806  CallArgs args = CallArgsFromVp(argc, vp);
   1807  return CallNonGenericMethod<IsTypedArrayObject, BufferGetterImpl>(cx, args);
   1808 }
   1809 
   1810 // ES2019 draft rev fc9ecdcd74294d0ca3146d4b274e2a8e79565dc3
   1811 // 22.2.3.32 get %TypedArray%.prototype [ @@toStringTag ]
   1812 static bool TypedArray_toStringTagGetter(JSContext* cx, unsigned argc,
   1813                                         Value* vp) {
   1814  CallArgs args = CallArgsFromVp(argc, vp);
   1815 
   1816  // Steps 1-2.
   1817  if (!args.thisv().isObject()) {
   1818    args.rval().setUndefined();
   1819    return true;
   1820  }
   1821 
   1822  JSObject* obj = CheckedUnwrapStatic(&args.thisv().toObject());
   1823  if (!obj) {
   1824    ReportAccessDenied(cx);
   1825    return false;
   1826  }
   1827 
   1828  // Step 3.
   1829  if (!obj->is<TypedArrayObject>()) {
   1830    args.rval().setUndefined();
   1831    return true;
   1832  }
   1833 
   1834  // Steps 4-6.
   1835  JSProtoKey protoKey = StandardProtoKeyOrNull(obj);
   1836  MOZ_ASSERT(protoKey);
   1837 
   1838  args.rval().setString(ClassName(protoKey, cx));
   1839  return true;
   1840 }
   1841 
   1842 /* static */ const JSPropertySpec TypedArrayObject::protoAccessors[] = {
   1843    JS_INLINABLE_PSG("length", TypedArray_lengthGetter, 0, TypedArrayLength),
   1844    JS_PSG("buffer", TypedArray_bufferGetter, 0),
   1845    JS_INLINABLE_PSG("byteLength", TypedArray_byteLengthGetter, 0,
   1846                     TypedArrayByteLength),
   1847    JS_INLINABLE_PSG("byteOffset", TypedArray_byteOffsetGetter, 0,
   1848                     TypedArrayByteOffset),
   1849    JS_SYM_GET(toStringTag, TypedArray_toStringTagGetter, 0),
   1850    JS_PS_END,
   1851 };
   1852 
   1853 template <typename T>
   1854 static inline bool SetFromTypedArray(TypedArrayObject* target,
   1855                                     size_t targetLength,
   1856                                     TypedArrayObject* source,
   1857                                     size_t sourceLength, size_t offset,
   1858                                     size_t sourceOffset = 0) {
   1859  // WARNING: |source| may be an unwrapped typed array from a different
   1860  // compartment. Proceed with caution!
   1861 
   1862  if (target->isSharedMemory() || source->isSharedMemory()) {
   1863    return ElementSpecific<T, SharedOps>::setFromTypedArray(
   1864        target, targetLength, source, sourceLength, offset, sourceOffset);
   1865  }
   1866  return ElementSpecific<T, UnsharedOps>::setFromTypedArray(
   1867      target, targetLength, source, sourceLength, offset, sourceOffset);
   1868 }
   1869 
   1870 template <typename T>
   1871 static inline bool SetFromNonTypedArray(JSContext* cx,
   1872                                        Handle<TypedArrayObject*> target,
   1873                                        HandleObject source, size_t len,
   1874                                        size_t offset) {
   1875  MOZ_ASSERT(!source->is<TypedArrayObject>(), "use SetFromTypedArray");
   1876 
   1877  if (target->isSharedMemory()) {
   1878    return ElementSpecific<T, SharedOps>::setFromNonTypedArray(
   1879        cx, target, source, len, offset);
   1880  }
   1881  return ElementSpecific<T, UnsharedOps>::setFromNonTypedArray(
   1882      cx, target, source, len, offset);
   1883 }
   1884 
   1885 // ES2023 draft rev 22cc56ab08fcab92a865978c0aa5c6f1d8ce250f
   1886 // 23.2.3.24.1 SetTypedArrayFromTypedArray ( target, targetOffset, source )
   1887 static bool SetTypedArrayFromTypedArray(JSContext* cx,
   1888                                        Handle<TypedArrayObject*> target,
   1889                                        double targetOffset,
   1890                                        size_t targetLength,
   1891                                        Handle<TypedArrayObject*> source) {
   1892  // WARNING: |source| may be an unwrapped typed array from a different
   1893  // compartment. Proceed with caution!
   1894 
   1895  MOZ_ASSERT(targetOffset >= 0);
   1896 
   1897  // Steps 1-3. (Performed in caller.)
   1898  MOZ_ASSERT(!target->hasDetachedBuffer());
   1899 
   1900  // Steps 4-5.
   1901  auto sourceLength = source->length();
   1902  if (!sourceLength) {
   1903    ReportOutOfBounds(cx, source);
   1904    return false;
   1905  }
   1906 
   1907  // Steps 13-14 (Split into two checks to provide better error messages).
   1908  if (targetOffset > targetLength) {
   1909    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
   1910    return false;
   1911  }
   1912 
   1913  // Step 14 (Cont'd).
   1914  size_t offset = size_t(targetOffset);
   1915  if (*sourceLength > targetLength - offset) {
   1916    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1917                              JSMSG_SOURCE_ARRAY_TOO_LONG);
   1918    return false;
   1919  }
   1920 
   1921  // Step 15.
   1922  if (Scalar::isBigIntType(target->type()) !=
   1923      Scalar::isBigIntType(source->type())) {
   1924    JS_ReportErrorNumberASCII(
   1925        cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_NOT_COMPATIBLE,
   1926        source->getClass()->name, target->getClass()->name);
   1927    return false;
   1928  }
   1929 
   1930  // Steps 6-12, 16-24.
   1931  switch (target->type()) {
   1932 #define SET_FROM_TYPED_ARRAY(_, T, N)                                      \
   1933  case Scalar::N:                                                          \
   1934    if (!SetFromTypedArray<T>(target, targetLength, source, *sourceLength, \
   1935                              offset)) {                                   \
   1936      ReportOutOfMemory(cx);                                               \
   1937      return false;                                                        \
   1938    }                                                                      \
   1939    break;
   1940    JS_FOR_EACH_TYPED_ARRAY(SET_FROM_TYPED_ARRAY)
   1941 #undef SET_FROM_TYPED_ARRAY
   1942    default:
   1943      MOZ_CRASH("Unsupported TypedArray type");
   1944  }
   1945 
   1946  return true;
   1947 }
   1948 
   1949 // ES2023 draft rev 22cc56ab08fcab92a865978c0aa5c6f1d8ce250f
   1950 // 23.2.3.24.1 SetTypedArrayFromArrayLike ( target, targetOffset, source )
   1951 static bool SetTypedArrayFromArrayLike(JSContext* cx,
   1952                                       Handle<TypedArrayObject*> target,
   1953                                       double targetOffset, size_t targetLength,
   1954                                       HandleObject src) {
   1955  MOZ_ASSERT(targetOffset >= 0);
   1956 
   1957  // Steps 1-2. (Performed in caller.)
   1958  MOZ_ASSERT(target->length().isSome());
   1959 
   1960  // Steps 3-4. (Performed in caller.)
   1961 
   1962  // Step 5.
   1963  uint64_t srcLength;
   1964  if (!GetLengthProperty(cx, src, &srcLength)) {
   1965    return false;
   1966  }
   1967 
   1968  // Steps 6-7 (Split into two checks to provide better error messages).
   1969  if (targetOffset > targetLength) {
   1970    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
   1971    return false;
   1972  }
   1973 
   1974  // Step 7 (Cont'd).
   1975  size_t offset = size_t(targetOffset);
   1976  if (srcLength > targetLength - offset) {
   1977    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1978                              JSMSG_SOURCE_ARRAY_TOO_LONG);
   1979    return false;
   1980  }
   1981 
   1982  MOZ_ASSERT(srcLength <= targetLength);
   1983 
   1984  // Steps 8-9.
   1985  if (srcLength > 0) {
   1986    switch (target->type()) {
   1987 #define SET_FROM_NON_TYPED_ARRAY(_, T, N)                             \
   1988  case Scalar::N:                                                     \
   1989    if (!SetFromNonTypedArray<T>(cx, target, src, srcLength, offset)) \
   1990      return false;                                                   \
   1991    break;
   1992      JS_FOR_EACH_TYPED_ARRAY(SET_FROM_NON_TYPED_ARRAY)
   1993 #undef SET_FROM_NON_TYPED_ARRAY
   1994      default:
   1995        MOZ_CRASH("Unsupported TypedArray type");
   1996    }
   1997  }
   1998 
   1999  // Step 10.
   2000  return true;
   2001 }
   2002 
   2003 // ES2023 draft rev 22cc56ab08fcab92a865978c0aa5c6f1d8ce250f
   2004 // 23.2.3.24 %TypedArray%.prototype.set ( source [ , offset ] )
   2005 // 23.2.3.24.1 SetTypedArrayFromTypedArray ( target, targetOffset, source )
   2006 // 23.2.3.24.2 SetTypedArrayFromArrayLike ( target, targetOffset, source )
   2007 static bool TypedArray_set(JSContext* cx, const CallArgs& args) {
   2008  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   2009 
   2010  // Steps 1-3 (Validation performed as part of CallNonGenericMethod).
   2011  Rooted<TypedArrayObject*> target(
   2012      cx, &args.thisv().toObject().as<TypedArrayObject>());
   2013 
   2014  // Additional step from Immutable ArrayBuffer proposal.
   2015  if (target->is<ImmutableTypedArrayObject>()) {
   2016    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2017                              JSMSG_ARRAYBUFFER_IMMUTABLE);
   2018    return false;
   2019  }
   2020 
   2021  // Steps 4-5.
   2022  double targetOffset = 0;
   2023  if (args.length() > 1) {
   2024    // Step 4.
   2025    if (!ToInteger(cx, args[1], &targetOffset)) {
   2026      return false;
   2027    }
   2028 
   2029    // Step 5.
   2030    if (targetOffset < 0) {
   2031      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
   2032      return false;
   2033    }
   2034  }
   2035 
   2036  // 23.2.3.24.1, steps 1-2.
   2037  // 23.2.3.24.2, steps 1-2.
   2038  auto targetLength = target->length();
   2039  if (!targetLength) {
   2040    ReportOutOfBounds(cx, target);
   2041    return false;
   2042  }
   2043 
   2044  // 23.2.3.24.2, step 4. (23.2.3.24.1 only applies if args[0] is a typed
   2045  // array, so it doesn't make a difference there to apply ToObject here.)
   2046  RootedObject src(cx, ToObject(cx, args.get(0)));
   2047  if (!src) {
   2048    return false;
   2049  }
   2050 
   2051  Rooted<TypedArrayObject*> srcTypedArray(cx);
   2052  {
   2053    JSObject* obj = CheckedUnwrapStatic(src);
   2054    if (!obj) {
   2055      ReportAccessDenied(cx);
   2056      return false;
   2057    }
   2058 
   2059    if (obj->is<TypedArrayObject>()) {
   2060      srcTypedArray = &obj->as<TypedArrayObject>();
   2061    }
   2062  }
   2063 
   2064  // Steps 6-7.
   2065  if (srcTypedArray) {
   2066    if (!SetTypedArrayFromTypedArray(cx, target, targetOffset, *targetLength,
   2067                                     srcTypedArray)) {
   2068      return false;
   2069    }
   2070  } else {
   2071    if (!SetTypedArrayFromArrayLike(cx, target, targetOffset, *targetLength,
   2072                                    src)) {
   2073      return false;
   2074    }
   2075  }
   2076 
   2077  // Step 8.
   2078  args.rval().setUndefined();
   2079  return true;
   2080 }
   2081 
   2082 static bool TypedArray_set(JSContext* cx, unsigned argc, Value* vp) {
   2083  CallArgs args = CallArgsFromVp(argc, vp);
   2084  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_set>(cx, args);
   2085 }
   2086 
   2087 static bool TypedArraySet(TypedArrayObject* target, TypedArrayObject* source,
   2088                          intptr_t offset) {
   2089  MOZ_ASSERT(offset >= 0);
   2090 
   2091  size_t targetLength = target->length().valueOr(0);
   2092  size_t sourceLength = source->length().valueOr(0);
   2093 
   2094  switch (target->type()) {
   2095 #define SET_FROM_TYPED_ARRAY(_, T, N)                                       \
   2096  case Scalar::N:                                                           \
   2097    return SetFromTypedArray<T>(target, targetLength, source, sourceLength, \
   2098                                size_t(offset));
   2099    JS_FOR_EACH_TYPED_ARRAY(SET_FROM_TYPED_ARRAY)
   2100 #undef SET_FROM_TYPED_ARRAY
   2101    default:
   2102      break;
   2103  }
   2104  MOZ_CRASH("Unsupported TypedArray type");
   2105 }
   2106 
   2107 bool js::TypedArraySet(JSContext* cx, TypedArrayObject* target,
   2108                       TypedArrayObject* source, intptr_t offset) {
   2109  if (!::TypedArraySet(target, source, offset)) {
   2110    ReportOutOfMemory(cx);
   2111    return false;
   2112  }
   2113  return true;
   2114 }
   2115 
   2116 void js::TypedArraySetInfallible(TypedArrayObject* target,
   2117                                 TypedArrayObject* source, intptr_t offset) {
   2118  AutoUnsafeCallWithABI unsafe;
   2119 
   2120  MOZ_ALWAYS_TRUE(::TypedArraySet(target, source, offset));
   2121 }
   2122 
   2123 static bool TypedArraySetFromSubarray(TypedArrayObject* target,
   2124                                      TypedArrayObject* source, intptr_t offset,
   2125                                      intptr_t sourceOffset,
   2126                                      intptr_t sourceLength) {
   2127  MOZ_ASSERT(offset >= 0);
   2128  MOZ_ASSERT(sourceOffset >= 0);
   2129  MOZ_ASSERT(sourceLength >= 0);
   2130 
   2131  size_t targetLength = target->length().valueOr(0);
   2132 
   2133  switch (target->type()) {
   2134 #define SET_FROM_TYPED_ARRAY(_, T, N)                                 \
   2135  case Scalar::N:                                                     \
   2136    return SetFromTypedArray<T>(target, targetLength, source,         \
   2137                                size_t(sourceLength), size_t(offset), \
   2138                                size_t(sourceOffset));
   2139    JS_FOR_EACH_TYPED_ARRAY(SET_FROM_TYPED_ARRAY)
   2140 #undef SET_FROM_TYPED_ARRAY
   2141    default:
   2142      break;
   2143  }
   2144  MOZ_CRASH("Unsupported TypedArray type");
   2145 }
   2146 
   2147 bool js::TypedArraySetFromSubarray(JSContext* cx, TypedArrayObject* target,
   2148                                   TypedArrayObject* source, intptr_t offset,
   2149                                   intptr_t sourceOffset,
   2150                                   intptr_t sourceLength) {
   2151  if (!::TypedArraySetFromSubarray(target, source, offset, sourceOffset,
   2152                                   sourceLength)) {
   2153    ReportOutOfMemory(cx);
   2154    return false;
   2155  }
   2156  return true;
   2157 }
   2158 
   2159 void js::TypedArraySetFromSubarrayInfallible(TypedArrayObject* target,
   2160                                             TypedArrayObject* source,
   2161                                             intptr_t offset,
   2162                                             intptr_t sourceOffset,
   2163                                             intptr_t sourceLength) {
   2164  AutoUnsafeCallWithABI unsafe;
   2165 
   2166  MOZ_ALWAYS_TRUE(::TypedArraySetFromSubarray(target, source, offset,
   2167                                              sourceOffset, sourceLength));
   2168 }
   2169 
   2170 // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
   2171 // 22.2.3.5 %TypedArray%.prototype.copyWithin ( target, start [ , end ] )
   2172 static bool TypedArray_copyWithin(JSContext* cx, const CallArgs& args) {
   2173  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   2174 
   2175  // Steps 1-2.
   2176  Rooted<TypedArrayObject*> tarray(
   2177      cx, &args.thisv().toObject().as<TypedArrayObject>());
   2178 
   2179  auto arrayLength = tarray->length();
   2180  if (!arrayLength) {
   2181    ReportOutOfBounds(cx, tarray);
   2182    return false;
   2183  }
   2184 
   2185  // Additional step from Immutable ArrayBuffer proposal.
   2186  if (tarray->is<ImmutableTypedArrayObject>()) {
   2187    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2188                              JSMSG_ARRAYBUFFER_IMMUTABLE);
   2189    return false;
   2190  }
   2191 
   2192  // Step 3.
   2193  size_t len = *arrayLength;
   2194 
   2195  // Steps 4-5.
   2196  size_t to = 0;
   2197  if (args.hasDefined(0)) {
   2198    if (!ToIntegerIndex(cx, args[0], len, &to)) {
   2199      return false;
   2200    }
   2201  }
   2202 
   2203  // Steps 6-7.
   2204  size_t from = 0;
   2205  if (args.hasDefined(1)) {
   2206    if (!ToIntegerIndex(cx, args[1], len, &from)) {
   2207      return false;
   2208    }
   2209  }
   2210 
   2211  // Steps 8-9.
   2212  size_t final_ = len;
   2213  if (args.hasDefined(2)) {
   2214    if (!ToIntegerIndex(cx, args[2], len, &final_)) {
   2215      return false;
   2216    }
   2217  }
   2218 
   2219  // Step 10.
   2220  MOZ_ASSERT(to <= len);
   2221  size_t count;
   2222  if (from <= final_) {
   2223    count = std::min(final_ - from, len - to);
   2224  } else {
   2225    count = 0;
   2226  }
   2227 
   2228  // Step 11.
   2229  //
   2230  // Note that this copies elements effectively by memmove, *not* in
   2231  // step 11's specified order.  This is unobservable, even when the underlying
   2232  // buffer is a SharedArrayBuffer instance, because the access is unordered and
   2233  // therefore is allowed to have data races.
   2234 
   2235  if (count == 0) {
   2236    args.rval().setObject(*tarray);
   2237    return true;
   2238  }
   2239 
   2240  // Reacquire the length because side-effects may have detached or resized the
   2241  // array buffer.
   2242  arrayLength = tarray->length();
   2243  if (!arrayLength) {
   2244    ReportOutOfBounds(cx, tarray);
   2245    return false;
   2246  }
   2247 
   2248  // Recompute the bounds if the current length is smaller.
   2249  if (*arrayLength < len) {
   2250    MOZ_ASSERT(to + count <= len);
   2251    MOZ_ASSERT(from + count <= len);
   2252 
   2253    len = *arrayLength;
   2254 
   2255    // Don't copy any bytes if either index is no longer in-bounds.
   2256    if (to >= len || from >= len) {
   2257      args.rval().setObject(*tarray);
   2258      return true;
   2259    }
   2260 
   2261    // Restrict |count| to not copy any bytes after the end of the array.
   2262    count = std::min(count, std::min(len - to, len - from));
   2263    MOZ_ASSERT(count > 0);
   2264  }
   2265 
   2266  // Don't multiply by |tarray->bytesPerElement()| in case the compiler can't
   2267  // strength-reduce multiplication by 1/2/4/8 into the equivalent shift.
   2268  const size_t ElementShift = TypedArrayShift(tarray->type());
   2269 
   2270  MOZ_ASSERT((SIZE_MAX >> ElementShift) > to);
   2271  size_t byteDest = to << ElementShift;
   2272 
   2273  MOZ_ASSERT((SIZE_MAX >> ElementShift) > from);
   2274  size_t byteSrc = from << ElementShift;
   2275 
   2276  MOZ_ASSERT((SIZE_MAX >> ElementShift) >= count);
   2277  size_t byteSize = count << ElementShift;
   2278 
   2279 #ifdef DEBUG
   2280  {
   2281    size_t viewByteLength = len << ElementShift;
   2282    MOZ_ASSERT(byteSize <= viewByteLength);
   2283    MOZ_ASSERT(byteDest < viewByteLength);
   2284    MOZ_ASSERT(byteSrc < viewByteLength);
   2285    MOZ_ASSERT(byteDest <= viewByteLength - byteSize);
   2286    MOZ_ASSERT(byteSrc <= viewByteLength - byteSize);
   2287  }
   2288 #endif
   2289 
   2290  if (tarray->isSharedMemory()) {
   2291    auto data = SharedOps::extract(tarray).cast<uint8_t*>();
   2292    SharedOps::memmove(data + byteDest, data + byteSrc, byteSize);
   2293  } else {
   2294    auto data = UnsharedOps::extract(tarray).cast<uint8_t*>();
   2295    UnsharedOps::memmove(data + byteDest, data + byteSrc, byteSize);
   2296  }
   2297 
   2298  args.rval().setObject(*tarray);
   2299  return true;
   2300 }
   2301 
   2302 static bool TypedArray_copyWithin(JSContext* cx, unsigned argc, Value* vp) {
   2303  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype",
   2304                                        "copyWithin");
   2305  CallArgs args = CallArgsFromVp(argc, vp);
   2306  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_copyWithin>(cx,
   2307                                                                         args);
   2308 }
   2309 
   2310 template <typename ExternalType, typename NativeType>
   2311 static bool TypedArrayJoinKernel(JSContext* cx,
   2312                                 Handle<TypedArrayObject*> tarray, size_t len,
   2313                                 Handle<JSLinearString*> sep,
   2314                                 JSStringBuilder& sb) {
   2315  // Steps 7-8.
   2316  for (size_t k = 0; k < len; k++) {
   2317    if (!CheckForInterrupt(cx)) {
   2318      return false;
   2319    }
   2320 
   2321    // Step 8.a.
   2322    if (k > 0 && sep->length() > 0 && !sb.append(sep)) {
   2323      return false;
   2324    }
   2325 
   2326    // Step 8.b-c.
   2327    auto element = TypedArrayObjectTemplate<NativeType>::getIndex(tarray, k);
   2328    if constexpr (std::numeric_limits<NativeType>::is_integer) {
   2329      // Plus one to include the largest number and plus one for the sign.
   2330      constexpr size_t MaximumLength =
   2331          std::numeric_limits<NativeType>::digits10 + 1 +
   2332          std::numeric_limits<NativeType>::is_signed;
   2333 
   2334      char str[MaximumLength] = {};
   2335      auto result = std::to_chars(str, std::end(str),
   2336                                  static_cast<ExternalType>(element), 10);
   2337      MOZ_ASSERT(result.ec == std::errc());
   2338 
   2339      size_t strlen = result.ptr - str;
   2340      if (!sb.append(str, strlen)) {
   2341        return false;
   2342      }
   2343    } else {
   2344      ToCStringBuf cbuf;
   2345      size_t strlen;
   2346      char* str = NumberToCString(&cbuf, static_cast<double>(element), &strlen);
   2347      if (!sb.append(str, strlen)) {
   2348        return false;
   2349      }
   2350    }
   2351  }
   2352  return true;
   2353 }
   2354 
   2355 /**
   2356 * %TypedArray%.prototype.join ( separator )
   2357 *
   2358 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   2359 */
   2360 static bool TypedArray_join(JSContext* cx, const CallArgs& args) {
   2361  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   2362 
   2363  // Steps 1-3.
   2364  Rooted<TypedArrayObject*> tarray(
   2365      cx, &args.thisv().toObject().as<TypedArrayObject>());
   2366 
   2367  auto arrayLength = tarray->length();
   2368  if (!arrayLength) {
   2369    ReportOutOfBounds(cx, tarray);
   2370    return false;
   2371  }
   2372  size_t len = *arrayLength;
   2373 
   2374  // Steps 4-5.
   2375  Rooted<JSLinearString*> sep(cx);
   2376  if (args.hasDefined(0)) {
   2377    JSString* s = ToString<CanGC>(cx, args[0]);
   2378    if (!s) {
   2379      return false;
   2380    }
   2381 
   2382    sep = s->ensureLinear(cx);
   2383    if (!sep) {
   2384      return false;
   2385    }
   2386  } else {
   2387    sep = cx->names().comma_;
   2388  }
   2389 
   2390  // Steps 6-9 (When the length is zero, directly return the empty string).
   2391  if (len == 0) {
   2392    args.rval().setString(cx->emptyString());
   2393    return true;
   2394  }
   2395 
   2396  // Step 6.
   2397  JSStringBuilder sb(cx);
   2398  if (sep->hasTwoByteChars() && !sb.ensureTwoByteChars()) {
   2399    return false;
   2400  }
   2401 
   2402  // Reacquire the length because side-effects may have detached or resized the
   2403  // array buffer.
   2404  size_t actualLength = std::min(len, tarray->length().valueOr(0));
   2405 
   2406  // The string representation of each element has at least one character.
   2407  auto res = mozilla::CheckedInt<uint32_t>(actualLength);
   2408 
   2409  // The separator will be added |length - 1| times, reserve space for that so
   2410  // that we don't have to unnecessarily grow the buffer.
   2411  size_t seplen = sep->length();
   2412  if (seplen > 0) {
   2413    if (len > UINT32_MAX) {
   2414      ReportAllocationOverflow(cx);
   2415      return false;
   2416    }
   2417    res += mozilla::CheckedInt<uint32_t>(seplen) * (uint32_t(len) - 1);
   2418  }
   2419  if (!res.isValid()) {
   2420    ReportAllocationOverflow(cx);
   2421    return false;
   2422  }
   2423  if (!sb.reserve(res.value())) {
   2424    return false;
   2425  }
   2426 
   2427  switch (tarray->type()) {
   2428 #define TYPED_ARRAY_JOIN(ExternalType, NativeType, Name) \
   2429  case Scalar::Name:                                     \
   2430    if (!TypedArrayJoinKernel<ExternalType, NativeType>( \
   2431            cx, tarray, actualLength, sep, sb)) {        \
   2432      return false;                                      \
   2433    }                                                    \
   2434    break;
   2435    JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_JOIN)
   2436 #undef TYPED_ARRAY_JOIN
   2437    default:
   2438      MOZ_CRASH("Unsupported TypedArray type");
   2439  }
   2440 
   2441  for (size_t k = actualLength; k < len; k++) {
   2442    if (!CheckForInterrupt(cx)) {
   2443      return false;
   2444    }
   2445 
   2446    // Step 8.a.
   2447    if (k > 0 && !sb.append(sep)) {
   2448      return false;
   2449    }
   2450 
   2451    // Steps 8.b-c. (Not applicable)
   2452  }
   2453 
   2454  // Step 9.
   2455  JSString* str = sb.finishString();
   2456  if (!str) {
   2457    return false;
   2458  }
   2459 
   2460  args.rval().setString(str);
   2461  return true;
   2462 }
   2463 
   2464 /**
   2465 * %TypedArray%.prototype.join ( separator )
   2466 *
   2467 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   2468 */
   2469 static bool TypedArray_join(JSContext* cx, unsigned argc, Value* vp) {
   2470  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype", "join");
   2471  CallArgs args = CallArgsFromVp(argc, vp);
   2472  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_join>(cx, args);
   2473 }
   2474 
   2475 template <typename Ops, typename NativeType>
   2476 static int64_t TypedArrayIndexOfNaive(TypedArrayObject* tarray, size_t k,
   2477                                      size_t len, NativeType searchElement) {
   2478  MOZ_RELEASE_ASSERT(k < len);
   2479  MOZ_RELEASE_ASSERT(len <= tarray->length().valueOr(0));
   2480 
   2481  SharedMem<NativeType*> data =
   2482      Ops::extract(tarray).template cast<NativeType*>();
   2483  for (size_t i = k; i < len; i++) {
   2484    NativeType element = Ops::load(data + i);
   2485    if (element == searchElement) {
   2486      return int64_t(i);
   2487    }
   2488  }
   2489  return -1;
   2490 }
   2491 
   2492 template <typename NativeType>
   2493 static int64_t TypedArrayIndexOfSIMD(TypedArrayObject* tarray, size_t k,
   2494                                     size_t len, NativeType searchElement) {
   2495  MOZ_RELEASE_ASSERT(k < len);
   2496  MOZ_RELEASE_ASSERT(len <= tarray->length().valueOr(0));
   2497 
   2498  if constexpr (sizeof(NativeType) == 1) {
   2499    auto* data = UnsharedOps::extract(tarray).cast<char*>().unwrapUnshared();
   2500    auto* ptr = mozilla::SIMD::memchr8(
   2501        data + k, mozilla::BitwiseCast<char>(searchElement), len - k);
   2502    if (!ptr) {
   2503      return -1;
   2504    }
   2505    return int64_t(ptr - data);
   2506  } else if constexpr (sizeof(NativeType) == 2) {
   2507    auto* data =
   2508        UnsharedOps::extract(tarray).cast<char16_t*>().unwrapUnshared();
   2509    auto* ptr = mozilla::SIMD::memchr16(
   2510        data + k, mozilla::BitwiseCast<char16_t>(searchElement), len - k);
   2511    if (!ptr) {
   2512      return -1;
   2513    }
   2514    return int64_t(ptr - data);
   2515  } else if constexpr (sizeof(NativeType) == 4) {
   2516    auto* data =
   2517        UnsharedOps::extract(tarray).cast<uint32_t*>().unwrapUnshared();
   2518    auto* ptr = mozilla::SIMD::memchr32(
   2519        data + k, mozilla::BitwiseCast<uint32_t>(searchElement), len - k);
   2520    if (!ptr) {
   2521      return -1;
   2522    }
   2523    return int64_t(ptr - data);
   2524  } else {
   2525    static_assert(sizeof(NativeType) == 8);
   2526 
   2527    auto* data =
   2528        UnsharedOps::extract(tarray).cast<uint64_t*>().unwrapUnshared();
   2529    auto* ptr = mozilla::SIMD::memchr64(
   2530        data + k, mozilla::BitwiseCast<uint64_t>(searchElement), len - k);
   2531    if (!ptr) {
   2532      return -1;
   2533    }
   2534    return int64_t(ptr - data);
   2535  }
   2536 }
   2537 
   2538 template <typename ExternalType, typename NativeType>
   2539 static typename std::enable_if_t<!std::numeric_limits<NativeType>::is_integer,
   2540                                 int64_t>
   2541 TypedArrayIndexOf(TypedArrayObject* tarray, size_t k, size_t len,
   2542                  const Value& searchElement) {
   2543  if (!searchElement.isNumber()) {
   2544    return -1;
   2545  }
   2546 
   2547  double d = searchElement.toNumber();
   2548  NativeType e = NativeType(d);
   2549 
   2550  // Return early if the search element is not representable using |NativeType|
   2551  // or if it's NaN.
   2552  if (double(e) != d) {
   2553    return -1;
   2554  }
   2555  MOZ_ASSERT(!std::isnan(d));
   2556 
   2557  if (tarray->isSharedMemory()) {
   2558    return TypedArrayIndexOfNaive<SharedOps>(tarray, k, len, e);
   2559  }
   2560  if (e == NativeType(0)) {
   2561    // Can't use bitwise comparison when searching for ±0.
   2562    return TypedArrayIndexOfNaive<UnsharedOps>(tarray, k, len, e);
   2563  }
   2564  return TypedArrayIndexOfSIMD(tarray, k, len, e);
   2565 }
   2566 
   2567 template <typename ExternalType, typename NativeType>
   2568 static typename std::enable_if_t<std::numeric_limits<NativeType>::is_integer &&
   2569                                     sizeof(NativeType) < 8,
   2570                                 int64_t>
   2571 TypedArrayIndexOf(TypedArrayObject* tarray, size_t k, size_t len,
   2572                  const Value& searchElement) {
   2573  if (!searchElement.isNumber()) {
   2574    return -1;
   2575  }
   2576 
   2577  int64_t d;
   2578  if (searchElement.isInt32()) {
   2579    d = searchElement.toInt32();
   2580  } else {
   2581    if (!mozilla::NumberEqualsInt64(searchElement.toDouble(), &d)) {
   2582      return -1;
   2583    }
   2584  }
   2585 
   2586  // Ensure search element is representable using |ExternalType|, which implies
   2587  // it can be represented using |NativeType|.
   2588  mozilla::CheckedInt<ExternalType> checked{d};
   2589  if (!checked.isValid()) {
   2590    return -1;
   2591  }
   2592  NativeType e = static_cast<NativeType>(checked.value());
   2593 
   2594  if (tarray->isSharedMemory()) {
   2595    return TypedArrayIndexOfNaive<SharedOps>(tarray, k, len, e);
   2596  }
   2597  return TypedArrayIndexOfSIMD(tarray, k, len, e);
   2598 }
   2599 
   2600 template <typename ExternalType, typename NativeType>
   2601 static typename std::enable_if_t<std::is_same_v<NativeType, int64_t>, int64_t>
   2602 TypedArrayIndexOf(TypedArrayObject* tarray, size_t k, size_t len,
   2603                  const Value& searchElement) {
   2604  if (!searchElement.isBigInt()) {
   2605    return -1;
   2606  }
   2607 
   2608  int64_t e;
   2609  if (!BigInt::isInt64(searchElement.toBigInt(), &e)) {
   2610    return -1;
   2611  }
   2612 
   2613  if (tarray->isSharedMemory()) {
   2614    return TypedArrayIndexOfNaive<SharedOps>(tarray, k, len, e);
   2615  }
   2616  return TypedArrayIndexOfSIMD(tarray, k, len, e);
   2617 }
   2618 
   2619 template <typename ExternalType, typename NativeType>
   2620 static typename std::enable_if_t<std::is_same_v<NativeType, uint64_t>, int64_t>
   2621 TypedArrayIndexOf(TypedArrayObject* tarray, size_t k, size_t len,
   2622                  const Value& searchElement) {
   2623  if (!searchElement.isBigInt()) {
   2624    return -1;
   2625  }
   2626 
   2627  uint64_t e;
   2628  if (!BigInt::isUint64(searchElement.toBigInt(), &e)) {
   2629    return -1;
   2630  }
   2631 
   2632  if (tarray->isSharedMemory()) {
   2633    return TypedArrayIndexOfNaive<SharedOps>(tarray, k, len, e);
   2634  }
   2635  return TypedArrayIndexOfSIMD(tarray, k, len, e);
   2636 }
   2637 
   2638 /**
   2639 * %TypedArray%.prototype.indexOf ( searchElement [ , fromIndex ] )
   2640 *
   2641 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   2642 */
   2643 static bool TypedArray_indexOf(JSContext* cx, const CallArgs& args) {
   2644  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   2645 
   2646  // Steps 1-3.
   2647  Rooted<TypedArrayObject*> tarray(
   2648      cx, &args.thisv().toObject().as<TypedArrayObject>());
   2649 
   2650  auto arrayLength = tarray->length();
   2651  if (!arrayLength) {
   2652    ReportOutOfBounds(cx, tarray);
   2653    return false;
   2654  }
   2655  size_t len = *arrayLength;
   2656 
   2657  // Step 4.
   2658  if (len == 0) {
   2659    args.rval().setInt32(-1);
   2660    return true;
   2661  }
   2662 
   2663  // Steps 5-10.
   2664  size_t k = 0;
   2665  if (args.hasDefined(1)) {
   2666    // Steps 5-6.
   2667    if (!ToIntegerIndex(cx, args[1], len, &k)) {
   2668      return false;
   2669    }
   2670 
   2671    // Reacquire the length because side-effects may have detached or resized
   2672    // the array buffer.
   2673    len = std::min(len, tarray->length().valueOr(0));
   2674 
   2675    // Return early if |k| exceeds the current length.
   2676    if (k >= len) {
   2677      args.rval().setInt32(-1);
   2678      return true;
   2679    }
   2680  }
   2681  MOZ_ASSERT(k < len);
   2682 
   2683  // Steps 11-12.
   2684  int64_t result;
   2685  switch (tarray->type()) {
   2686 #define TYPED_ARRAY_INDEXOF(ExternalType, NativeType, Name)              \
   2687  case Scalar::Name:                                                     \
   2688    result = TypedArrayIndexOf<ExternalType, NativeType>(tarray, k, len, \
   2689                                                         args.get(0));   \
   2690    break;
   2691    JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_INDEXOF)
   2692 #undef TYPED_ARRAY_INDEXOF
   2693    default:
   2694      MOZ_CRASH("Unsupported TypedArray type");
   2695  }
   2696  MOZ_ASSERT_IF(result >= 0, uint64_t(result) < len);
   2697  MOZ_ASSERT_IF(result < 0, result == -1);
   2698 
   2699  args.rval().setNumber(result);
   2700  return true;
   2701 }
   2702 
   2703 /**
   2704 * %TypedArray%.prototype.indexOf ( searchElement [ , fromIndex ] )
   2705 *
   2706 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   2707 */
   2708 static bool TypedArray_indexOf(JSContext* cx, unsigned argc, Value* vp) {
   2709  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype",
   2710                                        "indexOf");
   2711  CallArgs args = CallArgsFromVp(argc, vp);
   2712  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_indexOf>(cx, args);
   2713 }
   2714 
   2715 template <typename Ops, typename NativeType>
   2716 static int64_t TypedArrayLastIndexOf(TypedArrayObject* tarray, size_t k,
   2717                                     size_t len, NativeType searchElement) {
   2718  MOZ_RELEASE_ASSERT(k < len);
   2719  MOZ_RELEASE_ASSERT(len <= tarray->length().valueOr(0));
   2720 
   2721  SharedMem<NativeType*> data =
   2722      Ops::extract(tarray).template cast<NativeType*>();
   2723  for (size_t i = k + 1; i > 0;) {
   2724    NativeType element = Ops::load(data + --i);
   2725    if (element == searchElement) {
   2726      return int64_t(i);
   2727    }
   2728  }
   2729  return -1;
   2730 }
   2731 
   2732 template <typename ExternalType, typename NativeType>
   2733 static typename std::enable_if_t<!std::numeric_limits<NativeType>::is_integer,
   2734                                 int64_t>
   2735 TypedArrayLastIndexOf(TypedArrayObject* tarray, size_t k, size_t len,
   2736                      const Value& searchElement) {
   2737  if (!searchElement.isNumber()) {
   2738    return -1;
   2739  }
   2740 
   2741  double d = searchElement.toNumber();
   2742  NativeType e = NativeType(d);
   2743 
   2744  // Return early if the search element is not representable using |NativeType|
   2745  // or if it's NaN.
   2746  if (double(e) != d) {
   2747    return -1;
   2748  }
   2749  MOZ_ASSERT(!std::isnan(d));
   2750 
   2751  if (tarray->isSharedMemory()) {
   2752    return TypedArrayLastIndexOf<SharedOps>(tarray, k, len, e);
   2753  }
   2754  return TypedArrayLastIndexOf<UnsharedOps>(tarray, k, len, e);
   2755 }
   2756 
   2757 template <typename ExternalType, typename NativeType>
   2758 static typename std::enable_if_t<std::numeric_limits<NativeType>::is_integer &&
   2759                                     sizeof(NativeType) < 8,
   2760                                 int64_t>
   2761 TypedArrayLastIndexOf(TypedArrayObject* tarray, size_t k, size_t len,
   2762                      const Value& searchElement) {
   2763  if (!searchElement.isNumber()) {
   2764    return -1;
   2765  }
   2766 
   2767  int64_t d;
   2768  if (searchElement.isInt32()) {
   2769    d = searchElement.toInt32();
   2770  } else {
   2771    if (!mozilla::NumberEqualsInt64(searchElement.toDouble(), &d)) {
   2772      return -1;
   2773    }
   2774  }
   2775 
   2776  // Ensure search element is representable using |ExternalType|, which implies
   2777  // it can be represented using |NativeType|.
   2778  mozilla::CheckedInt<ExternalType> checked{d};
   2779  if (!checked.isValid()) {
   2780    return -1;
   2781  }
   2782  NativeType e = static_cast<NativeType>(checked.value());
   2783 
   2784  if (tarray->isSharedMemory()) {
   2785    return TypedArrayLastIndexOf<SharedOps>(tarray, k, len, e);
   2786  }
   2787  return TypedArrayLastIndexOf<UnsharedOps>(tarray, k, len, e);
   2788 }
   2789 
   2790 template <typename ExternalType, typename NativeType>
   2791 static typename std::enable_if_t<std::is_same_v<NativeType, int64_t>, int64_t>
   2792 TypedArrayLastIndexOf(TypedArrayObject* tarray, size_t k, size_t len,
   2793                      const Value& searchElement) {
   2794  if (!searchElement.isBigInt()) {
   2795    return -1;
   2796  }
   2797 
   2798  int64_t e;
   2799  if (!BigInt::isInt64(searchElement.toBigInt(), &e)) {
   2800    return -1;
   2801  }
   2802 
   2803  if (tarray->isSharedMemory()) {
   2804    return TypedArrayLastIndexOf<SharedOps>(tarray, k, len, e);
   2805  }
   2806  return TypedArrayLastIndexOf<UnsharedOps>(tarray, k, len, e);
   2807 }
   2808 
   2809 template <typename ExternalType, typename NativeType>
   2810 static typename std::enable_if_t<std::is_same_v<NativeType, uint64_t>, int64_t>
   2811 TypedArrayLastIndexOf(TypedArrayObject* tarray, size_t k, size_t len,
   2812                      const Value& searchElement) {
   2813  if (!searchElement.isBigInt()) {
   2814    return -1;
   2815  }
   2816 
   2817  uint64_t e;
   2818  if (!BigInt::isUint64(searchElement.toBigInt(), &e)) {
   2819    return -1;
   2820  }
   2821 
   2822  if (tarray->isSharedMemory()) {
   2823    return TypedArrayLastIndexOf<SharedOps>(tarray, k, len, e);
   2824  }
   2825  return TypedArrayLastIndexOf<UnsharedOps>(tarray, k, len, e);
   2826 }
   2827 
   2828 /**
   2829 * %TypedArray%.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
   2830 *
   2831 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   2832 */
   2833 static bool TypedArray_lastIndexOf(JSContext* cx, const CallArgs& args) {
   2834  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   2835 
   2836  // Steps 1-3.
   2837  Rooted<TypedArrayObject*> tarray(
   2838      cx, &args.thisv().toObject().as<TypedArrayObject>());
   2839 
   2840  auto arrayLength = tarray->length();
   2841  if (!arrayLength) {
   2842    ReportOutOfBounds(cx, tarray);
   2843    return false;
   2844  }
   2845  size_t len = *arrayLength;
   2846 
   2847  // Step 4.
   2848  if (len == 0) {
   2849    args.rval().setInt32(-1);
   2850    return true;
   2851  }
   2852 
   2853  // Steps 5-8.
   2854  size_t k = len - 1;
   2855  if (args.length() > 1) {
   2856    // Step 5.
   2857    double fromIndex;
   2858    if (!ToInteger(cx, args[1], &fromIndex)) {
   2859      return false;
   2860    }
   2861 
   2862    // Steps 6-8.
   2863    if (fromIndex >= 0) {
   2864      k = size_t(std::min(fromIndex, double(len - 1)));
   2865    } else {
   2866      double d = double(len) + fromIndex;
   2867      if (d < 0) {
   2868        args.rval().setInt32(-1);
   2869        return true;
   2870      }
   2871      k = size_t(d);
   2872    }
   2873    MOZ_ASSERT(k < len);
   2874 
   2875    // Reacquire the length because side-effects may have detached or resized
   2876    // the array buffer.
   2877    size_t currentLength = tarray->length().valueOr(0);
   2878 
   2879    // Restrict the search index and length if the new length is smaller.
   2880    if (currentLength < len) {
   2881      // Return early if the new length is zero.
   2882      if (currentLength == 0) {
   2883        args.rval().setInt32(-1);
   2884        return true;
   2885      }
   2886 
   2887      // Otherwise just restrict |k| and |len| to the current length.
   2888      k = std::min(k, currentLength - 1);
   2889      len = currentLength;
   2890    }
   2891  }
   2892  MOZ_ASSERT(k < len);
   2893 
   2894  // Steps 9-10.
   2895  int64_t result;
   2896  switch (tarray->type()) {
   2897 #define TYPED_ARRAY_LASTINDEXOF(ExternalType, NativeType, Name)              \
   2898  case Scalar::Name:                                                         \
   2899    result = TypedArrayLastIndexOf<ExternalType, NativeType>(tarray, k, len, \
   2900                                                             args.get(0));   \
   2901    break;
   2902    JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_LASTINDEXOF)
   2903 #undef TYPED_ARRAY_LASTINDEXOF
   2904    default:
   2905      MOZ_CRASH("Unsupported TypedArray type");
   2906  }
   2907  MOZ_ASSERT_IF(result >= 0, uint64_t(result) < len);
   2908  MOZ_ASSERT_IF(result < 0, result == -1);
   2909 
   2910  args.rval().setNumber(result);
   2911  return true;
   2912 }
   2913 
   2914 /**
   2915 * %TypedArray%.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
   2916 *
   2917 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   2918 */
   2919 static bool TypedArray_lastIndexOf(JSContext* cx, unsigned argc, Value* vp) {
   2920  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype",
   2921                                        "lastIndexOf");
   2922  CallArgs args = CallArgsFromVp(argc, vp);
   2923  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_lastIndexOf>(cx,
   2924                                                                          args);
   2925 }
   2926 
   2927 template <typename T>
   2928 static inline bool IsNaN(T num) {
   2929  if constexpr (std::is_same_v<T, float16>) {
   2930    return num != num;
   2931  } else {
   2932    // Static analysis complains when using self-comparison for built-in types,
   2933    // so we have to use `std::isnan`.
   2934    return std::isnan(num);
   2935  }
   2936 }
   2937 
   2938 template <typename Ops, typename NativeType>
   2939 static int64_t TypedArrayIncludesNaN(TypedArrayObject* tarray, size_t k,
   2940                                     size_t len) {
   2941  MOZ_RELEASE_ASSERT(k < len);
   2942  MOZ_RELEASE_ASSERT(len <= tarray->length().valueOr(0));
   2943 
   2944  SharedMem<NativeType*> data =
   2945      Ops::extract(tarray).template cast<NativeType*>();
   2946  for (size_t i = k; i < len; i++) {
   2947    NativeType element = Ops::load(data + i);
   2948    if (IsNaN(element)) {
   2949      return int64_t(i);
   2950    }
   2951  }
   2952  return -1;
   2953 }
   2954 
   2955 template <typename ExternalType, typename NativeType>
   2956 static typename std::enable_if_t<!std::numeric_limits<NativeType>::is_integer,
   2957                                 int64_t>
   2958 TypedArrayIncludes(TypedArrayObject* tarray, size_t k, size_t len,
   2959                   const Value& searchElement) {
   2960  if (searchElement.isDouble() && std::isnan(searchElement.toDouble())) {
   2961    if (tarray->isSharedMemory()) {
   2962      return TypedArrayIncludesNaN<SharedOps, NativeType>(tarray, k, len);
   2963    }
   2964    return TypedArrayIncludesNaN<UnsharedOps, NativeType>(tarray, k, len);
   2965  }
   2966 
   2967  // Delegate to TypedArrayIndexOf if not NaN.
   2968  return TypedArrayIndexOf<ExternalType, NativeType>(tarray, k, len,
   2969                                                     searchElement);
   2970 }
   2971 
   2972 template <typename ExternalType, typename NativeType>
   2973 static typename std::enable_if_t<std::numeric_limits<NativeType>::is_integer,
   2974                                 int64_t>
   2975 TypedArrayIncludes(TypedArrayObject* tarray, size_t k, size_t len,
   2976                   const Value& searchElement) {
   2977  // Delegate to TypedArrayIndexOf for integer types.
   2978  return TypedArrayIndexOf<ExternalType, NativeType>(tarray, k, len,
   2979                                                     searchElement);
   2980 }
   2981 
   2982 /**
   2983 * %TypedArray%.prototype.includes ( searchElement [ , fromIndex ] )
   2984 *
   2985 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   2986 */
   2987 static bool TypedArray_includes(JSContext* cx, const CallArgs& args) {
   2988  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   2989 
   2990  // Steps 1-3.
   2991  Rooted<TypedArrayObject*> tarray(
   2992      cx, &args.thisv().toObject().as<TypedArrayObject>());
   2993 
   2994  auto arrayLength = tarray->length();
   2995  if (!arrayLength) {
   2996    ReportOutOfBounds(cx, tarray);
   2997    return false;
   2998  }
   2999  size_t len = *arrayLength;
   3000 
   3001  // Step 4.
   3002  if (len == 0) {
   3003    args.rval().setBoolean(false);
   3004    return true;
   3005  }
   3006 
   3007  // Steps 5-10.
   3008  size_t k = 0;
   3009  if (args.hasDefined(1)) {
   3010    if (!ToIntegerIndex(cx, args[1], len, &k)) {
   3011      return false;
   3012    }
   3013 
   3014    // Reacquire the length because side-effects may have detached or resized
   3015    // the array buffer.
   3016    size_t currentLength = tarray->length().valueOr(0);
   3017 
   3018    // Contrary to `indexOf`, `includes` doesn't perform `HasProperty`, so we
   3019    // have to handle the case when the current length is smaller than the
   3020    // original length.
   3021    if (currentLength < len) {
   3022      // Accessing an element beyond the typed array length returns `undefined`,
   3023      // so return `true` iff the search element is `undefined`.
   3024      if (k < len && args[0].isUndefined()) {
   3025        args.rval().setBoolean(true);
   3026        return true;
   3027      }
   3028 
   3029      // Otherwise just restrict |len| to the current length.
   3030      len = currentLength;
   3031    }
   3032 
   3033    // Return early if |k| exceeds the current length.
   3034    if (k >= len) {
   3035      args.rval().setBoolean(false);
   3036      return true;
   3037    }
   3038  }
   3039  MOZ_ASSERT(k < len);
   3040 
   3041  // Steps 11-12.
   3042  int64_t result;
   3043  switch (tarray->type()) {
   3044 #define TYPED_ARRAY_INCLUDES(ExternalType, NativeType, Name)              \
   3045  case Scalar::Name:                                                      \
   3046    result = TypedArrayIncludes<ExternalType, NativeType>(tarray, k, len, \
   3047                                                          args.get(0));   \
   3048    break;
   3049    JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_INCLUDES)
   3050 #undef TYPED_ARRAY_INCLUDES
   3051    default:
   3052      MOZ_CRASH("Unsupported TypedArray type");
   3053  }
   3054  MOZ_ASSERT_IF(result >= 0, uint64_t(result) < len);
   3055  MOZ_ASSERT_IF(result < 0, result == -1);
   3056 
   3057  args.rval().setBoolean(result >= 0);
   3058  return true;
   3059 }
   3060 
   3061 /**
   3062 * %TypedArray%.prototype.includes ( searchElement [ , fromIndex ] )
   3063 *
   3064 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3065 */
   3066 static bool TypedArray_includes(JSContext* cx, unsigned argc, Value* vp) {
   3067  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype",
   3068                                        "includes");
   3069  CallArgs args = CallArgsFromVp(argc, vp);
   3070  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_includes>(cx,
   3071                                                                       args);
   3072 }
   3073 
   3074 template <typename Ops, typename NativeType>
   3075 static void TypedArrayFillLoop(TypedArrayObject* tarray, NativeType value,
   3076                               size_t startIndex, size_t endIndex) {
   3077  MOZ_RELEASE_ASSERT(startIndex <= endIndex);
   3078  MOZ_RELEASE_ASSERT(endIndex <= tarray->length().valueOr(0));
   3079 
   3080  SharedMem<NativeType*> data =
   3081      Ops::extract(tarray).template cast<NativeType*>();
   3082  for (size_t i = startIndex; i < endIndex; i++) {
   3083    Ops::store(data + i, value);
   3084  }
   3085 }
   3086 
   3087 template <typename NativeType>
   3088 static void TypedArrayFillStdMemset(TypedArrayObject* tarray, uint8_t value,
   3089                                    size_t startIndex, size_t endIndex) {
   3090  MOZ_RELEASE_ASSERT(startIndex <= endIndex);
   3091  MOZ_RELEASE_ASSERT(endIndex <= tarray->length().valueOr(0));
   3092 
   3093  SharedMem<uint8_t*> data = UnsharedOps::extract(tarray).cast<uint8_t*>();
   3094  std::memset(data.unwrapUnshared() + startIndex * sizeof(NativeType), value,
   3095              (endIndex - startIndex) * sizeof(NativeType));
   3096 }
   3097 
   3098 template <typename NativeType>
   3099 static void TypedArrayFillAtomicMemset(TypedArrayObject* tarray, uint8_t value,
   3100                                       size_t startIndex, size_t endIndex) {
   3101  MOZ_RELEASE_ASSERT(startIndex <= endIndex);
   3102  MOZ_RELEASE_ASSERT(endIndex <= tarray->length().valueOr(0));
   3103 
   3104  SharedMem<uint8_t*> data = SharedOps::extract(tarray).cast<uint8_t*>();
   3105  jit::AtomicOperations::memsetSafeWhenRacy(
   3106      data + startIndex * sizeof(NativeType), value,
   3107      (endIndex - startIndex) * sizeof(NativeType));
   3108 }
   3109 
   3110 template <typename NativeType>
   3111 static NativeType ConvertToNativeType(const Value& value) {
   3112  if constexpr (!std::numeric_limits<NativeType>::is_integer) {
   3113    double d = value.toNumber();
   3114 
   3115    if (js::SupportDifferentialTesting()) {
   3116      // See the comment in ElementSpecific::doubleToNative.
   3117      d = JS::CanonicalizeNaN(d);
   3118    }
   3119 
   3120    return ConvertNumber<NativeType>(d);
   3121  } else if constexpr (std::is_same_v<NativeType, int64_t>) {
   3122    return BigInt::toInt64(value.toBigInt());
   3123  } else if constexpr (std::is_same_v<NativeType, uint64_t>) {
   3124    return BigInt::toUint64(value.toBigInt());
   3125  } else {
   3126    return ConvertNumber<NativeType>(value.toNumber());
   3127  }
   3128 }
   3129 
   3130 template <typename NativeType>
   3131 static void TypedArrayFill(TypedArrayObject* tarray, NativeType val,
   3132                           size_t startIndex, size_t endIndex) {
   3133  using UnsignedT =
   3134      typename mozilla::UnsignedStdintTypeForSize<sizeof(NativeType)>::Type;
   3135  UnsignedT bits = mozilla::BitwiseCast<UnsignedT>(val);
   3136 
   3137  // Duplicate the LSB to check if we can call memset.
   3138  UnsignedT pattern;
   3139  std::memset(&pattern, uint8_t(bits), sizeof(UnsignedT));
   3140 
   3141  if (tarray->isSharedMemory()) {
   3142    // To prevent teared writes, only use memset on shared memory when copying
   3143    // single bytes.
   3144    if (bits == pattern && sizeof(NativeType) == 1) {
   3145      TypedArrayFillAtomicMemset<NativeType>(tarray, uint8_t(bits), startIndex,
   3146                                             endIndex);
   3147    } else {
   3148      TypedArrayFillLoop<SharedOps>(tarray, val, startIndex, endIndex);
   3149    }
   3150  } else {
   3151    if (bits == pattern) {
   3152      TypedArrayFillStdMemset<NativeType>(tarray, uint8_t(bits), startIndex,
   3153                                          endIndex);
   3154    } else {
   3155      TypedArrayFillLoop<UnsharedOps>(tarray, val, startIndex, endIndex);
   3156    }
   3157  }
   3158 }
   3159 
   3160 template <typename NativeType>
   3161 static void TypedArrayFill(TypedArrayObject* tarray, const Value& value,
   3162                           size_t startIndex, size_t endIndex) {
   3163  NativeType val = ConvertToNativeType<NativeType>(value);
   3164  TypedArrayFill(tarray, val, startIndex, endIndex);
   3165 }
   3166 
   3167 /**
   3168 * %TypedArray%.prototype.fill ( value [ , start [ , end ] ] )
   3169 *
   3170 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3171 */
   3172 static bool TypedArray_fill(JSContext* cx, const CallArgs& args) {
   3173  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   3174 
   3175  // Steps 1-2.
   3176  Rooted<TypedArrayObject*> tarray(
   3177      cx, &args.thisv().toObject().as<TypedArrayObject>());
   3178 
   3179  auto arrayLength = tarray->length();
   3180  if (!arrayLength) {
   3181    ReportOutOfBounds(cx, tarray);
   3182    return false;
   3183  }
   3184 
   3185  // Additional step from Immutable ArrayBuffer proposal.
   3186  if (tarray->is<ImmutableTypedArrayObject>()) {
   3187    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3188                              JSMSG_ARRAYBUFFER_IMMUTABLE);
   3189    return false;
   3190  }
   3191 
   3192  // Step 3
   3193  size_t len = *arrayLength;
   3194 
   3195  // Steps 4-5.
   3196  Rooted<Value> value(cx);
   3197  if (!tarray->convertValue(cx, args.get(0), &value)) {
   3198    return false;
   3199  }
   3200 
   3201  // Steps 6-9
   3202  size_t startIndex = 0;
   3203  if (args.hasDefined(1)) {
   3204    if (!ToIntegerIndex(cx, args[1], len, &startIndex)) {
   3205      return false;
   3206    }
   3207  }
   3208 
   3209  // Steps 10-13.
   3210  size_t endIndex = len;
   3211  if (args.hasDefined(2)) {
   3212    if (!ToIntegerIndex(cx, args[2], len, &endIndex)) {
   3213      return false;
   3214    }
   3215  }
   3216 
   3217  // Steps 14-16.
   3218  //
   3219  // Reacquire the length because side-effects may have detached or resized
   3220  // the array buffer.
   3221  arrayLength = tarray->length();
   3222  if (!arrayLength) {
   3223    ReportOutOfBounds(cx, tarray);
   3224    return false;
   3225  }
   3226  len = *arrayLength;
   3227 
   3228  // Step 17.
   3229  endIndex = std::min(endIndex, len);
   3230 
   3231  // Steps 18-19.
   3232  if (startIndex < endIndex) {
   3233    switch (tarray->type()) {
   3234 #define TYPED_ARRAY_FILL(_, NativeType, Name)                              \
   3235  case Scalar::Name:                                                       \
   3236    TypedArrayFill<NativeType>(tarray, value.get(), startIndex, endIndex); \
   3237    break;
   3238      JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_FILL)
   3239 #undef TYPED_ARRAY_FILL
   3240      default:
   3241        MOZ_CRASH("Unsupported TypedArray type");
   3242    }
   3243  }
   3244 
   3245  // Step 20.
   3246  args.rval().setObject(*tarray);
   3247  return true;
   3248 }
   3249 
   3250 /**
   3251 * %TypedArray%.prototype.fill ( value [ , start [ , end ] ] )
   3252 *
   3253 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3254 */
   3255 static bool TypedArray_fill(JSContext* cx, unsigned argc, Value* vp) {
   3256  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype", "fill");
   3257  CallArgs args = CallArgsFromVp(argc, vp);
   3258  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_fill>(cx, args);
   3259 }
   3260 
   3261 // Test if `ConvertNumber<To, From>` can be instantiated.
   3262 //
   3263 // For example `ConvertNumber<int64_t, double>` can't be instantiated. This is
   3264 // checked through static assertions in `ConvertNumber`.
   3265 //
   3266 // As a further optimization also avoid generating unreachable code, like for
   3267 // example `ConvertNumber<double, float>`.
   3268 template <typename To, typename From>
   3269 static constexpr bool IsValidForConvertNumber() {
   3270  if constexpr (!std::numeric_limits<From>::is_integer) {
   3271    return !std::numeric_limits<To>::is_integer && sizeof(From) >= sizeof(To);
   3272  } else if constexpr (sizeof(From) == sizeof(int64_t)) {
   3273    return std::numeric_limits<To>::is_integer && sizeof(From) == sizeof(To);
   3274  } else {
   3275    return std::numeric_limits<To>::is_integer && sizeof(From) >= sizeof(To);
   3276  }
   3277 }
   3278 
   3279 template <typename T>
   3280 static void TypedArrayFillFromJit(TypedArrayObject* obj, T fillValue,
   3281                                  intptr_t start, intptr_t end) {
   3282  if constexpr (!std::numeric_limits<T>::is_integer) {
   3283    MOZ_ASSERT(Scalar::isFloatingType(obj->type()));
   3284  } else if constexpr (std::is_same_v<T, int64_t>) {
   3285    MOZ_ASSERT(Scalar::isBigIntType(obj->type()));
   3286  } else {
   3287    static_assert(std::is_same_v<T, int32_t>);
   3288    MOZ_ASSERT(!Scalar::isFloatingType(obj->type()));
   3289    MOZ_ASSERT(!Scalar::isBigIntType(obj->type()));
   3290  }
   3291  MOZ_ASSERT(!obj->hasDetachedBuffer());
   3292  MOZ_ASSERT(!obj->is<ImmutableTypedArrayObject>());
   3293  MOZ_ASSERT(!obj->is<ResizableTypedArrayObject>());
   3294 
   3295  size_t length = obj->length().valueOr(0);
   3296  size_t startIndex = ToIntegerIndex(start, length);
   3297  size_t endIndex = ToIntegerIndex(end, length);
   3298 
   3299  // Return early if the fill range is empty.
   3300  if (startIndex >= endIndex) {
   3301    return;
   3302  }
   3303 
   3304  switch (obj->type()) {
   3305 #define TYPED_ARRAY_FILL(_, NativeType, Name)                               \
   3306  case Scalar::Name:                                                        \
   3307    if constexpr (IsValidForConvertNumber<NativeType, T>()) {               \
   3308      TypedArrayFill<NativeType>(obj, ConvertNumber<NativeType>(fillValue), \
   3309                                 startIndex, endIndex);                     \
   3310      return;                                                               \
   3311    }                                                                       \
   3312    break;
   3313    JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_FILL)
   3314 #undef TYPED_ARRAY_FILL
   3315    default:
   3316      MOZ_CRASH("Unsupported TypedArray type");
   3317  }
   3318  MOZ_CRASH("Unexpected invalid number conversion");
   3319 }
   3320 
   3321 void js::TypedArrayFillInt32(TypedArrayObject* obj, int32_t fillValue,
   3322                             intptr_t start, intptr_t end) {
   3323  AutoUnsafeCallWithABI unsafe;
   3324  TypedArrayFillFromJit(obj, fillValue, start, end);
   3325 }
   3326 
   3327 void js::TypedArrayFillDouble(TypedArrayObject* obj, double fillValue,
   3328                              intptr_t start, intptr_t end) {
   3329  AutoUnsafeCallWithABI unsafe;
   3330  TypedArrayFillFromJit(obj, fillValue, start, end);
   3331 }
   3332 
   3333 void js::TypedArrayFillFloat32(TypedArrayObject* obj, float fillValue,
   3334                               intptr_t start, intptr_t end) {
   3335  AutoUnsafeCallWithABI unsafe;
   3336  TypedArrayFillFromJit(obj, fillValue, start, end);
   3337 }
   3338 
   3339 void js::TypedArrayFillInt64(TypedArrayObject* obj, int64_t fillValue,
   3340                             intptr_t start, intptr_t end) {
   3341  AutoUnsafeCallWithABI unsafe;
   3342  TypedArrayFillFromJit(obj, fillValue, start, end);
   3343 }
   3344 
   3345 void js::TypedArrayFillBigInt(TypedArrayObject* obj, BigInt* fillValue,
   3346                              intptr_t start, intptr_t end) {
   3347  AutoUnsafeCallWithABI unsafe;
   3348  TypedArrayFillFromJit(obj, BigInt::toInt64(fillValue), start, end);
   3349 }
   3350 
   3351 template <typename Ops, typename NativeType>
   3352 static void TypedArrayReverse(TypedArrayObject* tarray, size_t len) {
   3353  MOZ_RELEASE_ASSERT(len > 0);
   3354  MOZ_RELEASE_ASSERT(len <= tarray->length().valueOr(0));
   3355 
   3356  SharedMem<NativeType*> lower =
   3357      Ops::extract(tarray).template cast<NativeType*>();
   3358  SharedMem<NativeType*> upper = lower + (len - 1);
   3359  for (; lower < upper; lower++, upper--) {
   3360    NativeType lowerValue = Ops::load(lower);
   3361    NativeType upperValue = Ops::load(upper);
   3362 
   3363    Ops::store(lower, upperValue);
   3364    Ops::store(upper, lowerValue);
   3365  }
   3366 }
   3367 
   3368 template <typename NativeType>
   3369 static void TypedArrayReverse(TypedArrayObject* tarray, size_t len) {
   3370  if (tarray->isSharedMemory()) {
   3371    TypedArrayReverse<SharedOps, NativeType>(tarray, len);
   3372  } else {
   3373    TypedArrayReverse<UnsharedOps, NativeType>(tarray, len);
   3374  }
   3375 }
   3376 
   3377 /**
   3378 * %TypedArray%.prototype.reverse ( )
   3379 *
   3380 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3381 */
   3382 static bool TypedArray_reverse(JSContext* cx, const CallArgs& args) {
   3383  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   3384 
   3385  // Steps 1-2.
   3386  Rooted<TypedArrayObject*> tarray(
   3387      cx, &args.thisv().toObject().as<TypedArrayObject>());
   3388 
   3389  auto arrayLength = tarray->length();
   3390  if (!arrayLength) {
   3391    ReportOutOfBounds(cx, tarray);
   3392    return false;
   3393  }
   3394 
   3395  // Additional step from Immutable ArrayBuffer proposal.
   3396  if (tarray->is<ImmutableTypedArrayObject>()) {
   3397    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3398                              JSMSG_ARRAYBUFFER_IMMUTABLE);
   3399    return false;
   3400  }
   3401 
   3402  // Step 3.
   3403  size_t len = *arrayLength;
   3404 
   3405  // Steps 4-6.
   3406  if (len > 1) {
   3407    switch (tarray->type()) {
   3408 #define TYPED_ARRAY_REVERSE(_, NativeType, Name) \
   3409  case Scalar::Name:                             \
   3410    TypedArrayReverse<NativeType>(tarray, len);  \
   3411    break;
   3412      JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_REVERSE)
   3413 #undef TYPED_ARRAY_REVERSE
   3414      default:
   3415        MOZ_CRASH("Unsupported TypedArray type");
   3416    }
   3417  }
   3418 
   3419  // Step 7.
   3420  args.rval().setObject(*tarray);
   3421  return true;
   3422 }
   3423 
   3424 /**
   3425 * %TypedArray%.prototype.reverse ( )
   3426 *
   3427 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3428 */
   3429 static bool TypedArray_reverse(JSContext* cx, unsigned argc, Value* vp) {
   3430  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype",
   3431                                        "reverse");
   3432  CallArgs args = CallArgsFromVp(argc, vp);
   3433  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_reverse>(cx, args);
   3434 }
   3435 
   3436 /**
   3437 * TypedArrayCreateSameType ( exemplar, argumentList )
   3438 *
   3439 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3440 */
   3441 static TypedArrayObject* TypedArrayCreateSameType(
   3442    JSContext* cx, Handle<TypedArrayObject*> exemplar, size_t length) {
   3443  switch (exemplar->type()) {
   3444 #define TYPED_ARRAY_CREATE(_, NativeType, Name) \
   3445  case Scalar::Name:                            \
   3446    return TypedArrayObjectTemplate<NativeType>::fromLength(cx, length);
   3447    JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_CREATE)
   3448 #undef TYPED_ARRAY_CREATE
   3449    default:
   3450      MOZ_CRASH("Unsupported TypedArray type");
   3451  }
   3452 }
   3453 
   3454 /**
   3455 * TypedArrayCreateSameType ( exemplar, argumentList )
   3456 *
   3457 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3458 */
   3459 static TypedArrayObject* TypedArrayCreateSameType(
   3460    JSContext* cx, Handle<TypedArrayObject*> exemplar,
   3461    Handle<ArrayBufferObjectMaybeShared*> buffer, size_t byteOffset) {
   3462  switch (exemplar->type()) {
   3463 #define TYPED_ARRAY_CREATE(_, NativeType, Name)                         \
   3464  case Scalar::Name:                                                    \
   3465    return TypedArrayObjectTemplate<NativeType>::fromBuffer(cx, buffer, \
   3466                                                            byteOffset);
   3467    JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_CREATE)
   3468 #undef TYPED_ARRAY_CREATE
   3469    default:
   3470      MOZ_CRASH("Unsupported TypedArray type");
   3471  }
   3472 }
   3473 
   3474 /**
   3475 * TypedArrayCreateSameType ( exemplar, argumentList )
   3476 *
   3477 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3478 */
   3479 static TypedArrayObject* TypedArrayCreateSameType(
   3480    JSContext* cx, Handle<TypedArrayObject*> exemplar,
   3481    Handle<ArrayBufferObjectMaybeShared*> buffer, size_t byteOffset,
   3482    size_t length) {
   3483  switch (exemplar->type()) {
   3484 #define TYPED_ARRAY_CREATE(_, NativeType, Name)              \
   3485  case Scalar::Name:                                         \
   3486    return TypedArrayObjectTemplate<NativeType>::fromBuffer( \
   3487        cx, buffer, byteOffset, length);
   3488    JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_CREATE)
   3489 #undef TYPED_ARRAY_CREATE
   3490    default:
   3491      MOZ_CRASH("Unsupported TypedArray type");
   3492  }
   3493 }
   3494 
   3495 template <typename NativeType>
   3496 static void TypedArrayCopyElements(TypedArrayObject* source,
   3497                                   TypedArrayObject* target, size_t length) {
   3498  MOZ_ASSERT(source->type() == target->type());
   3499  MOZ_ASSERT(!target->isSharedMemory());
   3500  MOZ_ASSERT(length > 0);
   3501  MOZ_RELEASE_ASSERT(length <= source->length().valueOr(0));
   3502  MOZ_RELEASE_ASSERT(length <= target->length().valueOr(0));
   3503 
   3504  auto dest = UnsharedOps::extract(target).cast<NativeType*>();
   3505  if (source->isSharedMemory()) {
   3506    auto src = SharedOps::extract(source).cast<NativeType*>();
   3507    SharedOps::podCopy(dest, src, length);
   3508  } else {
   3509    auto src = UnsharedOps::extract(source).cast<NativeType*>();
   3510    UnsharedOps::podCopy(dest, src, length);
   3511  }
   3512 }
   3513 
   3514 static void TypedArrayCopyElements(TypedArrayObject* source,
   3515                                   TypedArrayObject* target, size_t length) {
   3516  switch (source->type()) {
   3517 #define TYPED_ARRAY_COPY_ELEMENTS(_, NativeType, Name) \
   3518  case Scalar::Name:                                   \
   3519    return TypedArrayCopyElements<NativeType>(source, target, length);
   3520    JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_COPY_ELEMENTS)
   3521 #undef TYPED_ARRAY_COPY_ELEMENTS
   3522    default:
   3523      MOZ_CRASH("Unsupported TypedArray type");
   3524  }
   3525 }
   3526 
   3527 /**
   3528 * %TypedArray%.prototype.toReversed ( )
   3529 *
   3530 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3531 */
   3532 static bool TypedArray_toReversed(JSContext* cx, const CallArgs& args) {
   3533  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   3534 
   3535  // Steps 1-3.
   3536  Rooted<TypedArrayObject*> tarray(
   3537      cx, &args.thisv().toObject().as<TypedArrayObject>());
   3538 
   3539  auto arrayLength = tarray->length();
   3540  if (!arrayLength) {
   3541    ReportOutOfBounds(cx, tarray);
   3542    return false;
   3543  }
   3544  size_t length = *arrayLength;
   3545 
   3546  // Step 4.
   3547  TypedArrayObject* result = TypedArrayCreateSameType(cx, tarray, length);
   3548  if (!result) {
   3549    return false;
   3550  }
   3551 
   3552  // Steps 5-6.
   3553  if (length > 0) {
   3554    TypedArrayCopyElements(tarray, result, length);
   3555 
   3556    switch (result->type()) {
   3557 #define TYPED_ARRAY_TOREVERSED(_, NativeType, Name)             \
   3558  case Scalar::Name:                                            \
   3559    TypedArrayReverse<UnsharedOps, NativeType>(result, length); \
   3560    break;
   3561      JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_TOREVERSED)
   3562 #undef TYPED_ARRAY_TOREVERSED
   3563      default:
   3564        MOZ_CRASH("Unsupported TypedArray type");
   3565    }
   3566  }
   3567 
   3568  // Step 7.
   3569  args.rval().setObject(*result);
   3570  return true;
   3571 }
   3572 
   3573 /**
   3574 * %TypedArray%.prototype.toReversed ( )
   3575 *
   3576 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3577 */
   3578 static bool TypedArray_toReversed(JSContext* cx, unsigned argc, Value* vp) {
   3579  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype",
   3580                                        "toReversed");
   3581  CallArgs args = CallArgsFromVp(argc, vp);
   3582  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_toReversed>(cx,
   3583                                                                         args);
   3584 }
   3585 
   3586 template <typename Ops, typename NativeType>
   3587 static void TypedArraySetElement(TypedArrayObject* tarray, size_t index,
   3588                                 const Value& value) {
   3589  MOZ_RELEASE_ASSERT(index < tarray->length().valueOr(0));
   3590 
   3591  NativeType val = ConvertToNativeType<NativeType>(value);
   3592 
   3593  SharedMem<NativeType*> data =
   3594      Ops::extract(tarray).template cast<NativeType*>();
   3595  Ops::store(data + index, val);
   3596 }
   3597 
   3598 /**
   3599 * %TypedArray%.prototype.with ( index, value )
   3600 *
   3601 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3602 */
   3603 static bool TypedArray_with(JSContext* cx, const CallArgs& args) {
   3604  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   3605 
   3606  // Steps 1-3.
   3607  Rooted<TypedArrayObject*> tarray(
   3608      cx, &args.thisv().toObject().as<TypedArrayObject>());
   3609 
   3610  auto arrayLength = tarray->length();
   3611  if (!arrayLength) {
   3612    ReportOutOfBounds(cx, tarray);
   3613    return false;
   3614  }
   3615  size_t len = *arrayLength;
   3616 
   3617  // Step 4.
   3618  double relativeIndex;
   3619  if (!ToInteger(cx, args.get(0), &relativeIndex)) {
   3620    return false;
   3621  }
   3622 
   3623  // Steps 5-6.
   3624  double actualIndex;
   3625  if (relativeIndex >= 0) {
   3626    actualIndex = relativeIndex;
   3627  } else {
   3628    actualIndex = double(len) + relativeIndex;
   3629  }
   3630 
   3631  // Steps 7-8.
   3632  Rooted<Value> value(cx);
   3633  if (!tarray->convertValue(cx, args.get(1), &value)) {
   3634    return false;
   3635  }
   3636 
   3637  // Reacquire the length because side-effects may have detached or resized
   3638  // the array buffer.
   3639  size_t currentLength = tarray->length().valueOr(0);
   3640 
   3641  // Step 9. (Inlined IsValidIntegerIndex)
   3642  if (actualIndex < 0 || actualIndex >= double(currentLength)) {
   3643    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
   3644    return false;
   3645  }
   3646  MOZ_ASSERT(currentLength > 0);
   3647 
   3648  // Step 10.
   3649  Rooted<TypedArrayObject*> result(cx,
   3650                                   TypedArrayCreateSameType(cx, tarray, len));
   3651  if (!result) {
   3652    return false;
   3653  }
   3654 
   3655  // Steps 11-12.
   3656  if (len > 0) {
   3657    // Start with copying all elements from |tarray|.
   3658    TypedArrayCopyElements(tarray, result, std::min(len, currentLength));
   3659 
   3660    // Set the replacement value.
   3661    if (actualIndex < double(len)) {
   3662      switch (result->type()) {
   3663 #define TYPED_ARRAY_SET_ELEMENT(_, NativeType, Name)                           \
   3664  case Scalar::Name:                                                           \
   3665    TypedArraySetElement<UnsharedOps, NativeType>(result, size_t(actualIndex), \
   3666                                                  value);                      \
   3667    break;
   3668        JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_SET_ELEMENT)
   3669 #undef TYPED_ARRAY_SET_ELEMENT
   3670        default:
   3671          MOZ_CRASH("Unsupported TypedArray type");
   3672      }
   3673    }
   3674 
   3675    // Fill the remaining elements with `undefined`.
   3676    if (currentLength < len) {
   3677      if (!result->convertValue(cx, UndefinedHandleValue, &value)) {
   3678        return false;
   3679      }
   3680 
   3681      switch (result->type()) {
   3682 #define TYPED_ARRAY_FILL(_, NativeType, Name)                            \
   3683  case Scalar::Name:                                                     \
   3684    TypedArrayFill<NativeType>(result, value.get(), currentLength, len); \
   3685    break;
   3686        JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_FILL)
   3687 #undef TYPED_ARRAY_FILL
   3688        default:
   3689          MOZ_CRASH("Unsupported TypedArray type");
   3690      }
   3691    }
   3692  }
   3693 
   3694  // Step 13.
   3695  args.rval().setObject(*result);
   3696  return true;
   3697 }
   3698 
   3699 /**
   3700 * %TypedArray%.prototype.with ( index, value )
   3701 *
   3702 * ES2025 draft rev c4042979a7cdd96b663ffcc43aeee90af8d7a576
   3703 */
   3704 static bool TypedArray_with(JSContext* cx, unsigned argc, Value* vp) {
   3705  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype", "with");
   3706  CallArgs args = CallArgsFromVp(argc, vp);
   3707  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_with>(cx, args);
   3708 }
   3709 
   3710 /**
   3711 * TypedArrayCreateFromConstructor ( constructor, argumentList )
   3712 */
   3713 template <typename... Args>
   3714 static TypedArrayObject* TypedArrayCreateFromConstructor(
   3715    JSContext* cx, Handle<JSObject*> constructor, Args... args) {
   3716  // Step 1.
   3717  Rooted<JSObject*> resultObj(cx);
   3718  {
   3719    auto toNumberOrObjectValue = [](auto v) {
   3720      if constexpr (std::is_arithmetic_v<decltype(v)>) {
   3721        return NumberValue(v);
   3722      } else {
   3723        return ObjectValue(*v);
   3724      }
   3725    };
   3726 
   3727    FixedConstructArgs<sizeof...(args)> cargs(cx);
   3728 
   3729    size_t i = 0;
   3730    ((cargs[i].set(toNumberOrObjectValue(args)), i++), ...);
   3731 
   3732    Rooted<Value> ctorVal(cx, ObjectValue(*constructor));
   3733    if (!Construct(cx, ctorVal, cargs, ctorVal, &resultObj)) {
   3734      return nullptr;
   3735    }
   3736  }
   3737 
   3738  // Step 2.
   3739  auto* unwrapped = resultObj->maybeUnwrapIf<TypedArrayObject>();
   3740  if (!unwrapped) {
   3741    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3742                              JSMSG_NON_TYPED_ARRAY_RETURNED);
   3743    return nullptr;
   3744  }
   3745 
   3746  auto resultLength = unwrapped->length();
   3747  if (!resultLength) {
   3748    ReportOutOfBounds(cx, unwrapped);
   3749    return nullptr;
   3750  }
   3751 
   3752  // Step 3. (Assertion not applicable in our implementation.)
   3753 
   3754  // Step 4.
   3755  if constexpr (sizeof...(args) == 1) {
   3756    // Use nested if-statements because GCC miscompiles `decltype((args, ...))`.
   3757    auto length = (args, ...);
   3758    if constexpr (std::is_arithmetic_v<decltype(length)>) {
   3759      // Additional step from <https://tc39.es/proposal-immutable-arraybuffer>.
   3760      if (unwrapped->is<ImmutableTypedArrayObject>()) {
   3761        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3762                                  JSMSG_ARRAYBUFFER_IMMUTABLE);
   3763        return nullptr;
   3764      }
   3765 
   3766      // Steps 4.a-b. (Performed above)
   3767 
   3768      // Step 4.c.
   3769      if (*resultLength < length) {
   3770        ToCStringBuf lengthBuf;
   3771        ToCStringBuf resultLengthBuf;
   3772        JS_ReportErrorNumberASCII(
   3773            cx, GetErrorMessage, nullptr, JSMSG_SHORT_TYPED_ARRAY_RETURNED,
   3774            NumberToCString(&lengthBuf, length),
   3775            NumberToCString(&resultLengthBuf, *resultLength));
   3776        return nullptr;
   3777      }
   3778    }
   3779  }
   3780 
   3781  // Step 5.
   3782  return unwrapped;
   3783 }
   3784 
   3785 static bool HasBuiltinTypedArraySpecies(TypedArrayObject* obj, JSContext* cx) {
   3786  // Ensure `%TypedArray%.prototype.constructor` and `%TypedArray%[@@species]`
   3787  // haven't been mutated. Ensure concrete `TypedArray.prototype.constructor`
   3788  // and the prototype of `TypedArray.prototype` haven't been mutated.
   3789  if (!cx->realm()->realmFuses.optimizeTypedArraySpeciesFuse.intact()) {
   3790    return false;
   3791  }
   3792 
   3793  auto protoKey = StandardProtoKeyOrNull(obj);
   3794 
   3795  // Ensure |obj|'s prototype is the actual concrete TypedArray.prototype.
   3796  auto* proto = cx->global()->maybeGetPrototype(protoKey);
   3797  if (!proto || obj->staticPrototype() != proto) {
   3798    return false;
   3799  }
   3800 
   3801  // Fail if |obj| has an own `constructor` property.
   3802  if (obj->containsPure(cx->names().constructor)) {
   3803    return false;
   3804  }
   3805 
   3806  return true;
   3807 }
   3808 
   3809 static bool IsTypedArraySpecies(JSContext* cx, JSFunction* species) {
   3810  return IsSelfHostedFunctionWithName(species,
   3811                                      cx->names().dollar_TypedArraySpecies_);
   3812 }
   3813 
   3814 /**
   3815 * TypedArraySpeciesCreate ( exemplar, argumentList )
   3816 *
   3817 * ES2026 draft rev 60c4df0b65e1c2e6b6581a5bc08a9311223be1da
   3818 */
   3819 template <typename... Args>
   3820 static TypedArrayObject* TypedArraySpeciesCreateImpl(
   3821    JSContext* cx, Handle<TypedArrayObject*> exemplar, Args... args) {
   3822  if (HasBuiltinTypedArraySpecies(exemplar, cx)) {
   3823    return TypedArrayCreateSameType(cx, exemplar, args...);
   3824  }
   3825 
   3826  // Step 1.
   3827  auto ctorKey = StandardProtoKeyOrNull(exemplar);
   3828  Rooted<JSObject*> defaultCtor(
   3829      cx, GlobalObject::getOrCreateConstructor(cx, ctorKey));
   3830  if (!defaultCtor) {
   3831    return nullptr;
   3832  }
   3833 
   3834  // Steps 1-2.
   3835  Rooted<JSObject*> constructor(
   3836      cx, SpeciesConstructor(cx, exemplar, defaultCtor, IsTypedArraySpecies));
   3837  if (!constructor) {
   3838    return nullptr;
   3839  }
   3840 
   3841  if (constructor == defaultCtor) {
   3842    return TypedArrayCreateSameType(cx, exemplar, args...);
   3843  }
   3844 
   3845  // Step 3.
   3846  auto* unwrappedResult =
   3847      TypedArrayCreateFromConstructor(cx, constructor, args...);
   3848  if (!unwrappedResult) {
   3849    return nullptr;
   3850  }
   3851 
   3852  // Step 4.
   3853  if (Scalar::isBigIntType(exemplar->type()) !=
   3854      Scalar::isBigIntType(unwrappedResult->type())) {
   3855    JS_ReportErrorNumberASCII(
   3856        cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_NOT_COMPATIBLE,
   3857        exemplar->getClass()->name, unwrappedResult->getClass()->name);
   3858    return nullptr;
   3859  }
   3860 
   3861  // Step 5.
   3862  return unwrappedResult;
   3863 }
   3864 
   3865 /**
   3866 * TypedArraySpeciesCreate ( exemplar, argumentList )
   3867 *
   3868 * ES2026 draft rev 60c4df0b65e1c2e6b6581a5bc08a9311223be1da
   3869 */
   3870 static TypedArrayObject* TypedArraySpeciesCreate(
   3871    JSContext* cx, Handle<TypedArrayObject*> exemplar, size_t length) {
   3872  return TypedArraySpeciesCreateImpl(cx, exemplar, length);
   3873 }
   3874 
   3875 /**
   3876 * TypedArraySpeciesCreate ( exemplar, argumentList )
   3877 *
   3878 * ES2026 draft rev 60c4df0b65e1c2e6b6581a5bc08a9311223be1da
   3879 */
   3880 static TypedArrayObject* TypedArraySpeciesCreate(
   3881    JSContext* cx, Handle<TypedArrayObject*> exemplar,
   3882    Handle<ArrayBufferObjectMaybeShared*> buffer, size_t byteOffset) {
   3883  return TypedArraySpeciesCreateImpl(cx, exemplar, buffer, byteOffset);
   3884 }
   3885 
   3886 /**
   3887 * TypedArraySpeciesCreate ( exemplar, argumentList )
   3888 *
   3889 * ES2026 draft rev 60c4df0b65e1c2e6b6581a5bc08a9311223be1da
   3890 */
   3891 static TypedArrayObject* TypedArraySpeciesCreate(
   3892    JSContext* cx, Handle<TypedArrayObject*> exemplar,
   3893    Handle<ArrayBufferObjectMaybeShared*> buffer, size_t byteOffset,
   3894    size_t length) {
   3895  return TypedArraySpeciesCreateImpl(cx, exemplar, buffer, byteOffset, length);
   3896 }
   3897 
   3898 static void TypedArrayBitwiseSlice(TypedArrayObject* source, size_t startIndex,
   3899                                   size_t count, TypedArrayObject* target) {
   3900  MOZ_ASSERT(CanUseBitwiseCopy(target->type(), source->type()));
   3901  MOZ_ASSERT(!source->hasDetachedBuffer());
   3902  MOZ_ASSERT(!target->hasDetachedBuffer());
   3903  MOZ_ASSERT(!target->is<ImmutableTypedArrayObject>());
   3904  MOZ_ASSERT(count > 0);
   3905  MOZ_ASSERT(startIndex + count <= source->length().valueOr(0));
   3906  MOZ_ASSERT(count <= target->length().valueOr(0));
   3907 
   3908  size_t elementSize = TypedArrayElemSize(source->type());
   3909  MOZ_ASSERT(elementSize == TypedArrayElemSize(target->type()));
   3910 
   3911  SharedMem<uint8_t*> sourceData =
   3912      source->dataPointerEither().cast<uint8_t*>() + startIndex * elementSize;
   3913 
   3914  SharedMem<uint8_t*> targetData = target->dataPointerEither().cast<uint8_t*>();
   3915 
   3916  size_t byteLength = count * elementSize;
   3917 
   3918  // The same-type case requires exact copying preserving the bit-level encoding
   3919  // of the source data, so use memcpy if possible. If source and target are the
   3920  // same buffer, we can't use memcpy (or memmove), because the specification
   3921  // requires sequential copying of the values. This case is only possible if a
   3922  // @@species constructor created a specifically crafted typed array. It won't
   3923  // happen in normal code and hence doesn't need to be optimized.
   3924  if (!TypedArrayObject::sameBuffer(source, target)) {
   3925    if (source->isSharedMemory() || target->isSharedMemory()) {
   3926      jit::AtomicOperations::memcpySafeWhenRacy(targetData, sourceData,
   3927                                                byteLength);
   3928    } else {
   3929      std::memcpy(targetData.unwrapUnshared(), sourceData.unwrapUnshared(),
   3930                  byteLength);
   3931    }
   3932  } else {
   3933    for (; byteLength > 0; byteLength--) {
   3934      jit::AtomicOperations::storeSafeWhenRacy(
   3935          targetData++, jit::AtomicOperations::loadSafeWhenRacy(sourceData++));
   3936    }
   3937  }
   3938 }
   3939 
   3940 template <typename NativeType>
   3941 static double TypedArraySliceCopySlowGet(TypedArrayObject* tarray,
   3942                                         size_t index) {
   3943  return static_cast<double>(
   3944      TypedArrayObjectTemplate<NativeType>::getIndex(tarray, index));
   3945 }
   3946 
   3947 template <typename NativeType>
   3948 static void TypedArraySliceCopySlowSet(TypedArrayObject* tarray, size_t index,
   3949                                       double value) {
   3950  if constexpr (!std::numeric_limits<NativeType>::is_integer) {
   3951    if (js::SupportDifferentialTesting()) {
   3952      // See the comment in ElementSpecific::doubleToNative.
   3953      value = JS::CanonicalizeNaN(value);
   3954    }
   3955  }
   3956  TypedArrayObjectTemplate<NativeType>::setIndex(
   3957      *tarray, index, ConvertNumber<NativeType>(value));
   3958 }
   3959 
   3960 template <>
   3961 void TypedArraySliceCopySlowSet<int64_t>(TypedArrayObject* tarray, size_t index,
   3962                                         double value) {
   3963  // Specialization because ConvertNumber doesn't allow double to int64_t.
   3964  MOZ_CRASH("unexpected set with int64_t");
   3965 }
   3966 
   3967 template <>
   3968 void TypedArraySliceCopySlowSet<uint64_t>(TypedArrayObject* tarray,
   3969                                          size_t index, double value) {
   3970  // Specialization because ConvertNumber doesn't allow double to uint64_t.
   3971  MOZ_CRASH("unexpected set with uint64_t");
   3972 }
   3973 
   3974 static void TypedArraySliceCopySlow(TypedArrayObject* source, size_t startIndex,
   3975                                    size_t count, TypedArrayObject* target) {
   3976  MOZ_ASSERT(!CanUseBitwiseCopy(target->type(), source->type()));
   3977  MOZ_ASSERT(!source->hasDetachedBuffer());
   3978  MOZ_ASSERT(!target->hasDetachedBuffer());
   3979  MOZ_ASSERT(!target->is<ImmutableTypedArrayObject>());
   3980  MOZ_ASSERT(count > 0);
   3981  MOZ_ASSERT(startIndex + count <= source->length().valueOr(0));
   3982  MOZ_ASSERT(count <= target->length().valueOr(0));
   3983 
   3984  static_assert(
   3985      CanUseBitwiseCopy(Scalar::BigInt64, Scalar::BigUint64) &&
   3986          CanUseBitwiseCopy(Scalar::BigUint64, Scalar::BigInt64),
   3987      "BigInt contents, even if sign is different, can be copied bitwise");
   3988 
   3989  MOZ_ASSERT(!Scalar::isBigIntType(target->type()) &&
   3990             !Scalar::isBigIntType(source->type()));
   3991 
   3992  // Step 14.h.i.
   3993  size_t n = 0;
   3994 
   3995  // Step 14.h.ii.
   3996  size_t k = startIndex;
   3997 
   3998  // Step 14.h.iii.
   3999  while (n < count) {
   4000    // Step 14.h.iii.1. (Not applicable)
   4001 
   4002    // Step 14.h.iii.2.
   4003    double value;
   4004    switch (source->type()) {
   4005 #define GET_ELEMENT(_, T, N)                          \
   4006  case Scalar::N:                                     \
   4007    value = TypedArraySliceCopySlowGet<T>(source, k); \
   4008    break;
   4009      JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENT)
   4010 #undef GET_ELEMENT
   4011      case Scalar::MaxTypedArrayViewType:
   4012      case Scalar::Int64:
   4013      case Scalar::Simd128:
   4014        break;
   4015    }
   4016 
   4017    // Step 14.h.iii.3.
   4018    switch (target->type()) {
   4019 #define SET_ELEMENT(_, T, N)                         \
   4020  case Scalar::N:                                    \
   4021    TypedArraySliceCopySlowSet<T>(target, n, value); \
   4022    break;
   4023      JS_FOR_EACH_TYPED_ARRAY(SET_ELEMENT)
   4024 #undef SET_ELEMENT
   4025      case Scalar::MaxTypedArrayViewType:
   4026      case Scalar::Int64:
   4027      case Scalar::Simd128:
   4028        break;
   4029    }
   4030 
   4031    // Step 14.h.iii.4.
   4032    k += 1;
   4033 
   4034    // Step 14.h.iii.5.
   4035    n += 1;
   4036  }
   4037 }
   4038 
   4039 /**
   4040 * %TypedArray%.prototype.slice ( start, end )
   4041 *
   4042 * ES2026 draft rev 60c4df0b65e1c2e6b6581a5bc08a9311223be1da
   4043 */
   4044 static bool TypedArray_slice(JSContext* cx, const CallArgs& args) {
   4045  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   4046 
   4047  // Steps 1-3.
   4048  Rooted<TypedArrayObject*> tarray(
   4049      cx, &args.thisv().toObject().as<TypedArrayObject>());
   4050 
   4051  auto arrayLength = tarray->length();
   4052  if (!arrayLength) {
   4053    ReportOutOfBounds(cx, tarray);
   4054    return false;
   4055  }
   4056  size_t len = *arrayLength;
   4057 
   4058  // Steps 4-7.
   4059  size_t startIndex = 0;
   4060  if (args.hasDefined(0)) {
   4061    if (!ToIntegerIndex(cx, args[0], len, &startIndex)) {
   4062      return false;
   4063    }
   4064  }
   4065 
   4066  // Steps 8-11.
   4067  size_t endIndex = len;
   4068  if (args.hasDefined(1)) {
   4069    if (!ToIntegerIndex(cx, args[1], len, &endIndex)) {
   4070      return false;
   4071    }
   4072  }
   4073 
   4074  // Step 12.
   4075  size_t count = endIndex >= startIndex ? endIndex - startIndex : 0;
   4076 
   4077  // Step 13.
   4078  Rooted<TypedArrayObject*> unwrappedResult(
   4079      cx, TypedArraySpeciesCreate(cx, tarray, count));
   4080  if (!unwrappedResult) {
   4081    return false;
   4082  }
   4083 
   4084  // Additional step from <https://tc39.es/proposal-immutable-arraybuffer>.
   4085  MOZ_ASSERT(!unwrappedResult->is<ImmutableTypedArrayObject>());
   4086 
   4087  // Step 14.
   4088  if (count > 0) {
   4089    // Steps 14.a-b.
   4090    auto arrayLength = tarray->length();
   4091    if (!arrayLength) {
   4092      ReportOutOfBounds(cx, tarray);
   4093      return false;
   4094    }
   4095 
   4096    // Step 14.c.
   4097    endIndex = std::min(endIndex, *arrayLength);
   4098 
   4099    // Step 14.d.
   4100    count = endIndex >= startIndex ? endIndex - startIndex : 0;
   4101 
   4102    // Copy if updated |count| is still non-zero.
   4103    if (count > 0) {
   4104      // Step 14.e.
   4105      auto srcType = tarray->type();
   4106 
   4107      // Step 14.f.
   4108      auto targetType = unwrappedResult->type();
   4109 
   4110      // Steps 14.g-h.
   4111      //
   4112      // The specification requires us to perform bitwise copying when |result|
   4113      // and |tarray| have the same type. Additionally, as an optimization, we
   4114      // can also perform bitwise copying when both types have compatible
   4115      // bit-level representations.
   4116      if (MOZ_LIKELY(CanUseBitwiseCopy(targetType, srcType))) {
   4117        TypedArrayBitwiseSlice(tarray, startIndex, count, unwrappedResult);
   4118      } else {
   4119        TypedArraySliceCopySlow(tarray, startIndex, count, unwrappedResult);
   4120      }
   4121    }
   4122  }
   4123 
   4124  // Step 15.
   4125  if (MOZ_LIKELY(cx->compartment() == unwrappedResult->compartment())) {
   4126    args.rval().setObject(*unwrappedResult);
   4127  } else {
   4128    Rooted<JSObject*> wrappedResult(cx, unwrappedResult);
   4129    if (!cx->compartment()->wrap(cx, &wrappedResult)) {
   4130      return false;
   4131    }
   4132    args.rval().setObject(*wrappedResult);
   4133  }
   4134  return true;
   4135 }
   4136 
   4137 /**
   4138 * %TypedArray%.prototype.slice ( start, end )
   4139 *
   4140 * ES2026 draft rev 60c4df0b65e1c2e6b6581a5bc08a9311223be1da
   4141 */
   4142 static bool TypedArray_slice(JSContext* cx, unsigned argc, Value* vp) {
   4143  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype", "slice");
   4144  CallArgs args = CallArgsFromVp(argc, vp);
   4145  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_slice>(cx, args);
   4146 }
   4147 
   4148 /**
   4149 * %TypedArray%.prototype.subarray ( start, end )
   4150 *
   4151 * ES2026 draft rev 60c4df0b65e1c2e6b6581a5bc08a9311223be1da
   4152 */
   4153 static bool TypedArray_subarray(JSContext* cx, const CallArgs& args) {
   4154  MOZ_ASSERT(IsTypedArrayObject(args.thisv()));
   4155 
   4156  // Steps 1-3.
   4157  Rooted<TypedArrayObject*> tarray(
   4158      cx, &args.thisv().toObject().as<TypedArrayObject>());
   4159 
   4160  // Step 4.
   4161  if (!TypedArrayObject::ensureHasBuffer(cx, tarray)) {
   4162    return false;
   4163  }
   4164  Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, tarray->bufferEither());
   4165 
   4166  // Steps 5-7.
   4167  size_t srcLength = tarray->length().valueOr(0);
   4168 
   4169  // Step 13.
   4170  //
   4171  // Reordered because otherwise it'd be observable that we reset
   4172  // the byteOffset to zero when the underlying array buffer gets detached.
   4173  size_t srcByteOffset = tarray->byteOffsetMaybeOutOfBounds();
   4174 
   4175  // Steps 8-11.
   4176  size_t startIndex = 0;
   4177  if (args.hasDefined(0)) {
   4178    if (!ToIntegerIndex(cx, args[0], srcLength, &startIndex)) {
   4179      return false;
   4180    }
   4181  }
   4182 
   4183  // Step 12.
   4184  size_t elementSize = TypedArrayElemSize(tarray->type());
   4185 
   4186  // Step 14.
   4187  size_t beginByteOffset = srcByteOffset + (startIndex * elementSize);
   4188 
   4189  // Steps 15-16.
   4190  TypedArrayObject* unwrappedResult;
   4191  if (!args.hasDefined(1) && tarray->is<ResizableTypedArrayObject>() &&
   4192      tarray->as<ResizableTypedArrayObject>().isAutoLength()) {
   4193    // Step 15.a.
   4194    unwrappedResult =
   4195        TypedArraySpeciesCreate(cx, tarray, buffer, beginByteOffset);
   4196  } else {
   4197    // Steps 16.a-d.
   4198    size_t endIndex = srcLength;
   4199    if (args.hasDefined(1)) {
   4200      if (!ToIntegerIndex(cx, args[1], srcLength, &endIndex)) {
   4201        return false;
   4202      }
   4203    }
   4204 
   4205    // Step 16.e.
   4206    size_t newLength = endIndex >= startIndex ? endIndex - startIndex : 0;
   4207 
   4208    // Step 16.f.
   4209    unwrappedResult =
   4210        TypedArraySpeciesCreate(cx, tarray, buffer, beginByteOffset, newLength);
   4211  }
   4212  if (!unwrappedResult) {
   4213    return false;
   4214  }
   4215 
   4216  // Step 17.
   4217  if (MOZ_LIKELY(cx->compartment() == unwrappedResult->compartment())) {
   4218    args.rval().setObject(*unwrappedResult);
   4219  } else {
   4220    Rooted<JSObject*> wrappedResult(cx, unwrappedResult);
   4221    if (!cx->compartment()->wrap(cx, &wrappedResult)) {
   4222      return false;
   4223    }
   4224    args.rval().setObject(*wrappedResult);
   4225  }
   4226  return true;
   4227 }
   4228 
   4229 /**
   4230 * %TypedArray%.prototype.subarray ( start, end )
   4231 *
   4232 * ES2026 draft rev 60c4df0b65e1c2e6b6581a5bc08a9311223be1da
   4233 */
   4234 static bool TypedArray_subarray(JSContext* cx, unsigned argc, Value* vp) {
   4235  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype",
   4236                                        "subarray");
   4237  CallArgs args = CallArgsFromVp(argc, vp);
   4238  return CallNonGenericMethod<IsTypedArrayObject, TypedArray_subarray>(cx,
   4239                                                                       args);
   4240 }
   4241 
   4242 TypedArrayObject* js::TypedArraySubarray(JSContext* cx,
   4243                                         Handle<TypedArrayObject*> obj,
   4244                                         intptr_t start, intptr_t end) {
   4245  MOZ_ASSERT(!obj->hasDetachedBuffer());
   4246  MOZ_ASSERT(!obj->is<ResizableTypedArrayObject>());
   4247 
   4248  size_t srcLength = obj->length().valueOr(0);
   4249 
   4250  size_t startIndex = ToIntegerIndex(start, srcLength);
   4251  size_t endIndex = ToIntegerIndex(end, srcLength);
   4252 
   4253  size_t newLength = endIndex >= startIndex ? endIndex - startIndex : 0;
   4254 
   4255  return TypedArraySubarrayWithLength(cx, obj, startIndex, newLength);
   4256 }
   4257 
   4258 TypedArrayObject* js::TypedArraySubarrayWithLength(
   4259    JSContext* cx, Handle<TypedArrayObject*> obj, intptr_t start,
   4260    intptr_t length) {
   4261  MOZ_ASSERT(!obj->hasDetachedBuffer());
   4262  MOZ_ASSERT(!obj->is<ResizableTypedArrayObject>());
   4263  MOZ_ASSERT(start >= 0);
   4264  MOZ_ASSERT(length >= 0);
   4265  MOZ_ASSERT(size_t(start + length) <= obj->length().valueOr(0));
   4266 
   4267  if (!TypedArrayObject::ensureHasBuffer(cx, obj)) {
   4268    return nullptr;
   4269  }
   4270  Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, obj->bufferEither());
   4271 
   4272  size_t srcByteOffset = obj->byteOffset().valueOr(0);
   4273  size_t elementSize = TypedArrayElemSize(obj->type());
   4274  size_t beginByteOffset = srcByteOffset + (start * elementSize);
   4275 
   4276  auto* result =
   4277      TypedArrayCreateSameType(cx, obj, buffer, beginByteOffset, length);
   4278 
   4279  // Other exceptions aren't allowed, because TypedArraySubarray is a
   4280  // recoverable operation.
   4281  MOZ_ASSERT_IF(!result, cx->isThrowingOutOfMemory());
   4282 
   4283  return result;
   4284 }
   4285 
   4286 static auto* TypedArrayFromDetachedBuffer(JSContext* cx,
   4287                                          Handle<TypedArrayObject*> obj) {
   4288  MOZ_ASSERT(obj->hasDetachedBuffer());
   4289 
   4290  Rooted<ArrayBufferObject*> buffer(cx, obj->bufferUnshared());
   4291 
   4292  switch (obj->type()) {
   4293 #define TYPED_ARRAY_CREATE(_, NativeType, Name) \
   4294  case Scalar::Name:                            \
   4295    return FixedLengthTypedArrayObjectTemplate< \
   4296        NativeType>::fromDetachedBuffer(cx, buffer);
   4297    JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_CREATE)
   4298 #undef TYPED_ARRAY_CREATE
   4299    default:
   4300      MOZ_CRASH("Unsupported TypedArray type");
   4301  }
   4302 }
   4303 
   4304 TypedArrayObject* js::TypedArraySubarrayRecover(JSContext* cx,
   4305                                                Handle<TypedArrayObject*> obj,
   4306                                                intptr_t start,
   4307                                                intptr_t length) {
   4308  MOZ_ASSERT(!obj->is<ResizableTypedArrayObject>());
   4309  MOZ_ASSERT(start >= 0);
   4310  MOZ_ASSERT(length >= 0);
   4311 
   4312  // Special case: The buffer was detached after calling `subarray`. This case
   4313  // can only happen when recovering a TypedArraySubarray allocation.
   4314  if (obj->hasDetachedBuffer()) {
   4315    return TypedArrayFromDetachedBuffer(cx, obj);
   4316  }
   4317  return TypedArraySubarrayWithLength(cx, obj, start, length);
   4318 }
   4319 
   4320 // Byte vector with large enough inline storage to allow constructing small
   4321 // typed arrays without extra heap allocations.
   4322 using ByteVector =
   4323    js::Vector<uint8_t, FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT>;
   4324 
   4325 static UniqueChars QuoteString(JSContext* cx, char16_t ch) {
   4326  Sprinter sprinter(cx);
   4327  if (!sprinter.init()) {
   4328    return nullptr;
   4329  }
   4330 
   4331  StringEscape esc{};
   4332  js::EscapePrinter ep(sprinter, esc);
   4333  ep.putChar(ch);
   4334 
   4335  return sprinter.release();
   4336 }
   4337 
   4338 namespace Hex {
   4339 static constexpr int8_t InvalidChar = -1;
   4340 
   4341 static constexpr auto DecodeTable() {
   4342  std::array<int8_t, 256> result = {};
   4343 
   4344  // Initialize all elements to InvalidChar.
   4345  for (auto& e : result) {
   4346    e = InvalidChar;
   4347  }
   4348 
   4349  // Map the ASCII hexadecimal characters to their values.
   4350  for (uint8_t i = 0; i < 128; ++i) {
   4351    if (mozilla::IsAsciiHexDigit(char(i))) {
   4352      result[i] = mozilla::AsciiAlphanumericToNumber(char(i));
   4353    }
   4354  }
   4355 
   4356  return result;
   4357 }
   4358 
   4359 static constexpr auto Table = DecodeTable();
   4360 }  // namespace Hex
   4361 
   4362 /**
   4363 * FromHex ( string [ , maxLength ] )
   4364 *
   4365 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-fromhex
   4366 */
   4367 template <typename Ops, typename CharT>
   4368 static size_t FromHex(const CharT* chars, size_t length,
   4369                      TypedArrayObject* tarray) {
   4370  auto data = Ops::extract(tarray).template cast<uint8_t*>();
   4371 
   4372  static_assert(std::size(Hex::Table) == 256,
   4373                "can access decode table using Latin-1 character");
   4374 
   4375  auto decodeChar = [&](CharT ch) -> int32_t {
   4376    if constexpr (sizeof(CharT) == 1) {
   4377      return Hex::Table[ch];
   4378    } else {
   4379      return ch <= 255 ? Hex::Table[ch] : Hex::InvalidChar;
   4380    }
   4381  };
   4382 
   4383  auto decode2Chars = [&](const CharT* chars) {
   4384    return (decodeChar(chars[0]) << 4) | (decodeChar(chars[1]) << 0);
   4385  };
   4386 
   4387  auto decode4Chars = [&](const CharT* chars) {
   4388    return (decodeChar(chars[2]) << 12) | (decodeChar(chars[3]) << 8) |
   4389           (decodeChar(chars[0]) << 4) | (decodeChar(chars[1]) << 0);
   4390  };
   4391 
   4392  // Step 4.
   4393  size_t index = 0;
   4394 
   4395  // Step 5. (Checked in caller.)
   4396  MOZ_ASSERT(length % 2 == 0);
   4397 
   4398  // Process eight characters per loop iteration.
   4399  if (length >= 8) {
   4400    // Align |data| to uint32_t.
   4401    if (MOZ_UNLIKELY(data.unwrapValue() & 3)) {
   4402      // Performs at most three iterations until |data| is aligned, reading up
   4403      // to six characters.
   4404      while (data.unwrapValue() & 3) {
   4405        // Step 6.a and 6.d.
   4406        uint32_t byte = decode2Chars(chars + index);
   4407 
   4408        // Step 6.b.
   4409        if (MOZ_UNLIKELY(int32_t(byte) < 0)) {
   4410          return index;
   4411        }
   4412        MOZ_ASSERT(byte <= 0xff);
   4413 
   4414        // Step 6.c.
   4415        index += 2;
   4416 
   4417        // Step 6.e.
   4418        Ops::store(data++, uint8_t(byte));
   4419      }
   4420    }
   4421 
   4422    auto data32 = data.template cast<uint32_t*>();
   4423 
   4424    // Step 6.
   4425    size_t lastValidIndex = length - 8;
   4426    while (index <= lastValidIndex) {
   4427      // Steps 6.a and 6.d.
   4428      uint32_t word1 = decode4Chars(chars + index);
   4429 
   4430      // Step 6.b.
   4431      if (MOZ_UNLIKELY(int32_t(word1) < 0)) {
   4432        break;
   4433      }
   4434      MOZ_ASSERT(word1 <= 0xffff);
   4435 
   4436      // Step 6.a and 6.d.
   4437      uint32_t word2 = decode4Chars(chars + index + 4);
   4438 
   4439      // Step 6.b.
   4440      if (MOZ_UNLIKELY(int32_t(word2) < 0)) {
   4441        break;
   4442      }
   4443      MOZ_ASSERT(word2 <= 0xffff);
   4444 
   4445      // Step 6.c.
   4446      index += 4 * 2;
   4447 
   4448      // Step 6.e.
   4449      //
   4450      // The word was constructed in little-endian order, so in the unlikely
   4451      // case of a big-endian system we have to swap it.
   4452      uint32_t word =
   4453          mozilla::NativeEndian::swapFromLittleEndian((word2 << 16) | word1);
   4454      Ops::store(data32++, word);
   4455    }
   4456 
   4457    data = data32.template cast<uint8_t*>();
   4458  }
   4459 
   4460  // Step 6.
   4461  while (index < length) {
   4462    // Step 6.a and 6.d.
   4463    uint32_t byte = decode2Chars(chars + index);
   4464 
   4465    // Step 6.b.
   4466    if (MOZ_UNLIKELY(int32_t(byte) < 0)) {
   4467      return index;
   4468    }
   4469    MOZ_ASSERT(byte <= 0xff);
   4470 
   4471    // Step 6.c.
   4472    index += 2;
   4473 
   4474    // Step 6.e.
   4475    Ops::store(data++, uint8_t(byte));
   4476  }
   4477 
   4478  // Step 7.
   4479  return index;
   4480 }
   4481 
   4482 /**
   4483 * FromHex ( string [ , maxLength ] )
   4484 *
   4485 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-fromhex
   4486 */
   4487 template <typename Ops>
   4488 static size_t FromHex(JSLinearString* linear, size_t length,
   4489                      TypedArrayObject* tarray) {
   4490  JS::AutoCheckCannotGC nogc;
   4491  if (linear->hasLatin1Chars()) {
   4492    return FromHex<Ops>(linear->latin1Chars(nogc), length, tarray);
   4493  }
   4494  return FromHex<Ops>(linear->twoByteChars(nogc), length, tarray);
   4495 }
   4496 
   4497 /**
   4498 * FromHex ( string [ , maxLength ] )
   4499 *
   4500 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-fromhex
   4501 */
   4502 static bool FromHex(JSContext* cx, JSString* string, size_t maxLength,
   4503                    TypedArrayObject* tarray) {
   4504  MOZ_ASSERT(tarray->type() == Scalar::Uint8);
   4505 
   4506  // The underlying buffer must neither be detached nor shrunk. (It may have
   4507  // been grown when it's a growable shared buffer and a concurrent thread
   4508  // resized the buffer.)
   4509  MOZ_ASSERT(!tarray->hasDetachedBuffer());
   4510  MOZ_ASSERT(tarray->length().valueOr(0) >= maxLength);
   4511 
   4512  // Step 1. (Not applicable in our implementation.)
   4513 
   4514  // Step 2.
   4515  //
   4516  // Each byte is encoded in two characters.
   4517  size_t readLength = maxLength * 2;
   4518  MOZ_ASSERT(readLength <= string->length());
   4519 
   4520  // Step 3. (Not applicable in our implementation.)
   4521 
   4522  JSLinearString* linear = string->ensureLinear(cx);
   4523  if (!linear) {
   4524    return false;
   4525  }
   4526 
   4527  // Steps 4 and 6-7.
   4528  size_t index;
   4529  if (tarray->isSharedMemory()) {
   4530    index = FromHex<SharedOps>(linear, readLength, tarray);
   4531  } else {
   4532    index = FromHex<UnsharedOps>(linear, readLength, tarray);
   4533  }
   4534  if (MOZ_UNLIKELY(index < readLength)) {
   4535    char16_t c0 = linear->latin1OrTwoByteChar(index);
   4536    char16_t c1 = linear->latin1OrTwoByteChar(index + 1);
   4537    char16_t ch = !mozilla::IsAsciiHexDigit(c0) ? c0 : c1;
   4538    if (auto str = QuoteString(cx, ch)) {
   4539      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4540                                JSMSG_TYPED_ARRAY_BAD_HEX_DIGIT, str.get());
   4541    }
   4542    return false;
   4543  }
   4544  return true;
   4545 }
   4546 
   4547 namespace Base64 {
   4548 static constexpr int8_t InvalidChar = -1;
   4549 
   4550 static constexpr auto DecodeTable(const char (&alphabet)[65]) {
   4551  std::array<int8_t, 256> result = {};
   4552 
   4553  // Initialize all elements to InvalidChar.
   4554  for (auto& e : result) {
   4555    e = InvalidChar;
   4556  }
   4557 
   4558  // Map the base64 characters to their values.
   4559  for (uint8_t i = 0; i < 64; ++i) {
   4560    result[alphabet[i]] = i;
   4561  }
   4562 
   4563  return result;
   4564 }
   4565 }  // namespace Base64
   4566 
   4567 namespace Base64::Encode {
   4568 static constexpr const char Base64[] =
   4569    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
   4570 static_assert(std::char_traits<char>::length(Base64) == 64);
   4571 
   4572 static constexpr const char Base64Url[] =
   4573    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
   4574 static_assert(std::char_traits<char>::length(Base64Url) == 64);
   4575 }  // namespace Base64::Encode
   4576 
   4577 namespace Base64::Decode {
   4578 static constexpr auto Base64 = DecodeTable(Base64::Encode::Base64);
   4579 static_assert(Base64.size() == 256,
   4580              "256 elements to allow access through Latin-1 characters");
   4581 
   4582 static constexpr auto Base64Url = DecodeTable(Base64::Encode::Base64Url);
   4583 static_assert(Base64Url.size() == 256,
   4584              "256 elements to allow access through Latin-1 characters");
   4585 }  // namespace Base64::Decode
   4586 
   4587 enum class Alphabet {
   4588  /**
   4589   * Standard base64 alphabet.
   4590   */
   4591  Base64,
   4592 
   4593  /**
   4594   * URL and filename safe base64 alphabet.
   4595   */
   4596  Base64Url,
   4597 };
   4598 
   4599 enum class LastChunkHandling {
   4600  /**
   4601   * Allow partial chunks at the end of the input.
   4602   */
   4603  Loose,
   4604 
   4605  /**
   4606   * Disallow partial chunks at the end of the input.
   4607   */
   4608  Strict,
   4609 
   4610  /**
   4611   * Stop before partial chunks at the end of the input.
   4612   */
   4613  StopBeforePartial,
   4614 };
   4615 
   4616 enum class Base64Error {
   4617  None,
   4618  BadChar,
   4619  BadCharAfterPadding,
   4620  IncompleteChunk,
   4621  MissingPadding,
   4622  ExtraBits,
   4623 };
   4624 
   4625 struct Base64Result {
   4626  Base64Error error;
   4627  size_t index;
   4628  size_t written;
   4629 
   4630  bool isError() const { return error != Base64Error::None; }
   4631 
   4632  static auto Ok(size_t index, size_t written) {
   4633    return Base64Result{Base64Error::None, index, written};
   4634  }
   4635 
   4636  static auto Error(Base64Error error) {
   4637    MOZ_ASSERT(error != Base64Error::None);
   4638    return Base64Result{error, 0, 0};
   4639  }
   4640 
   4641  static auto ErrorAt(Base64Error error, size_t index) {
   4642    MOZ_ASSERT(error != Base64Error::None);
   4643    return Base64Result{error, index, 0};
   4644  }
   4645 };
   4646 
   4647 static void ReportBase64Error(JSContext* cx, Base64Result result,
   4648                              JSLinearString* string) {
   4649  MOZ_ASSERT(result.isError());
   4650  switch (result.error) {
   4651    case Base64Error::None:
   4652      break;
   4653    case Base64Error::BadChar:
   4654      if (auto str =
   4655              QuoteString(cx, string->latin1OrTwoByteChar(result.index))) {
   4656        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4657                                  JSMSG_TYPED_ARRAY_BAD_BASE64_CHAR, str.get());
   4658      }
   4659      return;
   4660    case Base64Error::BadCharAfterPadding:
   4661      if (auto str =
   4662              QuoteString(cx, string->latin1OrTwoByteChar(result.index))) {
   4663        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4664                                  JSMSG_TYPED_ARRAY_BAD_BASE64_AFTER_PADDING,
   4665                                  str.get());
   4666      }
   4667      return;
   4668    case Base64Error::IncompleteChunk:
   4669      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4670                                JSMSG_TYPED_ARRAY_BAD_INCOMPLETE_CHUNK);
   4671      return;
   4672    case Base64Error::MissingPadding:
   4673      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4674                                JSMSG_TYPED_ARRAY_MISSING_BASE64_PADDING);
   4675      return;
   4676    case Base64Error::ExtraBits:
   4677      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4678                                JSMSG_TYPED_ARRAY_EXTRA_BASE64_BITS);
   4679      return;
   4680  }
   4681  MOZ_CRASH("unexpected base64 error");
   4682 }
   4683 
   4684 /**
   4685 * FromBase64 ( string, alphabet, lastChunkHandling [ , maxLength ] )
   4686 *
   4687 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
   4688 */
   4689 template <class Ops, typename CharT>
   4690 static auto FromBase64(const CharT* chars, size_t length, Alphabet alphabet,
   4691                       LastChunkHandling lastChunkHandling,
   4692                       SharedMem<uint8_t*> data, size_t maxLength) {
   4693  const SharedMem<uint8_t*> dataBegin = data;
   4694  const SharedMem<uint8_t*> dataEnd = data + maxLength;
   4695 
   4696  auto canAppend = [&](size_t n) { return data + n <= dataEnd; };
   4697 
   4698  auto written = [&]() { return data.unwrap() - dataBegin.unwrap(); };
   4699 
   4700  // DecodeBase64Chunk ( chunk [ , throwOnExtraBits ] )
   4701  //
   4702  // Encode a complete base64 chunk.
   4703  auto decodeChunk = [&](uint32_t chunk) {
   4704    MOZ_ASSERT(chunk <= 0xffffff);
   4705    MOZ_ASSERT(canAppend(3));
   4706    Ops::store(data++, uint8_t(chunk >> 16));
   4707    Ops::store(data++, uint8_t(chunk >> 8));
   4708    Ops::store(data++, uint8_t(chunk));
   4709  };
   4710 
   4711  // DecodeBase64Chunk ( chunk [ , throwOnExtraBits ] )
   4712  //
   4713  // Encode a three element partial base64 chunk.
   4714  auto decodeChunk3 = [&](uint32_t chunk) {
   4715    MOZ_ASSERT(chunk <= 0x3ffff);
   4716    MOZ_ASSERT(canAppend(2));
   4717    Ops::store(data++, uint8_t(chunk >> 10));
   4718    Ops::store(data++, uint8_t(chunk >> 2));
   4719  };
   4720 
   4721  // DecodeBase64Chunk ( chunk [ , throwOnExtraBits ] )
   4722  //
   4723  // Encode a two element partial base64 chunk.
   4724  auto decodeChunk2 = [&](uint32_t chunk) {
   4725    MOZ_ASSERT(chunk <= 0xfff);
   4726    MOZ_ASSERT(canAppend(1));
   4727    Ops::store(data++, uint8_t(chunk >> 4));
   4728  };
   4729 
   4730  // DecodeBase64Chunk ( chunk [ , throwOnExtraBits ] )
   4731  //
   4732  // Encode a partial base64 chunk.
   4733  auto decodePartialChunk = [&](uint32_t chunk, uint32_t chunkLength) {
   4734    MOZ_ASSERT(chunkLength == 2 || chunkLength == 3);
   4735    chunkLength == 2 ? decodeChunk2(chunk) : decodeChunk3(chunk);
   4736  };
   4737 
   4738  // Steps 1-2. (Not applicable in our implementation.)
   4739 
   4740  // Step 3.
   4741  if (maxLength == 0) {
   4742    return Base64Result::Ok(0, 0);
   4743  }
   4744  MOZ_ASSERT(canAppend(1), "can append at least one byte if maxLength > 0");
   4745 
   4746  // Step 8.
   4747  //
   4748  // Current string index.
   4749  size_t index = 0;
   4750 
   4751  // Step 9. (Passed as parameter)
   4752 
   4753  static_assert(std::size(Base64::Decode::Base64) == 256 &&
   4754                    std::size(Base64::Decode::Base64Url) == 256,
   4755                "can access decode tables using Latin-1 character");
   4756 
   4757  const auto& decode = alphabet == Alphabet::Base64 ? Base64::Decode::Base64
   4758                                                    : Base64::Decode::Base64Url;
   4759 
   4760  auto decodeChar = [&](CharT ch) -> int32_t {
   4761    if constexpr (sizeof(CharT) == 1) {
   4762      return decode[ch];
   4763    } else {
   4764      return ch <= 255 ? decode[ch] : Base64::InvalidChar;
   4765    }
   4766  };
   4767 
   4768  auto decode4Chars = [&](const CharT* chars) {
   4769    return (decodeChar(chars[0]) << 18) | (decodeChar(chars[1]) << 12) |
   4770           (decodeChar(chars[2]) << 6) | (decodeChar(chars[3]));
   4771  };
   4772 
   4773  // Initial loop to process only full chunks. Doesn't perform any error
   4774  // reporting and expects that at least four characters can be read per loop
   4775  // iteration and that the output has enough space for a decoded chunk.
   4776  if (length >= 4) {
   4777    size_t lastValidIndex = length - 4;
   4778    while (canAppend(3) && index <= lastValidIndex) {
   4779      // Fast path: Read four consecutive characters.
   4780 
   4781      // Step 10.a. (Performed in slow path.)
   4782 
   4783      // Step 10.b. (Moved out of loop.)
   4784 
   4785      // Steps 10.c and 10.e-g.
   4786      uint32_t chunk = decode4Chars(chars + index);
   4787 
   4788      // Steps 10.h-i. (Not applicable in this loop.)
   4789 
   4790      // Steps 10.d and 10.j-l.
   4791      if (MOZ_LIKELY(int32_t(chunk) >= 0)) {
   4792        // Step 10.j-l.
   4793        decodeChunk(chunk);
   4794 
   4795        // Step 10.d.
   4796        index += 4;
   4797        continue;
   4798      }
   4799 
   4800      // Slow path: Read four characters, ignoring whitespace.
   4801 
   4802      // Steps 10.a and 10.b.
   4803      CharT part[4];
   4804      size_t i = index;
   4805      size_t j = 0;
   4806      while (i < length && j < 4) {
   4807        auto ch = chars[i++];
   4808 
   4809        // Step 10.a.
   4810        if (mozilla::IsAsciiWhitespace(ch)) {
   4811          continue;
   4812        }
   4813 
   4814        // Step 10.c.
   4815        part[j++] = ch;
   4816      }
   4817 
   4818      // Steps 10.d-l.
   4819      if (MOZ_LIKELY(j == 4)) {
   4820        // Steps 10.e-g.
   4821        uint32_t chunk = decode4Chars(part);
   4822 
   4823        // Steps 10.h-i. (Not applicable in this loop.)
   4824 
   4825        // Steps 10.d and 10.j-l.
   4826        if (MOZ_LIKELY(int32_t(chunk) >= 0)) {
   4827          // Step 10.j-l.
   4828          decodeChunk(chunk);
   4829 
   4830          // Step 10.d.
   4831          index = i;
   4832          continue;
   4833        }
   4834      }
   4835 
   4836      // Padding or invalid characters, or end of input. The next loop will
   4837      // process any characters left in the input.
   4838      break;
   4839    }
   4840 
   4841    // Step 10.b.ii.
   4842    if (index == length) {
   4843      return Base64Result::Ok(length, written());
   4844    }
   4845 
   4846    // Step 10.l.v. (Reordered)
   4847    if (!canAppend(1)) {
   4848      MOZ_ASSERT(written() > 0);
   4849      return Base64Result::Ok(index, written());
   4850    }
   4851  }
   4852 
   4853  // Step 4.
   4854  //
   4855  // String index after the last fully read base64 chunk.
   4856  size_t read = index;
   4857 
   4858  // Step 5. (Not applicable in our implementation.)
   4859 
   4860  // Step 6.
   4861  //
   4862  // Current base64 chunk, a uint24 number.
   4863  uint32_t chunk = 0;
   4864 
   4865  // Step 7.
   4866  //
   4867  // Current base64 chunk length, in the range [0..4].
   4868  size_t chunkLength = 0;
   4869 
   4870  // Step 10.
   4871  for (; index < length; index++) {
   4872    // Step 10.c. (Reordered)
   4873    auto ch = chars[index];
   4874 
   4875    // Step 10.a.
   4876    if (mozilla::IsAsciiWhitespace(ch)) {
   4877      continue;
   4878    }
   4879 
   4880    // Step 10.b. (Moved out of loop.)
   4881 
   4882    // Step 10.d. (Performed in for-loop step.)
   4883 
   4884    // Step 10.e.
   4885    if (ch == '=') {
   4886      break;
   4887    }
   4888 
   4889    // Steps 10.f-g.
   4890    uint32_t value = decodeChar(ch);
   4891    if (MOZ_UNLIKELY(int32_t(value) < 0)) {
   4892      return Base64Result::ErrorAt(Base64Error::BadChar, index);
   4893    }
   4894    MOZ_ASSERT(value <= 0x7f);
   4895 
   4896    // Step 10.h. (Not applicable in our implementation.)
   4897 
   4898    // Step 10.i.
   4899    if (chunkLength > 1 && !canAppend(chunkLength)) {
   4900      return Base64Result::Ok(read, written());
   4901    }
   4902 
   4903    // Step 10.j.
   4904    chunk = (chunk << 6) | value;
   4905 
   4906    // Step 10.k.
   4907    chunkLength += 1;
   4908 
   4909    // Step 10.l. (Full chunks are processed in the initial loop.)
   4910    MOZ_ASSERT(chunkLength < 4);
   4911  }
   4912 
   4913  // Step 10.b.
   4914  if (index == length) {
   4915    // Step 10.b.i.
   4916    if (chunkLength > 0) {
   4917      // Step 10.b.i.1.
   4918      if (lastChunkHandling == LastChunkHandling::StopBeforePartial) {
   4919        return Base64Result::Ok(read, written());
   4920      }
   4921 
   4922      // Steps 10.b.i.2-3.
   4923      if (lastChunkHandling == LastChunkHandling::Loose) {
   4924        // Step 10.b.i.2.a.
   4925        if (chunkLength == 1) {
   4926          return Base64Result::Error(Base64Error::IncompleteChunk);
   4927        }
   4928        MOZ_ASSERT(chunkLength == 2 || chunkLength == 3);
   4929 
   4930        // Step 10.b.i.2.b.
   4931        decodePartialChunk(chunk, chunkLength);
   4932      } else {
   4933        // Step 10.b.i.3.a.
   4934        MOZ_ASSERT(lastChunkHandling == LastChunkHandling::Strict);
   4935 
   4936        // Step 10.b.i.3.b.
   4937        return Base64Result::Error(Base64Error::IncompleteChunk);
   4938      }
   4939    }
   4940 
   4941    // Step 10.b.ii.
   4942    return Base64Result::Ok(length, written());
   4943  }
   4944 
   4945  // Step 10.e.
   4946  MOZ_ASSERT(index < length);
   4947  MOZ_ASSERT(chars[index] == '=');
   4948 
   4949  // Step 10.e.i.
   4950  if (chunkLength < 2) {
   4951    return Base64Result::Error(Base64Error::IncompleteChunk);
   4952  }
   4953  MOZ_ASSERT(chunkLength == 2 || chunkLength == 3);
   4954 
   4955  // Step 10.e.ii. (Inlined SkipAsciiWhitespace)
   4956  while (++index < length) {
   4957    auto ch = chars[index];
   4958    if (!mozilla::IsAsciiWhitespace(ch)) {
   4959      break;
   4960    }
   4961  }
   4962 
   4963  // Step 10.e.iii.
   4964  if (chunkLength == 2) {
   4965    // Step 10.e.iii.1.
   4966    if (index == length) {
   4967      // Step 10.e.iii.1.a.
   4968      if (lastChunkHandling == LastChunkHandling::StopBeforePartial) {
   4969        return Base64Result::Ok(read, written());
   4970      }
   4971 
   4972      // Step 10.e.iii.1.b.
   4973      return Base64Result::Error(Base64Error::MissingPadding);
   4974    }
   4975 
   4976    // Step 10.e.iii.2.
   4977    auto ch = chars[index];
   4978 
   4979    // Step 10.e.iii.3.
   4980    if (ch == '=') {
   4981      // Step 10.e.iii.3.a. (Inlined SkipAsciiWhitespace)
   4982      while (++index < length) {
   4983        auto ch = chars[index];
   4984        if (!mozilla::IsAsciiWhitespace(ch)) {
   4985          break;
   4986        }
   4987      }
   4988    }
   4989  }
   4990 
   4991  // Step 10.e.iv.
   4992  if (index < length) {
   4993    return Base64Result::ErrorAt(Base64Error::BadCharAfterPadding, index);
   4994  }
   4995 
   4996  // Steps 10.e.v-vi.
   4997  if (lastChunkHandling == LastChunkHandling::Strict) {
   4998    uint32_t extraBitsMask = chunkLength == 2 ? 0xf : 0x3;
   4999    if ((chunk & extraBitsMask) != 0) {
   5000      return Base64Result::Error(Base64Error::ExtraBits);
   5001    }
   5002  }
   5003 
   5004  // Step 10.e.vii.
   5005  decodePartialChunk(chunk, chunkLength);
   5006 
   5007  // Step 10.e.viii.
   5008  return Base64Result::Ok(length, written());
   5009 }
   5010 
   5011 /**
   5012 * FromBase64 ( string, alphabet, lastChunkHandling [ , maxLength ] )
   5013 *
   5014 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
   5015 */
   5016 template <class Ops>
   5017 static auto FromBase64(JSLinearString* string, Alphabet alphabet,
   5018                       LastChunkHandling lastChunkHandling,
   5019                       SharedMem<uint8_t*> data, size_t maxLength) {
   5020  JS::AutoCheckCannotGC nogc;
   5021  if (string->hasLatin1Chars()) {
   5022    return FromBase64<Ops>(string->latin1Chars(nogc), string->length(),
   5023                           alphabet, lastChunkHandling, data, maxLength);
   5024  }
   5025  return FromBase64<Ops>(string->twoByteChars(nogc), string->length(), alphabet,
   5026                         lastChunkHandling, data, maxLength);
   5027 }
   5028 
   5029 /**
   5030 * FromBase64 ( string, alphabet, lastChunkHandling [ , maxLength ] )
   5031 *
   5032 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
   5033 */
   5034 static auto FromBase64(JSLinearString* string, Alphabet alphabet,
   5035                       LastChunkHandling lastChunkHandling,
   5036                       TypedArrayObject* tarray, size_t maxLength) {
   5037  MOZ_ASSERT(tarray->type() == Scalar::Uint8);
   5038 
   5039  // The underlying buffer must neither be detached nor shrunk. (It may have
   5040  // been grown when it's a growable shared buffer and a concurrent thread
   5041  // resized the buffer.)
   5042  MOZ_ASSERT(!tarray->hasDetachedBuffer());
   5043  MOZ_ASSERT(tarray->length().valueOr(0) >= maxLength);
   5044 
   5045  auto data = tarray->dataPointerEither().cast<uint8_t*>();
   5046 
   5047  if (tarray->isSharedMemory()) {
   5048    return FromBase64<SharedOps>(string, alphabet, lastChunkHandling, data,
   5049                                 maxLength);
   5050  }
   5051  return FromBase64<UnsharedOps>(string, alphabet, lastChunkHandling, data,
   5052                                 maxLength);
   5053 }
   5054 
   5055 /**
   5056 * FromBase64 ( string, alphabet, lastChunkHandling [ , maxLength ] )
   5057 *
   5058 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
   5059 */
   5060 static auto FromBase64(JSLinearString* string, Alphabet alphabet,
   5061                       LastChunkHandling lastChunkHandling, ByteVector& bytes) {
   5062  auto data = SharedMem<uint8_t*>::unshared(bytes.begin());
   5063  size_t maxLength = bytes.length();
   5064  return FromBase64<UnsharedOps>(string, alphabet, lastChunkHandling, data,
   5065                                 maxLength);
   5066 }
   5067 
   5068 /**
   5069 * Uint8Array.fromBase64 ( string [ , options ] )
   5070 * Uint8Array.prototype.setFromBase64 ( string [ , options ] )
   5071 * Uint8Array.prototype.toBase64 ( [ options ] )
   5072 *
   5073 * Helper to retrieve the "alphabet" option.
   5074 */
   5075 static bool GetAlphabetOption(JSContext* cx, Handle<JSObject*> options,
   5076                              Alphabet* result) {
   5077  Rooted<Value> value(cx);
   5078  if (!GetProperty(cx, options, options, cx->names().alphabet, &value)) {
   5079    return false;
   5080  }
   5081 
   5082  if (value.isUndefined()) {
   5083    *result = Alphabet::Base64;
   5084    return true;
   5085  }
   5086 
   5087  if (!value.isString()) {
   5088    return ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK,
   5089                            value, nullptr, "not a string");
   5090  }
   5091 
   5092  auto* linear = value.toString()->ensureLinear(cx);
   5093  if (!linear) {
   5094    return false;
   5095  }
   5096 
   5097  if (StringEqualsLiteral(linear, "base64")) {
   5098    *result = Alphabet::Base64;
   5099    return true;
   5100  }
   5101 
   5102  if (StringEqualsLiteral(linear, "base64url")) {
   5103    *result = Alphabet::Base64Url;
   5104    return true;
   5105  }
   5106 
   5107  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5108                            JSMSG_TYPED_ARRAY_BAD_BASE64_ALPHABET);
   5109  return false;
   5110 }
   5111 
   5112 /**
   5113 * Uint8Array.fromBase64 ( string [ , options ] )
   5114 * Uint8Array.prototype.setFromBase64 ( string [ , options ] )
   5115 *
   5116 * Helper to retrieve the "lastChunkHandling" option.
   5117 */
   5118 static bool GetLastChunkHandlingOption(JSContext* cx, Handle<JSObject*> options,
   5119                                       LastChunkHandling* result) {
   5120  Rooted<Value> value(cx);
   5121  if (!GetProperty(cx, options, options, cx->names().lastChunkHandling,
   5122                   &value)) {
   5123    return false;
   5124  }
   5125 
   5126  if (value.isUndefined()) {
   5127    *result = LastChunkHandling::Loose;
   5128    return true;
   5129  }
   5130 
   5131  if (!value.isString()) {
   5132    return ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK,
   5133                            value, nullptr, "not a string");
   5134  }
   5135 
   5136  auto* linear = value.toString()->ensureLinear(cx);
   5137  if (!linear) {
   5138    return false;
   5139  }
   5140 
   5141  if (StringEqualsLiteral(linear, "loose")) {
   5142    *result = LastChunkHandling::Loose;
   5143    return true;
   5144  }
   5145 
   5146  if (StringEqualsLiteral(linear, "strict")) {
   5147    *result = LastChunkHandling::Strict;
   5148    return true;
   5149  }
   5150 
   5151  if (StringEqualsLiteral(linear, "stop-before-partial")) {
   5152    *result = LastChunkHandling::StopBeforePartial;
   5153    return true;
   5154  }
   5155 
   5156  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5157                            JSMSG_TYPED_ARRAY_BAD_BASE64_LAST_CHUNK_HANDLING);
   5158  return false;
   5159 }
   5160 
   5161 enum class OmitPadding : bool { No, Yes };
   5162 
   5163 /**
   5164 * Uint8Array.prototype.toBase64 ( [ options ] )
   5165 *
   5166 * Helper to retrieve the "omitPadding" option.
   5167 */
   5168 static bool GetOmitPaddingOption(JSContext* cx, Handle<JSObject*> options,
   5169                                 OmitPadding* result) {
   5170  Rooted<Value> value(cx);
   5171  if (!GetProperty(cx, options, options, cx->names().omitPadding, &value)) {
   5172    return false;
   5173  }
   5174 
   5175  *result = static_cast<OmitPadding>(JS::ToBoolean(value));
   5176  return true;
   5177 }
   5178 
   5179 /**
   5180 * Uint8Array.fromBase64 ( string [ , options ] )
   5181 *
   5182 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.frombase64
   5183 */
   5184 static bool uint8array_fromBase64(JSContext* cx, unsigned argc, Value* vp) {
   5185  CallArgs args = CallArgsFromVp(argc, vp);
   5186 
   5187  // Step 1.
   5188  if (!args.get(0).isString()) {
   5189    return ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK,
   5190                            args.get(0), nullptr, "not a string");
   5191  }
   5192  Rooted<JSString*> string(cx, args[0].toString());
   5193 
   5194  // Steps 2-9.
   5195  auto alphabet = Alphabet::Base64;
   5196  auto lastChunkHandling = LastChunkHandling::Loose;
   5197  if (args.hasDefined(1)) {
   5198    // Step 2. (Inlined GetOptionsObject)
   5199    Rooted<JSObject*> options(
   5200        cx, RequireObjectArg(cx, "options", "fromBase64", args[1]));
   5201    if (!options) {
   5202      return false;
   5203    }
   5204 
   5205    // Steps 3-6.
   5206    if (!GetAlphabetOption(cx, options, &alphabet)) {
   5207      return false;
   5208    }
   5209 
   5210    // Steps 7-9.
   5211    if (!GetLastChunkHandlingOption(cx, options, &lastChunkHandling)) {
   5212      return false;
   5213    }
   5214  }
   5215 
   5216  // Compute the output byte length. Four input characters are decoded into
   5217  // three bytes, so the output length can't be larger than ⌈length × 3/4⌉.
   5218  auto outLength = mozilla::CheckedInt<size_t>{string->length()};
   5219  outLength += 3;
   5220  outLength /= 4;
   5221  outLength *= 3;
   5222  MOZ_ASSERT(outLength.isValid(), "can't overflow");
   5223 
   5224  static_assert(JSString::MAX_LENGTH <= TypedArrayObject::ByteLengthLimit,
   5225                "string length doesn't exceed maximum typed array length");
   5226 
   5227  // Step 10.
   5228  ByteVector bytes(cx);
   5229  if (!bytes.resizeUninitialized(outLength.value())) {
   5230    return false;
   5231  }
   5232 
   5233  JSLinearString* linear = string->ensureLinear(cx);
   5234  if (!linear) {
   5235    return false;
   5236  }
   5237 
   5238  auto result = FromBase64(linear, alphabet, lastChunkHandling, bytes);
   5239  if (MOZ_UNLIKELY(result.isError())) {
   5240    ReportBase64Error(cx, result, linear);
   5241    return false;
   5242  }
   5243  MOZ_ASSERT(result.index <= linear->length());
   5244  MOZ_ASSERT(result.written <= bytes.length());
   5245 
   5246  // Step 11.
   5247  size_t resultLength = result.written;
   5248 
   5249  // Step 12.
   5250  auto* tarray =
   5251      TypedArrayObjectTemplate<uint8_t>::fromLength(cx, resultLength);
   5252  if (!tarray) {
   5253    return false;
   5254  }
   5255 
   5256  // Step 13.
   5257  auto target = SharedMem<uint8_t*>::unshared(tarray->dataPointerUnshared());
   5258  auto source = SharedMem<uint8_t*>::unshared(bytes.begin());
   5259  UnsharedOps::podCopy(target, source, resultLength);
   5260 
   5261  // Step 14.
   5262  args.rval().setObject(*tarray);
   5263  return true;
   5264 }
   5265 
   5266 /**
   5267 * Uint8Array.fromHex ( string )
   5268 *
   5269 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.fromhex
   5270 */
   5271 static bool uint8array_fromHex(JSContext* cx, unsigned argc, Value* vp) {
   5272  CallArgs args = CallArgsFromVp(argc, vp);
   5273 
   5274  // Step 1.
   5275  if (!args.get(0).isString()) {
   5276    return ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK,
   5277                            args.get(0), nullptr, "not a string");
   5278  }
   5279  Rooted<JSString*> string(cx, args[0].toString());
   5280 
   5281  // FromHex, step 2.
   5282  size_t stringLength = string->length();
   5283 
   5284  // FromHex, step 3.
   5285  if (stringLength % 2 != 0) {
   5286    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5287                              JSMSG_TYPED_ARRAY_BAD_HEX_STRING_LENGTH);
   5288    return false;
   5289  }
   5290 
   5291  // Step 4. (Reordered)
   5292  size_t resultLength = stringLength / 2;
   5293 
   5294  // Step 5. (Reordered)
   5295  Rooted<TypedArrayObject*> tarray(
   5296      cx, TypedArrayObjectTemplate<uint8_t>::fromLength(cx, resultLength));
   5297  if (!tarray) {
   5298    return false;
   5299  }
   5300 
   5301  // Steps 2-3.
   5302  if (!FromHex(cx, string, resultLength, tarray)) {
   5303    return false;
   5304  }
   5305 
   5306  // Step 6.
   5307  args.rval().setObject(*tarray);
   5308  return true;
   5309 }
   5310 
   5311 /**
   5312 * Uint8Array.prototype.setFromBase64 ( string [ , options ] )
   5313 *
   5314 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.setfrombase64
   5315 */
   5316 static bool uint8array_setFromBase64(JSContext* cx, const CallArgs& args) {
   5317  Rooted<TypedArrayObject*> tarray(
   5318      cx, &args.thisv().toObject().as<TypedArrayObject>());
   5319 
   5320  // Additional step from Immutable ArrayBuffer proposal.
   5321  if (tarray->is<ImmutableTypedArrayObject>()) {
   5322    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5323                              JSMSG_ARRAYBUFFER_IMMUTABLE);
   5324    return false;
   5325  }
   5326 
   5327  // Step 3.
   5328  if (!args.get(0).isString()) {
   5329    return ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK,
   5330                            args.get(0), nullptr, "not a string");
   5331  }
   5332  Rooted<JSString*> string(cx, args[0].toString());
   5333 
   5334  // Steps 4-11.
   5335  auto alphabet = Alphabet::Base64;
   5336  auto lastChunkHandling = LastChunkHandling::Loose;
   5337  if (args.hasDefined(1)) {
   5338    // Step 2. (Inlined GetOptionsObject)
   5339    Rooted<JSObject*> options(
   5340        cx, RequireObjectArg(cx, "options", "setFromBase64", args[1]));
   5341    if (!options) {
   5342      return false;
   5343    }
   5344 
   5345    // Steps 3-6.
   5346    if (!GetAlphabetOption(cx, options, &alphabet)) {
   5347      return false;
   5348    }
   5349 
   5350    // Steps 7-9.
   5351    if (!GetLastChunkHandlingOption(cx, options, &lastChunkHandling)) {
   5352      return false;
   5353    }
   5354  }
   5355 
   5356  // Steps 12-14.
   5357  auto length = tarray->length();
   5358  if (!length) {
   5359    ReportOutOfBounds(cx, tarray);
   5360    return false;
   5361  }
   5362 
   5363  // Steps 15-18.
   5364  size_t readLength = 0;
   5365  size_t written = 0;
   5366  if (*length > 0) {
   5367    JSLinearString* linear = string->ensureLinear(cx);
   5368    if (!linear) {
   5369      return false;
   5370    }
   5371 
   5372    auto result =
   5373        FromBase64(linear, alphabet, lastChunkHandling, tarray, *length);
   5374    if (MOZ_UNLIKELY(result.isError())) {
   5375      ReportBase64Error(cx, result, linear);
   5376      return false;
   5377    }
   5378    MOZ_ASSERT(result.index <= linear->length());
   5379    MOZ_ASSERT(result.written <= *length);
   5380 
   5381    readLength = result.index;
   5382    written = result.written;
   5383  }
   5384 
   5385  // Steps 19-21. (Not applicable in our implementation.)
   5386 
   5387  // Step 22.
   5388  Rooted<PlainObject*> result(cx, NewPlainObject(cx));
   5389  if (!result) {
   5390    return false;
   5391  }
   5392 
   5393  // Step 23.
   5394  Rooted<Value> readValue(cx, NumberValue(readLength));
   5395  if (!DefineDataProperty(cx, result, cx->names().read, readValue)) {
   5396    return false;
   5397  }
   5398 
   5399  // Step 24.
   5400  Rooted<Value> writtenValue(cx, NumberValue(written));
   5401  if (!DefineDataProperty(cx, result, cx->names().written, writtenValue)) {
   5402    return false;
   5403  }
   5404 
   5405  // Step 25.
   5406  args.rval().setObject(*result);
   5407  return true;
   5408 }
   5409 
   5410 /**
   5411 * Uint8Array.prototype.setFromBase64 ( string [ , options ] )
   5412 *
   5413 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.setfrombase64
   5414 */
   5415 static bool uint8array_setFromBase64(JSContext* cx, unsigned argc, Value* vp) {
   5416  CallArgs args = CallArgsFromVp(argc, vp);
   5417 
   5418  // Steps 1-2.
   5419  return CallNonGenericMethod<IsUint8ArrayObject, uint8array_setFromBase64>(
   5420      cx, args);
   5421 }
   5422 
   5423 /**
   5424 * Uint8Array.prototype.setFromHex ( string )
   5425 *
   5426 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.setfromhex
   5427 */
   5428 static bool uint8array_setFromHex(JSContext* cx, const CallArgs& args) {
   5429  Rooted<TypedArrayObject*> tarray(
   5430      cx, &args.thisv().toObject().as<TypedArrayObject>());
   5431 
   5432  // Additional step from Immutable ArrayBuffer proposal.
   5433  if (tarray->is<ImmutableTypedArrayObject>()) {
   5434    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5435                              JSMSG_ARRAYBUFFER_IMMUTABLE);
   5436    return false;
   5437  }
   5438 
   5439  // Step 3.
   5440  if (!args.get(0).isString()) {
   5441    return ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK,
   5442                            args.get(0), nullptr, "not a string");
   5443  }
   5444  Rooted<JSString*> string(cx, args[0].toString());
   5445 
   5446  // Steps 4-6.
   5447  auto byteLength = tarray->length();
   5448  if (!byteLength) {
   5449    ReportOutOfBounds(cx, tarray);
   5450    return false;
   5451  }
   5452 
   5453  // FromHex, step 2.
   5454  size_t stringLength = string->length();
   5455 
   5456  // FromHex, step 3.
   5457  if (stringLength % 2 != 0) {
   5458    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5459                              JSMSG_TYPED_ARRAY_BAD_HEX_STRING_LENGTH);
   5460    return false;
   5461  }
   5462 
   5463  // |string| encodes |stringLength / 2| bytes, but we can't write more bytes
   5464  // than there is space in |tarray|.
   5465  size_t maxLength = std::min(*byteLength, stringLength / 2);
   5466 
   5467  // Steps 7-9.
   5468  if (!FromHex(cx, string, maxLength, tarray)) {
   5469    return false;
   5470  }
   5471 
   5472  // Steps 10-13. (Not applicable in our implementation.)
   5473 
   5474  // Step 14.
   5475  Rooted<PlainObject*> result(cx, NewPlainObject(cx));
   5476  if (!result) {
   5477    return false;
   5478  }
   5479 
   5480  // Step 15.
   5481  //
   5482  // Each byte is encoded in two characters, so the number of read characters is
   5483  // exactly twice as large as |maxLength|.
   5484  Rooted<Value> readValue(cx, NumberValue(maxLength * 2));
   5485  if (!DefineDataProperty(cx, result, cx->names().read, readValue)) {
   5486    return false;
   5487  }
   5488 
   5489  // Step 16.
   5490  //
   5491  // |maxLength| was constrained to the number of bytes written on success.
   5492  Rooted<Value> writtenValue(cx, NumberValue(maxLength));
   5493  if (!DefineDataProperty(cx, result, cx->names().written, writtenValue)) {
   5494    return false;
   5495  }
   5496 
   5497  // Step 17.
   5498  args.rval().setObject(*result);
   5499  return true;
   5500 }
   5501 
   5502 /**
   5503 * Uint8Array.prototype.setFromHex ( string )
   5504 *
   5505 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.setfromhex
   5506 */
   5507 static bool uint8array_setFromHex(JSContext* cx, unsigned argc, Value* vp) {
   5508  CallArgs args = CallArgsFromVp(argc, vp);
   5509 
   5510  // Steps 1-2.
   5511  return CallNonGenericMethod<IsUint8ArrayObject, uint8array_setFromHex>(cx,
   5512                                                                         args);
   5513 }
   5514 
   5515 template <typename Ops>
   5516 static void ToBase64(TypedArrayObject* tarray, size_t length, Alphabet alphabet,
   5517                     OmitPadding omitPadding, JSStringBuilder& sb) {
   5518  const auto& base64Chars = alphabet == Alphabet::Base64
   5519                                ? Base64::Encode::Base64
   5520                                : Base64::Encode::Base64Url;
   5521 
   5522  auto encode = [&base64Chars](uint32_t value) {
   5523    return base64Chars[value & 0x3f];
   5524  };
   5525 
   5526  // Our implementation directly converts the bytes to their string
   5527  // representation instead of first collecting them into an intermediate list.
   5528  auto data = Ops::extract(tarray).template cast<uint8_t*>();
   5529  auto toRead = length;
   5530 
   5531  if (toRead >= 12) {
   5532    // Align |data| to uint32_t.
   5533    if (MOZ_UNLIKELY(data.unwrapValue() & 3)) {
   5534      // Performs at most three iterations until |data| is aligned, reading up
   5535      // to nine bytes.
   5536      while (data.unwrapValue() & 3) {
   5537        // Combine three input bytes into a single uint24 value.
   5538        auto byte0 = Ops::load(data++);
   5539        auto byte1 = Ops::load(data++);
   5540        auto byte2 = Ops::load(data++);
   5541        auto u24 = (uint32_t(byte0) << 16) | (uint32_t(byte1) << 8) | byte2;
   5542 
   5543        // Encode the uint24 value as base64.
   5544        char chars[] = {
   5545            encode(u24 >> 18),
   5546            encode(u24 >> 12),
   5547            encode(u24 >> 6),
   5548            encode(u24 >> 0),
   5549        };
   5550        sb.infallibleAppend(chars, sizeof(chars));
   5551 
   5552        MOZ_ASSERT(toRead >= 3);
   5553        toRead -= 3;
   5554      }
   5555    }
   5556 
   5557    auto data32 = data.template cast<uint32_t*>();
   5558    for (; toRead >= 12; toRead -= 12) {
   5559      // Read three 32-bit words.
   5560      auto word0 = mozilla::NativeEndian::swapToBigEndian(Ops::load(data32++));
   5561      auto word1 = mozilla::NativeEndian::swapToBigEndian(Ops::load(data32++));
   5562      auto word2 = mozilla::NativeEndian::swapToBigEndian(Ops::load(data32++));
   5563 
   5564      // Split into four uint24 values.
   5565      auto u24_0 = word0 >> 8;
   5566      auto u24_1 = (word0 << 16) | (word1 >> 16);
   5567      auto u24_2 = (word1 << 8) | (word2 >> 24);
   5568      auto u24_3 = word2;
   5569 
   5570      // Encode the uint24 values as base64 and write in blocks of eight
   5571      // characters.
   5572      char chars1[] = {
   5573          encode(u24_0 >> 18), encode(u24_0 >> 12),
   5574          encode(u24_0 >> 6),  encode(u24_0 >> 0),
   5575 
   5576          encode(u24_1 >> 18), encode(u24_1 >> 12),
   5577          encode(u24_1 >> 6),  encode(u24_1 >> 0),
   5578      };
   5579      sb.infallibleAppend(chars1, sizeof(chars1));
   5580 
   5581      char chars2[] = {
   5582          encode(u24_2 >> 18), encode(u24_2 >> 12),
   5583          encode(u24_2 >> 6),  encode(u24_2 >> 0),
   5584 
   5585          encode(u24_3 >> 18), encode(u24_3 >> 12),
   5586          encode(u24_3 >> 6),  encode(u24_3 >> 0),
   5587      };
   5588      sb.infallibleAppend(chars2, sizeof(chars2));
   5589    }
   5590    data = data32.template cast<uint8_t*>();
   5591  }
   5592 
   5593  for (; toRead >= 3; toRead -= 3) {
   5594    // Combine three input bytes into a single uint24 value.
   5595    auto byte0 = Ops::load(data++);
   5596    auto byte1 = Ops::load(data++);
   5597    auto byte2 = Ops::load(data++);
   5598    auto u24 = (uint32_t(byte0) << 16) | (uint32_t(byte1) << 8) | byte2;
   5599 
   5600    // Encode the uint24 value as base64.
   5601    char chars[] = {
   5602        encode(u24 >> 18),
   5603        encode(u24 >> 12),
   5604        encode(u24 >> 6),
   5605        encode(u24 >> 0),
   5606    };
   5607    sb.infallibleAppend(chars, sizeof(chars));
   5608  }
   5609 
   5610  // Trailing two and one element bytes are optionally padded with '='.
   5611  if (toRead == 2) {
   5612    // Combine two input bytes into a single uint24 value.
   5613    auto byte0 = Ops::load(data++);
   5614    auto byte1 = Ops::load(data++);
   5615    auto u24 = (uint32_t(byte0) << 16) | (uint32_t(byte1) << 8);
   5616 
   5617    // Encode the uint24 value as base64, optionally including padding.
   5618    sb.infallibleAppend(encode(u24 >> 18));
   5619    sb.infallibleAppend(encode(u24 >> 12));
   5620    sb.infallibleAppend(encode(u24 >> 6));
   5621    if (omitPadding == OmitPadding::No) {
   5622      sb.infallibleAppend('=');
   5623    }
   5624  } else if (toRead == 1) {
   5625    // Combine one input byte into a single uint24 value.
   5626    auto byte0 = Ops::load(data++);
   5627    auto u24 = uint32_t(byte0) << 16;
   5628 
   5629    // Encode the uint24 value as base64, optionally including padding.
   5630    sb.infallibleAppend(encode(u24 >> 18));
   5631    sb.infallibleAppend(encode(u24 >> 12));
   5632    if (omitPadding == OmitPadding::No) {
   5633      sb.infallibleAppend('=');
   5634      sb.infallibleAppend('=');
   5635    }
   5636  } else {
   5637    MOZ_ASSERT(toRead == 0);
   5638  }
   5639 }
   5640 
   5641 /**
   5642 * Uint8Array.prototype.toBase64 ( [ options ] )
   5643 *
   5644 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tobase64
   5645 */
   5646 static bool uint8array_toBase64(JSContext* cx, const CallArgs& args) {
   5647  Rooted<TypedArrayObject*> tarray(
   5648      cx, &args.thisv().toObject().as<TypedArrayObject>());
   5649 
   5650  // Steps 3-7.
   5651  auto alphabet = Alphabet::Base64;
   5652  auto omitPadding = OmitPadding::No;
   5653  if (args.hasDefined(0)) {
   5654    // Step 3. (Inlined GetOptionsObject)
   5655    Rooted<JSObject*> options(
   5656        cx, RequireObjectArg(cx, "options", "toBase64", args[0]));
   5657    if (!options) {
   5658      return false;
   5659    }
   5660 
   5661    // Steps 4-6.
   5662    if (!GetAlphabetOption(cx, options, &alphabet)) {
   5663      return false;
   5664    }
   5665 
   5666    // Step 7.
   5667    if (!GetOmitPaddingOption(cx, options, &omitPadding)) {
   5668      return false;
   5669    }
   5670  }
   5671 
   5672  // Step 8. (Partial)
   5673  auto length = tarray->length();
   5674  if (!length) {
   5675    ReportOutOfBounds(cx, tarray);
   5676    return false;
   5677  }
   5678 
   5679  // Compute the output string length. Three input bytes are encoded as four
   5680  // characters, so the output length is ⌈length × 4/3⌉. When omitting padding,
   5681  // the output length is length + ⌈length / 3⌉.
   5682  auto outLength = mozilla::CheckedInt<size_t>{*length};
   5683  outLength += 2;
   5684  outLength /= 3;
   5685  if (omitPadding == OmitPadding::No) {
   5686    outLength *= 4;
   5687  } else {
   5688    outLength += *length;
   5689  }
   5690  if (!outLength.isValid() || outLength.value() > JSString::MAX_LENGTH) {
   5691    ReportAllocationOverflow(cx);
   5692    return false;
   5693  }
   5694 
   5695  JSStringBuilder sb(cx);
   5696  if (!sb.reserve(outLength.value())) {
   5697    return false;
   5698  }
   5699 
   5700  // Steps 9-10.
   5701  if (tarray->isSharedMemory()) {
   5702    ToBase64<SharedOps>(tarray, *length, alphabet, omitPadding, sb);
   5703  } else {
   5704    ToBase64<UnsharedOps>(tarray, *length, alphabet, omitPadding, sb);
   5705  }
   5706 
   5707  MOZ_ASSERT(sb.length() == outLength.value(), "all characters were written");
   5708 
   5709  // Step 11.
   5710  auto* str = sb.finishString();
   5711  if (!str) {
   5712    return false;
   5713  }
   5714 
   5715  args.rval().setString(str);
   5716  return true;
   5717 }
   5718 
   5719 /**
   5720 * Uint8Array.prototype.toBase64 ( [ options ] )
   5721 *
   5722 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tobase64
   5723 */
   5724 static bool uint8array_toBase64(JSContext* cx, unsigned argc, Value* vp) {
   5725  CallArgs args = CallArgsFromVp(argc, vp);
   5726 
   5727  // Steps 1-2.
   5728  return CallNonGenericMethod<IsUint8ArrayObject, uint8array_toBase64>(cx,
   5729                                                                       args);
   5730 }
   5731 
   5732 template <typename Ops>
   5733 static void ToHex(TypedArrayObject* tarray, size_t length,
   5734                  JSStringBuilder& sb) {
   5735  // NB: Lower case hex digits.
   5736  static constexpr char HexDigits[] = "0123456789abcdef";
   5737  static_assert(std::char_traits<char>::length(HexDigits) == 16);
   5738 
   5739  // Process multiple bytes per loop to reduce number of calls to
   5740  // infallibleAppend. Choose four bytes because tested compilers can optimize
   5741  // this amount of bytes into a single write operation.
   5742  constexpr size_t BYTES_PER_LOOP = 4;
   5743 
   5744  size_t alignedLength = length & ~(BYTES_PER_LOOP - 1);
   5745 
   5746  // Steps 3 and 5.
   5747  //
   5748  // Our implementation directly converts the bytes to their string
   5749  // representation instead of first collecting them into an intermediate list.
   5750  auto data = Ops::extract(tarray).template cast<uint8_t*>();
   5751  for (size_t index = 0; index < alignedLength;) {
   5752    char chars[BYTES_PER_LOOP * 2];
   5753 
   5754    for (size_t i = 0; i < BYTES_PER_LOOP; ++i) {
   5755      auto byte = Ops::load(data + index++);
   5756      chars[i * 2 + 0] = HexDigits[byte >> 4];
   5757      chars[i * 2 + 1] = HexDigits[byte & 0xf];
   5758    }
   5759 
   5760    sb.infallibleAppend(chars, sizeof(chars));
   5761  }
   5762 
   5763  // Write the remaining characters.
   5764  for (size_t index = alignedLength; index < length;) {
   5765    char chars[2];
   5766 
   5767    auto byte = Ops::load(data + index++);
   5768    chars[0] = HexDigits[byte >> 4];
   5769    chars[1] = HexDigits[byte & 0xf];
   5770 
   5771    sb.infallibleAppend(chars, sizeof(chars));
   5772  }
   5773 }
   5774 
   5775 /**
   5776 * Uint8Array.prototype.toHex ( )
   5777 *
   5778 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tohex
   5779 */
   5780 static bool uint8array_toHex(JSContext* cx, const CallArgs& args) {
   5781  Rooted<TypedArrayObject*> tarray(
   5782      cx, &args.thisv().toObject().as<TypedArrayObject>());
   5783 
   5784  // Step 3. (Partial)
   5785  auto length = tarray->length();
   5786  if (!length) {
   5787    ReportOutOfBounds(cx, tarray);
   5788    return false;
   5789  }
   5790 
   5791  // |length| is limited by |ByteLengthLimit|, which ensures that multiplying it
   5792  // by two won't overflow.
   5793  static_assert(TypedArrayObject::ByteLengthLimit <=
   5794                std::numeric_limits<size_t>::max() / 2);
   5795  MOZ_ASSERT(*length <= TypedArrayObject::ByteLengthLimit);
   5796 
   5797  // Compute the output string length. Each byte is encoded as two characters,
   5798  // so the output length is exactly twice as large as |length|.
   5799  size_t outLength = *length * 2;
   5800  if (outLength > JSString::MAX_LENGTH) {
   5801    ReportAllocationOverflow(cx);
   5802    return false;
   5803  }
   5804 
   5805  // Step 4.
   5806  JSStringBuilder sb(cx);
   5807  if (!sb.reserve(outLength)) {
   5808    return false;
   5809  }
   5810 
   5811  // Steps 3 and 5.
   5812  if (tarray->isSharedMemory()) {
   5813    ToHex<SharedOps>(tarray, *length, sb);
   5814  } else {
   5815    ToHex<UnsharedOps>(tarray, *length, sb);
   5816  }
   5817 
   5818  MOZ_ASSERT(sb.length() == outLength, "all characters were written");
   5819 
   5820  // Step 6.
   5821  auto* str = sb.finishString();
   5822  if (!str) {
   5823    return false;
   5824  }
   5825 
   5826  args.rval().setString(str);
   5827  return true;
   5828 }
   5829 
   5830 /**
   5831 * Uint8Array.prototype.toHex ( )
   5832 *
   5833 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tohex
   5834 */
   5835 static bool uint8array_toHex(JSContext* cx, unsigned argc, Value* vp) {
   5836  CallArgs args = CallArgsFromVp(argc, vp);
   5837 
   5838  // Steps 1-2.
   5839  return CallNonGenericMethod<IsUint8ArrayObject, uint8array_toHex>(cx, args);
   5840 }
   5841 
   5842 /* static */ const JSFunctionSpec TypedArrayObject::protoFunctions[] = {
   5843    JS_INLINABLE_FN("subarray", TypedArray_subarray, 2, 0, TypedArraySubarray),
   5844    JS_INLINABLE_FN("set", TypedArray_set, 1, 0, TypedArraySet),
   5845    JS_FN("copyWithin", TypedArray_copyWithin, 2, 0),
   5846    JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 1, 0),
   5847    JS_INLINABLE_FN("fill", TypedArray_fill, 1, 0, TypedArrayFill),
   5848    JS_SELF_HOSTED_FN("filter", "TypedArrayFilter", 1, 0),
   5849    JS_SELF_HOSTED_FN("find", "TypedArrayFind", 1, 0),
   5850    JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 1, 0),
   5851    JS_SELF_HOSTED_FN("findLast", "TypedArrayFindLast", 1, 0),
   5852    JS_SELF_HOSTED_FN("findLastIndex", "TypedArrayFindLastIndex", 1, 0),
   5853    JS_SELF_HOSTED_FN("forEach", "TypedArrayForEach", 1, 0),
   5854    JS_FN("indexOf", TypedArray_indexOf, 1, 0),
   5855    JS_FN("join", TypedArray_join, 1, 0),
   5856    JS_FN("lastIndexOf", TypedArray_lastIndexOf, 1, 0),
   5857    JS_SELF_HOSTED_FN("map", "TypedArrayMap", 1, 0),
   5858    JS_SELF_HOSTED_FN("reduce", "TypedArrayReduce", 1, 0),
   5859    JS_SELF_HOSTED_FN("reduceRight", "TypedArrayReduceRight", 1, 0),
   5860    JS_FN("reverse", TypedArray_reverse, 0, 0),
   5861    JS_FN("slice", TypedArray_slice, 2, 0),
   5862    JS_SELF_HOSTED_FN("some", "TypedArraySome", 1, 0),
   5863    JS_TRAMPOLINE_FN("sort", TypedArrayObject::sort, 1, 0, TypedArraySort),
   5864    JS_SELF_HOSTED_FN("entries", "TypedArrayEntries", 0, 0),
   5865    JS_SELF_HOSTED_FN("keys", "TypedArrayKeys", 0, 0),
   5866    JS_SELF_HOSTED_FN("values", "$TypedArrayValues", 0, 0),
   5867    JS_SELF_HOSTED_SYM_FN(iterator, "$TypedArrayValues", 0, 0),
   5868    JS_FN("includes", TypedArray_includes, 1, 0),
   5869    JS_SELF_HOSTED_FN("toString", "ArrayToString", 0, 0),
   5870    JS_SELF_HOSTED_FN("toLocaleString", "TypedArrayToLocaleString", 2, 0),
   5871    JS_SELF_HOSTED_FN("at", "TypedArrayAt", 1, 0),
   5872    JS_FN("toReversed", TypedArray_toReversed, 0, 0),
   5873    JS_SELF_HOSTED_FN("toSorted", "TypedArrayToSorted", 1, 0),
   5874    JS_FN("with", TypedArray_with, 2, 0),
   5875    JS_FS_END,
   5876 };
   5877 
   5878 /* static */ const JSFunctionSpec TypedArrayObject::staticFunctions[] = {
   5879    JS_SELF_HOSTED_FN("from", "TypedArrayStaticFrom", 3, 0),
   5880    JS_SELF_HOSTED_FN("of", "TypedArrayStaticOf", 0, 0),
   5881    JS_FS_END,
   5882 };
   5883 
   5884 /* static */ const JSPropertySpec TypedArrayObject::staticProperties[] = {
   5885    JS_SELF_HOSTED_SYM_GET(species, "$TypedArraySpecies", 0),
   5886    JS_PS_END,
   5887 };
   5888 
   5889 static JSObject* CreateSharedTypedArrayPrototype(JSContext* cx,
   5890                                                 JSProtoKey key) {
   5891  return GlobalObject::createBlankPrototype(
   5892      cx, cx->global(), &TypedArrayObject::sharedTypedArrayPrototypeClass);
   5893 }
   5894 
   5895 static const ClassSpec TypedArrayObjectSharedTypedArrayPrototypeClassSpec = {
   5896    GenericCreateConstructor<TypedArrayConstructor, 0, gc::AllocKind::FUNCTION>,
   5897    CreateSharedTypedArrayPrototype,
   5898    TypedArrayObject::staticFunctions,
   5899    TypedArrayObject::staticProperties,
   5900    TypedArrayObject::protoFunctions,
   5901    TypedArrayObject::protoAccessors,
   5902    GenericFinishInit<WhichHasRealmFuseProperty::ProtoAndCtor>,
   5903    ClassSpec::DontDefineConstructor,
   5904 };
   5905 
   5906 /* static */ const JSClass TypedArrayObject::sharedTypedArrayPrototypeClass = {
   5907    "TypedArrayPrototype",
   5908    JSCLASS_HAS_CACHED_PROTO(JSProto_TypedArray),
   5909    JS_NULL_CLASS_OPS,
   5910    &TypedArrayObjectSharedTypedArrayPrototypeClassSpec,
   5911 };
   5912 
   5913 namespace {
   5914 
   5915 // This default implementation is only valid for integer types less
   5916 // than 32-bits in size.
   5917 template <typename NativeType>
   5918 bool TypedArrayObjectTemplate<NativeType>::getElementPure(
   5919    TypedArrayObject* tarray, size_t index, Value* vp) {
   5920  static_assert(sizeof(NativeType) < 4,
   5921                "this method must only handle NativeType values that are "
   5922                "always exact int32_t values");
   5923 
   5924  *vp = Int32Value(getIndex(tarray, index));
   5925  return true;
   5926 }
   5927 
   5928 // We need to specialize for floats and other integer types.
   5929 template <>
   5930 bool TypedArrayObjectTemplate<int32_t>::getElementPure(TypedArrayObject* tarray,
   5931                                                       size_t index,
   5932                                                       Value* vp) {
   5933  *vp = Int32Value(getIndex(tarray, index));
   5934  return true;
   5935 }
   5936 
   5937 template <>
   5938 bool TypedArrayObjectTemplate<uint32_t>::getElementPure(
   5939    TypedArrayObject* tarray, size_t index, Value* vp) {
   5940  uint32_t val = getIndex(tarray, index);
   5941  *vp = NumberValue(val);
   5942  return true;
   5943 }
   5944 
   5945 template <>
   5946 bool TypedArrayObjectTemplate<float16>::getElementPure(TypedArrayObject* tarray,
   5947                                                       size_t index,
   5948                                                       Value* vp) {
   5949  float16 f16 = getIndex(tarray, index);
   5950  /*
   5951   * Doubles in typed arrays could be typed-punned arrays of integers. This
   5952   * could allow user code to break the engine-wide invariant that only
   5953   * canonical nans are stored into jsvals, which means user code could
   5954   * confuse the engine into interpreting a double-typed jsval as an
   5955   * object-typed jsval.
   5956   *
   5957   * This could be removed for platforms/compilers known to convert a 32-bit
   5958   * non-canonical nan to a 64-bit canonical nan.
   5959   */
   5960  *vp = JS::CanonicalizedDoubleValue(static_cast<double>(f16));
   5961  return true;
   5962 }
   5963 
   5964 template <>
   5965 bool TypedArrayObjectTemplate<float>::getElementPure(TypedArrayObject* tarray,
   5966                                                     size_t index, Value* vp) {
   5967  float val = getIndex(tarray, index);
   5968  double dval = val;
   5969 
   5970  /*
   5971   * Doubles in typed arrays could be typed-punned arrays of integers. This
   5972   * could allow user code to break the engine-wide invariant that only
   5973   * canonical nans are stored into jsvals, which means user code could
   5974   * confuse the engine into interpreting a double-typed jsval as an
   5975   * object-typed jsval.
   5976   *
   5977   * This could be removed for platforms/compilers known to convert a 32-bit
   5978   * non-canonical nan to a 64-bit canonical nan.
   5979   */
   5980  *vp = JS::CanonicalizedDoubleValue(dval);
   5981  return true;
   5982 }
   5983 
   5984 template <>
   5985 bool TypedArrayObjectTemplate<double>::getElementPure(TypedArrayObject* tarray,
   5986                                                      size_t index, Value* vp) {
   5987  double val = getIndex(tarray, index);
   5988 
   5989  /*
   5990   * Doubles in typed arrays could be typed-punned arrays of integers. This
   5991   * could allow user code to break the engine-wide invariant that only
   5992   * canonical nans are stored into jsvals, which means user code could
   5993   * confuse the engine into interpreting a double-typed jsval as an
   5994   * object-typed jsval.
   5995   */
   5996  *vp = JS::CanonicalizedDoubleValue(val);
   5997  return true;
   5998 }
   5999 
   6000 template <>
   6001 bool TypedArrayObjectTemplate<int64_t>::getElementPure(TypedArrayObject* tarray,
   6002                                                       size_t index,
   6003                                                       Value* vp) {
   6004  return false;
   6005 }
   6006 
   6007 template <>
   6008 bool TypedArrayObjectTemplate<uint64_t>::getElementPure(
   6009    TypedArrayObject* tarray, size_t index, Value* vp) {
   6010  return false;
   6011 }
   6012 } /* anonymous namespace */
   6013 
   6014 namespace {
   6015 
   6016 template <typename NativeType>
   6017 bool TypedArrayObjectTemplate<NativeType>::getElement(JSContext* cx,
   6018                                                      TypedArrayObject* tarray,
   6019                                                      size_t index,
   6020                                                      MutableHandleValue val) {
   6021  MOZ_ALWAYS_TRUE(getElementPure(tarray, index, val.address()));
   6022  return true;
   6023 }
   6024 
   6025 template <>
   6026 bool TypedArrayObjectTemplate<int64_t>::getElement(JSContext* cx,
   6027                                                   TypedArrayObject* tarray,
   6028                                                   size_t index,
   6029                                                   MutableHandleValue val) {
   6030  int64_t n = getIndex(tarray, index);
   6031  BigInt* res = BigInt::createFromInt64(cx, n);
   6032  if (!res) {
   6033    return false;
   6034  }
   6035  val.setBigInt(res);
   6036  return true;
   6037 }
   6038 
   6039 template <>
   6040 bool TypedArrayObjectTemplate<uint64_t>::getElement(JSContext* cx,
   6041                                                    TypedArrayObject* tarray,
   6042                                                    size_t index,
   6043                                                    MutableHandleValue val) {
   6044  uint64_t n = getIndex(tarray, index);
   6045  BigInt* res = BigInt::createFromUint64(cx, n);
   6046  if (!res) {
   6047    return false;
   6048  }
   6049  val.setBigInt(res);
   6050  return true;
   6051 }
   6052 } /* anonymous namespace */
   6053 
   6054 namespace js {
   6055 
   6056 template <>
   6057 bool TypedArrayObject::getElement<CanGC>(JSContext* cx, size_t index,
   6058                                         MutableHandleValue val) {
   6059  switch (type()) {
   6060 #define GET_ELEMENT(_, T, N) \
   6061  case Scalar::N:            \
   6062    return TypedArrayObjectTemplate<T>::getElement(cx, this, index, val);
   6063    JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENT)
   6064 #undef GET_ELEMENT
   6065    case Scalar::MaxTypedArrayViewType:
   6066    case Scalar::Int64:
   6067    case Scalar::Simd128:
   6068      break;
   6069  }
   6070 
   6071  MOZ_CRASH("Unknown TypedArray type");
   6072 }
   6073 
   6074 template <>
   6075 bool TypedArrayObject::getElement<NoGC>(
   6076    JSContext* cx, size_t index,
   6077    typename MaybeRooted<Value, NoGC>::MutableHandleType vp) {
   6078  return getElementPure(index, vp.address());
   6079 }
   6080 
   6081 }  // namespace js
   6082 
   6083 bool TypedArrayObject::getElementPure(size_t index, Value* vp) {
   6084  switch (type()) {
   6085 #define GET_ELEMENT_PURE(_, T, N) \
   6086  case Scalar::N:                 \
   6087    return TypedArrayObjectTemplate<T>::getElementPure(this, index, vp);
   6088    JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENT_PURE)
   6089 #undef GET_ELEMENT
   6090    case Scalar::MaxTypedArrayViewType:
   6091    case Scalar::Int64:
   6092    case Scalar::Simd128:
   6093      break;
   6094  }
   6095 
   6096  MOZ_CRASH("Unknown TypedArray type");
   6097 }
   6098 
   6099 /* static */
   6100 bool TypedArrayObject::getElements(JSContext* cx,
   6101                                   Handle<TypedArrayObject*> tarray,
   6102                                   size_t length, Value* vp) {
   6103  MOZ_ASSERT(length <= tarray->length().valueOr(0));
   6104  MOZ_ASSERT_IF(length > 0, !tarray->hasDetachedBuffer());
   6105 
   6106  switch (tarray->type()) {
   6107 #define GET_ELEMENTS(_, T, N)                                               \
   6108  case Scalar::N:                                                           \
   6109    for (size_t i = 0; i < length; ++i, ++vp) {                             \
   6110      if (!TypedArrayObjectTemplate<T>::getElement(                         \
   6111              cx, tarray, i, MutableHandleValue::fromMarkedLocation(vp))) { \
   6112        return false;                                                       \
   6113      }                                                                     \
   6114    }                                                                       \
   6115    return true;
   6116    JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENTS)
   6117 #undef GET_ELEMENTS
   6118    case Scalar::MaxTypedArrayViewType:
   6119    case Scalar::Int64:
   6120    case Scalar::Simd128:
   6121      break;
   6122  }
   6123 
   6124  MOZ_CRASH("Unknown TypedArray type");
   6125 }
   6126 
   6127 /***
   6128 *** JS impl
   6129 ***/
   6130 
   6131 /*
   6132 * TypedArrayObject boilerplate
   6133 */
   6134 
   6135 static const JSClassOps TypedArrayClassOps = {
   6136    nullptr,                                // addProperty
   6137    nullptr,                                // delProperty
   6138    nullptr,                                // enumerate
   6139    nullptr,                                // newEnumerate
   6140    nullptr,                                // resolve
   6141    nullptr,                                // mayResolve
   6142    FixedLengthTypedArrayObject::finalize,  // finalize
   6143    nullptr,                                // call
   6144    nullptr,                                // construct
   6145    ArrayBufferViewObject::trace,           // trace
   6146 };
   6147 
   6148 static const JSClassOps ResizableTypedArrayClassOps = {
   6149    nullptr,                       // addProperty
   6150    nullptr,                       // delProperty
   6151    nullptr,                       // enumerate
   6152    nullptr,                       // newEnumerate
   6153    nullptr,                       // resolve
   6154    nullptr,                       // mayResolve
   6155    nullptr,                       // finalize
   6156    nullptr,                       // call
   6157    nullptr,                       // construct
   6158    ArrayBufferViewObject::trace,  // trace
   6159 };
   6160 
   6161 static const JSClassOps ImmutableTypedArrayClassOps = {
   6162    nullptr,                       // addProperty
   6163    nullptr,                       // delProperty
   6164    nullptr,                       // enumerate
   6165    nullptr,                       // newEnumerate
   6166    nullptr,                       // resolve
   6167    nullptr,                       // mayResolve
   6168    nullptr,                       // finalize
   6169    nullptr,                       // call
   6170    nullptr,                       // construct
   6171    ArrayBufferViewObject::trace,  // trace
   6172 };
   6173 
   6174 static const ClassExtension TypedArrayClassExtension = {
   6175    FixedLengthTypedArrayObject::objectMoved,  // objectMovedOp
   6176 };
   6177 
   6178 static const JSPropertySpec
   6179    static_prototype_properties[Scalar::MaxTypedArrayViewType][2] = {
   6180 #define IMPL_TYPED_ARRAY_PROPERTIES(ExternalType, NativeType, Name)        \
   6181  {                                                                        \
   6182      JS_INT32_PS("BYTES_PER_ELEMENT",                                     \
   6183                  TypedArrayObjectTemplate<NativeType>::BYTES_PER_ELEMENT, \
   6184                  JSPROP_READONLY | JSPROP_PERMANENT),                     \
   6185      JS_PS_END,                                                           \
   6186  },
   6187 
   6188        JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_PROPERTIES)
   6189 #undef IMPL_TYPED_ARRAY_PROPERTIES
   6190 };
   6191 
   6192 static const JSFunctionSpec uint8array_static_methods[] = {
   6193    JS_FN("fromBase64", uint8array_fromBase64, 1, 0),
   6194    JS_FN("fromHex", uint8array_fromHex, 1, 0),
   6195    JS_FS_END,
   6196 };
   6197 
   6198 static const JSFunctionSpec uint8array_methods[] = {
   6199    JS_FN("setFromBase64", uint8array_setFromBase64, 1, 0),
   6200    JS_FN("setFromHex", uint8array_setFromHex, 1, 0),
   6201    JS_FN("toBase64", uint8array_toBase64, 0, 0),
   6202    JS_FN("toHex", uint8array_toHex, 0, 0),
   6203    JS_FS_END,
   6204 };
   6205 
   6206 static constexpr const JSFunctionSpec* TypedArrayStaticMethods(
   6207    Scalar::Type type) {
   6208  if (type == Scalar::Uint8) {
   6209    return uint8array_static_methods;
   6210  }
   6211  return nullptr;
   6212 }
   6213 
   6214 static constexpr const JSFunctionSpec* TypedArrayMethods(Scalar::Type type) {
   6215  if (type == Scalar::Uint8) {
   6216    return uint8array_methods;
   6217  }
   6218  return nullptr;
   6219 }
   6220 
   6221 static const ClassSpec
   6222    TypedArrayObjectClassSpecs[Scalar::MaxTypedArrayViewType] = {
   6223 #define IMPL_TYPED_ARRAY_CLASS_SPEC(ExternalType, NativeType, Name) \
   6224  {                                                                 \
   6225      TypedArrayObjectTemplate<NativeType>::createConstructor,      \
   6226      TypedArrayObjectTemplate<NativeType>::createPrototype,        \
   6227      TypedArrayStaticMethods(Scalar::Type::Name),                  \
   6228      static_prototype_properties[Scalar::Type::Name],              \
   6229      TypedArrayMethods(Scalar::Type::Name),                        \
   6230      static_prototype_properties[Scalar::Type::Name],              \
   6231      GenericFinishInit<WhichHasRealmFuseProperty::Proto>,          \
   6232      JSProto_TypedArray,                                           \
   6233  },
   6234 
   6235        JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS_SPEC)
   6236 #undef IMPL_TYPED_ARRAY_CLASS_SPEC
   6237 };
   6238 
   6239 // Class definitions for fixed length, resizable, and immutable typed arrays.
   6240 // Stored into a 2-dimensional array to ensure the classes are in contiguous
   6241 // memory.
   6242 const JSClass TypedArrayObject::anyClasses[3][Scalar::MaxTypedArrayViewType] = {
   6243    // Class definitions for fixed length typed arrays.
   6244    {
   6245 #define IMPL_TYPED_ARRAY_CLASS(ExternalType, NativeType, Name)             \
   6246  {                                                                        \
   6247      #Name "Array",                                                       \
   6248      JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |       \
   6249          JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array) |                \
   6250          JSCLASS_DELAY_METADATA_BUILDER | JSCLASS_SKIP_NURSERY_FINALIZE | \
   6251          JSCLASS_BACKGROUND_FINALIZE,                                     \
   6252      &TypedArrayClassOps,                                                 \
   6253      &TypedArrayObjectClassSpecs[Scalar::Type::Name],                     \
   6254      &TypedArrayClassExtension,                                           \
   6255  },
   6256 
   6257        JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS)
   6258 #undef IMPL_TYPED_ARRAY_CLASS
   6259    },
   6260 
   6261    // Class definitions for immutable typed arrays.
   6262    {
   6263 #define IMPL_TYPED_ARRAY_CLASS(ExternalType, NativeType, Name)                \
   6264  {                                                                           \
   6265      #Name "Array",                                                          \
   6266      JSCLASS_HAS_RESERVED_SLOTS(ImmutableTypedArrayObject::RESERVED_SLOTS) | \
   6267          JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array) |                   \
   6268          JSCLASS_DELAY_METADATA_BUILDER,                                     \
   6269      &ImmutableTypedArrayClassOps,                                           \
   6270      &TypedArrayObjectClassSpecs[Scalar::Type::Name],                        \
   6271      JS_NULL_CLASS_EXT,                                                      \
   6272  },
   6273 
   6274        JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS)
   6275 #undef IMPL_TYPED_ARRAY_CLASS
   6276    },
   6277 
   6278    // Class definitions for resizable typed arrays.
   6279    {
   6280 #define IMPL_TYPED_ARRAY_CLASS(ExternalType, NativeType, Name)                \
   6281  {                                                                           \
   6282      #Name "Array",                                                          \
   6283      JSCLASS_HAS_RESERVED_SLOTS(ResizableTypedArrayObject::RESERVED_SLOTS) | \
   6284          JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array) |                   \
   6285          JSCLASS_DELAY_METADATA_BUILDER,                                     \
   6286      &ResizableTypedArrayClassOps,                                           \
   6287      &TypedArrayObjectClassSpecs[Scalar::Type::Name],                        \
   6288      JS_NULL_CLASS_EXT,                                                      \
   6289  },
   6290 
   6291        JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS)
   6292 #undef IMPL_TYPED_ARRAY_CLASS
   6293    },
   6294 };
   6295 
   6296 // The various typed array prototypes are supposed to 1) be normal objects,
   6297 // 2) stringify to "[object <name of constructor>]", and 3) (Gecko-specific)
   6298 // be xrayable.  The first and second requirements mandate (in the absence of
   6299 // @@toStringTag) a custom class.  The third requirement mandates that each
   6300 // prototype's class have the relevant typed array's cached JSProtoKey in them.
   6301 // Thus we need one class with cached prototype per kind of typed array, with a
   6302 // delegated ClassSpec.
   6303 //
   6304 // Actually ({}).toString.call(Uint8Array.prototype) should throw, because
   6305 // Uint8Array.prototype lacks the the typed array internal slots.  (Same as
   6306 // with %TypedArray%.prototype.)  It's not clear this is desirable (see
   6307 // above), but it's what we've always done, so keep doing it till we
   6308 // implement @@toStringTag or ES6 changes.
   6309 const JSClass TypedArrayObject::protoClasses[Scalar::MaxTypedArrayViewType] = {
   6310 #define IMPL_TYPED_ARRAY_PROTO_CLASS(ExternalType, NativeType, Name) \
   6311  {                                                                  \
   6312      #Name "Array.prototype",                                       \
   6313      JSCLASS_HAS_CACHED_PROTO(JSProto_##Name##Array),               \
   6314      JS_NULL_CLASS_OPS,                                             \
   6315      &TypedArrayObjectClassSpecs[Scalar::Type::Name],               \
   6316  },
   6317 
   6318    JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_PROTO_CLASS)
   6319 #undef IMPL_TYPED_ARRAY_PROTO_CLASS
   6320 };
   6321 
   6322 bool js::IsTypedArrayConstructor(const JSObject* obj) {
   6323 #define CHECK_TYPED_ARRAY_CONSTRUCTOR(_, T, N)                                 \
   6324  if (IsNativeFunction(obj, TypedArrayObjectTemplate<T>::class_constructor)) { \
   6325    return true;                                                               \
   6326  }
   6327  JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
   6328 #undef CHECK_TYPED_ARRAY_CONSTRUCTOR
   6329  return false;
   6330 }
   6331 
   6332 bool js::IsTypedArrayConstructor(HandleValue v, Scalar::Type type) {
   6333  return IsNativeFunction(v, TypedArrayConstructorNative(type));
   6334 }
   6335 
   6336 JSNative js::TypedArrayConstructorNative(Scalar::Type type) {
   6337 #define TYPED_ARRAY_CONSTRUCTOR_NATIVE(_, T, N)            \
   6338  if (type == Scalar::N) {                                 \
   6339    return TypedArrayObjectTemplate<T>::class_constructor; \
   6340  }
   6341  JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_CONSTRUCTOR_NATIVE)
   6342 #undef TYPED_ARRAY_CONSTRUCTOR_NATIVE
   6343 
   6344  MOZ_CRASH("unexpected typed array type");
   6345 }
   6346 
   6347 Scalar::Type js::TypedArrayConstructorType(const JSFunction* fun) {
   6348  if (!fun->isNativeFun()) {
   6349    return Scalar::MaxTypedArrayViewType;
   6350  }
   6351 
   6352 #define CHECK_TYPED_ARRAY_CONSTRUCTOR(_, T, N)                           \
   6353  if (fun->native() == TypedArrayObjectTemplate<T>::class_constructor) { \
   6354    return Scalar::N;                                                    \
   6355  }
   6356  JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
   6357 #undef CHECK_TYPED_ARRAY_CONSTRUCTOR
   6358 
   6359  return Scalar::MaxTypedArrayViewType;
   6360 }
   6361 
   6362 bool js::IsBufferSource(JSContext* cx, JSObject* object, bool allowShared,
   6363                        bool allowResizable, SharedMem<uint8_t*>* dataPointer,
   6364                        size_t* byteLength) {
   6365  if (object->is<TypedArrayObject>()) {
   6366    Rooted<TypedArrayObject*> view(cx, &object->as<TypedArrayObject>());
   6367    if (!allowShared && view->isSharedMemory()) {
   6368      return false;
   6369    }
   6370    if (!allowResizable && JS::IsResizableArrayBufferView(object)) {
   6371      return false;
   6372    }
   6373    // Ensure the pointer we pass out won't move as long as you properly root
   6374    // it. This is only needed for non-shared memory.
   6375    if (!view->isSharedMemory() &&
   6376        !ArrayBufferViewObject::ensureNonInline(cx, view)) {
   6377      return false;
   6378    }
   6379    *dataPointer = view->dataPointerEither().cast<uint8_t*>();
   6380    *byteLength = view->byteLength().valueOr(0);
   6381    return true;
   6382  }
   6383 
   6384  if (object->is<DataViewObject>()) {
   6385    Rooted<DataViewObject*> view(cx, &object->as<DataViewObject>());
   6386    if (!allowShared && view->isSharedMemory()) {
   6387      return false;
   6388    }
   6389    if (!allowResizable && JS::IsResizableArrayBufferView(object)) {
   6390      return false;
   6391    }
   6392    // Ensure the pointer we pass out won't move as long as you properly root
   6393    // it. This is only needed for non-shared memory.
   6394    if (!view->isSharedMemory() &&
   6395        !ArrayBufferViewObject::ensureNonInline(cx, view)) {
   6396      return false;
   6397    }
   6398    *dataPointer = view->dataPointerEither().cast<uint8_t*>();
   6399    *byteLength = view->byteLength().valueOr(0);
   6400    return true;
   6401  }
   6402 
   6403  if (object->is<ArrayBufferObject>()) {
   6404    Rooted<ArrayBufferObject*> buffer(cx, &object->as<ArrayBufferObject>());
   6405    if (!allowResizable && buffer->isResizable()) {
   6406      return false;
   6407    }
   6408    // Ensure the pointer we pass out won't move as long as you properly root
   6409    // it.
   6410    if (!ArrayBufferObject::ensureNonInline(cx, buffer)) {
   6411      return false;
   6412    }
   6413    *dataPointer = buffer->dataPointerEither();
   6414    *byteLength = buffer->byteLength();
   6415    return true;
   6416  }
   6417 
   6418  if (allowShared && object->is<SharedArrayBufferObject>()) {
   6419    SharedArrayBufferObject& buffer = object->as<SharedArrayBufferObject>();
   6420    if (!allowResizable && buffer.isResizable()) {
   6421      return false;
   6422    }
   6423    // This will always be locked and out of line.
   6424    *dataPointer = buffer.dataPointerShared();
   6425    *byteLength = buffer.byteLength();
   6426    return true;
   6427  }
   6428 
   6429  return false;
   6430 }
   6431 
   6432 template <typename CharT>
   6433 static inline bool StringIsInfinity(mozilla::Range<const CharT> s) {
   6434  static constexpr std::string_view Infinity = "Infinity";
   6435 
   6436  // Compilers optimize this to one |cmp| instruction on x64 resp. two for x86,
   6437  // when the input is a Latin-1 string, because the string "Infinity" is
   6438  // exactly eight characters long, so it can be represented as a single uint64
   6439  // value.
   6440  return s.length() == Infinity.length() &&
   6441         EqualChars(s.begin().get(), Infinity.data(), Infinity.length());
   6442 }
   6443 
   6444 template <typename CharT>
   6445 static inline bool StringIsNaN(mozilla::Range<const CharT> s) {
   6446  static constexpr std::string_view NaN = "NaN";
   6447 
   6448  // "NaN" is not as nicely optimizable as "Infinity", but oh well.
   6449  return s.length() == NaN.length() &&
   6450         EqualChars(s.begin().get(), NaN.data(), NaN.length());
   6451 }
   6452 
   6453 template <typename CharT>
   6454 static mozilla::Maybe<uint64_t> StringToTypedArrayIndexSlow(
   6455    mozilla::Range<const CharT> s) {
   6456  const mozilla::RangedPtr<const CharT> start = s.begin();
   6457  const mozilla::RangedPtr<const CharT> end = s.end();
   6458 
   6459  const CharT* actualEnd;
   6460  double result = js_strtod(start.get(), end.get(), &actualEnd);
   6461 
   6462  // The complete string must have been parsed.
   6463  if (actualEnd != end.get()) {
   6464    return mozilla::Nothing();
   6465  }
   6466 
   6467  // Now convert it back to a string.
   6468  ToCStringBuf cbuf;
   6469  size_t cstrlen;
   6470  const char* cstr = js::NumberToCString(&cbuf, result, &cstrlen);
   6471  MOZ_ASSERT(cstr);
   6472 
   6473  // Both strings must be equal for a canonical numeric index string.
   6474  if (s.length() != cstrlen || !EqualChars(start.get(), cstr, cstrlen)) {
   6475    return mozilla::Nothing();
   6476  }
   6477 
   6478  // Directly perform IsInteger() check and encode negative and non-integer
   6479  // indices as OOB.
   6480  // See 9.4.5.2 [[HasProperty]], steps 3.b.iii and 3.b.v.
   6481  // See 9.4.5.3 [[DefineOwnProperty]], steps 3.b.i and 3.b.iii.
   6482  // See 9.4.5.8 IntegerIndexedElementGet, steps 5 and 8.
   6483  // See 9.4.5.9 IntegerIndexedElementSet, steps 6 and 9.
   6484  if (result < 0 || !IsInteger(result)) {
   6485    return mozilla::Some(UINT64_MAX);
   6486  }
   6487 
   6488  // Anything equals-or-larger than 2^53 is definitely OOB, encode it
   6489  // accordingly so that the cast to uint64_t is well defined.
   6490  if (result >= DOUBLE_INTEGRAL_PRECISION_LIMIT) {
   6491    return mozilla::Some(UINT64_MAX);
   6492  }
   6493 
   6494  // The string is an actual canonical numeric index.
   6495  return mozilla::Some(result);
   6496 }
   6497 
   6498 template <typename CharT>
   6499 mozilla::Maybe<uint64_t> js::StringToTypedArrayIndex(
   6500    mozilla::Range<const CharT> s) {
   6501  mozilla::RangedPtr<const CharT> cp = s.begin();
   6502  const mozilla::RangedPtr<const CharT> end = s.end();
   6503 
   6504  MOZ_ASSERT(cp < end, "caller must check for empty strings");
   6505 
   6506  bool negative = false;
   6507  if (*cp == '-') {
   6508    negative = true;
   6509    if (++cp == end) {
   6510      return mozilla::Nothing();
   6511    }
   6512  }
   6513 
   6514  if (!IsAsciiDigit(*cp)) {
   6515    // Check for "NaN", "Infinity", or "-Infinity".
   6516    if ((!negative && StringIsNaN<CharT>({cp, end})) ||
   6517        StringIsInfinity<CharT>({cp, end})) {
   6518      return mozilla::Some(UINT64_MAX);
   6519    }
   6520    return mozilla::Nothing();
   6521  }
   6522 
   6523  uint32_t digit = AsciiDigitToNumber(*cp++);
   6524 
   6525  // Don't allow leading zeros.
   6526  if (digit == 0 && cp != end) {
   6527    // The string may be of the form "0.xyz". The exponent form isn't possible
   6528    // when the string starts with "0".
   6529    if (*cp == '.') {
   6530      return StringToTypedArrayIndexSlow(s);
   6531    }
   6532    return mozilla::Nothing();
   6533  }
   6534 
   6535  uint64_t index = digit;
   6536 
   6537  for (; cp < end; cp++) {
   6538    if (!IsAsciiDigit(*cp)) {
   6539      // Take the slow path when the string has fractional parts or an exponent.
   6540      if (*cp == '.' || *cp == 'e') {
   6541        return StringToTypedArrayIndexSlow(s);
   6542      }
   6543      return mozilla::Nothing();
   6544    }
   6545 
   6546    digit = AsciiDigitToNumber(*cp);
   6547 
   6548    static_assert(
   6549        uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT) < (UINT64_MAX - 10) / 10,
   6550        "2^53 is way below UINT64_MAX, so |10 * index + digit| can't overflow");
   6551 
   6552    index = 10 * index + digit;
   6553 
   6554    // Also take the slow path when the string is larger-or-equals 2^53.
   6555    if (index >= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)) {
   6556      return StringToTypedArrayIndexSlow(s);
   6557    }
   6558  }
   6559 
   6560  if (negative) {
   6561    return mozilla::Some(UINT64_MAX);
   6562  }
   6563  return mozilla::Some(index);
   6564 }
   6565 
   6566 template mozilla::Maybe<uint64_t> js::StringToTypedArrayIndex(
   6567    mozilla::Range<const char16_t> s);
   6568 
   6569 template mozilla::Maybe<uint64_t> js::StringToTypedArrayIndex(
   6570    mozilla::Range<const Latin1Char> s);
   6571 
   6572 bool js::SetTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj,
   6573                              uint64_t index, HandleValue v,
   6574                              ObjectOpResult& result) {
   6575  switch (obj->type()) {
   6576 #define SET_TYPED_ARRAY_ELEMENT(_, T, N) \
   6577  case Scalar::N:                        \
   6578    return TypedArrayObjectTemplate<T>::setElement(cx, obj, index, v, result);
   6579    JS_FOR_EACH_TYPED_ARRAY(SET_TYPED_ARRAY_ELEMENT)
   6580 #undef SET_TYPED_ARRAY_ELEMENT
   6581    case Scalar::MaxTypedArrayViewType:
   6582    case Scalar::Int64:
   6583    case Scalar::Simd128:
   6584      break;
   6585  }
   6586 
   6587  MOZ_CRASH("Unsupported TypedArray type");
   6588 }
   6589 
   6590 // ES2025 draft rev ac21460fedf4b926520b06c9820bdbebad596a8b
   6591 // 10.4.5.3 [[DefineOwnProperty]], step 1.b.
   6592 bool js::DefineTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj,
   6593                                 uint64_t index,
   6594                                 Handle<PropertyDescriptor> desc,
   6595                                 ObjectOpResult& result) {
   6596  // These are all substeps of 1.b.
   6597 
   6598  // Step i.
   6599  if (index >= obj->length().valueOr(0)) {
   6600    if (obj->hasDetachedBuffer()) {
   6601      return result.fail(JSMSG_TYPED_ARRAY_DETACHED);
   6602    }
   6603    return result.fail(JSMSG_DEFINE_BAD_INDEX);
   6604  }
   6605 
   6606  // TypedArray elements are modifiable unless the backing buffer is immutable.
   6607  bool modifiable = !obj->is<ImmutableTypedArrayObject>();
   6608 
   6609  // Step ii.
   6610  if (desc.hasConfigurable() && desc.configurable() != modifiable) {
   6611    return result.fail(JSMSG_CANT_REDEFINE_PROP);
   6612  }
   6613 
   6614  // Step iii.
   6615  if (desc.hasEnumerable() && !desc.enumerable()) {
   6616    return result.fail(JSMSG_CANT_REDEFINE_PROP);
   6617  }
   6618 
   6619  // Step iv.
   6620  if (desc.isAccessorDescriptor()) {
   6621    return result.fail(JSMSG_CANT_REDEFINE_PROP);
   6622  }
   6623 
   6624  // Step v.
   6625  if (desc.hasWritable() && desc.writable() != modifiable) {
   6626    return result.fail(JSMSG_CANT_REDEFINE_PROP);
   6627  }
   6628 
   6629  // Step vi.
   6630  if (desc.hasValue()) {
   6631    if (modifiable) {
   6632      return SetTypedArrayElement(cx, obj, index, desc.value(), result);
   6633    }
   6634 
   6635    Rooted<Value> currentValue(cx);
   6636    if (!obj->getElement<CanGC>(cx, index, &currentValue)) {
   6637      return false;
   6638    }
   6639 
   6640    bool sameValue;
   6641    if (!SameValue(cx, desc.value(), currentValue, &sameValue)) {
   6642      return false;
   6643    }
   6644 
   6645    if (!sameValue) {
   6646      return result.fail(JSMSG_CANT_REDEFINE_PROP);
   6647    }
   6648  }
   6649 
   6650  // Step vii.
   6651  return result.succeed();
   6652 }
   6653 
   6654 template <typename T, typename U>
   6655 static constexpr typename std::enable_if_t<
   6656    std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed, U>
   6657 UnsignedSortValue(U val) {
   6658  return val;
   6659 }
   6660 
   6661 template <typename T, typename U>
   6662 static constexpr typename std::enable_if_t<
   6663    std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed, U>
   6664 UnsignedSortValue(U val) {
   6665  // Flip sign bit.
   6666  return val ^ static_cast<U>(std::numeric_limits<T>::min());
   6667 }
   6668 
   6669 template <typename T, typename UnsignedT>
   6670 static constexpr
   6671    typename std::enable_if_t<!std::numeric_limits<T>::is_integer, UnsignedT>
   6672    UnsignedSortValue(UnsignedT val) {
   6673  // Flip sign bit for positive numbers; flip all bits for negative numbers,
   6674  // except negative NaNs.
   6675  using FloatingPoint = mozilla::FloatingPoint<T>;
   6676  static_assert(std::is_same_v<typename FloatingPoint::Bits, UnsignedT>,
   6677                "FloatingPoint::Bits matches the unsigned int representation");
   6678 
   6679  // FF80'0000 is negative infinity, (FF80'0000, FFFF'FFFF] are all NaNs with
   6680  // the sign-bit set (and the equivalent holds for double and float16 values).
   6681  // So any value larger than negative infinity is a negative NaN.
   6682  constexpr UnsignedT NegativeInfinity = mozilla::InfinityBits<T, 1>::value;
   6683  if (val > NegativeInfinity) {
   6684    return val;
   6685  }
   6686  if (val & FloatingPoint::kSignBit) {
   6687    return ~val;
   6688  }
   6689  return val ^ FloatingPoint::kSignBit;
   6690 }
   6691 
   6692 template <typename T, typename U>
   6693 static constexpr
   6694    typename std::enable_if_t<std::numeric_limits<T>::is_integer, U>
   6695    ToCountingSortKey(U val) {
   6696  return UnsignedSortValue<T, U>(val);
   6697 }
   6698 
   6699 template <typename T, typename U>
   6700 static constexpr
   6701    typename std::enable_if_t<std::numeric_limits<T>::is_integer, U>
   6702    FromCountingSortKey(U val) {
   6703  // ToCountingSortKey for integers is a self-inverse function.
   6704  return ToCountingSortKey<T, U>(val);
   6705 }
   6706 
   6707 /**
   6708 * UnsignedSortValue for floating point values isn't reversible, so we use a
   6709 * different implementation for counting sort to keep NaN payloads unmodified
   6710 * for consistency with TypedArrayRadixSort and TypedArrayStdSort.
   6711 *
   6712 * Mapping overview:
   6713 * - Largest -NaN      = FFFF -> FFFF
   6714 * - Smallest -NaN     = FC01 -> FC01
   6715 * - Negative infinity = FC00 -> 0000
   6716 * - Negative zero     = 8000 -> 7C00
   6717 * - Positive zero     = 0000 -> 7C01
   6718 * - Positive infinity = 7C00 -> F801
   6719 * - Smallest +NaN     = 7C01 -> F802
   6720 * - Largest +NaN      = 7FFF -> FC00
   6721 */
   6722 template <typename T, typename U>
   6723 static constexpr typename std::enable_if_t<std::is_same_v<T, js::float16>, U>
   6724 ToCountingSortKey(U val) {
   6725  using FloatingPoint = mozilla::FloatingPoint<T>;
   6726  static_assert(std::is_same_v<typename FloatingPoint::Bits, U>,
   6727                "FloatingPoint::Bits matches the unsigned int representation");
   6728 
   6729  constexpr U PositiveInfinity = mozilla::InfinityBits<T, 0>::value;
   6730  constexpr U NegativeInfinity = mozilla::InfinityBits<T, 1>::value;
   6731 
   6732  // Any value larger than negative infinity is a negative NaN. Place those at
   6733  // the very end.
   6734  if (val > NegativeInfinity) {
   6735    return val;
   6736  }
   6737 
   6738  // Map negative values, starting at negative infinity which is mapped to zero.
   6739  if (val & FloatingPoint::kSignBit) {
   6740    return NegativeInfinity - val;
   6741  }
   6742 
   6743  // Map positive values right after the last negative value (negative zero).
   6744  return val + (PositiveInfinity + 1);
   6745 }
   6746 
   6747 /**
   6748 * Reverse the mapping from ToCountingSortKey.
   6749 */
   6750 template <typename T, typename U>
   6751 static constexpr typename std::enable_if_t<std::is_same_v<T, js::float16>, U>
   6752 FromCountingSortKey(U val) {
   6753  using FloatingPoint = mozilla::FloatingPoint<T>;
   6754  static_assert(std::is_same_v<typename FloatingPoint::Bits, U>,
   6755                "FloatingPoint::Bits matches the unsigned int representation");
   6756 
   6757  constexpr U PositiveInfinity = mozilla::InfinityBits<T, 0>::value;
   6758  constexpr U NegativeInfinity = mozilla::InfinityBits<T, 1>::value;
   6759 
   6760  // Negative NaN are unchanged.
   6761  if (val > NegativeInfinity) {
   6762    return val;
   6763  }
   6764 
   6765  // Any value larger than 0x7C00 was a positive number, including positive NaN.
   6766  if (val > PositiveInfinity) {
   6767    return val - (PositiveInfinity + 1);
   6768  }
   6769 
   6770  // Any other value was a negative number, excluding negative NaN.
   6771  return NegativeInfinity - val;
   6772 }
   6773 
   6774 template <typename T>
   6775 static typename std::enable_if_t<std::numeric_limits<T>::is_integer>
   6776 TypedArrayStdSort(SharedMem<void*> data, size_t length) {
   6777  T* unwrapped = data.cast<T*>().unwrapUnshared();
   6778  std::sort(unwrapped, unwrapped + length);
   6779 }
   6780 
   6781 template <typename T>
   6782 static typename std::enable_if_t<!std::numeric_limits<T>::is_integer>
   6783 TypedArrayStdSort(SharedMem<void*> data, size_t length) {
   6784  // Sort on the unsigned representation for performance reasons.
   6785  using UnsignedT =
   6786      typename mozilla::UnsignedStdintTypeForSize<sizeof(T)>::Type;
   6787  UnsignedT* unwrapped = data.cast<UnsignedT*>().unwrapUnshared();
   6788  std::sort(unwrapped, unwrapped + length, [](UnsignedT x, UnsignedT y) {
   6789    constexpr auto SortValue = UnsignedSortValue<T, UnsignedT>;
   6790    return SortValue(x) < SortValue(y);
   6791  });
   6792 }
   6793 
   6794 template <typename T, typename Ops>
   6795 static typename std::enable_if_t<std::is_same_v<Ops, UnsharedOps>, bool>
   6796 TypedArrayStdSort(JSContext* cx, TypedArrayObject* typedArray, size_t length) {
   6797  TypedArrayStdSort<T>(typedArray->dataPointerEither(), length);
   6798  return true;
   6799 }
   6800 
   6801 template <typename T, typename Ops>
   6802 static typename std::enable_if_t<std::is_same_v<Ops, SharedOps>, bool>
   6803 TypedArrayStdSort(JSContext* cx, TypedArrayObject* typedArray, size_t length) {
   6804  // Always create a copy when sorting shared memory backed typed arrays to
   6805  // ensure concurrent write accesses doesn't lead to UB when calling std::sort.
   6806  auto ptr = cx->make_pod_array<T>(length);
   6807  if (!ptr) {
   6808    return false;
   6809  }
   6810  SharedMem<T*> unshared = SharedMem<T*>::unshared(ptr.get());
   6811  SharedMem<T*> data = typedArray->dataPointerShared().cast<T*>();
   6812 
   6813  Ops::podCopy(unshared, data, length);
   6814 
   6815  TypedArrayStdSort<T>(unshared.template cast<void*>(), length);
   6816 
   6817  Ops::podCopy(data, unshared, length);
   6818 
   6819  return true;
   6820 }
   6821 
   6822 template <typename T, typename Ops>
   6823 static bool TypedArrayCountingSort(JSContext* cx, TypedArrayObject* typedArray,
   6824                                   size_t length) {
   6825  // Determined by performance testing.
   6826  if (length <= 64) {
   6827    return TypedArrayStdSort<T, Ops>(cx, typedArray, length);
   6828  }
   6829 
   6830  // Map signed values onto the unsigned range when storing in buffer.
   6831  using UnsignedT =
   6832      typename mozilla::UnsignedStdintTypeForSize<sizeof(T)>::Type;
   6833 
   6834  constexpr size_t InlineStorage = sizeof(T) == 1 ? 256 : 0;
   6835  Vector<size_t, InlineStorage> buffer(cx);
   6836  if (!buffer.resize(size_t(std::numeric_limits<UnsignedT>::max()) + 1)) {
   6837    return false;
   6838  }
   6839 
   6840  SharedMem<UnsignedT*> data =
   6841      typedArray->dataPointerEither().cast<UnsignedT*>();
   6842 
   6843  // Populate the buffer.
   6844  for (size_t i = 0; i < length; i++) {
   6845    UnsignedT val = ToCountingSortKey<T, UnsignedT>(Ops::load(data + i));
   6846    buffer[val]++;
   6847  }
   6848 
   6849  // Traverse the buffer in order and write back elements to array.
   6850  UnsignedT val = UnsignedT(-1);  // intentional overflow on first increment
   6851  for (size_t i = 0; i < length;) {
   6852    size_t j;
   6853    do {
   6854      j = buffer[++val];
   6855    } while (j == 0);
   6856 
   6857    // Invariant: sum(buffer[val:]) == length-i
   6858    MOZ_ASSERT(j <= length - i);
   6859 
   6860    for (; j > 0; j--) {
   6861      Ops::store(data + i++, FromCountingSortKey<T, UnsignedT>(val));
   6862    }
   6863  }
   6864 
   6865  return true;
   6866 }
   6867 
   6868 template <typename T, typename U, typename Ops>
   6869 static void SortByColumn(SharedMem<U*> data, size_t length, SharedMem<U*> aux,
   6870                         uint8_t col) {
   6871  static_assert(std::is_unsigned_v<U>, "SortByColumn sorts on unsigned values");
   6872  static_assert(std::is_same_v<Ops, UnsharedOps>,
   6873                "SortByColumn only works on unshared data");
   6874 
   6875  // |counts| is used to compute the starting index position for each key.
   6876  // Letting counts[0] always be 0, simplifies the transform step below.
   6877  // Example:
   6878  //
   6879  // Computing frequency counts for the input [1 2 1] gives:
   6880  //      0 1 2 3 ... (keys)
   6881  //      0 0 2 1     (frequencies)
   6882  //
   6883  // Transforming frequencies to indexes gives:
   6884  //      0 1 2 3 ... (keys)
   6885  //      0 0 2 3     (indexes)
   6886 
   6887  constexpr size_t R = 256;
   6888 
   6889  // Initialize all entries to zero.
   6890  size_t counts[R + 1] = {};
   6891 
   6892  const auto ByteAtCol = [col](U x) {
   6893    U y = UnsignedSortValue<T, U>(x);
   6894    return static_cast<uint8_t>(y >> (col * 8));
   6895  };
   6896 
   6897  // Compute frequency counts.
   6898  for (size_t i = 0; i < length; i++) {
   6899    U val = Ops::load(data + i);
   6900    uint8_t b = ByteAtCol(val);
   6901    counts[b + 1]++;
   6902  }
   6903 
   6904  // Transform counts to indices.
   6905  std::partial_sum(std::begin(counts), std::end(counts), std::begin(counts));
   6906 
   6907  // Distribute
   6908  for (size_t i = 0; i < length; i++) {
   6909    U val = Ops::load(data + i);
   6910    uint8_t b = ByteAtCol(val);
   6911    size_t j = counts[b]++;
   6912    MOZ_ASSERT(j < length,
   6913               "index is in bounds when |data| can't be modified concurrently");
   6914    UnsharedOps::store(aux + j, val);
   6915  }
   6916 
   6917  // Copy back
   6918  Ops::podCopy(data, aux, length);
   6919 }
   6920 
   6921 template <typename T, typename Ops>
   6922 static bool TypedArrayRadixSort(JSContext* cx, TypedArrayObject* typedArray,
   6923                                size_t length) {
   6924  // Determined by performance testing.
   6925  constexpr size_t StdSortMinCutoff = sizeof(T) == 2 ? 64 : 256;
   6926 
   6927  // Radix sort uses O(n) additional space, limit this space to 64 MB.
   6928  constexpr size_t StdSortMaxCutoff = (64 * 1024 * 1024) / sizeof(T);
   6929 
   6930  if constexpr (sizeof(T) == 2) {
   6931    // Radix sort uses |n * sizeof(T)| additional space, whereas counting sort
   6932    // uses |65536 * sizeof(size_t)| additional space. When the additional
   6933    // space needed for radix sort exceeds the space needed for counting sort,
   6934    // switch over to counting sort.
   6935    //
   6936    // It's faster to switch slightly earlier, so subtract a constant from the
   6937    // exact value. This constant (2048) was determined by performance testing.
   6938    constexpr size_t CountingSortMaxCutoff =
   6939        65536 * (sizeof(size_t) / sizeof(T)) - 2048;
   6940    static_assert(CountingSortMaxCutoff < StdSortMaxCutoff);
   6941 
   6942    if (length >= CountingSortMaxCutoff) {
   6943      return TypedArrayCountingSort<T, Ops>(cx, typedArray, length);
   6944    }
   6945  }
   6946 
   6947  if (length <= StdSortMinCutoff || length >= StdSortMaxCutoff) {
   6948    return TypedArrayStdSort<T, Ops>(cx, typedArray, length);
   6949  }
   6950 
   6951  using UnsignedT =
   6952      typename mozilla::UnsignedStdintTypeForSize<sizeof(T)>::Type;
   6953 
   6954  auto ptr = cx->make_zeroed_pod_array<UnsignedT>(length);
   6955  if (!ptr) {
   6956    return false;
   6957  }
   6958  SharedMem<UnsignedT*> aux = SharedMem<UnsignedT*>::unshared(ptr.get());
   6959 
   6960  SharedMem<UnsignedT*> data =
   6961      typedArray->dataPointerEither().cast<UnsignedT*>();
   6962 
   6963  // Always create a copy when sorting shared memory backed typed arrays to
   6964  // ensure concurrent write accesses don't lead to computing bad indices.
   6965  SharedMem<UnsignedT*> unshared;
   6966  SharedMem<UnsignedT*> shared;
   6967  UniquePtr<UnsignedT[], JS::FreePolicy> ptrUnshared;
   6968  if constexpr (std::is_same_v<Ops, SharedOps>) {
   6969    ptrUnshared = cx->make_pod_array<UnsignedT>(length);
   6970    if (!ptrUnshared) {
   6971      return false;
   6972    }
   6973    unshared = SharedMem<UnsignedT*>::unshared(ptrUnshared.get());
   6974    shared = data;
   6975 
   6976    Ops::podCopy(unshared, shared, length);
   6977 
   6978    data = unshared;
   6979  }
   6980 
   6981  for (uint8_t col = 0; col < sizeof(UnsignedT); col++) {
   6982    SortByColumn<T, UnsignedT, UnsharedOps>(data, length, aux, col);
   6983  }
   6984 
   6985  if constexpr (std::is_same_v<Ops, SharedOps>) {
   6986    Ops::podCopy(shared, unshared, length);
   6987  }
   6988 
   6989  return true;
   6990 }
   6991 
   6992 using TypedArraySortFn = bool (*)(JSContext*, TypedArrayObject*, size_t length);
   6993 
   6994 template <typename T, typename Ops>
   6995 static constexpr typename std::enable_if_t<sizeof(T) == 1, TypedArraySortFn>
   6996 TypedArraySort() {
   6997  return TypedArrayCountingSort<T, Ops>;
   6998 }
   6999 
   7000 template <typename T, typename Ops>
   7001 static constexpr typename std::enable_if_t<sizeof(T) == 2 || sizeof(T) == 4,
   7002                                           TypedArraySortFn>
   7003 TypedArraySort() {
   7004  return TypedArrayRadixSort<T, Ops>;
   7005 }
   7006 
   7007 template <typename T, typename Ops>
   7008 static constexpr typename std::enable_if_t<sizeof(T) == 8, TypedArraySortFn>
   7009 TypedArraySort() {
   7010  return TypedArrayStdSort<T, Ops>;
   7011 }
   7012 
   7013 static bool TypedArraySortWithoutComparator(JSContext* cx,
   7014                                            TypedArrayObject* typedArray,
   7015                                            size_t len) {
   7016  bool isShared = typedArray->isSharedMemory();
   7017  switch (typedArray->type()) {
   7018 #define SORT(_, T, N)                                               \
   7019  case Scalar::N:                                                   \
   7020    if (isShared) {                                                 \
   7021      if (!TypedArraySort<T, SharedOps>()(cx, typedArray, len)) {   \
   7022        return false;                                               \
   7023      }                                                             \
   7024    } else {                                                        \
   7025      if (!TypedArraySort<T, UnsharedOps>()(cx, typedArray, len)) { \
   7026        return false;                                               \
   7027      }                                                             \
   7028    }                                                               \
   7029    break;
   7030    JS_FOR_EACH_TYPED_ARRAY(SORT)
   7031 #undef SORT
   7032    default:
   7033      MOZ_CRASH("Unsupported TypedArray type");
   7034  }
   7035  return true;
   7036 }
   7037 
   7038 static MOZ_ALWAYS_INLINE bool TypedArraySortPrologue(JSContext* cx,
   7039                                                     Handle<Value> thisv,
   7040                                                     Handle<Value> comparefn,
   7041                                                     ArraySortData* d,
   7042                                                     bool* done) {
   7043  // https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort
   7044  // 23.2.3.29 %TypedArray%.prototype.sort ( comparefn )
   7045 
   7046  // Step 1.
   7047  if (MOZ_UNLIKELY(!comparefn.isUndefined() && !IsCallable(comparefn))) {
   7048    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   7049                              JSMSG_BAD_TYPEDARRAY_SORT_ARG);
   7050    return false;
   7051  }
   7052 
   7053  // Steps 2-3.
   7054  Rooted<TypedArrayObject*> tarrayUnwrapped(
   7055      cx, UnwrapAndTypeCheckValue<TypedArrayObject>(cx, thisv, [cx, &thisv]() {
   7056        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   7057                                 JSMSG_INCOMPATIBLE_METHOD, "sort", "method",
   7058                                 InformalValueTypeName(thisv));
   7059      }));
   7060  if (!tarrayUnwrapped) {
   7061    return false;
   7062  }
   7063  auto arrayLength = tarrayUnwrapped->length();
   7064  if (!arrayLength) {
   7065    ReportOutOfBounds(cx, tarrayUnwrapped);
   7066    return false;
   7067  }
   7068 
   7069  // Additional step from Immutable ArrayBuffer proposal.
   7070  if (tarrayUnwrapped->is<ImmutableTypedArrayObject>()) {
   7071    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   7072                              JSMSG_ARRAYBUFFER_IMMUTABLE);
   7073    return false;
   7074  }
   7075 
   7076  // Step 4.
   7077  size_t len = *arrayLength;
   7078 
   7079  // Arrays with less than two elements remain unchanged when sorted.
   7080  if (len <= 1) {
   7081    d->setReturnValue(&thisv.toObject());
   7082    *done = true;
   7083    return true;
   7084  }
   7085 
   7086  // Fast path for sorting without a comparator.
   7087  if (comparefn.isUndefined()) {
   7088    if (!TypedArraySortWithoutComparator(cx, tarrayUnwrapped, len)) {
   7089      return false;
   7090    }
   7091    d->setReturnValue(&thisv.toObject());
   7092    *done = true;
   7093    return true;
   7094  }
   7095 
   7096  // Ensure length * 2 (used below) doesn't overflow UINT32_MAX.
   7097  if (MOZ_UNLIKELY(len > UINT32_MAX / 2)) {
   7098    ReportAllocationOverflow(cx);
   7099    return false;
   7100  }
   7101 
   7102  // Merge sort requires extra scratch space.
   7103  bool needsScratchSpace = len > ArraySortData::InsertionSortMaxLength;
   7104 
   7105  Rooted<ArraySortData::ValueVector> vec(cx);
   7106  if (MOZ_UNLIKELY(!vec.resize(needsScratchSpace ? (2 * len) : len))) {
   7107    ReportOutOfMemory(cx);
   7108    return false;
   7109  }
   7110 
   7111  // Copy elements to JS Value vector.
   7112  if (!TypedArrayObject::getElements(cx, tarrayUnwrapped, len, vec.begin())) {
   7113    return false;
   7114  }
   7115 
   7116  d->init(&thisv.toObject(), &comparefn.toObject(), std::move(vec.get()), len,
   7117          len);
   7118 
   7119  // Continue in ArraySortData::sortTypedArrayWithComparator.
   7120  MOZ_ASSERT(!*done);
   7121  return true;
   7122 }
   7123 
   7124 // Copies sorted elements back to the typed array.
   7125 template <typename T, typename Ops>
   7126 static void StoreSortedElements(TypedArrayObject* tarray, Value* elements,
   7127                                size_t len) {
   7128  SharedMem<T*> data = tarray->dataPointerEither().cast<T*>();
   7129  for (size_t i = 0; i < len; i++) {
   7130    T val;
   7131    if constexpr (!std::numeric_limits<T>::is_integer) {
   7132      val = elements[i].toDouble();
   7133    } else if constexpr (std::is_same_v<T, int64_t>) {
   7134      val = BigInt::toInt64(elements[i].toBigInt());
   7135    } else if constexpr (std::is_same_v<T, uint64_t>) {
   7136      val = BigInt::toUint64(elements[i].toBigInt());
   7137    } else if constexpr (std::is_same_v<T, uint32_t>) {
   7138      val = uint32_t(elements[i].toNumber());
   7139    } else {
   7140      val = elements[i].toInt32();
   7141    }
   7142    Ops::store(data + i, val);
   7143  }
   7144 }
   7145 
   7146 // static
   7147 ArraySortResult ArraySortData::sortTypedArrayWithComparator(ArraySortData* d) {
   7148  ArraySortResult result =
   7149      sortWithComparatorShared<ArraySortKind::TypedArray>(d);
   7150  if (result != ArraySortResult::Done) {
   7151    return result;
   7152  }
   7153 
   7154  // Copy sorted elements to the typed array.
   7155  JSContext* cx = d->cx();
   7156  Rooted<TypedArrayObject*> tarrayUnwrapped(
   7157      cx, UnwrapAndDowncastObject<TypedArrayObject>(cx, d->obj_));
   7158  if (MOZ_UNLIKELY(!tarrayUnwrapped)) {
   7159    return ArraySortResult::Failure;
   7160  }
   7161 
   7162  auto length = tarrayUnwrapped->length();
   7163  if (MOZ_LIKELY(length)) {
   7164    size_t len = std::min<size_t>(*length, d->denseLen);
   7165    Value* elements = d->list;
   7166    bool isShared = tarrayUnwrapped->isSharedMemory();
   7167    switch (tarrayUnwrapped->type()) {
   7168 #define SORT(_, T, N)                                                      \
   7169  case Scalar::N:                                                          \
   7170    if (isShared) {                                                        \
   7171      StoreSortedElements<T, SharedOps>(tarrayUnwrapped, elements, len);   \
   7172    } else {                                                               \
   7173      StoreSortedElements<T, UnsharedOps>(tarrayUnwrapped, elements, len); \
   7174    }                                                                      \
   7175    break;
   7176      JS_FOR_EACH_TYPED_ARRAY(SORT)
   7177 #undef SORT
   7178      default:
   7179        MOZ_CRASH("Unsupported TypedArray type");
   7180    }
   7181  }
   7182 
   7183  d->freeMallocData();
   7184  d->setReturnValue(d->obj_);
   7185  return ArraySortResult::Done;
   7186 }
   7187 
   7188 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort
   7189 // 23.2.3.29 %TypedArray%.prototype.sort ( comparefn )
   7190 // static
   7191 bool TypedArrayObject::sort(JSContext* cx, unsigned argc, Value* vp) {
   7192  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype", "sort");
   7193  CallArgs args = CallArgsFromVp(argc, vp);
   7194 
   7195  // If we have a comparator argument, use the JIT trampoline implementation
   7196  // instead. This avoids a performance cliff (especially with large arrays)
   7197  // because C++ => JIT calls are much slower than Trampoline => JIT calls.
   7198  if (args.hasDefined(0) && jit::IsBaselineInterpreterEnabled()) {
   7199    return CallTrampolineNativeJitCode(
   7200        cx, jit::TrampolineNative::TypedArraySort, args);
   7201  }
   7202 
   7203  Rooted<ArraySortData> data(cx, cx);
   7204 
   7205  // On all return paths other than ArraySortData::sortTypedArrayWithComparator
   7206  // returning Done, we call freeMallocData to not fail debug assertions. This
   7207  // matches the JIT trampoline where we can't rely on C++ destructors.
   7208  auto freeData =
   7209      mozilla::MakeScopeExit([&]() { data.get().freeMallocData(); });
   7210 
   7211  bool done = false;
   7212  if (!TypedArraySortPrologue(cx, args.thisv(), args.get(0), data.address(),
   7213                              &done)) {
   7214    return false;
   7215  }
   7216  if (done) {
   7217    args.rval().set(data.get().returnValue());
   7218    return true;
   7219  }
   7220 
   7221  FixedInvokeArgs<2> callArgs(cx);
   7222  Rooted<Value> rval(cx);
   7223 
   7224  while (true) {
   7225    ArraySortResult res =
   7226        ArraySortData::sortTypedArrayWithComparator(data.address());
   7227    switch (res) {
   7228      case ArraySortResult::Failure:
   7229        return false;
   7230 
   7231      case ArraySortResult::Done:
   7232        freeData.release();
   7233        args.rval().set(data.get().returnValue());
   7234        return true;
   7235 
   7236      case ArraySortResult::CallJS:
   7237      case ArraySortResult::CallJSSameRealmNoUnderflow:
   7238        MOZ_ASSERT(data.get().comparatorThisValue().isUndefined());
   7239        MOZ_ASSERT(&args[0].toObject() == data.get().comparator());
   7240        callArgs[0].set(data.get().comparatorArg(0));
   7241        callArgs[1].set(data.get().comparatorArg(1));
   7242        if (!js::Call(cx, args[0], UndefinedHandleValue, callArgs, &rval)) {
   7243          return false;
   7244        }
   7245        data.get().setComparatorReturnValue(rval);
   7246        break;
   7247    }
   7248  }
   7249 }
   7250 
   7251 ArraySortResult js::TypedArraySortFromJit(
   7252    JSContext* cx, jit::TrampolineNativeFrameLayout* frame) {
   7253  AutoJSMethodProfilerEntry pseudoFrame(cx, "[TypedArray].prototype", "sort");
   7254  // Initialize the ArraySortData class stored in the trampoline frame.
   7255  void* dataUninit = frame->getFrameData<ArraySortData>();
   7256  auto* data = new (dataUninit) ArraySortData(cx);
   7257 
   7258  Rooted<Value> thisv(cx, frame->thisv());
   7259  Rooted<Value> comparefn(cx);
   7260  if (frame->numActualArgs() > 0) {
   7261    comparefn = frame->actualArgs()[0];
   7262  }
   7263 
   7264  bool done = false;
   7265  if (!TypedArraySortPrologue(cx, thisv, comparefn, data, &done)) {
   7266    return ArraySortResult::Failure;
   7267  }
   7268  if (done) {
   7269    data->freeMallocData();
   7270    return ArraySortResult::Done;
   7271  }
   7272 
   7273  return ArraySortData::sortTypedArrayWithComparator(data);
   7274 }
   7275 
   7276 /* JS Public API */
   7277 
   7278 #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(ExternalType, NativeType, Name)   \
   7279  JS_PUBLIC_API JSObject* JS_New##Name##Array(JSContext* cx,                  \
   7280                                              size_t nelements) {             \
   7281    return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements);   \
   7282  }                                                                           \
   7283                                                                              \
   7284  JS_PUBLIC_API JSObject* JS_New##Name##ArrayFromArray(JSContext* cx,         \
   7285                                                       HandleObject other) {  \
   7286    return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other);        \
   7287  }                                                                           \
   7288                                                                              \
   7289  JS_PUBLIC_API JSObject* JS_New##Name##ArrayWithBuffer(                      \
   7290      JSContext* cx, HandleObject arrayBuffer, size_t byteOffset,             \
   7291      int64_t length) {                                                       \
   7292    return TypedArrayObjectTemplate<NativeType>::fromBuffer(                  \
   7293        cx, arrayBuffer, byteOffset, length);                                 \
   7294  }                                                                           \
   7295                                                                              \
   7296  JS_PUBLIC_API JSObject* js::Unwrap##Name##Array(JSObject* obj) {            \
   7297    obj = obj->maybeUnwrapIf<TypedArrayObject>();                             \
   7298    if (!obj) {                                                               \
   7299      return nullptr;                                                         \
   7300    }                                                                         \
   7301    const JSClass* clasp = obj->getClass();                                   \
   7302    if (clasp != FixedLengthTypedArrayObjectTemplate<                         \
   7303                     NativeType>::instanceClass() &&                          \
   7304        clasp !=                                                              \
   7305            ResizableTypedArrayObjectTemplate<NativeType>::instanceClass() && \
   7306        clasp !=                                                              \
   7307            ImmutableTypedArrayObjectTemplate<NativeType>::instanceClass()) { \
   7308      return nullptr;                                                         \
   7309    }                                                                         \
   7310    return obj;                                                               \
   7311  }                                                                           \
   7312                                                                              \
   7313  JS_PUBLIC_API ExternalType* JS_Get##Name##ArrayLengthAndData(               \
   7314      JSObject* obj, size_t* length, bool* isSharedMemory,                    \
   7315      const JS::AutoRequireNoGC& nogc) {                                      \
   7316    TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>();          \
   7317    if (!tarr) {                                                              \
   7318      return nullptr;                                                         \
   7319    }                                                                         \
   7320    mozilla::Span<ExternalType> span =                                        \
   7321        JS::TypedArray<JS::Scalar::Name>::fromObject(tarr).getData(           \
   7322            isSharedMemory, nogc);                                            \
   7323    *length = span.Length();                                                  \
   7324    return span.data();                                                       \
   7325  }                                                                           \
   7326                                                                              \
   7327  JS_PUBLIC_API ExternalType* JS_Get##Name##ArrayData(                        \
   7328      JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC& nogc) { \
   7329    size_t length;                                                            \
   7330    return JS_Get##Name##ArrayLengthAndData(obj, &length, isSharedMemory,     \
   7331                                            nogc);                            \
   7332  }                                                                           \
   7333  JS_PUBLIC_API JSObject* JS_GetObjectAs##Name##Array(                        \
   7334      JSObject* obj, size_t* length, bool* isShared, ExternalType** data) {   \
   7335    obj = js::Unwrap##Name##Array(obj);                                       \
   7336    if (!obj) {                                                               \
   7337      return nullptr;                                                         \
   7338    }                                                                         \
   7339    TypedArrayObject* tarr = &obj->as<TypedArrayObject>();                    \
   7340    *length = tarr->length().valueOr(0);                                      \
   7341    *isShared = tarr->isSharedMemory();                                       \
   7342    *data = static_cast<ExternalType*>(tarr->dataPointerEither().unwrap(      \
   7343        /*safe - caller sees isShared flag*/));                               \
   7344    return obj;                                                               \
   7345  }
   7346 
   7347 JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS)
   7348 #undef IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS
   7349 
   7350 JS_PUBLIC_API bool JS_IsTypedArrayObject(JSObject* obj) {
   7351  return obj->canUnwrapAs<TypedArrayObject>();
   7352 }
   7353 
   7354 JS_PUBLIC_API size_t JS_GetTypedArrayLength(JSObject* obj) {
   7355  TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>();
   7356  if (!tarr) {
   7357    return 0;
   7358  }
   7359  return tarr->length().valueOr(0);
   7360 }
   7361 
   7362 JS_PUBLIC_API size_t JS_GetTypedArrayByteOffset(JSObject* obj) {
   7363  TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>();
   7364  if (!tarr) {
   7365    return 0;
   7366  }
   7367  return tarr->byteOffset().valueOr(0);
   7368 }
   7369 
   7370 JS_PUBLIC_API size_t JS_GetTypedArrayByteLength(JSObject* obj) {
   7371  TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>();
   7372  if (!tarr) {
   7373    return 0;
   7374  }
   7375  return tarr->byteLength().valueOr(0);
   7376 }
   7377 
   7378 JS_PUBLIC_API bool JS_GetTypedArraySharedness(JSObject* obj) {
   7379  TypedArrayObject* tarr = obj->maybeUnwrapAs<TypedArrayObject>();
   7380  if (!tarr) {
   7381    return false;
   7382  }
   7383  return tarr->isSharedMemory();
   7384 }
   7385 
   7386 JS_PUBLIC_API JS::Scalar::Type JS_GetArrayBufferViewType(JSObject* obj) {
   7387  ArrayBufferViewObject* view = obj->maybeUnwrapAs<ArrayBufferViewObject>();
   7388  if (!view) {
   7389    return Scalar::MaxTypedArrayViewType;
   7390  }
   7391 
   7392  if (view->is<TypedArrayObject>()) {
   7393    return view->as<TypedArrayObject>().type();
   7394  }
   7395  if (view->is<DataViewObject>()) {
   7396    return Scalar::MaxTypedArrayViewType;
   7397  }
   7398  MOZ_CRASH("invalid ArrayBufferView type");
   7399 }
   7400 
   7401 JS_PUBLIC_API size_t JS_MaxMovableTypedArraySize() {
   7402  return FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT;
   7403 }
   7404 
   7405 namespace JS {
   7406 
   7407 const JSClass* const TypedArray_base::fixedLengthClasses =
   7408    TypedArrayObject::fixedLengthClasses;
   7409 const JSClass* const TypedArray_base::immutableClasses =
   7410    TypedArrayObject::immutableClasses;
   7411 const JSClass* const TypedArray_base::resizableClasses =
   7412    TypedArrayObject::resizableClasses;
   7413 
   7414 #define INSTANTIATE(ExternalType, NativeType, Name) \
   7415  template class TypedArray<JS::Scalar::Name>;
   7416 JS_FOR_EACH_TYPED_ARRAY(INSTANTIATE)
   7417 #undef INSTANTIATE
   7418 
   7419 JS::ArrayBufferOrView JS::ArrayBufferOrView::unwrap(JSObject* maybeWrapped) {
   7420  if (!maybeWrapped) {
   7421    return JS::ArrayBufferOrView(nullptr);
   7422  }
   7423  auto* ab = maybeWrapped->maybeUnwrapIf<ArrayBufferObjectMaybeShared>();
   7424  if (ab) {
   7425    return ArrayBufferOrView::fromObject(ab);
   7426  }
   7427 
   7428  return ArrayBufferView::unwrap(maybeWrapped);
   7429 }
   7430 
   7431 bool JS::ArrayBufferOrView::isDetached() const {
   7432  MOZ_ASSERT(obj);
   7433  if (obj->is<ArrayBufferObjectMaybeShared>()) {
   7434    return obj->as<ArrayBufferObjectMaybeShared>().isDetached();
   7435  } else {
   7436    return obj->as<ArrayBufferViewObject>().hasDetachedBuffer();
   7437  }
   7438 }
   7439 
   7440 bool JS::ArrayBufferOrView::isResizable() const {
   7441  MOZ_ASSERT(obj);
   7442  if (obj->is<ArrayBufferObjectMaybeShared>()) {
   7443    return obj->as<ArrayBufferObjectMaybeShared>().isResizable();
   7444  } else {
   7445    return obj->as<ArrayBufferViewObject>().hasResizableBuffer();
   7446  }
   7447 }
   7448 
   7449 bool JS::ArrayBufferOrView::isImmutable() const {
   7450  MOZ_ASSERT(obj);
   7451  if (obj->is<ArrayBufferObjectMaybeShared>()) {
   7452    return obj->as<ArrayBufferObjectMaybeShared>().isImmutable();
   7453  } else {
   7454    return obj->as<ArrayBufferViewObject>().hasImmutableBuffer();
   7455  }
   7456 }
   7457 
   7458 JS::TypedArray_base JS::TypedArray_base::fromObject(JSObject* unwrapped) {
   7459  if (unwrapped && unwrapped->is<TypedArrayObject>()) {
   7460    return TypedArray_base(unwrapped);
   7461  }
   7462  return TypedArray_base(nullptr);
   7463 }
   7464 
   7465 // Template getData function for TypedArrays, implemented here because
   7466 // it requires internal APIs.
   7467 template <JS::Scalar::Type EType>
   7468 typename mozilla::Span<typename TypedArray<EType>::DataType>
   7469 TypedArray<EType>::getData(bool* isSharedMemory, const AutoRequireNoGC&) {
   7470  using ExternalType = TypedArray<EType>::DataType;
   7471  if (!obj) {
   7472    return nullptr;
   7473  }
   7474  TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
   7475  MOZ_ASSERT(tarr);
   7476  *isSharedMemory = tarr->isSharedMemory();
   7477  return {static_cast<ExternalType*>(tarr->dataPointerEither().unwrap(
   7478              /*safe - caller sees isShared*/)),
   7479          tarr->length().valueOr(0)};
   7480 };
   7481 
   7482 // Force the method defined above to actually be instantianted in this
   7483 // compilation unit and emitted into the object file, since otherwise a binary
   7484 // could include the header file and emit an undefined symbol that would not be
   7485 // satisfied by the linker. (This happens with opt gtest, at least. In a DEBUG
   7486 // build, the header contains a call to this function so it will always be
   7487 // emitted.)
   7488 #define INSTANTIATE_GET_DATA(a, b, Name)                                  \
   7489  template mozilla::Span<typename TypedArray<JS::Scalar::Name>::DataType> \
   7490  TypedArray<JS::Scalar::Name>::getData(bool* isSharedMemory,             \
   7491                                        const AutoRequireNoGC&);
   7492 JS_FOR_EACH_TYPED_ARRAY(INSTANTIATE_GET_DATA)
   7493 #undef INSTANTIATE_GET_DATA
   7494 
   7495 } /* namespace JS */