tor-browser

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

ArrayBufferObject.cpp (138969B)


      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/ArrayBufferObject-inl.h"
      8 #include "vm/ArrayBufferObject.h"
      9 
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/Attributes.h"
     12 #include "mozilla/DebugOnly.h"
     13 #include "mozilla/Likely.h"
     14 #include "mozilla/Maybe.h"
     15 #include "mozilla/ScopeExit.h"
     16 #include "mozilla/TaggedAnonymousMemory.h"
     17 
     18 #include <algorithm>  // std::max, std::min
     19 #include <memory>     // std::uninitialized_copy_n
     20 #include <string.h>
     21 #if !defined(XP_WIN) && !defined(__wasi__)
     22 #  include <sys/mman.h>
     23 #endif
     24 #include <tuple>  // std::tuple
     25 #include <type_traits>
     26 #ifdef MOZ_VALGRIND
     27 #  include <valgrind/memcheck.h>
     28 #endif
     29 
     30 #include "jsnum.h"
     31 #include "jstypes.h"
     32 
     33 #include "gc/Barrier.h"
     34 #include "gc/Memory.h"
     35 #include "jit/InlinableNatives.h"
     36 #include "js/ArrayBuffer.h"
     37 #include "js/Conversions.h"
     38 #include "js/experimental/TypedData.h"  // JS_IsArrayBufferViewObject
     39 #include "js/friend/ErrorMessages.h"    // js::GetErrorMessage, JSMSG_*
     40 #include "js/MemoryMetrics.h"
     41 #include "js/Prefs.h"
     42 #include "js/PropertySpec.h"
     43 #include "js/SharedArrayBuffer.h"
     44 #include "js/Wrapper.h"
     45 #include "util/WindowsWrapper.h"
     46 #include "vm/GlobalObject.h"
     47 #include "vm/Interpreter.h"
     48 #include "vm/JSContext.h"
     49 #include "vm/JSObject.h"
     50 #include "vm/SelfHosting.h"
     51 #include "vm/SharedArrayObject.h"
     52 #include "vm/Warnings.h"  // js::WarnNumberASCII
     53 #include "wasm/WasmConstants.h"
     54 #include "wasm/WasmLog.h"
     55 #include "wasm/WasmMemory.h"
     56 #include "wasm/WasmModuleTypes.h"
     57 #include "wasm/WasmProcess.h"
     58 
     59 #include "gc/GCContext-inl.h"
     60 #include "gc/Marking-inl.h"
     61 #include "vm/NativeObject-inl.h"
     62 #include "vm/Realm-inl.h"  // js::AutoRealm
     63 
     64 using js::wasm::AddressType;
     65 using js::wasm::Pages;
     66 using mozilla::Atomic;
     67 using mozilla::DebugOnly;
     68 using mozilla::Maybe;
     69 using mozilla::Nothing;
     70 using mozilla::Some;
     71 
     72 using namespace js;
     73 
     74 // Wasm allows large amounts of memory to be reserved at a time. On 64-bit
     75 // platforms (with "huge memories") we reserve around 4GB of virtual address
     76 // space for every wasm memory; on 32-bit platforms we usually do not, but users
     77 // often initialize memories in the hundreds of megabytes.
     78 //
     79 // If too many wasm memories remain live, we run up against system resource
     80 // exhaustion (address space or number of memory map descriptors) - see bug
     81 // 1068684, bug 1073934, bug 1517412, bug 1502733 for details. The limiting case
     82 // seems to be Android on ARM64, where the per-process address space is limited
     83 // to 4TB (39 bits) by the organization of the page tables. An earlier problem
     84 // was Windows Vista Home 64-bit, where the per-process address space is limited
     85 // to 8TB (40 bits). And 32-bit platforms only have 4GB of address space anyway.
     86 //
     87 // Thus we track the amount of memory reserved for wasm, and set a limit per
     88 // process. We trigger GC work when we approach the limit and we throw an OOM
     89 // error if the per-process limit is exceeded. The limit (WasmReservedBytesMax)
     90 // is specific to architecture, OS, and OS configuration.
     91 //
     92 // Since the WasmReservedBytesMax limit is not generally accounted for by
     93 // any existing GC-trigger heuristics, we need an extra heuristic for triggering
     94 // GCs when the caller is allocating memories rapidly without other garbage
     95 // (e.g. bug 1773225). Thus, once the reserved memory crosses the threshold
     96 // WasmReservedBytesStartTriggering, we start triggering GCs every
     97 // WasmReservedBytesPerTrigger bytes. Once we reach
     98 // WasmReservedBytesStartSyncFullGC bytes reserved, we perform expensive
     99 // non-incremental full GCs as a last-ditch effort to avoid unnecessary failure.
    100 // Once we reach WasmReservedBytesMax, we perform further full GCs before giving
    101 // up.
    102 //
    103 // (History: The original implementation only tracked the number of "huge
    104 // memories" allocated by WASM, but this was found to be insufficient because
    105 // 32-bit platforms have similar resource exhaustion issues. We now track
    106 // reserved bytes directly.)
    107 //
    108 // (We also used to reserve significantly more than 4GB for huge memories, but
    109 // this was reduced in bug 1442544.)
    110 
    111 // ASAN and TSAN use a ton of vmem for bookkeeping leaving a lot less for the
    112 // program so use a lower limit.
    113 #if defined(MOZ_TSAN) || defined(MOZ_ASAN)
    114 static const uint64_t WasmMemAsanOverhead = 2;
    115 #else
    116 static const uint64_t WasmMemAsanOverhead = 1;
    117 #endif
    118 
    119 // WasmReservedStartTriggering + WasmReservedPerTrigger must be well below
    120 // WasmReservedStartSyncFullGC in order to provide enough time for incremental
    121 // GC to do its job.
    122 
    123 #if defined(JS_CODEGEN_ARM64) && defined(ANDROID)
    124 
    125 static const uint64_t WasmReservedBytesMax =
    126    75 * wasm::HugeMappedSize / WasmMemAsanOverhead;
    127 static const uint64_t WasmReservedBytesStartTriggering =
    128    15 * wasm::HugeMappedSize;
    129 static const uint64_t WasmReservedBytesStartSyncFullGC =
    130    WasmReservedBytesMax - 15 * wasm::HugeMappedSize;
    131 static const uint64_t WasmReservedBytesPerTrigger = 15 * wasm::HugeMappedSize;
    132 
    133 #elif defined(WASM_SUPPORTS_HUGE_MEMORY)
    134 
    135 static const uint64_t WasmReservedBytesMax =
    136    1000 * wasm::HugeMappedSize / WasmMemAsanOverhead;
    137 static const uint64_t WasmReservedBytesStartTriggering =
    138    100 * wasm::HugeMappedSize;
    139 static const uint64_t WasmReservedBytesStartSyncFullGC =
    140    WasmReservedBytesMax - 100 * wasm::HugeMappedSize;
    141 static const uint64_t WasmReservedBytesPerTrigger = 100 * wasm::HugeMappedSize;
    142 
    143 #else  // 32-bit (and weird 64-bit platforms without huge memory)
    144 
    145 static const uint64_t GiB = 1024 * 1024 * 1024;
    146 
    147 static const uint64_t WasmReservedBytesMax =
    148    (4 * GiB) / 2 / WasmMemAsanOverhead;
    149 static const uint64_t WasmReservedBytesStartTriggering = (4 * GiB) / 8;
    150 static const uint64_t WasmReservedBytesStartSyncFullGC =
    151    WasmReservedBytesMax - (4 * GiB) / 8;
    152 static const uint64_t WasmReservedBytesPerTrigger = (4 * GiB) / 8;
    153 
    154 #endif
    155 
    156 // The total number of bytes reserved for wasm memories.
    157 static Atomic<uint64_t, mozilla::ReleaseAcquire> wasmReservedBytes(0);
    158 // The number of bytes of wasm memory reserved since the last GC trigger.
    159 static Atomic<uint64_t, mozilla::ReleaseAcquire> wasmReservedBytesSinceLast(0);
    160 
    161 uint64_t js::WasmReservedBytes() { return wasmReservedBytes; }
    162 
    163 [[nodiscard]] static bool CheckArrayBufferTooLarge(JSContext* cx,
    164                                                   uint64_t nbytes) {
    165  // Refuse to allocate too large buffers.
    166  if (MOZ_UNLIKELY(nbytes > ArrayBufferObject::ByteLengthLimit)) {
    167    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    168                              JSMSG_BAD_ARRAY_LENGTH);
    169    return false;
    170  }
    171 
    172  return true;
    173 }
    174 
    175 void* js::MapBufferMemory(wasm::AddressType t, wasm::PageSize pageSize,
    176                          size_t mappedSize, size_t initialCommittedSize) {
    177  MOZ_ASSERT(mappedSize % gc::SystemPageSize() == 0);
    178  MOZ_ASSERT(initialCommittedSize % gc::SystemPageSize() == 0);
    179  MOZ_ASSERT(initialCommittedSize <= mappedSize);
    180 
    181  auto failed = mozilla::MakeScopeExit(
    182      [&] { wasmReservedBytes -= uint64_t(mappedSize); });
    183  wasmReservedBytes += uint64_t(mappedSize);
    184 
    185  // Test >= to guard against the case where multiple extant runtimes
    186  // race to allocate.
    187  if (wasmReservedBytes >= WasmReservedBytesMax) {
    188    if (OnLargeAllocationFailure) {
    189      OnLargeAllocationFailure();
    190    }
    191    if (wasmReservedBytes >= WasmReservedBytesMax) {
    192      return nullptr;
    193    }
    194  }
    195 
    196 #ifdef XP_WIN
    197  void* data = VirtualAlloc(nullptr, mappedSize, MEM_RESERVE, PAGE_NOACCESS);
    198  if (!data) {
    199    return nullptr;
    200  }
    201 
    202  if (!VirtualAlloc(data, initialCommittedSize, MEM_COMMIT, PAGE_READWRITE)) {
    203    VirtualFree(data, 0, MEM_RELEASE);
    204    return nullptr;
    205  }
    206 
    207  gc::RecordMemoryAlloc(initialCommittedSize);
    208 #elif defined(__wasi__)
    209  void* data = nullptr;
    210  if (int err = posix_memalign(&data, gc::SystemPageSize(), mappedSize)) {
    211    MOZ_ASSERT(err == ENOMEM);
    212    (void)err;
    213    return nullptr;
    214  }
    215  MOZ_ASSERT(data);
    216  memset(data, 0, mappedSize);
    217 #else   // !XP_WIN && !__wasi__
    218  void* data =
    219      MozTaggedAnonymousMmap(nullptr, mappedSize, PROT_NONE,
    220                             MAP_PRIVATE | MAP_ANON, -1, 0, "wasm-reserved");
    221  if (data == MAP_FAILED) {
    222    return nullptr;
    223  }
    224 
    225  // Note we will waste a page on zero-sized memories here
    226  if (mprotect(data, initialCommittedSize, PROT_READ | PROT_WRITE)) {
    227    munmap(data, mappedSize);
    228    return nullptr;
    229  }
    230 
    231  gc::RecordMemoryAlloc(initialCommittedSize);
    232 #endif  // !XP_WIN && !__wasi__
    233 
    234 #if defined(MOZ_VALGRIND) && \
    235    defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
    236  VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(
    237      (unsigned char*)data + initialCommittedSize,
    238      mappedSize - initialCommittedSize);
    239 #endif
    240 
    241  failed.release();
    242  return data;
    243 }
    244 
    245 bool js::CommitBufferMemory(void* dataEnd, size_t delta) {
    246  MOZ_ASSERT(delta);
    247  MOZ_ASSERT(delta % gc::SystemPageSize() == 0);
    248 
    249 #ifdef XP_WIN
    250  if (!VirtualAlloc(dataEnd, delta, MEM_COMMIT, PAGE_READWRITE)) {
    251    return false;
    252  }
    253 #elif defined(__wasi__)
    254  // posix_memalign'd memory is already committed
    255  return true;
    256 #else
    257  if (mprotect(dataEnd, delta, PROT_READ | PROT_WRITE)) {
    258    return false;
    259  }
    260 #endif  // XP_WIN
    261 
    262  gc::RecordMemoryAlloc(delta);
    263 
    264 #if defined(MOZ_VALGRIND) && \
    265    defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
    266  VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)dataEnd, delta);
    267 #endif
    268 
    269  return true;
    270 }
    271 
    272 void js::UnmapBufferMemory(wasm::AddressType t, void* base, size_t mappedSize,
    273                           size_t committedSize) {
    274  MOZ_ASSERT(mappedSize % gc::SystemPageSize() == 0);
    275  MOZ_ASSERT(committedSize % gc::SystemPageSize() == 0);
    276 
    277 #ifdef XP_WIN
    278  VirtualFree(base, 0, MEM_RELEASE);
    279  gc::RecordMemoryFree(committedSize);
    280 #elif defined(__wasi__)
    281  free(base);
    282  (void)committedSize;
    283 #else
    284  munmap(base, mappedSize);
    285  gc::RecordMemoryFree(committedSize);
    286 #endif  // XP_WIN
    287 
    288 #if defined(MOZ_VALGRIND) && \
    289    defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
    290  VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)base,
    291                                                mappedSize);
    292 #endif
    293 
    294  // Untrack reserved memory *after* releasing memory -- otherwise, a race
    295  // condition could enable the creation of unlimited buffers.
    296  wasmReservedBytes -= uint64_t(mappedSize);
    297 }
    298 
    299 /*
    300 * ArrayBufferObject
    301 *
    302 * This class holds the underlying raw buffer that the TypedArrayObject classes
    303 * access.  It can be created explicitly and passed to a TypedArrayObject, or
    304 * can be created implicitly by constructing a TypedArrayObject with a size.
    305 */
    306 
    307 /*
    308 * ArrayBufferObject (base)
    309 */
    310 
    311 static const JSClassOps ArrayBufferObjectClassOps = {
    312    nullptr,                      // addProperty
    313    nullptr,                      // delProperty
    314    nullptr,                      // enumerate
    315    nullptr,                      // newEnumerate
    316    nullptr,                      // resolve
    317    nullptr,                      // mayResolve
    318    ArrayBufferObject::finalize,  // finalize
    319    nullptr,                      // call
    320    nullptr,                      // construct
    321    nullptr,                      // trace
    322 };
    323 
    324 static const JSFunctionSpec arraybuffer_functions[] = {
    325    JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
    326    JS_FS_END,
    327 };
    328 
    329 static const JSPropertySpec arraybuffer_properties[] = {
    330    JS_SELF_HOSTED_SYM_GET(species, "$ArrayBufferSpecies", 0),
    331    JS_PS_END,
    332 };
    333 
    334 static const JSFunctionSpec arraybuffer_proto_functions[] = {
    335    JS_FN("slice", ArrayBufferObject::slice, 2, 0),
    336 #ifdef NIGHTLY_BUILD
    337    JS_FN("sliceToImmutable", ArrayBufferObject::sliceToImmutable, 2, 0),
    338 #endif
    339    JS_FN("resize", ArrayBufferObject::resize, 1, 0),
    340    JS_FN("transfer", ArrayBufferObject::transfer, 0, 0),
    341    JS_FN("transferToFixedLength", ArrayBufferObject::transferToFixedLength, 0,
    342          0),
    343 #ifdef NIGHTLY_BUILD
    344    JS_FN("transferToImmutable", ArrayBufferObject::transferToImmutable, 0, 0),
    345 #endif
    346    JS_FS_END,
    347 };
    348 
    349 static const JSPropertySpec arraybuffer_proto_properties[] = {
    350    JS_INLINABLE_PSG("byteLength", ArrayBufferObject::byteLengthGetter, 0,
    351                     ArrayBufferByteLength),
    352    JS_PSG("maxByteLength", ArrayBufferObject::maxByteLengthGetter, 0),
    353    JS_PSG("resizable", ArrayBufferObject::resizableGetter, 0),
    354    JS_PSG("detached", ArrayBufferObject::detachedGetter, 0),
    355 #ifdef NIGHTLY_BUILD
    356    JS_PSG("immutable", ArrayBufferObject::immutableGetter, 0),
    357 #endif
    358    JS_STRING_SYM_PS(toStringTag, "ArrayBuffer", JSPROP_READONLY),
    359    JS_PS_END,
    360 };
    361 
    362 static JSObject* CreateArrayBufferPrototype(JSContext* cx, JSProtoKey key) {
    363  return GlobalObject::createBlankPrototype(cx, cx->global(),
    364                                            &ArrayBufferObject::protoClass_);
    365 }
    366 
    367 static const ClassSpec ArrayBufferObjectClassSpec = {
    368    GenericCreateConstructor<ArrayBufferObject::class_constructor, 1,
    369                             gc::AllocKind::FUNCTION>,
    370    CreateArrayBufferPrototype,
    371    arraybuffer_functions,
    372    arraybuffer_properties,
    373    arraybuffer_proto_functions,
    374    arraybuffer_proto_properties,
    375    GenericFinishInit<WhichHasRealmFuseProperty::ProtoAndCtor>,
    376 };
    377 
    378 static const ClassExtension FixedLengthArrayBufferObjectClassExtension = {
    379    ArrayBufferObject::objectMoved<
    380        FixedLengthArrayBufferObject>,  // objectMovedOp
    381 };
    382 
    383 static const ClassExtension ResizableArrayBufferObjectClassExtension = {
    384    ArrayBufferObject::objectMoved<
    385        ResizableArrayBufferObject>,  // objectMovedOp
    386 };
    387 
    388 static const ClassExtension ImmutableArrayBufferObjectClassExtension = {
    389    ArrayBufferObject::objectMoved<
    390        ImmutableArrayBufferObject>,  // objectMovedOp
    391 };
    392 
    393 const JSClass ArrayBufferObject::protoClass_ = {
    394    "ArrayBuffer.prototype",
    395    JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
    396    JS_NULL_CLASS_OPS,
    397    &ArrayBufferObjectClassSpec,
    398 };
    399 
    400 const JSClass FixedLengthArrayBufferObject::class_ = {
    401    "ArrayBuffer",
    402    JSCLASS_DELAY_METADATA_BUILDER |
    403        JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
    404        JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
    405        JSCLASS_BACKGROUND_FINALIZE,
    406    &ArrayBufferObjectClassOps,
    407    &ArrayBufferObjectClassSpec,
    408    &FixedLengthArrayBufferObjectClassExtension,
    409 };
    410 
    411 const JSClass ResizableArrayBufferObject::class_ = {
    412    "ArrayBuffer",
    413    JSCLASS_DELAY_METADATA_BUILDER |
    414        JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
    415        JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
    416        JSCLASS_BACKGROUND_FINALIZE,
    417    &ArrayBufferObjectClassOps,
    418    &ArrayBufferObjectClassSpec,
    419    &ResizableArrayBufferObjectClassExtension,
    420 };
    421 
    422 const JSClass ImmutableArrayBufferObject::class_ = {
    423    "ArrayBuffer",
    424    JSCLASS_DELAY_METADATA_BUILDER |
    425        JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
    426        JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
    427        JSCLASS_BACKGROUND_FINALIZE,
    428    &ArrayBufferObjectClassOps,
    429    &ArrayBufferObjectClassSpec,
    430    &ImmutableArrayBufferObjectClassExtension,
    431 };
    432 
    433 static bool IsArrayBuffer(HandleValue v) {
    434  return v.isObject() && v.toObject().is<ArrayBufferObject>();
    435 }
    436 
    437 static bool IsResizableArrayBuffer(HandleValue v) {
    438  return v.isObject() && v.toObject().is<ResizableArrayBufferObject>();
    439 }
    440 
    441 MOZ_ALWAYS_INLINE bool ArrayBufferObject::byteLengthGetterImpl(
    442    JSContext* cx, const CallArgs& args) {
    443  MOZ_ASSERT(IsArrayBuffer(args.thisv()));
    444  auto* buffer = &args.thisv().toObject().as<ArrayBufferObject>();
    445  args.rval().setNumber(buffer->byteLength());
    446  return true;
    447 }
    448 
    449 bool ArrayBufferObject::byteLengthGetter(JSContext* cx, unsigned argc,
    450                                         Value* vp) {
    451  CallArgs args = CallArgsFromVp(argc, vp);
    452  return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args);
    453 }
    454 
    455 enum class PreserveResizability { No, Yes, Immutable };
    456 
    457 /**
    458 * ArrayBufferCopyAndDetach ( arrayBuffer, newLength, preserveResizability )
    459 *
    460 * https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffercopyanddetach
    461 */
    462 static ArrayBufferObject* ArrayBufferCopyAndDetach(
    463    JSContext* cx, Handle<ArrayBufferObject*> arrayBuffer,
    464    Handle<Value> newLength, PreserveResizability preserveResizability) {
    465  // Steps 1-2. (Not applicable in our implementation.)
    466 
    467  // Steps 3-4.
    468  uint64_t newByteLength;
    469  if (newLength.isUndefined()) {
    470    // Step 3.a.
    471    newByteLength = arrayBuffer->byteLength();
    472  } else {
    473    // Step 4.a.
    474    if (!ToIndex(cx, newLength, &newByteLength)) {
    475      return nullptr;
    476    }
    477  }
    478 
    479  // Step 5.
    480  if (arrayBuffer->isDetached()) {
    481    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    482                              JSMSG_TYPED_ARRAY_DETACHED);
    483    return nullptr;
    484  }
    485  if (arrayBuffer->isImmutable()) {
    486    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    487                              JSMSG_ARRAYBUFFER_IMMUTABLE);
    488    return nullptr;
    489  }
    490  if (arrayBuffer->isLengthPinned()) {
    491    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    492                              JSMSG_ARRAYBUFFER_LENGTH_PINNED);
    493    return nullptr;
    494  }
    495 
    496  // Step 8.
    497  if (arrayBuffer->hasDefinedDetachKey()) {
    498    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    499                              JSMSG_WASM_NO_TRANSFER);
    500    return nullptr;
    501  }
    502 
    503  // Steps 9-16.
    504  //
    505  // 25.1.2.1 AllocateArrayBuffer, step 2.
    506  // 6.2.9.1 CreateByteDataBlock, step 2.
    507  if (!CheckArrayBufferTooLarge(cx, newByteLength)) {
    508    return nullptr;
    509  }
    510 
    511 #ifdef NIGHTLY_BUILD
    512  // Additional step from Immutable ArrayBuffer proposal.
    513  if (preserveResizability == PreserveResizability::Immutable) {
    514    return ImmutableArrayBufferObject::copyAndDetach(cx, size_t(newByteLength),
    515                                                     arrayBuffer);
    516  }
    517 #endif
    518 
    519  // Steps 6-7.
    520  if (preserveResizability == PreserveResizability::Yes &&
    521      arrayBuffer->isResizable()) {
    522    Rooted<ResizableArrayBufferObject*> resizableBuffer(
    523        cx, &arrayBuffer->as<ResizableArrayBufferObject>());
    524 
    525    size_t maxByteLength = resizableBuffer->maxByteLength();
    526    if (size_t(newByteLength) > maxByteLength) {
    527      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    528                                JSMSG_ARRAYBUFFER_LENGTH_LARGER_THAN_MAXIMUM);
    529      return nullptr;
    530    }
    531 
    532    return ResizableArrayBufferObject::copyAndDetach(cx, size_t(newByteLength),
    533                                                     resizableBuffer);
    534  }
    535 
    536  return FixedLengthArrayBufferObject::copyAndDetach(cx, size_t(newByteLength),
    537                                                     arrayBuffer);
    538 }
    539 
    540 /**
    541 * get ArrayBuffer.prototype.maxByteLength
    542 *
    543 * https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.maxbytelength
    544 */
    545 bool ArrayBufferObject::maxByteLengthGetterImpl(JSContext* cx,
    546                                                const CallArgs& args) {
    547  MOZ_ASSERT(IsArrayBuffer(args.thisv()));
    548 
    549  auto* buffer = &args.thisv().toObject().as<ArrayBufferObject>();
    550 
    551  // Special case for wasm with potentially 64-bits memory.
    552  // Manually compute the maxByteLength to avoid an overflow on 32-bit machines.
    553  if (buffer->isWasm() && buffer->isResizable()) {
    554    Pages sourceMaxPages = buffer->wasmSourceMaxPages().value();
    555    uint64_t sourceMaxBytes = sourceMaxPages.byteLength64();
    556 
    557    MOZ_ASSERT(sourceMaxBytes <= wasm::StandardPageSizeBytes *
    558                                     wasm::MaxMemory64StandardPagesValidation);
    559    args.rval().setNumber(double(sourceMaxBytes));
    560 
    561    return true;
    562  }
    563 
    564  // Steps 4-6.
    565  size_t maxByteLength = buffer->maxByteLength();
    566  MOZ_ASSERT_IF(buffer->isDetached(), maxByteLength == 0);
    567 
    568  // Step 7.
    569  args.rval().setNumber(maxByteLength);
    570  return true;
    571 }
    572 
    573 /**
    574 * get ArrayBuffer.prototype.maxByteLength
    575 *
    576 * https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.maxbytelength
    577 */
    578 bool ArrayBufferObject::maxByteLengthGetter(JSContext* cx, unsigned argc,
    579                                            Value* vp) {
    580  // Steps 1-3.
    581  CallArgs args = CallArgsFromVp(argc, vp);
    582  return CallNonGenericMethod<IsArrayBuffer, maxByteLengthGetterImpl>(cx, args);
    583 }
    584 
    585 /**
    586 * get ArrayBuffer.prototype.resizable
    587 *
    588 * https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.resizable
    589 */
    590 bool ArrayBufferObject::resizableGetterImpl(JSContext* cx,
    591                                            const CallArgs& args) {
    592  MOZ_ASSERT(IsArrayBuffer(args.thisv()));
    593 
    594  // Step 4.
    595  auto* buffer = &args.thisv().toObject().as<ArrayBufferObject>();
    596  args.rval().setBoolean(buffer->isResizable());
    597  return true;
    598 }
    599 
    600 /**
    601 * get ArrayBuffer.prototype.resizable
    602 *
    603 * https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.resizable
    604 */
    605 bool ArrayBufferObject::resizableGetter(JSContext* cx, unsigned argc,
    606                                        Value* vp) {
    607  // Steps 1-3.
    608  CallArgs args = CallArgsFromVp(argc, vp);
    609  return CallNonGenericMethod<IsArrayBuffer, resizableGetterImpl>(cx, args);
    610 }
    611 
    612 /**
    613 * get ArrayBuffer.prototype.detached
    614 *
    615 * https://tc39.es/proposal-arraybuffer-transfer/#sec-get-arraybuffer.prototype.detached
    616 */
    617 bool ArrayBufferObject::detachedGetterImpl(JSContext* cx,
    618                                           const CallArgs& args) {
    619  MOZ_ASSERT(IsArrayBuffer(args.thisv()));
    620 
    621  // Step 4.
    622  auto* buffer = &args.thisv().toObject().as<ArrayBufferObject>();
    623  args.rval().setBoolean(buffer->isDetached());
    624  return true;
    625 }
    626 
    627 /**
    628 * get ArrayBuffer.prototype.detached
    629 *
    630 * https://tc39.es/proposal-arraybuffer-transfer/#sec-get-arraybuffer.prototype.detached
    631 */
    632 bool ArrayBufferObject::detachedGetter(JSContext* cx, unsigned argc,
    633                                       Value* vp) {
    634  // Steps 1-3.
    635  CallArgs args = CallArgsFromVp(argc, vp);
    636  return CallNonGenericMethod<IsArrayBuffer, detachedGetterImpl>(cx, args);
    637 }
    638 
    639 #ifdef NIGHTLY_BUILD
    640 /**
    641 * get ArrayBuffer.prototype.immutable
    642 *
    643 * https://tc39.es/proposal-immutable-arraybuffer/#sec-get-arraybuffer.prototype.immutable
    644 */
    645 bool ArrayBufferObject::immutableGetterImpl(JSContext* cx,
    646                                            const CallArgs& args) {
    647  MOZ_ASSERT(IsArrayBuffer(args.thisv()));
    648 
    649  // Step 4.
    650  auto* buffer = &args.thisv().toObject().as<ArrayBufferObject>();
    651  args.rval().setBoolean(buffer->isImmutable());
    652  return true;
    653 }
    654 
    655 /**
    656 * get ArrayBuffer.prototype.immutable
    657 *
    658 * https://tc39.es/proposal-immutable-arraybuffer/#sec-get-arraybuffer.prototype.immutable
    659 */
    660 bool ArrayBufferObject::immutableGetter(JSContext* cx, unsigned argc,
    661                                        Value* vp) {
    662  // Steps 1-3.
    663  CallArgs args = CallArgsFromVp(argc, vp);
    664  return CallNonGenericMethod<IsArrayBuffer, immutableGetterImpl>(cx, args);
    665 }
    666 #endif
    667 
    668 /**
    669 * ArrayBuffer.prototype.transfer ( [ newLength ] )
    670 *
    671 * https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfer
    672 */
    673 bool ArrayBufferObject::transferImpl(JSContext* cx, const CallArgs& args) {
    674  MOZ_ASSERT(IsArrayBuffer(args.thisv()));
    675 
    676  // Steps 1-2.
    677  Rooted<ArrayBufferObject*> buffer(
    678      cx, &args.thisv().toObject().as<ArrayBufferObject>());
    679  auto* newBuffer = ArrayBufferCopyAndDetach(cx, buffer, args.get(0),
    680                                             PreserveResizability::Yes);
    681  if (!newBuffer) {
    682    return false;
    683  }
    684 
    685  args.rval().setObject(*newBuffer);
    686  return true;
    687 }
    688 
    689 /**
    690 * ArrayBuffer.prototype.transfer ( [ newLength ] )
    691 *
    692 * https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfer
    693 */
    694 bool ArrayBufferObject::transfer(JSContext* cx, unsigned argc, Value* vp) {
    695  CallArgs args = CallArgsFromVp(argc, vp);
    696  return CallNonGenericMethod<IsArrayBuffer, transferImpl>(cx, args);
    697 }
    698 
    699 /**
    700 * ArrayBuffer.prototype.transferToFixedLength ( [ newLength ] )
    701 *
    702 * https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfertofixedlength
    703 */
    704 bool ArrayBufferObject::transferToFixedLengthImpl(JSContext* cx,
    705                                                  const CallArgs& args) {
    706  MOZ_ASSERT(IsArrayBuffer(args.thisv()));
    707 
    708  // Steps 1-2.
    709  Rooted<ArrayBufferObject*> buffer(
    710      cx, &args.thisv().toObject().as<ArrayBufferObject>());
    711  auto* newBuffer = ArrayBufferCopyAndDetach(cx, buffer, args.get(0),
    712                                             PreserveResizability::No);
    713  if (!newBuffer) {
    714    return false;
    715  }
    716 
    717  args.rval().setObject(*newBuffer);
    718  return true;
    719 }
    720 
    721 /**
    722 * ArrayBuffer.prototype.transferToFixedLength ( [ newLength ] )
    723 *
    724 * https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfertofixedlength
    725 */
    726 bool ArrayBufferObject::transferToFixedLength(JSContext* cx, unsigned argc,
    727                                              Value* vp) {
    728  CallArgs args = CallArgsFromVp(argc, vp);
    729  return CallNonGenericMethod<IsArrayBuffer, transferToFixedLengthImpl>(cx,
    730                                                                        args);
    731 }
    732 
    733 #ifdef NIGHTLY_BUILD
    734 /**
    735 * ArrayBuffer.prototype.transferToImmutable ( [ newLength ] )
    736 *
    737 * https://tc39.es/proposal-immutable-arraybuffer/#sec-arraybuffer.prototype.transfertoimmutable
    738 */
    739 bool ArrayBufferObject::transferToImmutableImpl(JSContext* cx,
    740                                                const CallArgs& args) {
    741  MOZ_ASSERT(IsArrayBuffer(args.thisv()));
    742 
    743  // Steps 1-2.
    744  Rooted<ArrayBufferObject*> buffer(
    745      cx, &args.thisv().toObject().as<ArrayBufferObject>());
    746  auto* newBuffer = ArrayBufferCopyAndDetach(cx, buffer, args.get(0),
    747                                             PreserveResizability::Immutable);
    748  if (!newBuffer) {
    749    return false;
    750  }
    751 
    752  args.rval().setObject(*newBuffer);
    753  return true;
    754 }
    755 
    756 /**
    757 * ArrayBuffer.prototype.transferToImmutable ( [ newLength ] )
    758 *
    759 * https://tc39.es/proposal-immutable-arraybuffer/#sec-arraybuffer.prototype.transfertoimmutable
    760 */
    761 bool ArrayBufferObject::transferToImmutable(JSContext* cx, unsigned argc,
    762                                            Value* vp) {
    763  CallArgs args = CallArgsFromVp(argc, vp);
    764  return CallNonGenericMethod<IsArrayBuffer, transferToImmutableImpl>(cx, args);
    765 }
    766 #endif
    767 
    768 /**
    769 * ArrayBuffer.prototype.resize ( newLength )
    770 *
    771 * https://tc39.es/ecma262/#sec-arraybuffer.prototype.resize
    772 */
    773 bool ArrayBufferObject::resizeImpl(JSContext* cx, const CallArgs& args) {
    774  MOZ_ASSERT(IsResizableArrayBuffer(args.thisv()));
    775 
    776  Rooted<ResizableArrayBufferObject*> obj(
    777      cx, &args.thisv().toObject().as<ResizableArrayBufferObject>());
    778 
    779  // Step 4.
    780  uint64_t newByteLength;
    781  if (!ToIndex(cx, args.get(0), &newByteLength)) {
    782    return false;
    783  }
    784 
    785  // Step 5.
    786  if (obj->isDetached()) {
    787    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    788                              JSMSG_TYPED_ARRAY_DETACHED);
    789    return false;
    790  }
    791  if (obj->isLengthPinned()) {
    792    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    793                              JSMSG_ARRAYBUFFER_LENGTH_PINNED);
    794    return false;
    795  }
    796 
    797  // Additional step from Immutable ArrayBuffer proposal.
    798  MOZ_ASSERT(!obj->isImmutable(), "resizable array buffers aren't immutable");
    799 
    800  // Step 6.
    801  if (newByteLength > obj->maxByteLength()) {
    802    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    803                              JSMSG_ARRAYBUFFER_LENGTH_LARGER_THAN_MAXIMUM);
    804    return false;
    805  }
    806 
    807  if (obj->isWasm()) {
    808    // Special case for resizing of Wasm buffers.
    809    if (newByteLength % wasm::StandardPageSizeBytes != 0) {
    810      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    811                                JSMSG_WASM_ARRAYBUFFER_PAGE_MULTIPLE);
    812      return false;
    813    }
    814    if (newByteLength < obj->byteLength()) {
    815      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    816                                JSMSG_WASM_ARRAYBUFFER_CANNOT_SHRINK);
    817      return false;
    818    }
    819 
    820    Pages newPages =
    821        Pages::fromByteLengthExact(newByteLength, obj->wasmPageSize());
    822    MOZ_RELEASE_ASSERT(WasmArrayBufferSourceMaxPages(obj).isSome());
    823    Rooted<ArrayBufferObject*> res(
    824        cx,
    825        obj->wasmGrowToPagesInPlace(obj->wasmAddressType(), newPages, obj, cx));
    826    MOZ_ASSERT_IF(res, res == obj);
    827    return !!res;
    828  }
    829 
    830  // Steps 7-15.
    831  obj->resize(size_t(newByteLength));
    832 
    833  // Step 16.
    834  args.rval().setUndefined();
    835  return true;
    836 }
    837 
    838 /**
    839 * ArrayBuffer.prototype.resize ( newLength )
    840 *
    841 * https://tc39.es/ecma262/#sec-arraybuffer.prototype.resize
    842 */
    843 bool ArrayBufferObject::resize(JSContext* cx, unsigned argc, Value* vp) {
    844  // Steps 1-3.
    845  CallArgs args = CallArgsFromVp(argc, vp);
    846  return CallNonGenericMethod<IsResizableArrayBuffer, resizeImpl>(cx, args);
    847 }
    848 
    849 static bool IsArrayBufferSpecies(JSContext* cx, JSFunction* species) {
    850  return IsSelfHostedFunctionWithName(species,
    851                                      cx->names().dollar_ArrayBufferSpecies_);
    852 }
    853 
    854 static bool HasBuiltinArrayBufferSpecies(ArrayBufferObject* obj,
    855                                         JSContext* cx) {
    856  // Ensure `ArrayBuffer.prototype.constructor` and `ArrayBuffer[@@species]`
    857  // haven't been mutated.
    858  if (!cx->realm()->realmFuses.optimizeArrayBufferSpeciesFuse.intact()) {
    859    return false;
    860  }
    861 
    862  // Ensure |obj|'s prototype is the actual ArrayBuffer.prototype.
    863  auto* proto = cx->global()->maybeGetPrototype(JSProto_ArrayBuffer);
    864  if (!proto || obj->staticPrototype() != proto) {
    865    return false;
    866  }
    867 
    868  // Fail if |obj| has an own `constructor` property.
    869  if (obj->containsPure(NameToId(cx->names().constructor))) {
    870    return false;
    871  }
    872 
    873  return true;
    874 }
    875 
    876 /**
    877 * ArrayBuffer.prototype.slice ( start, end )
    878 *
    879 * https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice
    880 */
    881 bool ArrayBufferObject::sliceImpl(JSContext* cx, const CallArgs& args) {
    882  MOZ_ASSERT(IsArrayBuffer(args.thisv()));
    883 
    884  Rooted<ArrayBufferObject*> obj(
    885      cx, &args.thisv().toObject().as<ArrayBufferObject>());
    886 
    887  // Step 4.
    888  if (obj->isDetached()) {
    889    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    890                              JSMSG_TYPED_ARRAY_DETACHED);
    891    return false;
    892  }
    893 
    894  // Step 5.
    895  size_t len = obj->byteLength();
    896 
    897  // Steps 6-9.
    898  size_t first = 0;
    899  if (args.hasDefined(0)) {
    900    if (!ToIntegerIndex(cx, args[0], len, &first)) {
    901      return false;
    902    }
    903  }
    904 
    905  // Steps 10-13.
    906  size_t final_ = len;
    907  if (args.hasDefined(1)) {
    908    if (!ToIntegerIndex(cx, args[1], len, &final_)) {
    909      return false;
    910    }
    911  }
    912 
    913  // Step 14.
    914  size_t newLen = final_ >= first ? final_ - first : 0;
    915  MOZ_ASSERT(newLen <= ArrayBufferObject::ByteLengthLimit);
    916 
    917  // Steps 15-21.
    918  Rooted<JSObject*> resultObj(cx);
    919  ArrayBufferObject* unwrappedResult = nullptr;
    920  if (HasBuiltinArrayBufferSpecies(obj, cx)) {
    921    // Steps 15-16.
    922    unwrappedResult = createZeroed(cx, newLen);
    923    if (!unwrappedResult) {
    924      return false;
    925    }
    926    resultObj.set(unwrappedResult);
    927 
    928    // Steps 17-18. (Not applicable)
    929 
    930    // Step 19.
    931    MOZ_ASSERT(!unwrappedResult->isDetached());
    932 
    933    // Step 20.
    934    MOZ_ASSERT(unwrappedResult != obj);
    935 
    936    // Step 21.
    937    MOZ_ASSERT(unwrappedResult->byteLength() == newLen);
    938  } else {
    939    // Step 15.
    940    Rooted<JSObject*> ctor(cx, SpeciesConstructor(cx, obj, JSProto_ArrayBuffer,
    941                                                  IsArrayBufferSpecies));
    942    if (!ctor) {
    943      return false;
    944    }
    945 
    946    // Step 16.
    947    {
    948      FixedConstructArgs<1> cargs(cx);
    949      cargs[0].setNumber(newLen);
    950 
    951      Rooted<Value> ctorVal(cx, ObjectValue(*ctor));
    952      if (!Construct(cx, ctorVal, cargs, ctorVal, &resultObj)) {
    953        return false;
    954      }
    955    }
    956 
    957    // Steps 17-18.
    958    unwrappedResult = resultObj->maybeUnwrapIf<ArrayBufferObject>();
    959    if (!unwrappedResult) {
    960      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    961                                JSMSG_NON_ARRAY_BUFFER_RETURNED);
    962      return false;
    963    }
    964 
    965    // Step 19.
    966    if (unwrappedResult->isDetached()) {
    967      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    968                                JSMSG_TYPED_ARRAY_DETACHED);
    969      return false;
    970    }
    971 
    972    // Additional step from Immutable ArrayBuffer proposal.
    973    if (unwrappedResult->isImmutable()) {
    974      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    975                                JSMSG_ARRAYBUFFER_IMMUTABLE);
    976      return false;
    977    }
    978 
    979    // Step 20.
    980    if (unwrappedResult == obj) {
    981      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    982                                JSMSG_SAME_ARRAY_BUFFER_RETURNED);
    983      return false;
    984    }
    985 
    986    // Step 21.
    987    size_t resultByteLength = unwrappedResult->byteLength();
    988    if (resultByteLength < newLen) {
    989      ToCStringBuf resultLenCbuf;
    990      const char* resultLenStr =
    991          NumberToCString(&resultLenCbuf, double(resultByteLength));
    992 
    993      ToCStringBuf newLenCbuf;
    994      const char* newLenStr = NumberToCString(&newLenCbuf, double(newLen));
    995 
    996      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    997                                JSMSG_SHORT_ARRAY_BUFFER_RETURNED, newLenStr,
    998                                resultLenStr);
    999      return false;
   1000    }
   1001  }
   1002 
   1003  // Steps 22-23.
   1004  if (obj->isDetached()) {
   1005    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1006                              JSMSG_TYPED_ARRAY_DETACHED);
   1007    return false;
   1008  }
   1009 
   1010  // Step 26.
   1011  //
   1012  // Reacquire the length in case the buffer has been resized.
   1013  size_t currentLen = obj->byteLength();
   1014 
   1015  // Steps 24-25 and 27.
   1016  if (first < currentLen) {
   1017    // Step 27.a.
   1018    size_t count = std::min(newLen, currentLen - first);
   1019 
   1020    // Steps 24-25 and 27.b.
   1021    ArrayBufferObject::copyData(unwrappedResult, 0, obj, first, count);
   1022  }
   1023 
   1024  // Step 28.
   1025  args.rval().setObject(*resultObj);
   1026  return true;
   1027 }
   1028 
   1029 /**
   1030 * ArrayBuffer.prototype.slice ( start, end )
   1031 *
   1032 * https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice
   1033 */
   1034 bool ArrayBufferObject::slice(JSContext* cx, unsigned argc, Value* vp) {
   1035  // Steps 1-3.
   1036  CallArgs args = CallArgsFromVp(argc, vp);
   1037  return CallNonGenericMethod<IsArrayBuffer, sliceImpl>(cx, args);
   1038 }
   1039 
   1040 #ifdef NIGHTLY_BUILD
   1041 /**
   1042 * ArrayBuffer.prototype.sliceToImmutable ( start, end )
   1043 *
   1044 * https://tc39.es/proposal-immutable-arraybuffer/#sec-arraybuffer.prototype.slicetoimmutable
   1045 */
   1046 bool ArrayBufferObject::sliceToImmutableImpl(JSContext* cx,
   1047                                             const CallArgs& args) {
   1048  MOZ_ASSERT(IsArrayBuffer(args.thisv()));
   1049 
   1050  Rooted<ArrayBufferObject*> obj(
   1051      cx, &args.thisv().toObject().as<ArrayBufferObject>());
   1052 
   1053  // Step 4.
   1054  if (obj->isDetached()) {
   1055    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1056                              JSMSG_TYPED_ARRAY_DETACHED);
   1057    return false;
   1058  }
   1059 
   1060  // Step 5.
   1061  size_t len = obj->byteLength();
   1062 
   1063  // Steps 6 and 7.
   1064  size_t first = 0;
   1065  if (args.hasDefined(0)) {
   1066    if (!ToIntegerIndex(cx, args[0], len, &first)) {
   1067      return false;
   1068    }
   1069  }
   1070 
   1071  // Steps 6 and 8.
   1072  size_t final_ = len;
   1073  if (args.hasDefined(1)) {
   1074    if (!ToIntegerIndex(cx, args[1], len, &final_)) {
   1075      return false;
   1076    }
   1077  }
   1078 
   1079  // Step 9.
   1080  size_t newLen = final_ >= first ? final_ - first : 0;
   1081  MOZ_ASSERT(newLen <= ArrayBufferObject::ByteLengthLimit);
   1082 
   1083  // Steps 10-11.
   1084  if (obj->isDetached()) {
   1085    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1086                              JSMSG_TYPED_ARRAY_DETACHED);
   1087    return false;
   1088  }
   1089 
   1090  // Step 13.
   1091  //
   1092  // Reacquire the length in case the buffer has been resized.
   1093  size_t currentLen = obj->byteLength();
   1094 
   1095  // Step 14.
   1096  if (currentLen < final_) {
   1097    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1098                              JSMSG_ARRAYBUFFER_COPY_RANGE);
   1099    return false;
   1100  }
   1101 
   1102  // Steps 12 and 15.
   1103  auto* newBuffer = ImmutableArrayBufferObject::slice(cx, newLen, obj, first);
   1104  if (!newBuffer) {
   1105    return false;
   1106  }
   1107 
   1108  // Step 16.
   1109  args.rval().setObject(*newBuffer);
   1110  return true;
   1111 }
   1112 
   1113 /**
   1114 * ArrayBuffer.prototype.sliceToImmutable ( start, end )
   1115 *
   1116 * https://tc39.es/proposal-immutable-arraybuffer/#sec-arraybuffer.prototype.slicetoimmutable
   1117 */
   1118 bool ArrayBufferObject::sliceToImmutable(JSContext* cx, unsigned argc,
   1119                                         Value* vp) {
   1120  // Steps 1-3.
   1121  CallArgs args = CallArgsFromVp(argc, vp);
   1122  return CallNonGenericMethod<IsArrayBuffer, sliceToImmutableImpl>(cx, args);
   1123 }
   1124 #endif
   1125 
   1126 /*
   1127 * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
   1128 */
   1129 bool ArrayBufferObject::fun_isView(JSContext* cx, unsigned argc, Value* vp) {
   1130  CallArgs args = CallArgsFromVp(argc, vp);
   1131  args.rval().setBoolean(args.get(0).isObject() &&
   1132                         JS_IsArrayBufferViewObject(&args.get(0).toObject()));
   1133  return true;
   1134 }
   1135 
   1136 // ES2024 draft rev 3a773fc9fae58be023228b13dbbd402ac18eeb6b
   1137 // 25.1.4.1 ArrayBuffer ( length [ , options ] )
   1138 bool ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc,
   1139                                          Value* vp) {
   1140  CallArgs args = CallArgsFromVp(argc, vp);
   1141 
   1142  // Step 1.
   1143  if (!ThrowIfNotConstructing(cx, args, "ArrayBuffer")) {
   1144    return false;
   1145  }
   1146 
   1147  // Step 2.
   1148  uint64_t byteLength;
   1149  if (!ToIndex(cx, args.get(0), &byteLength)) {
   1150    return false;
   1151  }
   1152 
   1153  // Step 3.
   1154  mozilla::Maybe<uint64_t> maxByteLength;
   1155  // Inline call to GetArrayBufferMaxByteLengthOption.
   1156  if (args.get(1).isObject()) {
   1157    Rooted<JSObject*> options(cx, &args[1].toObject());
   1158 
   1159    Rooted<Value> val(cx);
   1160    if (!GetProperty(cx, options, options, cx->names().maxByteLength, &val)) {
   1161      return false;
   1162    }
   1163    if (!val.isUndefined()) {
   1164      uint64_t maxByteLengthInt;
   1165      if (!ToIndex(cx, val, &maxByteLengthInt)) {
   1166        return false;
   1167      }
   1168 
   1169      // 25.1.3.1 AllocateArrayBuffer, step 3.a.
   1170      if (byteLength > maxByteLengthInt) {
   1171        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1172                                  JSMSG_ARRAYBUFFER_LENGTH_LARGER_THAN_MAXIMUM);
   1173        return false;
   1174      }
   1175      maxByteLength = mozilla::Some(maxByteLengthInt);
   1176    }
   1177  }
   1178 
   1179  // Step 4 (Inlined 25.1.3.1 AllocateArrayBuffer).
   1180  // 25.1.3.1, step 4 (Inlined 10.1.13 OrdinaryCreateFromConstructor, step 2).
   1181  RootedObject proto(cx);
   1182  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ArrayBuffer,
   1183                                          &proto)) {
   1184    return false;
   1185  }
   1186 
   1187  // 25.1.3.1, step 5 (Inlined 6.2.9.1 CreateByteDataBlock, step 2).
   1188  if (!CheckArrayBufferTooLarge(cx, byteLength)) {
   1189    return false;
   1190  }
   1191 
   1192  if (maxByteLength) {
   1193    // 25.1.3.1, step 8.a.
   1194    if (!CheckArrayBufferTooLarge(cx, *maxByteLength)) {
   1195      return false;
   1196    }
   1197 
   1198    // 25.1.3.1, remaining steps.
   1199    auto* bufobj = ResizableArrayBufferObject::createZeroed(
   1200        cx, byteLength, *maxByteLength, proto);
   1201    if (!bufobj) {
   1202      return false;
   1203    }
   1204    args.rval().setObject(*bufobj);
   1205    return true;
   1206  }
   1207 
   1208  // 25.1.3.1, remaining steps.
   1209  JSObject* bufobj = createZeroed(cx, byteLength, proto);
   1210  if (!bufobj) {
   1211    return false;
   1212  }
   1213  args.rval().setObject(*bufobj);
   1214  return true;
   1215 }
   1216 
   1217 using ArrayBufferContents = UniquePtr<uint8_t[], JS::FreePolicy>;
   1218 
   1219 static ArrayBufferContents AllocateUninitializedArrayBufferContents(
   1220    JSContext* cx, size_t nbytes) {
   1221  // First attempt a normal allocation.
   1222  uint8_t* p =
   1223      cx->maybe_pod_arena_malloc<uint8_t>(js::ArrayBufferContentsArena, nbytes);
   1224  if (MOZ_UNLIKELY(!p)) {
   1225    // Otherwise attempt a large allocation, calling the
   1226    // large-allocation-failure callback if necessary.
   1227    p = static_cast<uint8_t*>(cx->runtime()->onOutOfMemoryCanGC(
   1228        js::AllocFunction::Malloc, js::ArrayBufferContentsArena, nbytes));
   1229    if (!p) {
   1230      MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode,
   1231                            "OOM in AllocateUninitializedArrayBufferContents");
   1232      ReportOutOfMemory(cx);
   1233    }
   1234  }
   1235 
   1236  return ArrayBufferContents(p);
   1237 }
   1238 
   1239 static ArrayBufferContents AllocateArrayBufferContents(JSContext* cx,
   1240                                                       size_t nbytes) {
   1241  // First attempt a normal allocation.
   1242  uint8_t* p =
   1243      cx->maybe_pod_arena_calloc<uint8_t>(js::ArrayBufferContentsArena, nbytes);
   1244  if (MOZ_UNLIKELY(!p)) {
   1245    // Otherwise attempt a large allocation, calling the
   1246    // large-allocation-failure callback if necessary.
   1247    p = static_cast<uint8_t*>(cx->runtime()->onOutOfMemoryCanGC(
   1248        js::AllocFunction::Calloc, js::ArrayBufferContentsArena, nbytes));
   1249    if (!p) {
   1250      ReportOutOfMemory(cx);
   1251    }
   1252  }
   1253 
   1254  return ArrayBufferContents(p);
   1255 }
   1256 
   1257 static ArrayBufferContents ReallocateArrayBufferContents(JSContext* cx,
   1258                                                         uint8_t* old,
   1259                                                         size_t oldSize,
   1260                                                         size_t newSize) {
   1261  // First attempt a normal reallocation.
   1262  uint8_t* p = cx->maybe_pod_arena_realloc<uint8_t>(
   1263      js::ArrayBufferContentsArena, old, oldSize, newSize);
   1264  if (MOZ_UNLIKELY(!p)) {
   1265    // Otherwise attempt a large allocation, calling the
   1266    // large-allocation-failure callback if necessary.
   1267    p = static_cast<uint8_t*>(cx->runtime()->onOutOfMemoryCanGC(
   1268        js::AllocFunction::Realloc, js::ArrayBufferContentsArena, newSize,
   1269        old));
   1270    if (!p) {
   1271      ReportOutOfMemory(cx);
   1272    }
   1273  }
   1274 
   1275  return ArrayBufferContents(p);
   1276 }
   1277 
   1278 static ArrayBufferContents NewCopiedBufferContents(
   1279    JSContext* cx, Handle<ArrayBufferObject*> buffer) {
   1280  ArrayBufferContents dataCopy =
   1281      AllocateUninitializedArrayBufferContents(cx, buffer->byteLength());
   1282  if (dataCopy) {
   1283    if (auto count = buffer->byteLength()) {
   1284      memcpy(dataCopy.get(), buffer->dataPointer(), count);
   1285    }
   1286  }
   1287  return dataCopy;
   1288 }
   1289 
   1290 /* static */
   1291 void ArrayBufferObject::detach(JSContext* cx,
   1292                               Handle<ArrayBufferObject*> buffer) {
   1293  cx->check(buffer);
   1294  MOZ_ASSERT(!buffer->isPreparedForAsmJS());
   1295  MOZ_ASSERT(!buffer->isLengthPinned());
   1296  MOZ_ASSERT(!buffer->isImmutable());
   1297 
   1298  // Update all views of the buffer to account for the buffer having been
   1299  // detached, and clear the buffer's data and list of views.
   1300 
   1301  auto& innerViews = ObjectRealm::get(buffer).innerViews.get();
   1302  if (InnerViewTable::ViewVector* views =
   1303          innerViews.maybeViewsUnbarriered(buffer)) {
   1304    AutoTouchingGrayThings tgt;
   1305    for (size_t i = 0; i < views->length(); i++) {
   1306      JSObject* view = (*views)[i];
   1307      view->as<ArrayBufferViewObject>().notifyBufferDetached();
   1308    }
   1309    innerViews.removeViews(buffer);
   1310  }
   1311  if (JSObject* view = buffer->firstView()) {
   1312    view->as<ArrayBufferViewObject>().notifyBufferDetached();
   1313    buffer->setFirstView(nullptr);
   1314  }
   1315 
   1316  if (buffer->dataPointer()) {
   1317    buffer->releaseData(cx->gcContext());
   1318    buffer->setDataPointer(BufferContents::createNoData());
   1319  }
   1320 
   1321  buffer->setByteLength(0);
   1322  buffer->setIsDetached();
   1323  if (buffer->isResizable()) {
   1324    buffer->as<ResizableArrayBufferObject>().setMaxByteLength(0);
   1325  }
   1326 }
   1327 
   1328 void ResizableArrayBufferObject::resize(size_t newByteLength) {
   1329  MOZ_ASSERT(!isPreparedForAsmJS());
   1330  MOZ_ASSERT(!isWasm());
   1331  MOZ_ASSERT(!isDetached());
   1332  MOZ_ASSERT(!isImmutable());
   1333  MOZ_ASSERT(!isLengthPinned());
   1334  MOZ_ASSERT(isResizable());
   1335  MOZ_ASSERT(newByteLength <= maxByteLength());
   1336 
   1337  // Clear the bytes between `data[newByteLength..oldByteLength]` when
   1338  // shrinking the buffer. We don't need to clear any bytes when growing the
   1339  // buffer, because the new space was either initialized to zero when creating
   1340  // the buffer, or a prior shrink zeroed it out here.
   1341  size_t oldByteLength = byteLength();
   1342  if (newByteLength < oldByteLength) {
   1343    size_t nbytes = oldByteLength - newByteLength;
   1344    memset(dataPointer() + newByteLength, 0, nbytes);
   1345  }
   1346 
   1347  setByteLength(newByteLength);
   1348 
   1349  // Update all views of the buffer to account for the buffer having been
   1350  // resized.
   1351 
   1352  auto& innerViews = ObjectRealm::get(this).innerViews.get();
   1353  if (InnerViewTable::ViewVector* views =
   1354          innerViews.maybeViewsUnbarriered(this)) {
   1355    AutoTouchingGrayThings tgt;
   1356    for (auto& view : *views) {
   1357      view->notifyBufferResized();
   1358    }
   1359  }
   1360  if (auto* view = firstView()) {
   1361    view->as<ArrayBufferViewObject>().notifyBufferResized();
   1362  }
   1363 }
   1364 
   1365 /*
   1366 * [SMDOC] WASM Linear Memory structure
   1367 *
   1368 * The wasm linear memory is, on its face, a simple buffer of memory. However,
   1369 * we perform several optimizations with the memory allocation to avoid bounds
   1370 * checks and to avoid moving grows. Many different types/objects are involved
   1371 * in our memory allocations:
   1372 *
   1373 *  - wasm::Instance - stores information about each memory in a
   1374 *    MemoryInstanceData, which itself holds a reference to a WasmMemoryObject.
   1375 *    Memory 0 gets special treatment to avoid loading the MemoryInstanceData at
   1376 *    runtime.
   1377 *
   1378 *  - WasmMemoryObject - stores a reference to a (Shared)ArrayBufferObject for
   1379 *    the backing storage.
   1380 *
   1381 *  - ArrayBufferObject - owns the actual buffer of memory for asm.js memories
   1382 *    and non-shared wasm memories. For wasm memories (but NOT asm.js memories),
   1383 *    additional wasm metadata is stored in a WasmArrayRawBuffer next to the
   1384 *    data itself.
   1385 *
   1386 *  - SharedArrayBufferObject - owns the actual buffer of memory for shared wasm
   1387 *    memories, in the form of a WasmSharedArrayRawBuffer.
   1388 *
   1389 *  - WasmArrayRawBuffer - metadata for a non-shared wasm memory allocation. See
   1390 *    below for details.
   1391 *
   1392 *  - WasmSharedArrayRawBuffer - a nearly equivalent class to WasmArrayRawBuffer
   1393 *    used for shared wasm memories only. The same terminology from
   1394 *    WasmArrayRawBuffer applies, but the memory allocation strategy is
   1395 *    different.
   1396 *
   1397 *
   1398 * ## Wasm memory terminology
   1399 *
   1400 * A wasm/asm.js linear memory is an mmap'd array buffer. In the general case,
   1401 * accesses to memory must be bounds checked, but bounds checks can be
   1402 * simplified, omitted, or deferred to signal handling based on the properties
   1403 * of the memory (such as a known maximum size). Some common terminology applies
   1404 * to all asm.js and wasm memories, and is generally handled by
   1405 * WasmMemoryObject. The following terms are all expressed in bytes for clarity,
   1406 * but in practice they may be stored as a page count instead:
   1407 *
   1408 *  - byteLength - the actual current length of the buffer. Accesses in
   1409 *    the range [0, byteLength) must succeed; accesses >= byteLength must fail.
   1410 *    May only increase, if the memory grows.
   1411 *
   1412 *  - boundsCheckLimit - the size against which we perform bounds checks. This
   1413 *    is sometimes equal to byteLength, but may be greater. Regardless, it is
   1414 *    always safe to use in bounds checks for reasons described in "Linear
   1415 *    memory addresses and bounds checking" in wasm/WasmMemory.cpp.
   1416 *
   1417 *  - sourceMaxSize - the optional declared limit on how far byteLength can
   1418 *    grow. This is the unmodified maximum size from the source module or JS-API
   1419 *    invocation. This may not be representable in pointer size, nor feasible
   1420 *    for a module to actually grow to due to implementation limits. It is used
   1421 *    for correct linking checks and js-types reflection.
   1422 *
   1423 *  - clampedMaxSize - the maximum size on how far the byteLength can grow. This
   1424 *    value respects implementation limits. Every memory has a clampedMaxSize,
   1425 *    even if no maximum was specified in source. This value may be greater
   1426 *    than or less than mappedSize.
   1427 *
   1428 *  - mappedSize - the actual mmap'd size. Access in the range [0, mappedSize)
   1429 *    will either succeed, or be handled by the wasm signal handlers. The amount
   1430 *    that we map can vary according to multiple factors - see "Allocation
   1431 *    strategies" below. (This property does not apply to asm.js.)
   1432 *
   1433 * The below diagram shows the layout of the wasm heap. The wasm-visible portion
   1434 * of the heap starts at 0. There is one extra page prior to the start of the
   1435 * wasm heap which contains the WasmArrayRawBuffer struct at its end (i.e. right
   1436 * before the start of the WASM memory). The mappedSize does NOT include this
   1437 * page.
   1438 *
   1439 *
   1440 *          Wasm(Shared)ArrayRawBuffer
   1441 *          │ dataPointer() (from (Shared)ArrayBufferObject)
   1442 *          │ │
   1443 *   ┌──────│─│──────────────────────────────────────────────────────┐
   1444 *   └──────│─│──────────────│─────────────────────────────────│─────│
   1445 *            0          byteLength             boundsCheckLimit     mappedSize
   1446 *
   1447 *   └───────────────────────┘
   1448 *           COMMITTED
   1449 *                           └─────────────────────────────────┴─────┘
   1450 *                                             SLOP
   1451 *   └───────────────────────────────────────────────────────────────┘
   1452 *                                MAPPED
   1453 *
   1454 *
   1455 * Invariants on byteLength and mappedSize:
   1456 *  - byteLength only increases.
   1457 *  - 0 <= byteLength <= mappedSize.
   1458 *  - If sourceMaxSize is not specified, mappedSize may grow.
   1459 *    Otherwise, mappedSize is constant.
   1460 *
   1461 * Invariants on sourceMaxSize and clampedMaxSize:
   1462 *  - initialLength <= clampedMaxSize <= sourceMaxSize (if present).
   1463 *  - clampedMaxSize <= wasm::MaxMemoryPages().
   1464 *
   1465 * Invariants on boundsCheckLimit:
   1466 *  - For asm.js code: boundsCheckLimit == byteLength.
   1467 *    Signal handlers will not be invoked.
   1468 *  - For wasm code without the huge memory trick:
   1469 *    byteLength <= boundsCheckLimit < mappedSize
   1470 *  - For wasm code with the huge memory trick:
   1471 *    boundsCheckLimit is irrelevant as bounds checks are unnecessary.
   1472 *  - If sourceMaxSize is present, boundsCheckLimit is constant.
   1473 *    Otherwise, it may increase with mappedSize.
   1474 *  - On ARM, boundsCheckLimit must be a valid ARM immediate.
   1475 *
   1476 * The region between byteLength and mappedSize is the SLOP - an area where we
   1477 * use signal handlers to catch invalid accesses, including those that slip by
   1478 * bounds checks. Logically it has two parts:
   1479 *
   1480 *  - From byteLength to boundsCheckLimit - as described above, we sometimes set
   1481 *    boundsCheckLimit higher than byteLength, and this part of the SLOP catches
   1482 *    accesses to memory that therefore pass a bounds check but should
   1483 *    nonetheless trap.
   1484 *
   1485 *  - From boundsCheckLimit to mappedSize - this part of the SLOP is a guard
   1486 *    region that allows us to ignore most offsets when performing bounds
   1487 *    checks.
   1488 *
   1489 * For more information about both of these cases, see "Linear memory addresses
   1490 * and bounds checking" in wasm/WasmMemory.cpp.
   1491 *
   1492 *
   1493 * ## Allocation strategies
   1494 *
   1495 * Our memory allocation strategy varies according to several factors.
   1496 *
   1497 *  - Huge memories - if allocating a 32-bit memory on a platform with
   1498 *    sufficient address space, we simply map 4GiB of memory plus ample guard
   1499 *    space. This eliminates the need for bounds checks entirely except in cases
   1500 *    of very large offsets. This applies whether or not the memory has a
   1501 *    declared maximum. See "Linear memory addresses and bounds checking" in
   1502 *    wasm/WasmMemory.cpp.
   1503 *
   1504 *  - Memories with no maximum - rather than over-allocate, we conservatively
   1505 *    map only the requested initial pages plus the required guard region.
   1506 *
   1507 *  - Memories with a maximum - we do our best to reserve the entire maximum
   1508 *    space up front so that grows will not fail later. If we fail to map the
   1509 *    entire maximum, we iteratively map less and less memory until allocation
   1510 *    succeeds.
   1511 *
   1512 *
   1513 * ## Grow strategies
   1514 *
   1515 * We have two strategies for growing a wasm memory.
   1516 *
   1517 *  - Growing in place - if mappedSize is large enough to contain the new size,
   1518 *    we can simply commit the previously-mapped pages. This is always the case
   1519 *    for huge memories, and for memories with a declared max (and by extension
   1520 *    shared memories).
   1521 *
   1522 *  - Moving grow - if we have not previously mapped enough space, we must
   1523 *    allocate an entirely new buffer and copy the contents of the old buffer.
   1524 *    This causes the memory to move and therefore requires us to update
   1525 *    information on the Instance. It also increases memory pressure because
   1526 *    both the old and new memories must be resident at the same time, and
   1527 *    afterwards the entirety of the old buffer's contents will be resident in
   1528 *    memory (even if some pages had never been touched before the move).
   1529 *
   1530 *    A moving grow can fall back to a grow in place if the mappedSize allows
   1531 *    for it. However, at the time of writing, our memory allocation/grow
   1532 *    strategies do not allow this to happen.
   1533 *
   1534 */
   1535 
   1536 [[nodiscard]] bool WasmArrayRawBuffer::growToPagesInPlace(Pages newPages) {
   1537  size_t newSize = newPages.byteLength();
   1538  size_t oldSize = byteLength();
   1539 
   1540  MOZ_ASSERT(newSize >= oldSize);
   1541  MOZ_ASSERT(newPages <= clampedMaxPages());
   1542  MOZ_ASSERT(newSize <= mappedSize());
   1543 
   1544  size_t delta = newSize - oldSize;
   1545  MOZ_ASSERT(delta % wasm::StandardPageSizeBytes == 0);
   1546 
   1547  uint8_t* dataEnd = dataPointer() + oldSize;
   1548  MOZ_ASSERT(uintptr_t(dataEnd) % gc::SystemPageSize() == 0);
   1549 
   1550  if (delta && !CommitBufferMemory(dataEnd, delta)) {
   1551    return false;
   1552  }
   1553 
   1554  length_ = newSize;
   1555 
   1556  return true;
   1557 }
   1558 
   1559 void WasmArrayRawBuffer::discard(size_t byteOffset, size_t byteLen) {
   1560  uint8_t* memBase = dataPointer();
   1561 
   1562  // The caller is responsible for ensuring these conditions are met; see this
   1563  // function's comment in ArrayBufferObject.h.
   1564  MOZ_ASSERT(byteOffset % wasm::StandardPageSizeBytes == 0);
   1565  MOZ_ASSERT(byteLen % wasm::StandardPageSizeBytes == 0);
   1566  MOZ_ASSERT(wasm::MemoryBoundsCheck(uint64_t(byteOffset), uint64_t(byteLen),
   1567                                     byteLength()));
   1568 
   1569  // Discarding zero bytes "succeeds" with no effect.
   1570  if (byteLen == 0) {
   1571    return;
   1572  }
   1573 
   1574  void* addr = memBase + uintptr_t(byteOffset);
   1575 
   1576  // On POSIX-ish platforms, we discard memory by overwriting previously-mapped
   1577  // pages with freshly-mapped pages (which are all zeroed). The operating
   1578  // system recognizes this and decreases the process RSS, and eventually
   1579  // collects the abandoned physical pages.
   1580  //
   1581  // On Windows, committing over previously-committed pages has no effect, and
   1582  // the memory must be explicitly decommitted first. This is not the same as an
   1583  // munmap; the address space is still reserved.
   1584 
   1585 #ifdef XP_WIN
   1586  if (!VirtualFree(addr, byteLen, MEM_DECOMMIT)) {
   1587    MOZ_CRASH("wasm discard: failed to decommit memory");
   1588  }
   1589  if (!VirtualAlloc(addr, byteLen, MEM_COMMIT, PAGE_READWRITE)) {
   1590    MOZ_CRASH("wasm discard: decommitted memory but failed to recommit");
   1591  };
   1592 #elif defined(__wasi__)
   1593  memset(addr, 0, byteLen);
   1594 #else  // !XP_WIN
   1595  void* data = MozTaggedAnonymousMmap(addr, byteLen, PROT_READ | PROT_WRITE,
   1596                                      MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0,
   1597                                      "wasm-reserved");
   1598  if (data == MAP_FAILED) {
   1599    MOZ_CRASH("failed to discard wasm memory; memory mappings may be broken");
   1600  }
   1601 #endif
   1602 }
   1603 
   1604 /* static */
   1605 WasmArrayRawBuffer* WasmArrayRawBuffer::AllocateWasm(
   1606    AddressType addressType, wasm::PageSize pageSize, Pages initialPages,
   1607    Pages clampedMaxPages, const Maybe<Pages>& sourceMaxPages,
   1608    const Maybe<size_t>& mapped) {
   1609  // Prior code has asserted that initial pages is within our implementation
   1610  // limits (wasm::MaxMemoryPages) and we can assume it is a valid size_t.
   1611  MOZ_RELEASE_ASSERT(initialPages.pageSize() == pageSize);
   1612  MOZ_RELEASE_ASSERT(clampedMaxPages.pageSize() == pageSize);
   1613  MOZ_RELEASE_ASSERT(!sourceMaxPages.isSome() ||
   1614                     (pageSize == sourceMaxPages->pageSize()));
   1615  MOZ_ASSERT(initialPages.hasByteLength());
   1616  size_t numBytes = initialPages.byteLength();
   1617 
   1618  // If there is a specified maximum, attempt to map the whole range for
   1619  // clampedMaxPages. Or else map only what's required for initialPages.
   1620  Pages initialMappedPages =
   1621      sourceMaxPages.isSome() ? clampedMaxPages : initialPages;
   1622 
   1623  // Use an override mapped size, or else compute the mapped size from
   1624  // initialMappedPages.
   1625 #ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES
   1626  MOZ_ASSERT_IF(pageSize == wasm::PageSize::Tiny, !mapped.isSome());
   1627 #endif
   1628  size_t mappedSize =
   1629      mapped.isSome() ? *mapped : wasm::ComputeMappedSize(initialMappedPages);
   1630 
   1631  MOZ_RELEASE_ASSERT(mappedSize <= SIZE_MAX - gc::SystemPageSize());
   1632  MOZ_RELEASE_ASSERT(numBytes <= SIZE_MAX - gc::SystemPageSize());
   1633  MOZ_RELEASE_ASSERT(initialPages <= clampedMaxPages);
   1634  // With custom page sizes, the wasm-visible byte length may not fall along
   1635  // system page boundaries.
   1636  MOZ_ASSERT_IF(pageSize == wasm::PageSize::Standard,
   1637                numBytes % gc::SystemPageSize() == 0);
   1638  MOZ_ASSERT(mappedSize % gc::SystemPageSize() == 0);
   1639 
   1640  uint64_t mappedSizeWithHeader = mappedSize + gc::SystemPageSize();
   1641 #ifndef ENABLE_WASM_CUSTOM_PAGE_SIZES
   1642  uint64_t numBytesWithHeader = numBytes + gc::SystemPageSize();
   1643 #else
   1644  // For tiny page size, the mapped size and the committed size are the
   1645  // same since we do not have a slop area or guard page.
   1646  uint64_t numBytesWithHeader = pageSize == wasm::PageSize::Tiny
   1647                                    ? mappedSizeWithHeader
   1648                                    : (numBytes + gc::SystemPageSize());
   1649 #endif
   1650 
   1651  MOZ_ASSERT(numBytesWithHeader % gc::SystemPageSize() == 0);
   1652 
   1653  void* data =
   1654      MapBufferMemory(addressType, pageSize, (size_t)mappedSizeWithHeader,
   1655                      (size_t)numBytesWithHeader);
   1656  if (!data) {
   1657    return nullptr;
   1658  }
   1659 
   1660  uint8_t* base = reinterpret_cast<uint8_t*>(data) + gc::SystemPageSize();
   1661  uint8_t* header = base - sizeof(WasmArrayRawBuffer);
   1662 
   1663  auto rawBuf = new (header)
   1664      WasmArrayRawBuffer(addressType, pageSize, base, clampedMaxPages,
   1665                         sourceMaxPages, mappedSize, numBytes);
   1666  return rawBuf;
   1667 }
   1668 
   1669 /* static */
   1670 void WasmArrayRawBuffer::Release(void* mem) {
   1671  WasmArrayRawBuffer* header =
   1672      (WasmArrayRawBuffer*)((uint8_t*)mem - sizeof(WasmArrayRawBuffer));
   1673 
   1674  MOZ_RELEASE_ASSERT(header->mappedSize() <= SIZE_MAX - gc::SystemPageSize());
   1675  size_t mappedSizeWithHeader = header->mappedSize() + gc::SystemPageSize();
   1676 #ifndef ENABLE_WASM_CUSTOM_PAGE_SIZES
   1677  size_t committedSize = header->byteLength() + gc::SystemPageSize();
   1678 #else
   1679  // See above for numBytesWithHeader in AllocateWasm.
   1680  size_t committedSize = header->pageSize() == wasm::PageSize::Tiny
   1681                             ? mappedSizeWithHeader
   1682                             : (header->byteLength() + gc::SystemPageSize());
   1683 #endif
   1684  MOZ_ASSERT(committedSize % gc::SystemPageSize() == 0);
   1685 
   1686  static_assert(std::is_trivially_destructible_v<WasmArrayRawBuffer>,
   1687                "no need to call the destructor");
   1688 
   1689  UnmapBufferMemory(header->addressType(), header->basePointer(),
   1690                    mappedSizeWithHeader, committedSize);
   1691 }
   1692 
   1693 WasmArrayRawBuffer* ArrayBufferObject::BufferContents::wasmBuffer() const {
   1694  MOZ_RELEASE_ASSERT(kind_ == WASM);
   1695  return (WasmArrayRawBuffer*)(data_ - sizeof(WasmArrayRawBuffer));
   1696 }
   1697 
   1698 template <typename ObjT, typename RawbufT>
   1699 static ArrayBufferObjectMaybeShared* CreateSpecificWasmBuffer(
   1700    JSContext* cx, const wasm::MemoryDesc& memory) {
   1701  bool useHugeMemory =
   1702      wasm::IsHugeMemoryEnabled(memory.addressType(), memory.pageSize());
   1703  wasm::PageSize pageSize = memory.pageSize();
   1704  Pages initialPages = memory.initialPages();
   1705  Maybe<Pages> sourceMaxPages = memory.maximumPages();
   1706  Pages clampedMaxPages = wasm::ClampedMaxPages(
   1707      memory.addressType(), initialPages, sourceMaxPages, useHugeMemory);
   1708 
   1709  Maybe<size_t> mappedSize;
   1710 #ifdef WASM_SUPPORTS_HUGE_MEMORY
   1711  // Override the mapped size if we are using huge memory. If we are not, then
   1712  // it will be calculated by the raw buffer we are using.
   1713  if (useHugeMemory) {
   1714    mappedSize = Some(wasm::HugeMappedSize);
   1715  }
   1716 #endif
   1717 
   1718  RawbufT* buffer = RawbufT::AllocateWasm(
   1719      memory.limits.addressType, memory.pageSize(), initialPages,
   1720      clampedMaxPages, sourceMaxPages, mappedSize);
   1721  if (!buffer) {
   1722    if (useHugeMemory) {
   1723      WarnNumberASCII(cx, JSMSG_WASM_HUGE_MEMORY_FAILED);
   1724      if (cx->isExceptionPending()) {
   1725        cx->clearPendingException();
   1726      }
   1727 
   1728      ReportOutOfMemory(cx);
   1729      return nullptr;
   1730    }
   1731 
   1732    // If we fail, and have a sourceMaxPages, try to reserve the biggest
   1733    // chunk in the range [initialPages, clampedMaxPages) using log backoff.
   1734    if (!sourceMaxPages) {
   1735      wasm::Log(cx,
   1736                "new Memory({initial=%" PRIu64
   1737                " pages, "
   1738                "pageSize=%" PRIu32 " bytes}) failed",
   1739                initialPages.pageCount(),
   1740                wasm::PageSizeInBytes(initialPages.pageSize()));
   1741      ReportOutOfMemory(cx);
   1742      return nullptr;
   1743    }
   1744 
   1745    uint64_t cur = clampedMaxPages.pageCount() / 2;
   1746    for (; cur > initialPages.pageCount(); cur /= 2) {
   1747      buffer = RawbufT::AllocateWasm(
   1748          memory.limits.addressType, pageSize, initialPages,
   1749          Pages::fromPageCount(cur, pageSize), sourceMaxPages, mappedSize);
   1750      if (buffer) {
   1751        break;
   1752      }
   1753    }
   1754 
   1755    if (!buffer) {
   1756      wasm::Log(cx,
   1757                "new Memory({initial=%" PRIu64
   1758                " pages, "
   1759                "pageSize=%" PRIu32 " bytes}) failed",
   1760                initialPages.pageCount(),
   1761                wasm::PageSizeInBytes(initialPages.pageSize()));
   1762      ReportOutOfMemory(cx);
   1763      return nullptr;
   1764    }
   1765  }
   1766 
   1767  // ObjT::createFromNewRawBuffer assumes ownership of |buffer| even in case
   1768  // of failure.
   1769  Rooted<ArrayBufferObjectMaybeShared*> object(
   1770      cx, ObjT::createFromNewRawBuffer(cx, buffer, initialPages.byteLength()));
   1771  if (!object) {
   1772    return nullptr;
   1773  }
   1774 
   1775  // See MaximumLiveMappedBuffers comment above.
   1776  if (wasmReservedBytes > WasmReservedBytesStartSyncFullGC) {
   1777    JS::PrepareForFullGC(cx);
   1778    JS::NonIncrementalGC(cx, JS::GCOptions::Normal,
   1779                         JS::GCReason::TOO_MUCH_WASM_MEMORY);
   1780    wasmReservedBytesSinceLast = 0;
   1781  } else if (wasmReservedBytes > WasmReservedBytesStartTriggering) {
   1782    wasmReservedBytesSinceLast += uint64_t(buffer->mappedSize());
   1783    if (wasmReservedBytesSinceLast > WasmReservedBytesPerTrigger) {
   1784      (void)cx->runtime()->gc.triggerGC(JS::GCReason::TOO_MUCH_WASM_MEMORY);
   1785      wasmReservedBytesSinceLast = 0;
   1786    }
   1787  } else {
   1788    wasmReservedBytesSinceLast = 0;
   1789  }
   1790 
   1791  // Log the result with details on the memory allocation
   1792  if (sourceMaxPages) {
   1793    if (useHugeMemory) {
   1794      wasm::Log(cx,
   1795                "new Memory({initial:%" PRIu64 " pages, maximum:%" PRIu64
   1796                " pages, pageSize:%" PRIu32 " bytes}) succeeded",
   1797                initialPages.pageCount(), sourceMaxPages->pageCount(),
   1798                wasm::PageSizeInBytes(initialPages.pageSize()));
   1799    } else {
   1800      wasm::Log(cx,
   1801                "new Memory({initial:%" PRIu64 " pages, maximum:%" PRIu64
   1802                " pages, pageSize:%" PRIu32
   1803                " bytes}) succeeded "
   1804                "with internal maximum of %" PRIu64 " pages",
   1805                initialPages.pageCount(), sourceMaxPages->pageCount(),
   1806                wasm::PageSizeInBytes(initialPages.pageSize()),
   1807                object->wasmClampedMaxPages().pageCount());
   1808    }
   1809  } else {
   1810    wasm::Log(cx,
   1811              "new Memory({initial:%" PRIu64 " pages, pageSize:%" PRIu32
   1812              " bytes}) succeeded",
   1813              initialPages.pageCount(),
   1814              wasm::PageSizeInBytes(initialPages.pageSize()));
   1815  }
   1816 
   1817  return object;
   1818 }
   1819 
   1820 ArrayBufferObjectMaybeShared* js::CreateWasmBuffer(
   1821    JSContext* cx, const wasm::MemoryDesc& memory) {
   1822  MOZ_RELEASE_ASSERT(
   1823      memory.initialPages() <=
   1824      wasm::MaxMemoryPages(memory.addressType(), memory.pageSize()));
   1825  MOZ_RELEASE_ASSERT(cx->wasm().haveSignalHandlers);
   1826 #ifndef ENABLE_WASM_CUSTOM_PAGE_SIZES
   1827  MOZ_ASSERT(memory.pageSize() == wasm::PageSize::Standard);
   1828 #endif
   1829 
   1830  if (memory.isShared()) {
   1831    if (!cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()) {
   1832      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1833                                JSMSG_WASM_NO_SHMEM_LINK);
   1834      return nullptr;
   1835    }
   1836    return CreateSpecificWasmBuffer<SharedArrayBufferObject,
   1837                                    WasmSharedArrayRawBuffer>(cx, memory);
   1838  }
   1839  return CreateSpecificWasmBuffer<ArrayBufferObject, WasmArrayRawBuffer>(
   1840      cx, memory);
   1841 }
   1842 
   1843 bool ArrayBufferObject::prepareForAsmJS() {
   1844  MOZ_ASSERT(byteLength() % wasm::StandardPageSizeBytes == 0,
   1845             "prior size checking should have guaranteed page-size multiple");
   1846  MOZ_ASSERT(byteLength() > 0,
   1847             "prior size checking should have excluded empty buffers");
   1848  MOZ_ASSERT(!isResizable(),
   1849             "prior checks should have excluded resizable buffers");
   1850  MOZ_ASSERT(!isImmutable(),
   1851             "prior checks should have excluded immutable buffers");
   1852 
   1853  switch (bufferKind()) {
   1854    case MALLOCED_ARRAYBUFFER_CONTENTS_ARENA:
   1855    case MALLOCED_UNKNOWN_ARENA:
   1856    case MAPPED:
   1857    case EXTERNAL:
   1858      // It's okay if this uselessly sets the flag a second time.
   1859      setIsPreparedForAsmJS();
   1860      return true;
   1861 
   1862    case INLINE_DATA:
   1863      static_assert(wasm::StandardPageSizeBytes >
   1864                        FixedLengthArrayBufferObject::MaxInlineBytes,
   1865                    "inline data must be too small to be a page size multiple");
   1866      MOZ_ASSERT_UNREACHABLE(
   1867          "inline-data buffers should be implicitly excluded by size checks");
   1868      return false;
   1869 
   1870    case NO_DATA:
   1871      MOZ_ASSERT_UNREACHABLE(
   1872          "size checking should have excluded detached or empty buffers");
   1873      return false;
   1874 
   1875    // asm.js code and associated buffers are potentially long-lived.  Yet a
   1876    // buffer of user-owned data *must* be detached by the user before the
   1877    // user-owned data is disposed.  No caller wants to use a user-owned
   1878    // ArrayBuffer with asm.js, so just don't support this and avoid a mess of
   1879    // complexity.
   1880    case USER_OWNED:
   1881    // wasm buffers can be detached at any time.
   1882    case WASM:
   1883      MOZ_ASSERT(!isPreparedForAsmJS());
   1884      return false;
   1885  }
   1886 
   1887  MOZ_ASSERT_UNREACHABLE("non-exhaustive kind-handling switch?");
   1888  return false;
   1889 }
   1890 
   1891 ArrayBufferObject::BufferContents ArrayBufferObject::createMappedContents(
   1892    int fd, size_t offset, size_t length) {
   1893  void* data =
   1894      gc::AllocateMappedContent(fd, offset, length, ARRAY_BUFFER_ALIGNMENT);
   1895  return BufferContents::createMapped(data);
   1896 }
   1897 
   1898 uint8_t* FixedLengthArrayBufferObject::inlineDataPointer() const {
   1899  return static_cast<uint8_t*>(fixedData(JSCLASS_RESERVED_SLOTS(&class_)));
   1900 }
   1901 
   1902 uint8_t* ResizableArrayBufferObject::inlineDataPointer() const {
   1903  return static_cast<uint8_t*>(fixedData(JSCLASS_RESERVED_SLOTS(&class_)));
   1904 }
   1905 
   1906 uint8_t* ImmutableArrayBufferObject::inlineDataPointer() const {
   1907  return static_cast<uint8_t*>(fixedData(JSCLASS_RESERVED_SLOTS(&class_)));
   1908 }
   1909 
   1910 uint8_t* ArrayBufferObject::dataPointer() const {
   1911  return static_cast<uint8_t*>(getFixedSlot(DATA_SLOT).toPrivate());
   1912 }
   1913 
   1914 SharedMem<uint8_t*> ArrayBufferObject::dataPointerShared() const {
   1915  return SharedMem<uint8_t*>::unshared(getFixedSlot(DATA_SLOT).toPrivate());
   1916 }
   1917 
   1918 ArrayBufferObject::FreeInfo* ArrayBufferObject::freeInfo() const {
   1919  MOZ_ASSERT(isExternal());
   1920  MOZ_ASSERT(!isResizable());
   1921  MOZ_ASSERT(!isImmutable());
   1922  auto* data = as<FixedLengthArrayBufferObject>().inlineDataPointer();
   1923  return reinterpret_cast<FreeInfo*>(data);
   1924 }
   1925 
   1926 void ArrayBufferObject::releaseData(JS::GCContext* gcx) {
   1927  switch (bufferKind()) {
   1928    case INLINE_DATA:
   1929      // Inline data doesn't require releasing.
   1930      break;
   1931    case MALLOCED_ARRAYBUFFER_CONTENTS_ARENA:
   1932    case MALLOCED_UNKNOWN_ARENA:
   1933      gcx->free_(this, dataPointer(), associatedBytes(),
   1934                 MemoryUse::ArrayBufferContents);
   1935      break;
   1936    case NO_DATA:
   1937      // There's nothing to release if there's no data.
   1938      MOZ_ASSERT(dataPointer() == nullptr);
   1939      break;
   1940    case USER_OWNED:
   1941      // User-owned data is released by, well, the user.
   1942      break;
   1943    case MAPPED:
   1944      gc::DeallocateMappedContent(dataPointer(), byteLength());
   1945      gcx->removeCellMemory(this, associatedBytes(),
   1946                            MemoryUse::ArrayBufferContents);
   1947      break;
   1948    case WASM:
   1949      WasmArrayRawBuffer::Release(dataPointer());
   1950      gcx->removeCellMemory(this, byteLength(), MemoryUse::ArrayBufferContents);
   1951      break;
   1952    case EXTERNAL:
   1953      MOZ_ASSERT(freeInfo()->freeFunc);
   1954      {
   1955        // The analyzer can't know for sure whether the embedder-supplied
   1956        // free function will GC. We give the analyzer a hint here.
   1957        // (Doing a GC in the free function is considered a programmer
   1958        // error.)
   1959        JS::AutoSuppressGCAnalysis nogc;
   1960        freeInfo()->freeFunc(dataPointer(), freeInfo()->freeUserData);
   1961      }
   1962      break;
   1963  }
   1964 }
   1965 
   1966 void ArrayBufferObject::setDataPointer(BufferContents contents) {
   1967  setFixedSlot(DATA_SLOT, PrivateValue(contents.data()));
   1968  setFlags((flags() & ~KIND_MASK) | contents.kind());
   1969 
   1970  if (isExternal()) {
   1971    auto info = freeInfo();
   1972    info->freeFunc = contents.freeFunc();
   1973    info->freeUserData = contents.freeUserData();
   1974  }
   1975 }
   1976 
   1977 size_t ArrayBufferObject::byteLength() const {
   1978  return size_t(getFixedSlot(BYTE_LENGTH_SLOT).toPrivate());
   1979 }
   1980 
   1981 inline size_t ArrayBufferObject::associatedBytes() const {
   1982  if (isMalloced()) {
   1983    return maxByteLength();
   1984  }
   1985  if (isMapped()) {
   1986    return RoundUp(byteLength(), js::gc::SystemPageSize());
   1987  }
   1988  MOZ_CRASH("Unexpected buffer kind");
   1989 }
   1990 
   1991 void ArrayBufferObject::setByteLength(size_t length) {
   1992  MOZ_ASSERT(length <= ArrayBufferObject::ByteLengthLimit);
   1993  setFixedSlot(BYTE_LENGTH_SLOT, PrivateValue(length));
   1994 }
   1995 
   1996 size_t ArrayBufferObject::wasmMappedSize() const {
   1997  if (isWasm()) {
   1998    return contents().wasmBuffer()->mappedSize();
   1999  }
   2000  return byteLength();
   2001 }
   2002 
   2003 AddressType ArrayBufferObject::wasmAddressType() const {
   2004  if (isWasm()) {
   2005    return contents().wasmBuffer()->addressType();
   2006  }
   2007  MOZ_ASSERT(isPreparedForAsmJS());
   2008  return wasm::AddressType::I32;
   2009 }
   2010 
   2011 wasm::PageSize ArrayBufferObject::wasmPageSize() const {
   2012  if (isWasm()) {
   2013    return contents().wasmBuffer()->pageSize();
   2014  }
   2015  MOZ_ASSERT(isPreparedForAsmJS());
   2016  return wasm::PageSize::Standard;
   2017 }
   2018 
   2019 Pages ArrayBufferObject::wasmPages() const {
   2020  if (isWasm()) {
   2021    return contents().wasmBuffer()->pages();
   2022  }
   2023  MOZ_ASSERT(isPreparedForAsmJS());
   2024  return Pages::fromByteLengthExact(byteLength(), wasmPageSize());
   2025 }
   2026 
   2027 Pages ArrayBufferObject::wasmClampedMaxPages() const {
   2028  if (isWasm()) {
   2029    return contents().wasmBuffer()->clampedMaxPages();
   2030  }
   2031  MOZ_ASSERT(isPreparedForAsmJS());
   2032  return Pages::fromByteLengthExact(byteLength(), wasmPageSize());
   2033 }
   2034 
   2035 Maybe<Pages> ArrayBufferObject::wasmSourceMaxPages() const {
   2036  if (isWasm()) {
   2037    return contents().wasmBuffer()->sourceMaxPages();
   2038  }
   2039  MOZ_ASSERT(isPreparedForAsmJS());
   2040  return Some<Pages>(Pages::fromByteLengthExact(byteLength(), wasmPageSize()));
   2041 }
   2042 
   2043 size_t js::WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf) {
   2044  if (buf->is<ArrayBufferObject>()) {
   2045    return buf->as<ArrayBufferObject>().wasmMappedSize();
   2046  }
   2047  return buf->as<SharedArrayBufferObject>().wasmMappedSize();
   2048 }
   2049 
   2050 AddressType js::WasmArrayBufferAddressType(
   2051    const ArrayBufferObjectMaybeShared* buf) {
   2052  if (buf->is<ArrayBufferObject>()) {
   2053    return buf->as<ArrayBufferObject>().wasmAddressType();
   2054  }
   2055  return buf->as<SharedArrayBufferObject>().wasmAddressType();
   2056 }
   2057 wasm::PageSize js::WasmArrayBufferPageSize(
   2058    const ArrayBufferObjectMaybeShared* buf) {
   2059  if (buf->is<ArrayBufferObject>()) {
   2060    return buf->as<ArrayBufferObject>().wasmPageSize();
   2061  }
   2062  return buf->as<SharedArrayBufferObject>().wasmPageSize();
   2063 }
   2064 Pages js::WasmArrayBufferPages(const ArrayBufferObjectMaybeShared* buf) {
   2065  if (buf->is<ArrayBufferObject>()) {
   2066    return buf->as<ArrayBufferObject>().wasmPages();
   2067  }
   2068  return buf->as<SharedArrayBufferObject>().volatileWasmPages();
   2069 }
   2070 Pages js::WasmArrayBufferClampedMaxPages(
   2071    const ArrayBufferObjectMaybeShared* buf) {
   2072  if (buf->is<ArrayBufferObject>()) {
   2073    return buf->as<ArrayBufferObject>().wasmClampedMaxPages();
   2074  }
   2075  return buf->as<SharedArrayBufferObject>().wasmClampedMaxPages();
   2076 }
   2077 Maybe<Pages> js::WasmArrayBufferSourceMaxPages(
   2078    const ArrayBufferObjectMaybeShared* buf) {
   2079  if (buf->is<ArrayBufferObject>()) {
   2080    return buf->as<ArrayBufferObject>().wasmSourceMaxPages();
   2081  }
   2082  return Some(buf->as<SharedArrayBufferObject>().wasmSourceMaxPages());
   2083 }
   2084 
   2085 static void CheckStealPreconditions(Handle<ArrayBufferObject*> buffer,
   2086                                    JSContext* cx) {
   2087  cx->check(buffer);
   2088 
   2089  MOZ_ASSERT(!buffer->isDetached(), "can't steal from a detached buffer");
   2090  MOZ_ASSERT(!buffer->isImmutable(), "can't steal from an immutable buffer");
   2091  MOZ_ASSERT(!buffer->isLengthPinned(),
   2092             "can't steal from a buffer with a pinned length");
   2093  MOZ_ASSERT(!buffer->isPreparedForAsmJS(),
   2094             "asm.js-prepared buffers don't have detachable/stealable data");
   2095 }
   2096 
   2097 /* static */
   2098 ArrayBufferObject* ArrayBufferObject::wasmGrowToPagesInPlace(
   2099    wasm::AddressType t, Pages newPages, Handle<ArrayBufferObject*> oldBuf,
   2100    JSContext* cx) {
   2101  if (oldBuf->isLengthPinned()) {
   2102    return nullptr;
   2103  }
   2104 
   2105  MOZ_ASSERT(oldBuf->isWasm());
   2106 
   2107  // Check that the new pages is within our allowable range. This will
   2108  // simultaneously check against the maximum specified in source and our
   2109  // implementation limits.
   2110  if (newPages > oldBuf->wasmClampedMaxPages()) {
   2111    return nullptr;
   2112  }
   2113  MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t, newPages.pageSize()) &&
   2114             newPages.byteLength() <= ArrayBufferObject::ByteLengthLimit);
   2115 
   2116  if (oldBuf->is<ResizableArrayBufferObject>()) {
   2117    RemoveCellMemory(oldBuf, oldBuf->byteLength(),
   2118                     MemoryUse::ArrayBufferContents);
   2119 
   2120    if (!oldBuf->contents().wasmBuffer()->growToPagesInPlace(newPages)) {
   2121      // If fails, the buffer still exists on oldBuf, keep tracking
   2122      // cell memory there.
   2123      AddCellMemory(oldBuf, oldBuf->byteLength(),
   2124                    MemoryUse::ArrayBufferContents);
   2125      return nullptr;
   2126    }
   2127    oldBuf->setByteLength(newPages.byteLength());
   2128    AddCellMemory(oldBuf, newPages.byteLength(),
   2129                  MemoryUse::ArrayBufferContents);
   2130    return oldBuf;
   2131  }
   2132 
   2133  CheckStealPreconditions(oldBuf, cx);
   2134 
   2135  // We have checked against the clamped maximum and so we know we can convert
   2136  // to byte lengths now.
   2137  size_t newSize = newPages.byteLength();
   2138 
   2139  // On failure, do not throw and ensure that the original buffer is
   2140  // unmodified and valid. After WasmArrayRawBuffer::growToPagesInPlace(), the
   2141  // wasm-visible length of the buffer has been increased so it must be the
   2142  // last fallible operation.
   2143 
   2144  auto* newBuf = ArrayBufferObject::createEmpty(cx);
   2145  if (!newBuf) {
   2146    cx->clearPendingException();
   2147    return nullptr;
   2148  }
   2149 
   2150  MOZ_ASSERT(newBuf->isNoData());
   2151 
   2152  if (!oldBuf->contents().wasmBuffer()->growToPagesInPlace(newPages)) {
   2153    return nullptr;
   2154  }
   2155 
   2156  // Extract the grown contents from |oldBuf|.
   2157  BufferContents oldContents = oldBuf->contents();
   2158 
   2159  // Overwrite |oldBuf|'s data pointer *without* releasing old data.
   2160  oldBuf->setDataPointer(BufferContents::createNoData());
   2161 
   2162  // Detach |oldBuf| now that doing so won't release |oldContents|.
   2163  RemoveCellMemory(oldBuf, oldBuf->byteLength(),
   2164                   MemoryUse::ArrayBufferContents);
   2165  ArrayBufferObject::detach(cx, oldBuf);
   2166 
   2167  // Set |newBuf|'s contents to |oldBuf|'s original contents.
   2168  newBuf->initialize(newSize, oldContents);
   2169  AddCellMemory(newBuf, newSize, MemoryUse::ArrayBufferContents);
   2170 
   2171  return newBuf;
   2172 }
   2173 
   2174 /* static */
   2175 ArrayBufferObject* ArrayBufferObject::wasmMovingGrowToPages(
   2176    AddressType t, Pages newPages, Handle<ArrayBufferObject*> oldBuf,
   2177    JSContext* cx) {
   2178  MOZ_ASSERT(oldBuf->is<FixedLengthArrayBufferObject>());
   2179 
   2180  // On failure, do not throw and ensure that the original buffer is
   2181  // unmodified and valid.
   2182  if (oldBuf->isLengthPinned()) {
   2183    return nullptr;
   2184  }
   2185 
   2186  // Check that the new pages is within our allowable range. This will
   2187  // simultaneously check against the maximum specified in source and our
   2188  // implementation limits.
   2189  if (newPages > oldBuf->wasmClampedMaxPages()) {
   2190    return nullptr;
   2191  }
   2192  MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t, newPages.pageSize()) &&
   2193             newPages.byteLength() <= ArrayBufferObject::ByteLengthLimit);
   2194 
   2195  // We have checked against the clamped maximum and so we know we can convert
   2196  // to byte lengths now.
   2197  size_t newSize = newPages.byteLength();
   2198 
   2199  // TODO: If the memory reservation allows, or if we can extend the mapping in
   2200  // place, fall back to wasmGrowToPagesInPlace to avoid a new allocation +
   2201  // copy.
   2202 
   2203  Rooted<ArrayBufferObject*> newBuf(cx, ArrayBufferObject::createEmpty(cx));
   2204  if (!newBuf) {
   2205    cx->clearPendingException();
   2206    return nullptr;
   2207  }
   2208 
   2209  Pages clampedMaxPages =
   2210      wasm::ClampedMaxPages(t, newPages, Nothing(), /* hugeMemory */ false);
   2211  MOZ_ASSERT(newPages.pageSize() == oldBuf->wasmPageSize());
   2212  WasmArrayRawBuffer* newRawBuf = WasmArrayRawBuffer::AllocateWasm(
   2213      oldBuf->wasmAddressType(), oldBuf->wasmPageSize(), newPages,
   2214      clampedMaxPages, Nothing(), Nothing());
   2215  if (!newRawBuf) {
   2216    return nullptr;
   2217  }
   2218 
   2219  AddCellMemory(newBuf, newSize, MemoryUse::ArrayBufferContents);
   2220 
   2221  BufferContents contents =
   2222      BufferContents::createWasm(newRawBuf->dataPointer());
   2223  newBuf->initialize(newSize, contents);
   2224 
   2225  memcpy(newBuf->dataPointer(), oldBuf->dataPointer(), oldBuf->byteLength());
   2226  ArrayBufferObject::detach(cx, oldBuf);
   2227 
   2228  return newBuf;
   2229 }
   2230 
   2231 /* static */
   2232 void ArrayBufferObject::wasmDiscard(Handle<ArrayBufferObject*> buf,
   2233                                    uint64_t byteOffset, uint64_t byteLen) {
   2234  MOZ_ASSERT(buf->isWasm());
   2235  buf->contents().wasmBuffer()->discard(byteOffset, byteLen);
   2236 }
   2237 
   2238 uint32_t ArrayBufferObject::flags() const {
   2239  return uint32_t(getFixedSlot(FLAGS_SLOT).toInt32());
   2240 }
   2241 
   2242 void ArrayBufferObject::setFlags(uint32_t flags) {
   2243  setFixedSlot(FLAGS_SLOT, Int32Value(flags));
   2244 }
   2245 
   2246 static constexpr js::gc::AllocKind GetArrayBufferGCObjectKind(size_t numSlots) {
   2247  if (numSlots <= 4) {
   2248    return js::gc::AllocKind::ARRAYBUFFER4;
   2249  }
   2250  if (numSlots <= 6) {
   2251    return js::gc::AllocKind::ARRAYBUFFER6;
   2252  }
   2253  if (numSlots <= 8) {
   2254    return js::gc::AllocKind::ARRAYBUFFER8;
   2255  }
   2256  if (numSlots <= 12) {
   2257    return js::gc::AllocKind::ARRAYBUFFER12;
   2258  }
   2259  return js::gc::AllocKind::ARRAYBUFFER16;
   2260 }
   2261 
   2262 template <class ArrayBufferType>
   2263 static ArrayBufferType* NewArrayBufferObject(JSContext* cx, HandleObject proto_,
   2264                                             gc::AllocKind allocKind) {
   2265  MOZ_ASSERT(allocKind == gc::AllocKind::ARRAYBUFFER4 ||
   2266             allocKind == gc::AllocKind::ARRAYBUFFER6 ||
   2267             allocKind == gc::AllocKind::ARRAYBUFFER8 ||
   2268             allocKind == gc::AllocKind::ARRAYBUFFER12 ||
   2269             allocKind == gc::AllocKind::ARRAYBUFFER16);
   2270 
   2271  static_assert(std::is_same_v<ArrayBufferType, FixedLengthArrayBufferObject> ||
   2272                std::is_same_v<ArrayBufferType, ResizableArrayBufferObject> ||
   2273                std::is_same_v<ArrayBufferType, ImmutableArrayBufferObject>);
   2274 
   2275  RootedObject proto(cx, proto_);
   2276  if (!proto) {
   2277    proto = GlobalObject::getOrCreatePrototype(cx, JSProto_ArrayBuffer);
   2278    if (!proto) {
   2279      MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "creating ArrayBuffer proto");
   2280      return nullptr;
   2281    }
   2282  }
   2283 
   2284  const JSClass* clasp = &ArrayBufferType::class_;
   2285 
   2286  // Array buffers can store data inline so we only use fixed slots to cover the
   2287  // reserved slots, ignoring the AllocKind.
   2288  MOZ_ASSERT(ClassCanHaveFixedData(clasp));
   2289  constexpr size_t nfixed = ArrayBufferType::RESERVED_SLOTS;
   2290  static_assert(nfixed <= NativeObject::MAX_FIXED_SLOTS);
   2291 
   2292  Rooted<SharedShape*> shape(
   2293      cx,
   2294      SharedShape::getInitialShape(cx, clasp, cx->realm(), AsTaggedProto(proto),
   2295                                   nfixed, ObjectFlags()));
   2296  if (!shape) {
   2297    MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "get ArrayBuffer initial shape");
   2298    return nullptr;
   2299  }
   2300 
   2301  // Array buffers can't be nursery allocated but can be background-finalized.
   2302  MOZ_ASSERT(IsBackgroundFinalized(allocKind));
   2303  MOZ_ASSERT(!CanNurseryAllocateFinalizedClass(clasp));
   2304  constexpr gc::Heap heap = gc::Heap::Tenured;
   2305 
   2306  auto* buffer =
   2307      NativeObject::create<ArrayBufferType>(cx, allocKind, heap, shape);
   2308  if (!buffer) {
   2309    MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "create NativeObject failed");
   2310  }
   2311  return buffer;
   2312 }
   2313 
   2314 // Creates a new ArrayBufferObject with %ArrayBuffer.prototype% as proto and no
   2315 // space for inline data.
   2316 static auto* NewArrayBufferObject(JSContext* cx) {
   2317  constexpr auto allocKind =
   2318      GetArrayBufferGCObjectKind(FixedLengthArrayBufferObject::RESERVED_SLOTS);
   2319  return NewArrayBufferObject<FixedLengthArrayBufferObject>(cx, nullptr,
   2320                                                            allocKind);
   2321 }
   2322 static auto* NewResizableArrayBufferObject(JSContext* cx) {
   2323  constexpr auto allocKind =
   2324      GetArrayBufferGCObjectKind(ResizableArrayBufferObject::RESERVED_SLOTS);
   2325  return NewArrayBufferObject<ResizableArrayBufferObject>(cx, nullptr,
   2326                                                          allocKind);
   2327 }
   2328 static auto* NewImmutableArrayBufferObject(JSContext* cx) {
   2329  constexpr auto allocKind =
   2330      GetArrayBufferGCObjectKind(ImmutableArrayBufferObject::RESERVED_SLOTS);
   2331  return NewArrayBufferObject<ImmutableArrayBufferObject>(cx, nullptr,
   2332                                                          allocKind);
   2333 }
   2334 
   2335 ArrayBufferObject* ArrayBufferObject::createForContents(
   2336    JSContext* cx, size_t nbytes, BufferContents contents) {
   2337  MOZ_ASSERT(contents);
   2338  MOZ_ASSERT(contents.kind() != INLINE_DATA);
   2339  MOZ_ASSERT(contents.kind() != NO_DATA);
   2340  MOZ_ASSERT(contents.kind() != WASM);
   2341 
   2342  // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
   2343  if (!CheckArrayBufferTooLarge(cx, nbytes)) {
   2344    return nullptr;
   2345  }
   2346 
   2347  // Some |contents| kinds need to store extra data in the ArrayBuffer beyond a
   2348  // data pointer.  If needed for the particular kind, add extra fixed slots to
   2349  // the ArrayBuffer for use as raw storage to store such information.
   2350  constexpr size_t reservedSlots = FixedLengthArrayBufferObject::RESERVED_SLOTS;
   2351 
   2352  size_t nAllocated = 0;
   2353  size_t nslots = reservedSlots;
   2354  if (contents.kind() == USER_OWNED) {
   2355    // No accounting to do in this case.
   2356  } else if (contents.kind() == EXTERNAL) {
   2357    // Store the FreeInfo in the inline data slots so that we
   2358    // don't use up slots for it in non-refcounted array buffers.
   2359    constexpr size_t freeInfoSlots = HowMany(sizeof(FreeInfo), sizeof(Value));
   2360    static_assert(
   2361        reservedSlots + freeInfoSlots <= NativeObject::MAX_FIXED_SLOTS,
   2362        "FreeInfo must fit in inline slots");
   2363    nslots += freeInfoSlots;
   2364  } else {
   2365    // The ABO is taking ownership, so account the bytes against the zone.
   2366    nAllocated = nbytes;
   2367    if (contents.kind() == MAPPED) {
   2368      nAllocated = RoundUp(nbytes, js::gc::SystemPageSize());
   2369    } else {
   2370      MOZ_ASSERT(contents.kind() == MALLOCED_ARRAYBUFFER_CONTENTS_ARENA ||
   2371                     contents.kind() == MALLOCED_UNKNOWN_ARENA,
   2372                 "should have handled all possible callers' kinds");
   2373    }
   2374  }
   2375 
   2376  gc::AllocKind allocKind = GetArrayBufferGCObjectKind(nslots);
   2377 
   2378  AutoSetNewObjectMetadata metadata(cx);
   2379  Rooted<ArrayBufferObject*> buffer(
   2380      cx, NewArrayBufferObject<FixedLengthArrayBufferObject>(cx, nullptr,
   2381                                                             allocKind));
   2382  if (!buffer) {
   2383    return nullptr;
   2384  }
   2385 
   2386  MOZ_ASSERT(!gc::IsInsideNursery(buffer),
   2387             "ArrayBufferObject has a finalizer that must be called to not "
   2388             "leak in some cases, so it can't be nursery-allocated");
   2389 
   2390  buffer->initialize(nbytes, contents);
   2391 
   2392  if (contents.kind() == MAPPED ||
   2393      contents.kind() == MALLOCED_ARRAYBUFFER_CONTENTS_ARENA ||
   2394      contents.kind() == MALLOCED_UNKNOWN_ARENA) {
   2395    AddCellMemory(buffer, nAllocated, MemoryUse::ArrayBufferContents);
   2396  }
   2397 
   2398  return buffer;
   2399 }
   2400 
   2401 ArrayBufferObject* ArrayBufferObject::createFromTypedArrayMallocedElements(
   2402    JSContext* cx, Handle<FixedLengthTypedArrayObject*> tarray) {
   2403  MOZ_ASSERT(cx->realm() == tarray->realm());
   2404  MOZ_ASSERT(tarray->hasMallocedElements(cx));
   2405 
   2406  size_t byteLength = tarray->byteLength();
   2407 
   2408  // The typed array's byteLength must be a valid array buffer length.
   2409  static_assert(TypedArrayObject::ByteLengthLimit ==
   2410                ArrayBufferObject::ByteLengthLimit);
   2411  MOZ_RELEASE_ASSERT(byteLength <= ArrayBufferObject::ByteLengthLimit);
   2412 
   2413  constexpr size_t reservedSlots = FixedLengthArrayBufferObject::RESERVED_SLOTS;
   2414  constexpr gc::AllocKind allocKind = GetArrayBufferGCObjectKind(reservedSlots);
   2415 
   2416  AutoSetNewObjectMetadata metadata(cx);
   2417  Rooted<ArrayBufferObject*> buffer(
   2418      cx, NewArrayBufferObject<FixedLengthArrayBufferObject>(cx, nullptr,
   2419                                                             allocKind));
   2420  if (!buffer) {
   2421    return nullptr;
   2422  }
   2423 
   2424  MOZ_ASSERT(!gc::IsInsideNursery(buffer),
   2425             "ArrayBufferObject has a finalizer that must be called to not "
   2426             "leak in some cases, so it can't be nursery-allocated");
   2427 
   2428  // Transfer ownership of the malloced buffer from the typed array to the
   2429  // new array buffer.
   2430 
   2431  size_t nbytes = RoundUp(byteLength, sizeof(Value));
   2432  if (!tarray->isTenured()) {
   2433    cx->nursery().removeMallocedBuffer(tarray->elements(), nbytes);
   2434  }
   2435  RemoveCellMemory(tarray, nbytes, MemoryUse::TypedArrayElements);
   2436 
   2437  auto contents = BufferContents::createMallocedArrayBufferContentsArena(
   2438      tarray->elements());
   2439  buffer->initialize(byteLength, contents);
   2440  AddCellMemory(buffer, byteLength, MemoryUse::ArrayBufferContents);
   2441 
   2442  return buffer;
   2443 }
   2444 
   2445 template <class ArrayBufferType, ArrayBufferObject::FillContents FillType>
   2446 /* static */ std::tuple<ArrayBufferType*, uint8_t*>
   2447 ArrayBufferObject::createUninitializedBufferAndData(
   2448    JSContext* cx, size_t nbytes, AutoSetNewObjectMetadata&,
   2449    JS::Handle<JSObject*> proto) {
   2450  MOZ_ASSERT(nbytes <= ArrayBufferObject::ByteLengthLimit,
   2451             "caller must validate the byte count it passes");
   2452 
   2453  static_assert(std::is_same_v<ArrayBufferType, FixedLengthArrayBufferObject> ||
   2454                std::is_same_v<ArrayBufferType, ResizableArrayBufferObject> ||
   2455                std::is_same_v<ArrayBufferType, ImmutableArrayBufferObject>);
   2456 
   2457  // Try fitting the data inline with the object by repurposing fixed-slot
   2458  // storage.  Add extra fixed slots if necessary to accomplish this, but don't
   2459  // exceed the maximum number of fixed slots!
   2460  size_t nslots = ArrayBufferType::RESERVED_SLOTS;
   2461  ArrayBufferContents data;
   2462  if (nbytes <= ArrayBufferType::MaxInlineBytes) {
   2463    int newSlots = HowMany(nbytes, sizeof(Value));
   2464    MOZ_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
   2465 
   2466    nslots += newSlots;
   2467  } else {
   2468    data = FillType == FillContents::Uninitialized
   2469               ? AllocateUninitializedArrayBufferContents(cx, nbytes)
   2470               : AllocateArrayBufferContents(cx, nbytes);
   2471    if (!data) {
   2472      if (cx->brittleMode) {
   2473        if (nbytes < INT32_MAX) {
   2474          MOZ_DIAGNOSTIC_CRASH("ArrayBuffer allocation OOM < 2GB - 1");
   2475        } else {
   2476          MOZ_DIAGNOSTIC_ASSERT(
   2477              false,
   2478              "ArrayBuffer allocation OOM between 2GB and ByteLengthLimit");
   2479        }
   2480      }
   2481      return {nullptr, nullptr};
   2482    }
   2483  }
   2484 
   2485  gc::AllocKind allocKind = GetArrayBufferGCObjectKind(nslots);
   2486 
   2487  auto* buffer = NewArrayBufferObject<ArrayBufferType>(cx, proto, allocKind);
   2488  if (!buffer) {
   2489    return {nullptr, nullptr};
   2490  }
   2491 
   2492  MOZ_ASSERT(!gc::IsInsideNursery(buffer),
   2493             "ArrayBufferObject has a finalizer that must be called to not "
   2494             "leak in some cases, so it can't be nursery-allocated");
   2495 
   2496  if (data) {
   2497    return {buffer, data.release()};
   2498  }
   2499 
   2500  if constexpr (FillType == FillContents::Zero) {
   2501    memset(buffer->inlineDataPointer(), 0, nbytes);
   2502  }
   2503  return {buffer, nullptr};
   2504 }
   2505 
   2506 template <class ArrayBufferType, ArrayBufferObject::FillContents FillType>
   2507 /* static */ std::tuple<ArrayBufferType*, uint8_t*>
   2508 ArrayBufferObject::createBufferAndData(
   2509    JSContext* cx, size_t nbytes, AutoSetNewObjectMetadata& metadata,
   2510    JS::Handle<JSObject*> proto /* = nullptr */) {
   2511  MOZ_ASSERT(nbytes <= ArrayBufferObject::ByteLengthLimit,
   2512             "caller must validate the byte count it passes");
   2513 
   2514  static_assert(!std::is_same_v<ArrayBufferType, ResizableArrayBufferObject>,
   2515                "Use ResizableArrayBufferObject::createBufferAndData");
   2516 
   2517  auto [buffer, data] =
   2518      createUninitializedBufferAndData<ArrayBufferType, FillType>(
   2519          cx, nbytes, metadata, proto);
   2520  if (!buffer) {
   2521    return {nullptr, nullptr};
   2522  }
   2523 
   2524  if (data) {
   2525    buffer->initialize(
   2526        nbytes, BufferContents::createMallocedArrayBufferContentsArena(data));
   2527    AddCellMemory(buffer, nbytes, MemoryUse::ArrayBufferContents);
   2528  } else {
   2529    data = buffer->inlineDataPointer();
   2530    buffer->initialize(nbytes, BufferContents::createInlineData(data));
   2531  }
   2532  return {buffer, data};
   2533 }
   2534 
   2535 template <ArrayBufferObject::FillContents FillType>
   2536 /* static */ std::tuple<ResizableArrayBufferObject*, uint8_t*>
   2537 ResizableArrayBufferObject::createBufferAndData(
   2538    JSContext* cx, size_t byteLength, size_t maxByteLength,
   2539    AutoSetNewObjectMetadata& metadata, Handle<JSObject*> proto) {
   2540  MOZ_ASSERT(byteLength <= maxByteLength);
   2541  MOZ_ASSERT(maxByteLength <= ArrayBufferObject::ByteLengthLimit,
   2542             "caller must validate the byte count it passes");
   2543 
   2544  // NOTE: The spec proposal for resizable ArrayBuffers suggests to use a
   2545  // virtual memory based approach to avoid eagerly allocating the maximum byte
   2546  // length. We don't yet support this and instead are allocating the maximum
   2547  // byte length direct from the start.
   2548  size_t nbytes = maxByteLength;
   2549 
   2550  auto [buffer, data] =
   2551      createUninitializedBufferAndData<ResizableArrayBufferObject, FillType>(
   2552          cx, nbytes, metadata, proto);
   2553  if (!buffer) {
   2554    return {nullptr, nullptr};
   2555  }
   2556 
   2557  if (data) {
   2558    buffer->initialize(
   2559        byteLength, maxByteLength,
   2560        BufferContents::createMallocedArrayBufferContentsArena(data));
   2561    AddCellMemory(buffer, nbytes, MemoryUse::ArrayBufferContents);
   2562  } else {
   2563    data = buffer->inlineDataPointer();
   2564    buffer->initialize(byteLength, maxByteLength,
   2565                       BufferContents::createInlineData(data));
   2566  }
   2567  return {buffer, data};
   2568 }
   2569 
   2570 template <class ArrayBufferType>
   2571 /* static */ ArrayBufferType* ArrayBufferObject::copy(
   2572    JSContext* cx, size_t newByteLength,
   2573    JS::Handle<ArrayBufferObject*> source) {
   2574  MOZ_ASSERT(!source->isDetached());
   2575  MOZ_ASSERT(newByteLength <= ArrayBufferObject::ByteLengthLimit,
   2576             "caller must validate the byte count it passes");
   2577 
   2578  size_t sourceByteLength = source->byteLength();
   2579 
   2580  if (newByteLength > sourceByteLength) {
   2581    // Copy into a larger buffer.
   2582    AutoSetNewObjectMetadata metadata(cx);
   2583    auto [buffer, toFill] =
   2584        createBufferAndData<ArrayBufferType, FillContents::Zero>(
   2585            cx, newByteLength, metadata, nullptr);
   2586    if (!buffer) {
   2587      return nullptr;
   2588    }
   2589 
   2590    std::copy_n(source->dataPointer(), sourceByteLength, toFill);
   2591 
   2592    return buffer;
   2593  }
   2594 
   2595  // Copy into a smaller or same size buffer.
   2596  AutoSetNewObjectMetadata metadata(cx);
   2597  auto [buffer, toFill] =
   2598      createBufferAndData<ArrayBufferType, FillContents::Uninitialized>(
   2599          cx, newByteLength, metadata, nullptr);
   2600  if (!buffer) {
   2601    return nullptr;
   2602  }
   2603 
   2604  std::uninitialized_copy_n(source->dataPointer(), newByteLength, toFill);
   2605 
   2606  return buffer;
   2607 }
   2608 
   2609 /* static */ ResizableArrayBufferObject* ResizableArrayBufferObject::copy(
   2610    JSContext* cx, size_t newByteLength,
   2611    JS::Handle<ResizableArrayBufferObject*> source) {
   2612  MOZ_ASSERT(!source->isDetached());
   2613  MOZ_ASSERT(newByteLength <= source->maxByteLength());
   2614 
   2615  size_t sourceByteLength = source->byteLength();
   2616  size_t newMaxByteLength = source->maxByteLength();
   2617 
   2618  if (newByteLength > sourceByteLength) {
   2619    // Copy into a larger buffer.
   2620    AutoSetNewObjectMetadata metadata(cx);
   2621    auto [buffer, toFill] = createBufferAndData<FillContents::Zero>(
   2622        cx, newByteLength, newMaxByteLength, metadata, nullptr);
   2623    if (!buffer) {
   2624      return nullptr;
   2625    }
   2626 
   2627    // The `createBufferAndData()` call first zero-initializes the complete
   2628    // buffer and then we copy over |sourceByteLength| bytes from |source|. It
   2629    // seems prudent to only zero-initialize the trailing bytes of |toFill|
   2630    // to avoid writing twice to `toFill[0..newByteLength]`. We don't yet
   2631    // implement this optimization, because this method is only called for
   2632    // small, inline buffers, so any write optimizations probably won't make
   2633    // much of a difference.
   2634    std::copy_n(source->dataPointer(), sourceByteLength, toFill);
   2635 
   2636    return buffer;
   2637  }
   2638 
   2639  // Copy into a smaller or same size buffer.
   2640  AutoSetNewObjectMetadata metadata(cx);
   2641  auto [buffer, toFill] = createBufferAndData<FillContents::Uninitialized>(
   2642      cx, newByteLength, newMaxByteLength, metadata, nullptr);
   2643  if (!buffer) {
   2644    return nullptr;
   2645  }
   2646 
   2647  std::uninitialized_copy_n(source->dataPointer(), newByteLength, toFill);
   2648 
   2649  return buffer;
   2650 }
   2651 
   2652 template <class ArrayBufferType>
   2653 /* static */ ArrayBufferType* ArrayBufferObject::copyAndDetach(
   2654    JSContext* cx, size_t newByteLength,
   2655    JS::Handle<ArrayBufferObject*> source) {
   2656  MOZ_ASSERT(!source->isDetached());
   2657  MOZ_ASSERT(!source->isImmutable());
   2658  MOZ_ASSERT(!source->isLengthPinned());
   2659  MOZ_ASSERT(newByteLength <= ArrayBufferObject::ByteLengthLimit,
   2660             "caller must validate the byte count it passes");
   2661 
   2662  if (newByteLength > ArrayBufferType::MaxInlineBytes && source->isMalloced()) {
   2663    if (newByteLength == source->associatedBytes()) {
   2664      return copyAndDetachSteal<ArrayBufferType>(cx, source);
   2665    }
   2666    if (source->bufferKind() ==
   2667        ArrayBufferObject::MALLOCED_ARRAYBUFFER_CONTENTS_ARENA) {
   2668      return copyAndDetachRealloc<ArrayBufferType>(cx, newByteLength, source);
   2669    }
   2670  }
   2671 
   2672  auto* newBuffer =
   2673      ArrayBufferObject::copy<ArrayBufferType>(cx, newByteLength, source);
   2674  if (!newBuffer) {
   2675    return nullptr;
   2676  }
   2677  ArrayBufferObject::detach(cx, source);
   2678 
   2679  return newBuffer;
   2680 }
   2681 
   2682 template <class ArrayBufferType>
   2683 /* static */ ArrayBufferType* ArrayBufferObject::copyAndDetachSteal(
   2684    JSContext* cx, JS::Handle<ArrayBufferObject*> source) {
   2685  MOZ_ASSERT(!source->isDetached());
   2686  MOZ_ASSERT(!source->isImmutable());
   2687  MOZ_ASSERT(!source->isLengthPinned());
   2688  MOZ_ASSERT(source->isMalloced());
   2689 
   2690  size_t newByteLength = source->associatedBytes();
   2691  MOZ_ASSERT(newByteLength > ArrayBufferType::MaxInlineBytes,
   2692             "prefer copying small buffers");
   2693  MOZ_ASSERT(source->byteLength() <= newByteLength,
   2694             "source length is less-or-equal to |newByteLength|");
   2695 
   2696  auto* newBuffer = ArrayBufferType::createEmpty(cx);
   2697  if (!newBuffer) {
   2698    return nullptr;
   2699  }
   2700 
   2701  // Extract the contents from |source|.
   2702  BufferContents contents = source->contents();
   2703  MOZ_ASSERT(contents);
   2704  MOZ_ASSERT(contents.kind() == MALLOCED_ARRAYBUFFER_CONTENTS_ARENA ||
   2705             contents.kind() == MALLOCED_UNKNOWN_ARENA);
   2706 
   2707  // Overwrite |source|'s data pointer *without* releasing the data.
   2708  source->setDataPointer(BufferContents::createNoData());
   2709 
   2710  // Detach |source| now that doing so won't release |contents|.
   2711  RemoveCellMemory(source, newByteLength, MemoryUse::ArrayBufferContents);
   2712  ArrayBufferObject::detach(cx, source);
   2713 
   2714  // Set |newBuffer|'s contents to |source|'s original contents.
   2715  newBuffer->initialize(newByteLength, contents);
   2716  AddCellMemory(newBuffer, newByteLength, MemoryUse::ArrayBufferContents);
   2717 
   2718  return newBuffer;
   2719 }
   2720 
   2721 template <class ArrayBufferType>
   2722 /* static */ ArrayBufferType* ArrayBufferObject::copyAndDetachRealloc(
   2723    JSContext* cx, size_t newByteLength,
   2724    JS::Handle<ArrayBufferObject*> source) {
   2725  MOZ_ASSERT(!source->isDetached());
   2726  MOZ_ASSERT(!source->isImmutable());
   2727  MOZ_ASSERT(!source->isLengthPinned());
   2728  MOZ_ASSERT(source->bufferKind() == MALLOCED_ARRAYBUFFER_CONTENTS_ARENA);
   2729  MOZ_ASSERT(newByteLength > ArrayBufferType::MaxInlineBytes,
   2730             "prefer copying small buffers");
   2731  MOZ_ASSERT(newByteLength <= ArrayBufferObject::ByteLengthLimit,
   2732             "caller must validate the byte count it passes");
   2733 
   2734  size_t oldByteLength = source->associatedBytes();
   2735  MOZ_ASSERT(oldByteLength != newByteLength,
   2736             "steal instead of realloc same size buffers");
   2737  MOZ_ASSERT(source->byteLength() <= oldByteLength,
   2738             "source length is less-or-equal to |oldByteLength|");
   2739 
   2740  Rooted<ArrayBufferType*> newBuffer(cx, ArrayBufferType::createEmpty(cx));
   2741  if (!newBuffer) {
   2742    return nullptr;
   2743  }
   2744 
   2745  // Extract the contents from |source|.
   2746  BufferContents contents = source->contents();
   2747  MOZ_ASSERT(contents);
   2748  MOZ_ASSERT(contents.kind() == MALLOCED_ARRAYBUFFER_CONTENTS_ARENA);
   2749 
   2750  // Reallocate the data pointer.
   2751  auto newData = ReallocateArrayBufferContents(cx, contents.data(),
   2752                                               oldByteLength, newByteLength);
   2753  if (!newData) {
   2754    // If reallocation failed, the old pointer is still valid, so just return.
   2755    return nullptr;
   2756  }
   2757  auto newContents =
   2758      BufferContents::createMallocedArrayBufferContentsArena(newData.release());
   2759 
   2760  // Overwrite |source|'s data pointer *without* releasing the data.
   2761  source->setDataPointer(BufferContents::createNoData());
   2762 
   2763  // Detach |source| now that doing so won't release |contents|.
   2764  RemoveCellMemory(source, oldByteLength, MemoryUse::ArrayBufferContents);
   2765  ArrayBufferObject::detach(cx, source);
   2766 
   2767  // Set |newBuffer|'s contents to |newContents|.
   2768  newBuffer->initialize(newByteLength, newContents);
   2769  AddCellMemory(newBuffer, newByteLength, MemoryUse::ArrayBufferContents);
   2770 
   2771  // Zero initialize the newly allocated memory, if necessary.
   2772  if (newByteLength > oldByteLength) {
   2773    size_t count = newByteLength - oldByteLength;
   2774    std::uninitialized_fill_n(newContents.data() + oldByteLength, count, 0);
   2775  }
   2776 
   2777  return newBuffer;
   2778 }
   2779 
   2780 /* static */ ResizableArrayBufferObject*
   2781 ResizableArrayBufferObject::copyAndDetach(
   2782    JSContext* cx, size_t newByteLength,
   2783    JS::Handle<ResizableArrayBufferObject*> source) {
   2784  MOZ_ASSERT(!source->isDetached());
   2785  MOZ_ASSERT(!source->isImmutable());
   2786  MOZ_ASSERT(!source->isLengthPinned());
   2787  MOZ_ASSERT(newByteLength <= source->maxByteLength());
   2788 
   2789  if (source->maxByteLength() > ResizableArrayBufferObject::MaxInlineBytes &&
   2790      source->isMalloced()) {
   2791    return copyAndDetachSteal(cx, newByteLength, source);
   2792  }
   2793 
   2794  auto* newBuffer = ResizableArrayBufferObject::copy(cx, newByteLength, source);
   2795  if (!newBuffer) {
   2796    return nullptr;
   2797  }
   2798  ArrayBufferObject::detach(cx, source);
   2799 
   2800  return newBuffer;
   2801 }
   2802 
   2803 /* static */ ResizableArrayBufferObject*
   2804 ResizableArrayBufferObject::copyAndDetachSteal(
   2805    JSContext* cx, size_t newByteLength,
   2806    JS::Handle<ResizableArrayBufferObject*> source) {
   2807  MOZ_ASSERT(!source->isDetached());
   2808  MOZ_ASSERT(!source->isImmutable());
   2809  MOZ_ASSERT(!source->isLengthPinned());
   2810  MOZ_ASSERT(newByteLength <= source->maxByteLength());
   2811  MOZ_ASSERT(source->isMalloced());
   2812 
   2813  size_t sourceByteLength = source->byteLength();
   2814  size_t maxByteLength = source->maxByteLength();
   2815  MOZ_ASSERT(maxByteLength > ResizableArrayBufferObject::MaxInlineBytes,
   2816             "prefer copying small buffers");
   2817 
   2818  auto* newBuffer = ResizableArrayBufferObject::createEmpty(cx);
   2819  if (!newBuffer) {
   2820    return nullptr;
   2821  }
   2822 
   2823  // Extract the contents from |source|.
   2824  BufferContents contents = source->contents();
   2825  MOZ_ASSERT(contents);
   2826  MOZ_ASSERT(contents.kind() == MALLOCED_ARRAYBUFFER_CONTENTS_ARENA ||
   2827             contents.kind() == MALLOCED_UNKNOWN_ARENA);
   2828 
   2829  // Overwrite |source|'s data pointer *without* releasing the data.
   2830  source->setDataPointer(BufferContents::createNoData());
   2831 
   2832  // Detach |source| now that doing so won't release |contents|.
   2833  RemoveCellMemory(source, maxByteLength, MemoryUse::ArrayBufferContents);
   2834  ArrayBufferObject::detach(cx, source);
   2835 
   2836  // Set |newBuffer|'s contents to |source|'s original contents.
   2837  newBuffer->initialize(newByteLength, maxByteLength, contents);
   2838  AddCellMemory(newBuffer, maxByteLength, MemoryUse::ArrayBufferContents);
   2839 
   2840  // Clear the bytes between `data[newByteLength..sourceByteLength]`.
   2841  if (newByteLength < sourceByteLength) {
   2842    size_t nbytes = sourceByteLength - newByteLength;
   2843    memset(newBuffer->dataPointer() + newByteLength, 0, nbytes);
   2844  }
   2845 
   2846  return newBuffer;
   2847 }
   2848 
   2849 /* static */ ImmutableArrayBufferObject* ImmutableArrayBufferObject::slice(
   2850    JSContext* cx, size_t newByteLength, JS::Handle<ArrayBufferObject*> source,
   2851    size_t sourceByteOffset) {
   2852  MOZ_ASSERT(newByteLength <= ArrayBufferObject::ByteLengthLimit,
   2853             "caller must validate the byte count it passes");
   2854  MOZ_ASSERT(!source->isDetached());
   2855  MOZ_ASSERT(source->byteLength() >= sourceByteOffset);
   2856  MOZ_ASSERT(source->byteLength() >= sourceByteOffset + newByteLength);
   2857 
   2858  AutoSetNewObjectMetadata metadata(cx);
   2859  auto [newBuffer, toFill] = createBufferAndData<ImmutableArrayBufferObject,
   2860                                                 FillContents::Uninitialized>(
   2861      cx, newByteLength, metadata);
   2862  if (!newBuffer) {
   2863    return nullptr;
   2864  }
   2865 
   2866  std::uninitialized_copy_n(source->dataPointer() + sourceByteOffset,
   2867                            newByteLength, toFill);
   2868 
   2869  return newBuffer;
   2870 }
   2871 
   2872 FixedLengthArrayBufferObject* ArrayBufferObject::createZeroed(
   2873    JSContext* cx, size_t nbytes, HandleObject proto /* = nullptr */) {
   2874  // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
   2875  if (!CheckArrayBufferTooLarge(cx, nbytes)) {
   2876    MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "buffer too large");
   2877    return nullptr;
   2878  }
   2879 
   2880  AutoSetNewObjectMetadata metadata(cx);
   2881  auto [buffer, toFill] =
   2882      createBufferAndData<FixedLengthArrayBufferObject, FillContents::Zero>(
   2883          cx, nbytes, metadata, proto);
   2884  (void)toFill;
   2885  return buffer;
   2886 }
   2887 
   2888 ResizableArrayBufferObject* ResizableArrayBufferObject::createZeroed(
   2889    JSContext* cx, size_t byteLength, size_t maxByteLength,
   2890    HandleObject proto /* = nullptr */) {
   2891  // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
   2892  if (!CheckArrayBufferTooLarge(cx, byteLength) ||
   2893      !CheckArrayBufferTooLarge(cx, maxByteLength)) {
   2894    return nullptr;
   2895  }
   2896  if (byteLength > maxByteLength) {
   2897    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2898                              JSMSG_ARRAYBUFFER_LENGTH_LARGER_THAN_MAXIMUM);
   2899    return nullptr;
   2900  }
   2901 
   2902  AutoSetNewObjectMetadata metadata(cx);
   2903  auto [buffer, toFill] = createBufferAndData<FillContents::Zero>(
   2904      cx, byteLength, maxByteLength, metadata, proto);
   2905  (void)toFill;
   2906  return buffer;
   2907 }
   2908 
   2909 ImmutableArrayBufferObject* ImmutableArrayBufferObject::createZeroed(
   2910    JSContext* cx, size_t byteLength, HandleObject proto /* = nullptr */) {
   2911  // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
   2912  if (!CheckArrayBufferTooLarge(cx, byteLength)) {
   2913    MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "buffer too large");
   2914    return nullptr;
   2915  }
   2916 
   2917  AutoSetNewObjectMetadata metadata(cx);
   2918  auto [buffer, toFill] =
   2919      createBufferAndData<ImmutableArrayBufferObject, FillContents::Zero>(
   2920          cx, byteLength, metadata, proto);
   2921  (void)toFill;
   2922  return buffer;
   2923 }
   2924 
   2925 FixedLengthArrayBufferObject* ArrayBufferObject::createEmpty(JSContext* cx) {
   2926  AutoSetNewObjectMetadata metadata(cx);
   2927  auto* obj = NewArrayBufferObject(cx);
   2928  if (!obj) {
   2929    return nullptr;
   2930  }
   2931 
   2932  obj->initialize(0, BufferContents::createNoData());
   2933  return obj;
   2934 }
   2935 
   2936 ResizableArrayBufferObject* ResizableArrayBufferObject::createEmpty(
   2937    JSContext* cx) {
   2938  AutoSetNewObjectMetadata metadata(cx);
   2939  auto* obj = NewResizableArrayBufferObject(cx);
   2940  if (!obj) {
   2941    return nullptr;
   2942  }
   2943 
   2944  obj->initialize(0, 0, BufferContents::createNoData());
   2945  return obj;
   2946 }
   2947 
   2948 ImmutableArrayBufferObject* ImmutableArrayBufferObject::createEmpty(
   2949    JSContext* cx) {
   2950  AutoSetNewObjectMetadata metadata(cx);
   2951  auto* obj = NewImmutableArrayBufferObject(cx);
   2952  if (!obj) {
   2953    return nullptr;
   2954  }
   2955 
   2956  obj->initialize(0, BufferContents::createNoData());
   2957  return obj;
   2958 }
   2959 
   2960 ArrayBufferObject* ArrayBufferObject::createFromNewRawBuffer(
   2961    JSContext* cx, WasmArrayRawBuffer* rawBuffer, size_t initialSize) {
   2962  AutoSetNewObjectMetadata metadata(cx);
   2963  ArrayBufferObject* buffer = NewArrayBufferObject(cx);
   2964  if (!buffer) {
   2965    WasmArrayRawBuffer::Release(rawBuffer->dataPointer());
   2966    return nullptr;
   2967  }
   2968 
   2969  MOZ_ASSERT(initialSize == rawBuffer->byteLength());
   2970 
   2971  auto contents = BufferContents::createWasm(rawBuffer->dataPointer());
   2972  buffer->initialize(initialSize, contents);
   2973 
   2974  AddCellMemory(buffer, initialSize, MemoryUse::ArrayBufferContents);
   2975 
   2976  return buffer;
   2977 }
   2978 
   2979 template <typename ArrayBufferType>
   2980 ArrayBufferType* ArrayBufferObject::createFromWasmObject(
   2981    JSContext* cx, Handle<ArrayBufferObject*> donor) {
   2982  AutoSetNewObjectMetadata metadata(cx);
   2983  // Similar to NewResizableArrayBufferObject/NewArrayBufferObject
   2984  constexpr auto allocKind =
   2985      GetArrayBufferGCObjectKind(ArrayBufferType::RESERVED_SLOTS);
   2986  ArrayBufferType* buffer =
   2987      NewArrayBufferObject<ArrayBufferType>(cx, nullptr, allocKind);
   2988  if (!buffer) {
   2989    return nullptr;
   2990  }
   2991 
   2992  RemoveCellMemory(donor, donor->byteLength(), MemoryUse::ArrayBufferContents);
   2993 
   2994  MOZ_RELEASE_ASSERT(donor->isWasm());
   2995  // The only flag allowed to be set on the `donor` is the resizable flag.
   2996  MOZ_RELEASE_ASSERT((donor->flags() & ~KIND_MASK & ~RESIZABLE) == 0);
   2997  BufferContents contents(donor->dataPointer(), WASM);
   2998  size_t byteLength = donor->byteLength();
   2999  [[maybe_unused]] size_t maxByteLength = donor->wasmClampedMaxByteLength();
   3000 
   3001  // Overwrite |oldBuf|'s data pointer *without* releasing old data.
   3002  donor->setDataPointer(BufferContents::createNoData());
   3003  ArrayBufferObject::detach(cx, donor);
   3004 
   3005  if constexpr (std::is_same_v<ArrayBufferType, ResizableArrayBufferObject>) {
   3006    buffer->initialize(byteLength, maxByteLength, contents);
   3007  } else {
   3008    static_assert(
   3009        std::is_same_v<ArrayBufferType, FixedLengthArrayBufferObject>);
   3010    buffer->initialize(byteLength, contents);
   3011  }
   3012 
   3013  AddCellMemory(buffer, buffer->byteLength(), MemoryUse::ArrayBufferContents);
   3014  return buffer;
   3015 }
   3016 
   3017 template FixedLengthArrayBufferObject*
   3018 ArrayBufferObject::createFromWasmObject<FixedLengthArrayBufferObject>(
   3019    JSContext* cx, Handle<ArrayBufferObject*> donor);
   3020 
   3021 template ResizableArrayBufferObject*
   3022 ArrayBufferObject::createFromWasmObject<ResizableArrayBufferObject>(
   3023    JSContext* cx, Handle<ArrayBufferObject*> donor);
   3024 
   3025 /* static */ uint8_t* ArrayBufferObject::stealMallocedContents(
   3026    JSContext* cx, Handle<ArrayBufferObject*> buffer) {
   3027  CheckStealPreconditions(buffer, cx);
   3028 
   3029  switch (buffer->bufferKind()) {
   3030    case MALLOCED_ARRAYBUFFER_CONTENTS_ARENA:
   3031    case MALLOCED_UNKNOWN_ARENA: {
   3032      uint8_t* stolenData = buffer->dataPointer();
   3033      MOZ_ASSERT(stolenData);
   3034 
   3035      // Resizable buffers are initially allocated with their maximum
   3036      // byte-length. When stealing the buffer contents shrink the allocated
   3037      // memory to the actually used byte-length.
   3038      if (buffer->isResizable()) {
   3039        auto* resizableBuffer = &buffer->as<ResizableArrayBufferObject>();
   3040        size_t byteLength = resizableBuffer->byteLength();
   3041        size_t maxByteLength = resizableBuffer->maxByteLength();
   3042        MOZ_ASSERT(byteLength <= maxByteLength);
   3043 
   3044        if (byteLength < maxByteLength) {
   3045          auto newData = ReallocateArrayBufferContents(
   3046              cx, stolenData, maxByteLength, byteLength);
   3047          if (!newData) {
   3048            // If reallocation failed, the old pointer is still valid. The
   3049            // ArrayBuffer isn't detached and still owns the malloc'ed memory.
   3050            return nullptr;
   3051          }
   3052 
   3053          // The following code must be infallible, because the data pointer of
   3054          // |buffer| is possibly no longer valid after the above realloc.
   3055 
   3056          stolenData = newData.release();
   3057        }
   3058      }
   3059 
   3060      RemoveCellMemory(buffer, buffer->associatedBytes(),
   3061                       MemoryUse::ArrayBufferContents);
   3062 
   3063      // Overwrite the old data pointer *without* releasing the contents
   3064      // being stolen.
   3065      buffer->setDataPointer(BufferContents::createNoData());
   3066 
   3067      // Detach |buffer| now that doing so won't free |stolenData|.
   3068      ArrayBufferObject::detach(cx, buffer);
   3069      return stolenData;
   3070    }
   3071 
   3072    case INLINE_DATA:
   3073    case NO_DATA:
   3074    case USER_OWNED:
   3075    case MAPPED:
   3076    case EXTERNAL: {
   3077      // We can't use these data types directly.  Make a copy to return.
   3078      ArrayBufferContents copiedData = NewCopiedBufferContents(cx, buffer);
   3079      if (!copiedData) {
   3080        return nullptr;
   3081      }
   3082 
   3083      // Detach |buffer|.  This immediately releases the currently owned
   3084      // contents, freeing or unmapping data in the MAPPED and EXTERNAL cases.
   3085      ArrayBufferObject::detach(cx, buffer);
   3086      return copiedData.release();
   3087    }
   3088 
   3089    case WASM:
   3090      MOZ_ASSERT_UNREACHABLE(
   3091          "wasm buffers aren't stealable except by a "
   3092          "memory.grow operation that shouldn't call this "
   3093          "function");
   3094      return nullptr;
   3095  }
   3096 
   3097  MOZ_ASSERT_UNREACHABLE("garbage kind computed");
   3098  return nullptr;
   3099 }
   3100 
   3101 /* static */ ArrayBufferObject::BufferContents
   3102 ArrayBufferObject::extractStructuredCloneContents(
   3103    JSContext* cx, Handle<ArrayBufferObject*> buffer) {
   3104  if (buffer->isLengthPinned()) {
   3105    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3106                              JSMSG_ARRAYBUFFER_LENGTH_PINNED);
   3107    return BufferContents::createFailed();
   3108  }
   3109 
   3110  CheckStealPreconditions(buffer, cx);
   3111 
   3112  // We don't yet support extracting the contents of resizable buffers.
   3113  MOZ_ASSERT(!buffer->isResizable(),
   3114             "extracting the contents of resizable buffers not supported");
   3115 
   3116  BufferContents contents = buffer->contents();
   3117 
   3118  switch (contents.kind()) {
   3119    case INLINE_DATA:
   3120    case NO_DATA:
   3121    case USER_OWNED: {
   3122      ArrayBufferContents copiedData = NewCopiedBufferContents(cx, buffer);
   3123      if (!copiedData) {
   3124        return BufferContents::createFailed();
   3125      }
   3126 
   3127      ArrayBufferObject::detach(cx, buffer);
   3128      return BufferContents::createMallocedArrayBufferContentsArena(
   3129          copiedData.release());
   3130    }
   3131 
   3132    case MALLOCED_ARRAYBUFFER_CONTENTS_ARENA:
   3133    case MALLOCED_UNKNOWN_ARENA:
   3134    case MAPPED: {
   3135      MOZ_ASSERT(contents);
   3136 
   3137      RemoveCellMemory(buffer, buffer->associatedBytes(),
   3138                       MemoryUse::ArrayBufferContents);
   3139 
   3140      // Overwrite the old data pointer *without* releasing old data.
   3141      buffer->setDataPointer(BufferContents::createNoData());
   3142 
   3143      // Detach |buffer| now that doing so won't release |oldContents|.
   3144      ArrayBufferObject::detach(cx, buffer);
   3145      return contents;
   3146    }
   3147 
   3148    case WASM:
   3149      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3150                                JSMSG_WASM_NO_TRANSFER);
   3151      return BufferContents::createFailed();
   3152 
   3153    case EXTERNAL:
   3154      MOZ_ASSERT_UNREACHABLE(
   3155          "external ArrayBuffer shouldn't have passed the "
   3156          "structured-clone preflighting");
   3157      break;
   3158  }
   3159 
   3160  MOZ_ASSERT_UNREACHABLE("garbage kind computed");
   3161  return BufferContents::createFailed();
   3162 }
   3163 
   3164 /* static */
   3165 bool ArrayBufferObject::ensureNonInline(JSContext* cx,
   3166                                        Handle<ArrayBufferObject*> buffer) {
   3167  if (buffer->isDetached() || buffer->isPreparedForAsmJS()) {
   3168    return true;
   3169  }
   3170 
   3171  if (buffer->isLengthPinned()) {
   3172    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3173                              JSMSG_ARRAYBUFFER_LENGTH_PINNED);
   3174    MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "ArrayBuffer length pinned");
   3175    return false;
   3176  }
   3177 
   3178  BufferContents inlineContents = buffer->contents();
   3179  if (inlineContents.kind() != INLINE_DATA) {
   3180    return true;
   3181  }
   3182 
   3183  size_t nbytes = buffer->maxByteLength();
   3184  ArrayBufferContents copy = NewCopiedBufferContents(cx, buffer);
   3185  if (!copy) {
   3186    return false;
   3187  }
   3188  BufferContents outOfLineContents =
   3189      BufferContents::createMallocedArrayBufferContentsArena(copy.release());
   3190  buffer->setDataPointer(outOfLineContents);
   3191  AddCellMemory(buffer, nbytes, MemoryUse::ArrayBufferContents);
   3192 
   3193  if (!buffer->firstView()) {
   3194    return true;  // No views! Easy!
   3195  }
   3196 
   3197  buffer->firstView()->as<ArrayBufferViewObject>().notifyBufferMoved(
   3198      inlineContents.data(), outOfLineContents.data());
   3199 
   3200  auto& innerViews = ObjectRealm::get(buffer).innerViews.get();
   3201  if (InnerViewTable::ViewVector* views =
   3202          innerViews.maybeViewsUnbarriered(buffer)) {
   3203    for (JSObject* view : *views) {
   3204      view->as<ArrayBufferViewObject>().notifyBufferMoved(
   3205          inlineContents.data(), outOfLineContents.data());
   3206    }
   3207  }
   3208 
   3209  return true;
   3210 }
   3211 
   3212 /* static */
   3213 void ArrayBufferObject::addSizeOfExcludingThis(
   3214    JSObject* obj, mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info,
   3215    JS::RuntimeSizes* runtimeSizes) {
   3216  auto& buffer = obj->as<ArrayBufferObject>();
   3217  switch (buffer.bufferKind()) {
   3218    case INLINE_DATA:
   3219      // Inline data's size should be reported by this object's size-class
   3220      // reporting.
   3221      break;
   3222    case MALLOCED_ARRAYBUFFER_CONTENTS_ARENA:
   3223    case MALLOCED_UNKNOWN_ARENA:
   3224      if (buffer.isPreparedForAsmJS()) {
   3225        info->objectsMallocHeapElementsAsmJS +=
   3226            mallocSizeOf(buffer.dataPointer());
   3227      } else {
   3228        info->objectsMallocHeapElementsNormal +=
   3229            mallocSizeOf(buffer.dataPointer());
   3230      }
   3231      break;
   3232    case NO_DATA:
   3233      // No data is no memory.
   3234      MOZ_ASSERT(buffer.dataPointer() == nullptr);
   3235      break;
   3236    case USER_OWNED:
   3237      // User-owned data should be accounted for by the user.
   3238      break;
   3239    case EXTERNAL:
   3240      // External data will be accounted for by the owner of the buffer,
   3241      // not this view.
   3242      break;
   3243    case MAPPED:
   3244      info->objectsNonHeapElementsNormal += buffer.byteLength();
   3245      break;
   3246    case WASM:
   3247      if (!buffer.isDetached()) {
   3248        info->objectsNonHeapElementsWasm += buffer.byteLength();
   3249        if (runtimeSizes) {
   3250          MOZ_ASSERT(buffer.wasmMappedSize() >= buffer.byteLength());
   3251          runtimeSizes->wasmGuardPages +=
   3252              buffer.wasmMappedSize() - buffer.byteLength();
   3253        }
   3254      }
   3255      break;
   3256  }
   3257 }
   3258 
   3259 /* static */
   3260 void ArrayBufferObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   3261  obj->as<ArrayBufferObject>().releaseData(gcx);
   3262 }
   3263 
   3264 /* static */
   3265 void ArrayBufferObject::copyData(ArrayBufferObject* toBuffer, size_t toIndex,
   3266                                 ArrayBufferObject* fromBuffer,
   3267                                 size_t fromIndex, size_t count) {
   3268  MOZ_ASSERT(!toBuffer->isDetached());
   3269  MOZ_ASSERT(toBuffer->byteLength() >= count);
   3270  MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count);
   3271  MOZ_ASSERT(!fromBuffer->isDetached());
   3272  MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
   3273  MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
   3274 
   3275  memcpy(toBuffer->dataPointer() + toIndex,
   3276         fromBuffer->dataPointer() + fromIndex, count);
   3277 }
   3278 
   3279 template <class ArrayBufferType>
   3280 /* static */
   3281 size_t ArrayBufferObject::objectMoved(JSObject* obj, JSObject* old) {
   3282  auto& dst = obj->as<ArrayBufferType>();
   3283  const auto& src = old->as<ArrayBufferType>();
   3284 
   3285 #ifdef DEBUG
   3286  // Check the data pointer is not inside the nursery, but take account of the
   3287  // fact that inline data pointers for zero length buffers can point to the end
   3288  // of a chunk which can abut the start of the nursery.
   3289  if (src.byteLength() != 0 || (uintptr_t(src.dataPointer()) & gc::ChunkMask)) {
   3290    Nursery& nursery = obj->runtimeFromMainThread()->gc.nursery();
   3291    MOZ_ASSERT(!nursery.isInside(src.dataPointer()));
   3292  }
   3293 #endif
   3294 
   3295  // Fix up possible inline data pointer.
   3296  if (src.hasInlineData()) {
   3297    dst.setFixedSlot(DATA_SLOT, PrivateValue(dst.inlineDataPointer()));
   3298  }
   3299 
   3300  return 0;
   3301 }
   3302 
   3303 JSObject* ArrayBufferObject::firstView() {
   3304  return getFixedSlot(FIRST_VIEW_SLOT).isObject()
   3305             ? &getFixedSlot(FIRST_VIEW_SLOT).toObject()
   3306             : nullptr;
   3307 }
   3308 
   3309 void ArrayBufferObject::setFirstView(ArrayBufferViewObject* view) {
   3310  setFixedSlot(FIRST_VIEW_SLOT, ObjectOrNullValue(view));
   3311 }
   3312 
   3313 bool ArrayBufferObject::addView(JSContext* cx, ArrayBufferViewObject* view) {
   3314  if (!firstView()) {
   3315    setFirstView(view);
   3316    return true;
   3317  }
   3318 
   3319  return ObjectRealm::get(this).innerViews.get().addView(cx, this, view);
   3320 }
   3321 
   3322 #if defined(DEBUG) || defined(JS_JITSPEW)
   3323 
   3324 template <typename KnownF, typename UnknownF>
   3325 void BufferKindToString(ArrayBufferObject::BufferKind kind, KnownF known,
   3326                        UnknownF unknown) {
   3327  switch (kind) {
   3328    case ArrayBufferObject::BufferKind::INLINE_DATA:
   3329      known("INLINE_DATA");
   3330      break;
   3331    case ArrayBufferObject::BufferKind::MALLOCED_ARRAYBUFFER_CONTENTS_ARENA:
   3332      known("MALLOCED_ARRAYBUFFER_CONTENTS_ARENA");
   3333      break;
   3334    case ArrayBufferObject::BufferKind::NO_DATA:
   3335      known("NO_DATA");
   3336      break;
   3337    case ArrayBufferObject::BufferKind::USER_OWNED:
   3338      known("USER_OWNED");
   3339      break;
   3340    case ArrayBufferObject::BufferKind::WASM:
   3341      known("WASM");
   3342      break;
   3343    case ArrayBufferObject::BufferKind::MAPPED:
   3344      known("MAPPED");
   3345      break;
   3346    case ArrayBufferObject::BufferKind::EXTERNAL:
   3347      known("EXTERNAL");
   3348      break;
   3349    case ArrayBufferObject::BufferKind::MALLOCED_UNKNOWN_ARENA:
   3350      known("MALLOCED_UNKNOWN_ARENA");
   3351      break;
   3352    default:
   3353      unknown(uint8_t(kind));
   3354      break;
   3355  }
   3356 }
   3357 
   3358 template <typename KnownF, typename UnknownF>
   3359 void ForEachArrayBufferFlag(uint32_t flags, KnownF known, UnknownF unknown) {
   3360  for (uint32_t i = ArrayBufferObject::ArrayBufferFlags::BUFFER_KIND_MASK + 1;
   3361       i; i = i << 1) {
   3362    if (!(flags & i)) {
   3363      continue;
   3364    }
   3365    switch (ArrayBufferObject::ArrayBufferFlags(flags & i)) {
   3366      case ArrayBufferObject::ArrayBufferFlags::DETACHED:
   3367        known("DETACHED");
   3368        break;
   3369      case ArrayBufferObject::ArrayBufferFlags::FOR_ASMJS:
   3370        known("FOR_ASMJS");
   3371        break;
   3372      default:
   3373        unknown(i);
   3374        break;
   3375    }
   3376  }
   3377 }
   3378 
   3379 void ArrayBufferObject::dumpOwnFields(js::JSONPrinter& json) const {
   3380  json.formatProperty("byteLength", "%zu",
   3381                      size_t(getFixedSlot(BYTE_LENGTH_SLOT).toPrivate()));
   3382 
   3383  BufferKindToString(
   3384      bufferKind(),
   3385      [&](const char* name) { json.property("bufferKind", name); },
   3386      [&](uint8_t value) {
   3387        json.formatProperty("bufferKind", "Unknown(%02x)", value);
   3388      });
   3389 
   3390  json.beginInlineListProperty("flags");
   3391  ForEachArrayBufferFlag(
   3392      flags(), [&](const char* name) { json.value("%s", name); },
   3393      [&](uint32_t value) { json.value("Unknown(%08x)", value); });
   3394  json.endInlineList();
   3395 
   3396  void* data = dataPointer();
   3397  if (data) {
   3398    json.formatProperty("data", "0x%p", data);
   3399  } else {
   3400    json.nullProperty("data");
   3401  }
   3402 }
   3403 
   3404 void ArrayBufferObject::dumpOwnStringContent(js::GenericPrinter& out) const {
   3405  out.printf("byteLength=%zu, ",
   3406             size_t(getFixedSlot(BYTE_LENGTH_SLOT).toPrivate()));
   3407 
   3408  BufferKindToString(
   3409      bufferKind(),
   3410      [&](const char* name) { out.printf("bufferKind=%s, ", name); },
   3411      [&](uint8_t value) { out.printf("bufferKind=Unknown(%02x), ", value); });
   3412 
   3413  out.printf("flags=[");
   3414  bool first = true;
   3415  ForEachArrayBufferFlag(
   3416      flags(),
   3417      [&](const char* name) {
   3418        if (!first) {
   3419          out.put(",");
   3420        }
   3421        first = false;
   3422        out.put(name);
   3423      },
   3424      [&](uint32_t value) {
   3425        if (!first) {
   3426          out.put(",");
   3427        }
   3428        first = false;
   3429        out.printf("Unknown(%08x)", value);
   3430      });
   3431  out.put("], ");
   3432 
   3433  void* data = dataPointer();
   3434  if (data) {
   3435    out.printf("data=0x%p", data);
   3436  } else {
   3437    out.put("data=null");
   3438  }
   3439 }
   3440 #endif
   3441 
   3442 /*
   3443 * InnerViewTable
   3444 */
   3445 
   3446 inline bool InnerViewTable::Views::empty() { return views.empty(); }
   3447 
   3448 inline bool InnerViewTable::Views::hasNurseryViews() {
   3449  return firstNurseryView < views.length();
   3450 }
   3451 
   3452 bool InnerViewTable::Views::addView(ArrayBufferViewObject* view) {
   3453  // Add the view to the list, ensuring that all nursery views are at end.
   3454 
   3455  if (!views.append(view)) {
   3456    return false;
   3457  }
   3458 
   3459  if (!gc::IsInsideNursery(view)) {
   3460    // Move tenured views before |firstNurseryView|.
   3461    if (firstNurseryView != views.length() - 1) {
   3462      std::swap(views[firstNurseryView], views.back());
   3463    }
   3464    firstNurseryView++;
   3465  }
   3466 
   3467  check();
   3468 
   3469  return true;
   3470 }
   3471 
   3472 bool InnerViewTable::Views::sweepAfterMinorGC(JSTracer* trc) {
   3473  return traceWeak(trc, firstNurseryView);
   3474 }
   3475 
   3476 bool InnerViewTable::Views::traceWeak(JSTracer* trc, size_t startIndex) {
   3477  // Use |trc| to trace the view vector from |startIndex| to the end, removing
   3478  // dead views and updating |firstNurseryView|.
   3479 
   3480  size_t index = startIndex;
   3481  bool sawNurseryView = false;
   3482  views.mutableEraseIf(
   3483      [&](auto& view) {
   3484        if (!JS::GCPolicy<ViewVector::ElementType>::traceWeak(trc, &view)) {
   3485          return true;
   3486        }
   3487 
   3488        if (!sawNurseryView && gc::IsInsideNursery(view)) {
   3489          sawNurseryView = true;
   3490          firstNurseryView = index;
   3491        }
   3492 
   3493        index++;
   3494        return false;
   3495      },
   3496      startIndex);
   3497 
   3498  if (!sawNurseryView) {
   3499    firstNurseryView = views.length();
   3500  }
   3501 
   3502  check();
   3503 
   3504  return !views.empty();
   3505 }
   3506 
   3507 inline void InnerViewTable::Views::check() {
   3508 #ifdef DEBUG
   3509  MOZ_ASSERT(firstNurseryView <= views.length());
   3510  if (views.length() < 100) {
   3511    for (size_t i = 0; i < views.length(); i++) {
   3512      MOZ_ASSERT(gc::IsInsideNursery(views[i]) == (i >= firstNurseryView));
   3513    }
   3514  }
   3515 #endif
   3516 }
   3517 
   3518 bool InnerViewTable::addView(JSContext* cx, ArrayBufferObject* buffer,
   3519                             ArrayBufferViewObject* view) {
   3520  // ArrayBufferObject entries are only added when there are multiple views.
   3521  MOZ_ASSERT(buffer->firstView());
   3522  MOZ_ASSERT(!gc::IsInsideNursery(buffer));
   3523 
   3524  // Ensure the buffer is present in the map, getting the list of views.
   3525  auto ptr = map.lookupForAdd(buffer);
   3526  if (!ptr && !map.add(ptr, buffer, Views(cx->zone()))) {
   3527    ReportOutOfMemory(cx);
   3528    return false;
   3529  }
   3530  Views& views = ptr->value();
   3531 
   3532  bool isNurseryView = gc::IsInsideNursery(view);
   3533  bool hadNurseryViews = views.hasNurseryViews();
   3534  if (!views.addView(view)) {
   3535    ReportOutOfMemory(cx);
   3536    return false;
   3537  }
   3538 
   3539  // If we added the first nursery view, add the buffer to the list of buffers
   3540  // which have nursery views.
   3541  if (isNurseryView && !hadNurseryViews && nurseryKeysValid) {
   3542 #ifdef DEBUG
   3543    if (nurseryKeys.length() < 100) {
   3544      for (const auto& key : nurseryKeys) {
   3545        MOZ_ASSERT(key != buffer);
   3546      }
   3547    }
   3548 #endif
   3549    if (!nurseryKeys.append(buffer)) {
   3550      nurseryKeysValid = false;
   3551    }
   3552  }
   3553 
   3554  return true;
   3555 }
   3556 
   3557 InnerViewTable::ViewVector* InnerViewTable::maybeViewsUnbarriered(
   3558    ArrayBufferObject* buffer) {
   3559  auto ptr = map.lookup(buffer);
   3560  if (ptr) {
   3561    return &ptr->value().views;
   3562  }
   3563  return nullptr;
   3564 }
   3565 
   3566 void InnerViewTable::removeViews(ArrayBufferObject* buffer) {
   3567  auto ptr = map.lookup(buffer);
   3568  MOZ_ASSERT(ptr);
   3569 
   3570  map.remove(ptr);
   3571 }
   3572 
   3573 bool InnerViewTable::traceWeak(JSTracer* trc) {
   3574  nurseryKeys.traceWeak(trc);
   3575  map.traceWeak(trc);
   3576  return true;
   3577 }
   3578 
   3579 void InnerViewTable::sweepAfterMinorGC(JSTracer* trc) {
   3580  MOZ_ASSERT(needsSweepAfterMinorGC());
   3581 
   3582  NurseryKeysVector keys;
   3583  bool valid = true;
   3584  std::swap(nurseryKeys, keys);
   3585  std::swap(nurseryKeysValid, valid);
   3586 
   3587  // Use nursery keys vector if possible.
   3588  if (valid) {
   3589    for (ArrayBufferObject* buffer : keys) {
   3590      MOZ_ASSERT(!gc::IsInsideNursery(buffer));
   3591      auto ptr = map.lookup(buffer);
   3592      if (ptr && !sweepViewsAfterMinorGC(trc, buffer, ptr->value())) {
   3593        map.remove(ptr);
   3594      }
   3595    }
   3596    return;
   3597  }
   3598 
   3599  // Otherwise look at every map entry.
   3600  for (ArrayBufferViewMap::Enum e(map); !e.empty(); e.popFront()) {
   3601    MOZ_ASSERT(!gc::IsInsideNursery(e.front().key()));
   3602    if (!sweepViewsAfterMinorGC(trc, e.front().key(), e.front().value())) {
   3603      e.removeFront();
   3604    }
   3605  }
   3606 }
   3607 
   3608 bool InnerViewTable::sweepViewsAfterMinorGC(JSTracer* trc,
   3609                                            ArrayBufferObject* buffer,
   3610                                            Views& views) {
   3611  if (!views.sweepAfterMinorGC(trc)) {
   3612    return false;  // No more views.
   3613  }
   3614 
   3615  if (views.hasNurseryViews() && !nurseryKeys.append(buffer)) {
   3616    nurseryKeysValid = false;
   3617  }
   3618 
   3619  return true;
   3620 }
   3621 
   3622 size_t InnerViewTable::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
   3623  size_t vectorSize = 0;
   3624  for (auto r = map.all(); !r.empty(); r.popFront()) {
   3625    vectorSize += r.front().value().views.sizeOfExcludingThis(mallocSizeOf);
   3626  }
   3627 
   3628  return vectorSize + map.shallowSizeOfExcludingThis(mallocSizeOf) +
   3629         nurseryKeys.sizeOfExcludingThis(mallocSizeOf);
   3630 }
   3631 
   3632 template <>
   3633 bool JSObject::is<js::ArrayBufferObjectMaybeShared>() const {
   3634  return is<ArrayBufferObject>() || is<SharedArrayBufferObject>();
   3635 }
   3636 
   3637 JS_PUBLIC_API size_t JS::GetArrayBufferByteLength(JSObject* obj) {
   3638  ArrayBufferObject* aobj = obj->maybeUnwrapAs<ArrayBufferObject>();
   3639  return aobj ? aobj->byteLength() : 0;
   3640 }
   3641 
   3642 JS_PUBLIC_API uint8_t* JS::GetArrayBufferData(JSObject* obj,
   3643                                              bool* isSharedMemory,
   3644                                              const JS::AutoRequireNoGC&) {
   3645  ArrayBufferObject* aobj = obj->maybeUnwrapIf<ArrayBufferObject>();
   3646  if (!aobj) {
   3647    return nullptr;
   3648  }
   3649  *isSharedMemory = false;
   3650  return aobj->dataPointer();
   3651 }
   3652 
   3653 static ArrayBufferObject* UnwrapOrReportArrayBuffer(
   3654    JSContext* cx, JS::Handle<JSObject*> maybeArrayBuffer) {
   3655  JSObject* obj = CheckedUnwrapStatic(maybeArrayBuffer);
   3656  if (!obj) {
   3657    ReportAccessDenied(cx);
   3658    return nullptr;
   3659  }
   3660 
   3661  if (!obj->is<ArrayBufferObject>()) {
   3662    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3663                              JSMSG_ARRAYBUFFER_REQUIRED);
   3664    return nullptr;
   3665  }
   3666 
   3667  return &obj->as<ArrayBufferObject>();
   3668 }
   3669 
   3670 JS_PUBLIC_API bool JS::DetachArrayBuffer(JSContext* cx, HandleObject obj) {
   3671  AssertHeapIsIdle();
   3672  CHECK_THREAD(cx);
   3673  cx->check(obj);
   3674 
   3675  Rooted<ArrayBufferObject*> unwrappedBuffer(
   3676      cx, UnwrapOrReportArrayBuffer(cx, obj));
   3677  if (!unwrappedBuffer) {
   3678    return false;
   3679  }
   3680 
   3681  if (unwrappedBuffer->hasDefinedDetachKey()) {
   3682    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3683                              JSMSG_WASM_NO_TRANSFER);
   3684    return false;
   3685  }
   3686  if (unwrappedBuffer->isImmutable()) {
   3687    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3688                              JSMSG_ARRAYBUFFER_IMMUTABLE);
   3689    return false;
   3690  }
   3691  if (unwrappedBuffer->isLengthPinned()) {
   3692    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3693                              JSMSG_ARRAYBUFFER_LENGTH_PINNED);
   3694    return false;
   3695  }
   3696 
   3697  AutoRealm ar(cx, unwrappedBuffer);
   3698  ArrayBufferObject::detach(cx, unwrappedBuffer);
   3699  return true;
   3700 }
   3701 
   3702 JS_PUBLIC_API bool JS::HasDefinedArrayBufferDetachKey(JSContext* cx,
   3703                                                      HandleObject obj,
   3704                                                      bool* isDefined) {
   3705  Rooted<ArrayBufferObject*> unwrappedBuffer(
   3706      cx, UnwrapOrReportArrayBuffer(cx, obj));
   3707  if (!unwrappedBuffer) {
   3708    return false;
   3709  }
   3710 
   3711  *isDefined = unwrappedBuffer->hasDefinedDetachKey();
   3712  return true;
   3713 }
   3714 
   3715 JS_PUBLIC_API bool JS::IsDetachedArrayBufferObject(JSObject* obj) {
   3716  ArrayBufferObject* aobj = obj->maybeUnwrapIf<ArrayBufferObject>();
   3717  if (!aobj) {
   3718    return false;
   3719  }
   3720 
   3721  return aobj->isDetached();
   3722 }
   3723 
   3724 JS_PUBLIC_API JSObject* JS::NewArrayBuffer(JSContext* cx, size_t nbytes) {
   3725  AssertHeapIsIdle();
   3726  CHECK_THREAD(cx);
   3727 
   3728  return ArrayBufferObject::createZeroed(cx, nbytes);
   3729 }
   3730 
   3731 JS_PUBLIC_API JSObject* JS::NewArrayBufferWithContents(
   3732    JSContext* cx, size_t nbytes,
   3733    mozilla::UniquePtr<void, JS::FreePolicy> contents) {
   3734  auto* result = NewArrayBufferWithContents(
   3735      cx, nbytes, contents.get(),
   3736      JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory);
   3737  if (result) {
   3738    // If and only if an ArrayBuffer is successfully created, ownership of
   3739    // |contents| is transferred to the new ArrayBuffer.
   3740    (void)contents.release();
   3741  }
   3742  return result;
   3743 }
   3744 
   3745 JS_PUBLIC_API JSObject* JS::NewArrayBufferWithContents(
   3746    JSContext* cx, size_t nbytes, void* data, NewArrayBufferOutOfMemory) {
   3747  AssertHeapIsIdle();
   3748  CHECK_THREAD(cx);
   3749  MOZ_ASSERT_IF(!data, nbytes == 0);
   3750 
   3751  if (!data) {
   3752    // Don't pass nulled contents to |createForContents|.
   3753    return ArrayBufferObject::createZeroed(cx, 0);
   3754  }
   3755 
   3756  using BufferContents = ArrayBufferObject::BufferContents;
   3757 
   3758  BufferContents contents = BufferContents::createMallocedUnknownArena(data);
   3759  return ArrayBufferObject::createForContents(cx, nbytes, contents);
   3760 }
   3761 
   3762 JS_PUBLIC_API JSObject* JS::CopyArrayBuffer(JSContext* cx,
   3763                                            Handle<JSObject*> arrayBuffer) {
   3764  AssertHeapIsIdle();
   3765  CHECK_THREAD(cx);
   3766 
   3767  MOZ_ASSERT(arrayBuffer != nullptr);
   3768 
   3769  Rooted<ArrayBufferObject*> unwrappedSource(
   3770      cx, UnwrapOrReportArrayBuffer(cx, arrayBuffer));
   3771  if (!unwrappedSource) {
   3772    return nullptr;
   3773  }
   3774 
   3775  if (unwrappedSource->isDetached()) {
   3776    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3777                              JSMSG_TYPED_ARRAY_DETACHED);
   3778    return nullptr;
   3779  }
   3780 
   3781  return FixedLengthArrayBufferObject::copy(cx, unwrappedSource->byteLength(),
   3782                                            unwrappedSource);
   3783 }
   3784 
   3785 JS_PUBLIC_API JSObject* JS::NewExternalArrayBuffer(
   3786    JSContext* cx, size_t nbytes,
   3787    mozilla::UniquePtr<void, JS::BufferContentsDeleter> contents) {
   3788  AssertHeapIsIdle();
   3789  CHECK_THREAD(cx);
   3790 
   3791  MOZ_ASSERT(contents);
   3792 
   3793  using BufferContents = ArrayBufferObject::BufferContents;
   3794 
   3795  BufferContents bufferContents = BufferContents::createExternal(
   3796      contents.get(), contents.get_deleter().freeFunc(),
   3797      contents.get_deleter().userData());
   3798  auto* result =
   3799      ArrayBufferObject::createForContents(cx, nbytes, bufferContents);
   3800  if (result) {
   3801    // If and only if an ArrayBuffer is successfully created, ownership of
   3802    // |contents| is transferred to the new ArrayBuffer.
   3803    (void)contents.release();
   3804  }
   3805  return result;
   3806 }
   3807 
   3808 JS_PUBLIC_API JSObject* JS::NewArrayBufferWithUserOwnedContents(JSContext* cx,
   3809                                                                size_t nbytes,
   3810                                                                void* data) {
   3811  AssertHeapIsIdle();
   3812  CHECK_THREAD(cx);
   3813 
   3814  MOZ_ASSERT(data);
   3815 
   3816  using BufferContents = ArrayBufferObject::BufferContents;
   3817 
   3818  BufferContents contents = BufferContents::createUserOwned(data);
   3819  return ArrayBufferObject::createForContents(cx, nbytes, contents);
   3820 }
   3821 
   3822 JS_PUBLIC_API bool JS::IsArrayBufferObject(JSObject* obj) {
   3823  return obj->canUnwrapAs<ArrayBufferObject>();
   3824 }
   3825 
   3826 JS_PUBLIC_API bool JS::ArrayBufferHasData(JSObject* obj) {
   3827  return !obj->unwrapAs<ArrayBufferObject>().isDetached();
   3828 }
   3829 
   3830 JS_PUBLIC_API JSObject* JS::UnwrapArrayBuffer(JSObject* obj) {
   3831  return obj->maybeUnwrapIf<ArrayBufferObject>();
   3832 }
   3833 
   3834 JS_PUBLIC_API JSObject* JS::UnwrapSharedArrayBuffer(JSObject* obj) {
   3835  return obj->maybeUnwrapIf<SharedArrayBufferObject>();
   3836 }
   3837 
   3838 JS_PUBLIC_API void* JS::StealArrayBufferContents(JSContext* cx,
   3839                                                 HandleObject obj) {
   3840  AssertHeapIsIdle();
   3841  CHECK_THREAD(cx);
   3842  cx->check(obj);
   3843 
   3844  Rooted<ArrayBufferObject*> unwrappedBuffer(
   3845      cx, UnwrapOrReportArrayBuffer(cx, obj));
   3846  if (!unwrappedBuffer) {
   3847    return nullptr;
   3848  }
   3849 
   3850  if (unwrappedBuffer->isDetached()) {
   3851    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3852                              JSMSG_TYPED_ARRAY_DETACHED);
   3853    return nullptr;
   3854  }
   3855  if (unwrappedBuffer->isImmutable()) {
   3856    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3857                              JSMSG_ARRAYBUFFER_IMMUTABLE);
   3858    return nullptr;
   3859  }
   3860  if (unwrappedBuffer->isLengthPinned()) {
   3861    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3862                              JSMSG_ARRAYBUFFER_LENGTH_PINNED);
   3863    return nullptr;
   3864  }
   3865 
   3866  if (unwrappedBuffer->hasDefinedDetachKey()) {
   3867    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3868                              JSMSG_WASM_NO_TRANSFER);
   3869    return nullptr;
   3870  }
   3871 
   3872  AutoRealm ar(cx, unwrappedBuffer);
   3873  return ArrayBufferObject::stealMallocedContents(cx, unwrappedBuffer);
   3874 }
   3875 
   3876 JS_PUBLIC_API JSObject* JS::NewMappedArrayBufferWithContents(JSContext* cx,
   3877                                                             size_t nbytes,
   3878                                                             void* data) {
   3879  AssertHeapIsIdle();
   3880  CHECK_THREAD(cx);
   3881 
   3882  MOZ_ASSERT(data);
   3883 
   3884  using BufferContents = ArrayBufferObject::BufferContents;
   3885 
   3886  BufferContents contents = BufferContents::createMapped(data);
   3887  return ArrayBufferObject::createForContents(cx, nbytes, contents);
   3888 }
   3889 
   3890 JS_PUBLIC_API void* JS::CreateMappedArrayBufferContents(int fd, size_t offset,
   3891                                                        size_t length) {
   3892  return ArrayBufferObject::createMappedContents(fd, offset, length).data();
   3893 }
   3894 
   3895 JS_PUBLIC_API void JS::ReleaseMappedArrayBufferContents(void* contents,
   3896                                                        size_t length) {
   3897  gc::DeallocateMappedContent(contents, length);
   3898 }
   3899 
   3900 JS_PUBLIC_API bool JS::IsMappedArrayBufferObject(JSObject* obj) {
   3901  ArrayBufferObject* aobj = obj->maybeUnwrapIf<ArrayBufferObject>();
   3902  if (!aobj) {
   3903    return false;
   3904  }
   3905 
   3906  return aobj->isMapped();
   3907 }
   3908 
   3909 JS_PUBLIC_API JSObject* JS::GetObjectAsArrayBuffer(JSObject* obj,
   3910                                                   size_t* length,
   3911                                                   uint8_t** data) {
   3912  ArrayBufferObject* aobj = obj->maybeUnwrapIf<ArrayBufferObject>();
   3913  if (!aobj) {
   3914    return nullptr;
   3915  }
   3916 
   3917  *length = aobj->byteLength();
   3918  *data = aobj->dataPointer();
   3919 
   3920  return aobj;
   3921 }
   3922 
   3923 JS_PUBLIC_API void JS::GetArrayBufferLengthAndData(JSObject* obj,
   3924                                                   size_t* length,
   3925                                                   bool* isSharedMemory,
   3926                                                   uint8_t** data) {
   3927  auto& aobj = obj->as<ArrayBufferObject>();
   3928  *length = aobj.byteLength();
   3929  *data = aobj.dataPointer();
   3930  *isSharedMemory = false;
   3931 }
   3932 
   3933 const JSClass* const JS::ArrayBuffer::FixedLengthUnsharedClass =
   3934    &FixedLengthArrayBufferObject::class_;
   3935 const JSClass* const JS::ArrayBuffer::ResizableUnsharedClass =
   3936    &ResizableArrayBufferObject::class_;
   3937 const JSClass* const JS::ArrayBuffer::ImmutableUnsharedClass =
   3938    &ImmutableArrayBufferObject::class_;
   3939 const JSClass* const JS::ArrayBuffer::FixedLengthSharedClass =
   3940    &FixedLengthSharedArrayBufferObject::class_;
   3941 const JSClass* const JS::ArrayBuffer::GrowableSharedClass =
   3942    &GrowableSharedArrayBufferObject::class_;
   3943 
   3944 /* static */ JS::ArrayBuffer JS::ArrayBuffer::create(JSContext* cx,
   3945                                                     size_t nbytes) {
   3946  AssertHeapIsIdle();
   3947  CHECK_THREAD(cx);
   3948  return JS::ArrayBuffer(ArrayBufferObject::createZeroed(cx, nbytes));
   3949 }
   3950 
   3951 mozilla::Span<uint8_t> JS::ArrayBuffer::getData(
   3952    bool* isSharedMemory, const JS::AutoRequireNoGC& nogc) {
   3953  auto* buffer = obj->maybeUnwrapAs<ArrayBufferObjectMaybeShared>();
   3954  if (!buffer) {
   3955    return nullptr;
   3956  }
   3957  size_t length = buffer->byteLength();
   3958  if (buffer->is<SharedArrayBufferObject>()) {
   3959    *isSharedMemory = true;
   3960    return {buffer->dataPointerEither().unwrap(), length};
   3961  }
   3962  *isSharedMemory = false;
   3963  return {buffer->as<ArrayBufferObject>().dataPointer(), length};
   3964 };
   3965 
   3966 JS::ArrayBuffer JS::ArrayBuffer::unwrap(JSObject* maybeWrapped) {
   3967  if (!maybeWrapped) {
   3968    return JS::ArrayBuffer(nullptr);
   3969  }
   3970  auto* ab = maybeWrapped->maybeUnwrapIf<ArrayBufferObjectMaybeShared>();
   3971  return fromObject(ab);
   3972 }
   3973 
   3974 bool JS::ArrayBufferCopyData(JSContext* cx, Handle<JSObject*> toBlock,
   3975                             size_t toIndex, Handle<JSObject*> fromBlock,
   3976                             size_t fromIndex, size_t count) {
   3977  Rooted<ArrayBufferObjectMaybeShared*> unwrappedToBlock(
   3978      cx, toBlock->maybeUnwrapIf<ArrayBufferObjectMaybeShared>());
   3979  if (!unwrappedToBlock) {
   3980    ReportAccessDenied(cx);
   3981    return false;
   3982  }
   3983 
   3984  Rooted<ArrayBufferObjectMaybeShared*> unwrappedFromBlock(
   3985      cx, fromBlock->maybeUnwrapIf<ArrayBufferObjectMaybeShared>());
   3986  if (!unwrappedFromBlock) {
   3987    ReportAccessDenied(cx);
   3988    return false;
   3989  }
   3990 
   3991  // Verify that lengths still make sense and throw otherwise.
   3992  if (toIndex + count < toIndex ||      // size_t overflow
   3993      fromIndex + count < fromIndex ||  // size_t overflow
   3994      toIndex + count > unwrappedToBlock->byteLength() ||
   3995      fromIndex + count > unwrappedFromBlock->byteLength()) {
   3996    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3997                              JSMSG_ARRAYBUFFER_COPY_RANGE);
   3998    return false;
   3999  }
   4000 
   4001  MOZ_ASSERT(!unwrappedToBlock->isDetached());
   4002  MOZ_ASSERT(!unwrappedToBlock->isImmutable());
   4003  MOZ_ASSERT(!unwrappedFromBlock->isDetached());
   4004 
   4005  // If both are array buffers, can use ArrayBufferCopyData
   4006  if (unwrappedToBlock->is<ArrayBufferObject>() &&
   4007      unwrappedFromBlock->is<ArrayBufferObject>()) {
   4008    Rooted<ArrayBufferObject*> toArray(
   4009        cx, &unwrappedToBlock->as<ArrayBufferObject>());
   4010    Rooted<ArrayBufferObject*> fromArray(
   4011        cx, &unwrappedFromBlock->as<ArrayBufferObject>());
   4012    ArrayBufferObject::copyData(toArray, toIndex, fromArray, fromIndex, count);
   4013    return true;
   4014  }
   4015 
   4016  Rooted<ArrayBufferObjectMaybeShared*> toArray(
   4017      cx, &unwrappedToBlock->as<ArrayBufferObjectMaybeShared>());
   4018  Rooted<ArrayBufferObjectMaybeShared*> fromArray(
   4019      cx, &unwrappedFromBlock->as<ArrayBufferObjectMaybeShared>());
   4020  SharedArrayBufferObject::copyData(toArray, toIndex, fromArray, fromIndex,
   4021                                    count);
   4022 
   4023  return true;
   4024 }
   4025 
   4026 // https://tc39.es/ecma262/#sec-clonearraybuffer
   4027 // We only support the case where cloneConstructor is %ArrayBuffer%. Note,
   4028 // this means that cloning a SharedArrayBuffer will produce an ArrayBuffer
   4029 JSObject* JS::ArrayBufferClone(JSContext* cx, Handle<JSObject*> srcBuffer,
   4030                               size_t srcByteOffset, size_t srcLength) {
   4031  MOZ_ASSERT(srcBuffer->is<ArrayBufferObjectMaybeShared>());
   4032 
   4033  // 2. (reordered) If IsDetachedBuffer(srcBuffer) is true, throw a TypeError
   4034  // exception.
   4035  if (IsDetachedArrayBufferObject(srcBuffer)) {
   4036    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4037                              JSMSG_TYPED_ARRAY_DETACHED);
   4038    return nullptr;
   4039  }
   4040 
   4041  // 1. Let targetBuffer be ? AllocateArrayBuffer(cloneConstructor, srcLength).
   4042  JS::RootedObject targetBuffer(cx, JS::NewArrayBuffer(cx, srcLength));
   4043  if (!targetBuffer) {
   4044    return nullptr;
   4045  }
   4046 
   4047  // 3. Let srcBlock be srcBuffer.[[ArrayBufferData]].
   4048  // 4. Let targetBlock be targetBuffer.[[ArrayBufferData]].
   4049  // 5. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset,
   4050  // srcLength).
   4051  if (!ArrayBufferCopyData(cx, targetBuffer, 0, srcBuffer, srcByteOffset,
   4052                           srcLength)) {
   4053    return nullptr;
   4054  }
   4055 
   4056  // 6. Return targetBuffer.
   4057  return targetBuffer;
   4058 }