tor-browser

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

SharedArrayObject.cpp (37377B)


      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/SharedArrayObject.h"
      8 
      9 #include "mozilla/Atomics.h"
     10 #include "mozilla/DebugOnly.h"
     11 #include "mozilla/TaggedAnonymousMemory.h"
     12 
     13 #include "jsnum.h"
     14 
     15 #include "gc/GCContext.h"
     16 #include "gc/Memory.h"
     17 #include "jit/AtomicOperations.h"
     18 #include "jit/InlinableNatives.h"
     19 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     20 #include "js/Prefs.h"
     21 #include "js/PropertySpec.h"
     22 #include "js/SharedArrayBuffer.h"
     23 #include "util/Memory.h"
     24 #include "util/WindowsWrapper.h"
     25 #include "vm/Interpreter.h"
     26 #include "vm/SelfHosting.h"
     27 #include "vm/SharedMem.h"
     28 #include "wasm/WasmConstants.h"
     29 #include "wasm/WasmMemory.h"
     30 
     31 #include "vm/ArrayBufferObject-inl.h"
     32 #include "vm/JSObject-inl.h"
     33 #include "vm/NativeObject-inl.h"
     34 
     35 using js::wasm::Pages;
     36 using mozilla::DebugOnly;
     37 
     38 using namespace js;
     39 using namespace js::jit;
     40 
     41 static size_t WasmSharedArrayAccessibleSize(size_t length) {
     42  return AlignBytes(length, gc::SystemPageSize());
     43 }
     44 
     45 static size_t NonWasmSharedArrayAllocSize(size_t length) {
     46  MOZ_ASSERT(length <= ArrayBufferObject::ByteLengthLimit);
     47  return sizeof(SharedArrayRawBuffer) + length;
     48 }
     49 
     50 // The mapped size for a plain shared array buffer, used only for tracking
     51 // memory usage. This is incorrect for some WASM cases, and for hypothetical
     52 // callers of js::SharedArrayBufferObject::createFromNewRawBuffer that do not
     53 // currently exist, but it's fine as a signal of GC pressure.
     54 static size_t SharedArrayMappedSize(bool isWasm, size_t length) {
     55  // Wasm buffers use MapBufferMemory and allocate a full page for the header.
     56  // Non-Wasm buffers use malloc.
     57  if (isWasm) {
     58    return WasmSharedArrayAccessibleSize(length) + gc::SystemPageSize();
     59  }
     60  return NonWasmSharedArrayAllocSize(length);
     61 }
     62 
     63 SharedArrayRawBuffer* SharedArrayRawBuffer::Allocate(bool isGrowable,
     64                                                     size_t length,
     65                                                     size_t maxLength) {
     66  MOZ_RELEASE_ASSERT(length <= ArrayBufferObject::ByteLengthLimit);
     67  MOZ_RELEASE_ASSERT(maxLength <= ArrayBufferObject::ByteLengthLimit);
     68  MOZ_ASSERT_IF(!isGrowable, length == maxLength);
     69  MOZ_ASSERT_IF(isGrowable, length <= maxLength);
     70 
     71  size_t allocSize = NonWasmSharedArrayAllocSize(maxLength);
     72  uint8_t* p = js_pod_calloc<uint8_t>(allocSize);
     73  if (!p) {
     74    return nullptr;
     75  }
     76  MOZ_ASSERT(reinterpret_cast<uintptr_t>(p) %
     77                     ArrayBufferObject::ARRAY_BUFFER_ALIGNMENT ==
     78                 0,
     79             "shared array buffer memory is aligned");
     80 
     81  // jemalloc tiny allocations can produce allocations not aligned to the
     82  // smallest std::malloc allocation. Ensure shared array buffer allocations
     83  // don't have to worry about this special case.
     84  static_assert(sizeof(SharedArrayRawBuffer) > sizeof(void*),
     85                "SharedArrayRawBuffer doesn't fit in jemalloc tiny allocation");
     86 
     87  static_assert(sizeof(SharedArrayRawBuffer) %
     88                        ArrayBufferObject::ARRAY_BUFFER_ALIGNMENT ==
     89                    0,
     90                "sizeof(SharedArrayRawBuffer) is a multiple of the array "
     91                "buffer alignment, so |p + sizeof(SharedArrayRawBuffer)| is "
     92                "also array buffer aligned");
     93 
     94  uint8_t* buffer = p + sizeof(SharedArrayRawBuffer);
     95  return new (p) SharedArrayRawBuffer(isGrowable, buffer, length);
     96 }
     97 
     98 WasmSharedArrayRawBuffer* WasmSharedArrayRawBuffer::AllocateWasm(
     99    wasm::AddressType addressType, wasm::PageSize pageSize, Pages initialPages,
    100    wasm::Pages clampedMaxPages,
    101    const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
    102    const mozilla::Maybe<size_t>& mappedSize) {
    103  // Prior code has asserted that initial pages is within our implementation
    104  // limits (wasm::MaxMemoryPages()) and we can assume it is a valid size_t.
    105  MOZ_RELEASE_ASSERT(initialPages.pageSize() == pageSize);
    106  MOZ_RELEASE_ASSERT(clampedMaxPages.pageSize() == pageSize);
    107  MOZ_RELEASE_ASSERT(!sourceMaxPages.isSome() ||
    108                     (pageSize == sourceMaxPages->pageSize()));
    109  MOZ_ASSERT(initialPages.hasByteLength());
    110  size_t length = initialPages.byteLength();
    111 
    112  MOZ_RELEASE_ASSERT(length <= ArrayBufferObject::ByteLengthLimit);
    113 
    114  size_t accessibleSize = WasmSharedArrayAccessibleSize(length);
    115  if (accessibleSize < length) {
    116    return nullptr;
    117  }
    118 
    119  size_t computedMappedSize = mappedSize.isSome()
    120                                  ? *mappedSize
    121                                  : wasm::ComputeMappedSize(clampedMaxPages);
    122  MOZ_ASSERT(accessibleSize <= computedMappedSize);
    123 
    124  uint64_t mappedSizeWithHeader = computedMappedSize + gc::SystemPageSize();
    125  uint64_t accessibleSizeWithHeader = accessibleSize + gc::SystemPageSize();
    126 
    127  void* p = MapBufferMemory(addressType, pageSize, mappedSizeWithHeader,
    128                            accessibleSizeWithHeader);
    129  if (!p) {
    130    return nullptr;
    131  }
    132 
    133  uint8_t* buffer = reinterpret_cast<uint8_t*>(p) + gc::SystemPageSize();
    134  uint8_t* base = buffer - sizeof(WasmSharedArrayRawBuffer);
    135  return new (base) WasmSharedArrayRawBuffer(
    136      buffer, length, addressType, pageSize, clampedMaxPages,
    137      sourceMaxPages.valueOr(Pages::fromPageCount(0, pageSize)),
    138      computedMappedSize);
    139 }
    140 
    141 bool WasmSharedArrayRawBuffer::wasmGrowToPagesInPlace(const Lock&,
    142                                                      wasm::AddressType t,
    143                                                      wasm::Pages newPages) {
    144  // Check that the new pages is within our allowable range. This will
    145  // simultaneously check against the maximum specified in source and our
    146  // implementation limits.
    147  if (newPages > clampedMaxPages_) {
    148    return false;
    149  }
    150  MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t, newPages.pageSize()) &&
    151             newPages.byteLength() <= ArrayBufferObject::ByteLengthLimit);
    152 
    153  // We have checked against the clamped maximum and so we know we can convert
    154  // to byte lengths now.
    155  size_t newLength = newPages.byteLength();
    156 
    157  MOZ_ASSERT(newLength >= length_);
    158 
    159  if (newLength == length_) {
    160    return true;
    161  }
    162 
    163  size_t delta = newLength - length_;
    164  MOZ_ASSERT(delta % wasm::StandardPageSizeBytes == 0);
    165 
    166  uint8_t* dataEnd = dataPointerShared().unwrap(/* for resize */) + length_;
    167  MOZ_ASSERT(uintptr_t(dataEnd) % gc::SystemPageSize() == 0);
    168 
    169  if (!CommitBufferMemory(dataEnd, delta)) {
    170    return false;
    171  }
    172 
    173  // We rely on CommitBufferMemory (and therefore memmap/VirtualAlloc) to only
    174  // return once it has committed memory for all threads. We only update with a
    175  // new length once this has occurred.
    176  length_ = newLength;
    177 
    178  return true;
    179 }
    180 
    181 void WasmSharedArrayRawBuffer::discard(size_t byteOffset, size_t byteLen) {
    182  SharedMem<uint8_t*> memBase = dataPointerShared();
    183 
    184  // The caller is responsible for ensuring these conditions are met; see this
    185  // function's comment in SharedArrayObject.h.
    186  MOZ_ASSERT(byteOffset % wasm::StandardPageSizeBytes == 0);
    187  MOZ_ASSERT(byteLen % wasm::StandardPageSizeBytes == 0);
    188  MOZ_ASSERT(wasm::MemoryBoundsCheck(uint64_t(byteOffset), uint64_t(byteLen),
    189                                     volatileByteLength()));
    190 
    191  // Discarding zero bytes "succeeds" with no effect.
    192  if (byteLen == 0) {
    193    return;
    194  }
    195 
    196  SharedMem<uint8_t*> addr = memBase + uintptr_t(byteOffset);
    197 
    198  // On POSIX-ish platforms, we discard memory by overwriting previously-mapped
    199  // pages with freshly-mapped pages (which are all zeroed). The operating
    200  // system recognizes this and decreases the process RSS, and eventually
    201  // collects the abandoned physical pages.
    202  //
    203  // On Windows, committing over previously-committed pages has no effect. We
    204  // could decommit and recommit, but this doesn't work for shared memories
    205  // since other threads could access decommitted memory - causing a trap.
    206  // Instead, we simply zero memory (memset 0), and then VirtualUnlock(), which
    207  // for Historical Reasons immediately removes the pages from the working set.
    208  // And then, because the pages were zeroed, Windows will actually reclaim the
    209  // memory entirely instead of paging it out to disk. Naturally this behavior
    210  // is not officially documented, but a Raymond Chen blog post is basically as
    211  // good as MSDN, right?
    212  //
    213  // https://devblogs.microsoft.com/oldnewthing/20170113-00/?p=95185
    214 
    215 #ifdef XP_WIN
    216  // Discarding the entire region at once causes us to page the entire region
    217  // into the working set, only to throw it out again. This can be actually
    218  // disastrous when discarding already-discarded memory. To mitigate this, we
    219  // discard a chunk of memory at a time - this comes at a small performance
    220  // cost from syscalls and potentially less-optimal memsets.
    221  size_t numPages = byteLen / wasm::StandardPageSizeBytes;
    222  for (size_t i = 0; i < numPages; i++) {
    223    AtomicOperations::memsetSafeWhenRacy(
    224        addr + (i * wasm::StandardPageSizeBytes), 0,
    225        wasm::StandardPageSizeBytes);
    226    DebugOnly<bool> result =
    227        VirtualUnlock(addr.unwrap() + (i * wasm::StandardPageSizeBytes),
    228                      wasm::StandardPageSizeBytes);
    229    MOZ_ASSERT(!result);  // this always "fails" when unlocking unlocked
    230                          // memory...which is the only case we care about
    231  }
    232 #elif defined(__wasi__)
    233  AtomicOperations::memsetSafeWhenRacy(addr, 0, byteLen);
    234 #else  // !XP_WIN
    235  void* data = MozTaggedAnonymousMmap(
    236      addr.unwrap(), byteLen, PROT_READ | PROT_WRITE,
    237      MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0, "wasm-reserved");
    238  if (data == MAP_FAILED) {
    239    MOZ_CRASH("failed to discard wasm memory; memory mappings may be broken");
    240  }
    241 #endif
    242 }
    243 
    244 bool SharedArrayRawBuffer::addReference() {
    245  MOZ_RELEASE_ASSERT(refcount_ > 0);
    246 
    247  // Be careful never to overflow the refcount field.
    248  for (;;) {
    249    uint32_t old_refcount = refcount_;
    250    uint32_t new_refcount = old_refcount + 1;
    251    if (new_refcount == 0) {
    252      return false;
    253    }
    254    if (refcount_.compareExchange(old_refcount, new_refcount)) {
    255      return true;
    256    }
    257  }
    258 }
    259 
    260 void SharedArrayRawBuffer::dropReference() {
    261  // Normally if the refcount is zero then the memory will have been unmapped
    262  // and this test may just crash, but if the memory has been retained for any
    263  // reason we will catch the underflow here.
    264  MOZ_RELEASE_ASSERT(refcount_ > 0);
    265 
    266  // Drop the reference to the buffer.
    267  uint32_t new_refcount = --refcount_;  // Atomic.
    268  if (new_refcount) {
    269    return;
    270  }
    271 
    272  // This was the final reference, so release the buffer.
    273  if (isWasm()) {
    274    WasmSharedArrayRawBuffer* wasmBuf = toWasmBuffer();
    275    wasm::AddressType addressType = wasmBuf->wasmAddressType();
    276    uint8_t* basePointer = wasmBuf->basePointer();
    277    size_t mappedSizeWithHeader = wasmBuf->mappedSize() + gc::SystemPageSize();
    278    size_t committedSize = wasmBuf->volatileByteLength() + gc::SystemPageSize();
    279    // Call the destructor to destroy the growLock_ Mutex.
    280    wasmBuf->~WasmSharedArrayRawBuffer();
    281    UnmapBufferMemory(addressType, basePointer, mappedSizeWithHeader,
    282                      committedSize);
    283  } else {
    284    js_delete(this);
    285  }
    286 }
    287 
    288 bool SharedArrayRawBuffer::growJS(size_t newByteLength) {
    289  MOZ_ASSERT(!isWasm());
    290  MOZ_RELEASE_ASSERT(isGrowableJS());
    291 
    292  // The caller is responsible to ensure |newByteLength| doesn't exceed the
    293  // maximum allowed byte length.
    294 
    295  while (true) {
    296    // `mozilla::Atomic::compareExchange` doesn't return the current value, so
    297    // we need to perform a normal load here. (bug 1005335)
    298    size_t oldByteLength = length_;
    299    if (newByteLength == oldByteLength) {
    300      return true;
    301    }
    302    if (newByteLength < oldByteLength) {
    303      return false;
    304    }
    305    if (length_.compareExchange(oldByteLength, newByteLength)) {
    306      return true;
    307    }
    308  }
    309 }
    310 
    311 static bool IsSharedArrayBuffer(HandleValue v) {
    312  return v.isObject() && v.toObject().is<SharedArrayBufferObject>();
    313 }
    314 
    315 static bool IsGrowableSharedArrayBuffer(HandleValue v) {
    316  return v.isObject() && v.toObject().is<GrowableSharedArrayBufferObject>();
    317 }
    318 
    319 MOZ_ALWAYS_INLINE bool SharedArrayBufferObject::byteLengthGetterImpl(
    320    JSContext* cx, const CallArgs& args) {
    321  MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
    322  auto* buffer = &args.thisv().toObject().as<SharedArrayBufferObject>();
    323  args.rval().setNumber(buffer->byteLength());
    324  return true;
    325 }
    326 
    327 bool SharedArrayBufferObject::byteLengthGetter(JSContext* cx, unsigned argc,
    328                                               Value* vp) {
    329  CallArgs args = CallArgsFromVp(argc, vp);
    330  return CallNonGenericMethod<IsSharedArrayBuffer, byteLengthGetterImpl>(cx,
    331                                                                         args);
    332 }
    333 
    334 /**
    335 * get SharedArrayBuffer.prototype.maxByteLength
    336 */
    337 bool SharedArrayBufferObject::maxByteLengthGetterImpl(JSContext* cx,
    338                                                      const CallArgs& args) {
    339  MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
    340  auto* buffer = &args.thisv().toObject().as<SharedArrayBufferObject>();
    341 
    342  // Special case for wasm with potentially 64-bits memory.
    343  // Manually compute the maxByteLength to avoid an overflow on 32-bit machines.
    344  if (buffer->isWasm() && buffer->isResizable()) {
    345    Pages sourceMaxPages = buffer->rawWasmBufferObject()->wasmSourceMaxPages();
    346    uint64_t sourceMaxBytes = sourceMaxPages.byteLength64();
    347 
    348    MOZ_ASSERT(sourceMaxBytes <= wasm::StandardPageSizeBytes *
    349                                     wasm::MaxMemory64StandardPagesValidation);
    350    args.rval().setNumber(double(sourceMaxBytes));
    351 
    352    return true;
    353  }
    354 
    355  // Steps 4-6.
    356  args.rval().setNumber(buffer->byteLengthOrMaxByteLength());
    357  return true;
    358 }
    359 
    360 /**
    361 * get SharedArrayBuffer.prototype.maxByteLength
    362 */
    363 bool SharedArrayBufferObject::maxByteLengthGetter(JSContext* cx, unsigned argc,
    364                                                  Value* vp) {
    365  // Steps 1-3.
    366  CallArgs args = CallArgsFromVp(argc, vp);
    367  return CallNonGenericMethod<IsSharedArrayBuffer, maxByteLengthGetterImpl>(
    368      cx, args);
    369 }
    370 
    371 /**
    372 * get SharedArrayBuffer.prototype.growable
    373 */
    374 bool SharedArrayBufferObject::growableGetterImpl(JSContext* cx,
    375                                                 const CallArgs& args) {
    376  MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
    377  auto* buffer = &args.thisv().toObject().as<SharedArrayBufferObject>();
    378 
    379  // Step 4.
    380  args.rval().setBoolean(buffer->isGrowable());
    381  return true;
    382 }
    383 
    384 /**
    385 * get SharedArrayBuffer.prototype.growable
    386 */
    387 bool SharedArrayBufferObject::growableGetter(JSContext* cx, unsigned argc,
    388                                             Value* vp) {
    389  // Steps 1-3.
    390  CallArgs args = CallArgsFromVp(argc, vp);
    391  return CallNonGenericMethod<IsSharedArrayBuffer, growableGetterImpl>(cx,
    392                                                                       args);
    393 }
    394 
    395 /**
    396 * SharedArrayBuffer.prototype.grow ( newLength )
    397 */
    398 bool SharedArrayBufferObject::growImpl(JSContext* cx, const CallArgs& args) {
    399  MOZ_ASSERT(IsGrowableSharedArrayBuffer(args.thisv()));
    400  Rooted<GrowableSharedArrayBufferObject*> buffer(
    401      cx, &args.thisv().toObject().as<GrowableSharedArrayBufferObject>());
    402 
    403  // Step 4.
    404  uint64_t newByteLength;
    405  if (!ToIndex(cx, args.get(0), &newByteLength)) {
    406    return false;
    407  }
    408 
    409  // Steps 5-11.
    410  if (newByteLength > buffer->maxByteLength()) {
    411    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    412                              JSMSG_ARRAYBUFFER_LENGTH_LARGER_THAN_MAXIMUM);
    413    return false;
    414  }
    415 
    416  if (buffer->isWasm()) {
    417    // Special case for resizing of Wasm buffers.
    418    if (newByteLength % wasm::StandardPageSizeBytes != 0) {
    419      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    420                                JSMSG_WASM_ARRAYBUFFER_PAGE_MULTIPLE);
    421      return false;
    422    }
    423 
    424    mozilla::Maybe<WasmSharedArrayRawBuffer::Lock> lock(
    425        mozilla::Some(buffer->rawWasmBufferObject()));
    426 
    427    if (newByteLength < buffer->rawWasmBufferObject()->volatileByteLength()) {
    428      lock.reset();
    429      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    430                                JSMSG_WASM_ARRAYBUFFER_CANNOT_SHRINK);
    431      return false;
    432    }
    433 
    434    Pages newPages =
    435        Pages::fromByteLengthExact(newByteLength, buffer->wasmPageSize());
    436    return buffer->rawWasmBufferObject()->wasmGrowToPagesInPlace(
    437        *lock, buffer->wasmAddressType(), newPages);
    438  }
    439 
    440  if (!buffer->rawBufferObject()->growJS(newByteLength)) {
    441    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    442                              JSMSG_SHARED_ARRAY_LENGTH_SMALLER_THAN_CURRENT);
    443    return false;
    444  }
    445 
    446  args.rval().setUndefined();
    447  return true;
    448 }
    449 
    450 /**
    451 * SharedArrayBuffer.prototype.grow ( newLength )
    452 */
    453 bool SharedArrayBufferObject::grow(JSContext* cx, unsigned argc, Value* vp) {
    454  // Steps 1-3.
    455  CallArgs args = CallArgsFromVp(argc, vp);
    456  return CallNonGenericMethod<IsGrowableSharedArrayBuffer, growImpl>(cx, args);
    457 }
    458 
    459 static bool IsSharedArrayBufferSpecies(JSContext* cx, JSFunction* species) {
    460  return IsSelfHostedFunctionWithName(
    461      species, cx->names().dollar_SharedArrayBufferSpecies_);
    462 }
    463 
    464 static bool HasBuiltinSharedArrayBufferSpecies(SharedArrayBufferObject* obj,
    465                                               JSContext* cx) {
    466  // Ensure `SharedArrayBuffer.prototype.constructor` and
    467  // `SharedArrayBuffer[@@species]` haven't been mutated.
    468  if (!cx->realm()->realmFuses.optimizeSharedArrayBufferSpeciesFuse.intact()) {
    469    return false;
    470  }
    471 
    472  // Ensure |obj|'s prototype is the actual SharedArrayBuffer.prototype.
    473  auto* proto = cx->global()->maybeGetPrototype(JSProto_SharedArrayBuffer);
    474  if (!proto || obj->staticPrototype() != proto) {
    475    return false;
    476  }
    477 
    478  // Fail if |obj| has an own `constructor` property.
    479  if (obj->containsPure(NameToId(cx->names().constructor))) {
    480    return false;
    481  }
    482 
    483  return true;
    484 }
    485 
    486 /**
    487 * SharedArrayBuffer.prototype.slice ( start, end )
    488 *
    489 * https://tc39.es/ecma262/#sec-sharedarraybuffer.prototype.slice
    490 */
    491 bool SharedArrayBufferObject::sliceImpl(JSContext* cx, const CallArgs& args) {
    492  MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
    493 
    494  Rooted<SharedArrayBufferObject*> obj(
    495      cx, &args.thisv().toObject().as<SharedArrayBufferObject>());
    496 
    497  // Step 4.
    498  size_t len = obj->byteLength();
    499 
    500  // Steps 5-8.
    501  size_t first = 0;
    502  if (args.hasDefined(0)) {
    503    if (!ToIntegerIndex(cx, args[0], len, &first)) {
    504      return false;
    505    }
    506  }
    507 
    508  // Steps 9-12.
    509  size_t final_ = len;
    510  if (args.hasDefined(1)) {
    511    if (!ToIntegerIndex(cx, args[1], len, &final_)) {
    512      return false;
    513    }
    514  }
    515 
    516  // Step 13.
    517  size_t newLen = final_ >= first ? final_ - first : 0;
    518  MOZ_ASSERT(newLen <= ArrayBufferObject::ByteLengthLimit);
    519 
    520  // Steps 14-19.
    521  Rooted<JSObject*> resultObj(cx);
    522  SharedArrayBufferObject* unwrappedResult = nullptr;
    523  if (HasBuiltinSharedArrayBufferSpecies(obj, cx)) {
    524    // Steps 14-15.
    525    unwrappedResult = New(cx, newLen);
    526    if (!unwrappedResult) {
    527      return false;
    528    }
    529    resultObj.set(unwrappedResult);
    530 
    531    // Steps 16-17. (Not applicable)
    532 
    533    // Step 18.
    534    MOZ_ASSERT(obj->rawBufferObject() != unwrappedResult->rawBufferObject());
    535 
    536    // Step 19.
    537    MOZ_ASSERT(unwrappedResult->byteLength() == newLen);
    538  } else {
    539    // Step 14.
    540    Rooted<JSObject*> ctor(
    541        cx, SpeciesConstructor(cx, obj, JSProto_SharedArrayBuffer,
    542                               IsSharedArrayBufferSpecies));
    543    if (!ctor) {
    544      return false;
    545    }
    546 
    547    // Step 15.
    548    {
    549      FixedConstructArgs<1> cargs(cx);
    550      cargs[0].setNumber(newLen);
    551 
    552      Rooted<Value> ctorVal(cx, ObjectValue(*ctor));
    553      if (!Construct(cx, ctorVal, cargs, ctorVal, &resultObj)) {
    554        return false;
    555      }
    556    }
    557 
    558    // Steps 16-17.
    559    unwrappedResult = resultObj->maybeUnwrapIf<SharedArrayBufferObject>();
    560    if (!unwrappedResult) {
    561      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    562                                JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED);
    563      return false;
    564    }
    565 
    566    // Step 18.
    567    if (obj->rawBufferObject() == unwrappedResult->rawBufferObject()) {
    568      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    569                                JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED);
    570      return false;
    571    }
    572 
    573    // Step 19.
    574    size_t resultByteLength = unwrappedResult->byteLength();
    575    if (resultByteLength < newLen) {
    576      ToCStringBuf resultLenCbuf;
    577      const char* resultLenStr =
    578          NumberToCString(&resultLenCbuf, double(resultByteLength));
    579 
    580      ToCStringBuf newLenCbuf;
    581      const char* newLenStr = NumberToCString(&newLenCbuf, double(newLen));
    582 
    583      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    584                                JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED,
    585                                newLenStr, resultLenStr);
    586      return false;
    587    }
    588  }
    589 
    590  // Steps 20-22.
    591  SharedArrayBufferObject::copyData(unwrappedResult, 0, obj, first, newLen);
    592 
    593  // Step 23.
    594  args.rval().setObject(*resultObj);
    595  return true;
    596 }
    597 
    598 /**
    599 * SharedArrayBuffer.prototype.slice ( start, end )
    600 *
    601 * https://tc39.es/ecma262/#sec-sharedarraybuffer.prototype.slice
    602 */
    603 bool SharedArrayBufferObject::slice(JSContext* cx, unsigned argc, Value* vp) {
    604  // Steps 1-3.
    605  CallArgs args = CallArgsFromVp(argc, vp);
    606  return CallNonGenericMethod<IsSharedArrayBuffer, sliceImpl>(cx, args);
    607 }
    608 
    609 // ES2024 draft rev 3a773fc9fae58be023228b13dbbd402ac18eeb6b
    610 // 25.2.3.1 SharedArrayBuffer ( length [ , options ] )
    611 bool SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc,
    612                                                Value* vp) {
    613  CallArgs args = CallArgsFromVp(argc, vp);
    614 
    615  // Step 1.
    616  if (!ThrowIfNotConstructing(cx, args, "SharedArrayBuffer")) {
    617    return false;
    618  }
    619 
    620  // Step 2.
    621  uint64_t byteLength;
    622  if (!ToIndex(cx, args.get(0), &byteLength)) {
    623    return false;
    624  }
    625 
    626  // Step 3.
    627  mozilla::Maybe<uint64_t> maxByteLength;
    628  // Inline call to GetArrayBufferMaxByteLengthOption.
    629  if (args.get(1).isObject()) {
    630    Rooted<JSObject*> options(cx, &args[1].toObject());
    631 
    632    Rooted<Value> val(cx);
    633    if (!GetProperty(cx, options, options, cx->names().maxByteLength, &val)) {
    634      return false;
    635    }
    636    if (!val.isUndefined()) {
    637      uint64_t maxByteLengthInt;
    638      if (!ToIndex(cx, val, &maxByteLengthInt)) {
    639        return false;
    640      }
    641 
    642      // 25.2.2.1 AllocateSharedArrayBuffer, step 3.a.
    643      if (byteLength > maxByteLengthInt) {
    644        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    645                                  JSMSG_ARRAYBUFFER_LENGTH_LARGER_THAN_MAXIMUM);
    646        return false;
    647      }
    648      maxByteLength = mozilla::Some(maxByteLengthInt);
    649    }
    650  }
    651 
    652  // Step 4 (Inlined 25.2.2.1 AllocateSharedArrayBuffer).
    653  // 25.2.2.1, step 5 (Inlined 10.1.13 OrdinaryCreateFromConstructor, step 2).
    654  RootedObject proto(cx);
    655  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_SharedArrayBuffer,
    656                                          &proto)) {
    657    return false;
    658  }
    659 
    660  // 25.2.2.1, step 6.
    661  uint64_t allocLength = maxByteLength.valueOr(byteLength);
    662 
    663  // 25.2.2.1, step 7 (Inlined 6.2.9.2 CreateSharedByteDataBlock, step 1).
    664  // Refuse to allocate too large buffers.
    665  if (allocLength > ArrayBufferObject::ByteLengthLimit) {
    666    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    667                              JSMSG_SHARED_ARRAY_BAD_LENGTH);
    668    return false;
    669  }
    670 
    671  if (maxByteLength) {
    672    // 25.2.2.1, remaining steps.
    673    auto* bufobj = NewGrowable(cx, byteLength, *maxByteLength, proto);
    674    if (!bufobj) {
    675      return false;
    676    }
    677    args.rval().setObject(*bufobj);
    678    return true;
    679  }
    680 
    681  // 25.2.2.1, remaining steps.
    682  JSObject* bufobj = New(cx, byteLength, proto);
    683  if (!bufobj) {
    684    return false;
    685  }
    686  args.rval().setObject(*bufobj);
    687  return true;
    688 }
    689 
    690 FixedLengthSharedArrayBufferObject* SharedArrayBufferObject::New(
    691    JSContext* cx, size_t length, HandleObject proto) {
    692  bool isGrowable = false;
    693  size_t maxLength = length;
    694  auto* buffer = SharedArrayRawBuffer::Allocate(isGrowable, length, maxLength);
    695  if (!buffer) {
    696    js::ReportOutOfMemory(cx);
    697    return nullptr;
    698  }
    699 
    700  auto* obj = New(cx, buffer, length, proto);
    701  if (!obj) {
    702    buffer->dropReference();
    703    return nullptr;
    704  }
    705 
    706  return obj;
    707 }
    708 
    709 FixedLengthSharedArrayBufferObject* SharedArrayBufferObject::New(
    710    JSContext* cx, SharedArrayRawBuffer* buffer, size_t length,
    711    HandleObject proto) {
    712  return NewWith<FixedLengthSharedArrayBufferObject>(cx, buffer, length, proto);
    713 }
    714 
    715 GrowableSharedArrayBufferObject* SharedArrayBufferObject::NewGrowable(
    716    JSContext* cx, size_t length, size_t maxLength, HandleObject proto) {
    717  bool isGrowable = true;
    718  auto* buffer = SharedArrayRawBuffer::Allocate(isGrowable, length, maxLength);
    719  if (!buffer) {
    720    js::ReportOutOfMemory(cx);
    721    return nullptr;
    722  }
    723 
    724  auto* obj = NewGrowable(cx, buffer, maxLength, proto);
    725  if (!obj) {
    726    buffer->dropReference();
    727    return nullptr;
    728  }
    729 
    730  return obj;
    731 }
    732 
    733 GrowableSharedArrayBufferObject* SharedArrayBufferObject::NewGrowable(
    734    JSContext* cx, SharedArrayRawBuffer* buffer, size_t maxLength,
    735    HandleObject proto) {
    736  return NewWith<GrowableSharedArrayBufferObject>(cx, buffer, maxLength, proto);
    737 }
    738 
    739 template <class SharedArrayBufferType>
    740 SharedArrayBufferType* SharedArrayBufferObject::NewWith(
    741    JSContext* cx, SharedArrayRawBuffer* buffer, size_t length,
    742    HandleObject proto) {
    743  MOZ_ASSERT(cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
    744 
    745  static_assert(
    746      std::is_same_v<SharedArrayBufferType,
    747                     FixedLengthSharedArrayBufferObject> ||
    748      std::is_same_v<SharedArrayBufferType, GrowableSharedArrayBufferObject>);
    749 
    750  if (!buffer->isWasm()) {
    751    if constexpr (std::is_same_v<SharedArrayBufferType,
    752                                 FixedLengthSharedArrayBufferObject>) {
    753      MOZ_ASSERT(!buffer->isGrowableJS());
    754    } else {
    755      MOZ_ASSERT(buffer->isGrowableJS());
    756    }
    757  }
    758 
    759  AutoSetNewObjectMetadata metadata(cx);
    760  auto* obj = NewObjectWithClassProto<SharedArrayBufferType>(cx, proto);
    761  if (!obj) {
    762    return nullptr;
    763  }
    764 
    765  MOZ_ASSERT(obj->getClass() == &SharedArrayBufferType::class_);
    766 
    767  cx->runtime()->incSABCount();
    768 
    769  if (!obj->acceptRawBuffer(buffer, length)) {
    770    js::ReportOutOfMemory(cx);
    771    return nullptr;
    772  }
    773 
    774  return obj;
    775 }
    776 
    777 bool SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer* buffer,
    778                                              size_t length) {
    779  MOZ_ASSERT(!isInitialized());
    780  if (!zone()->addSharedMemory(buffer,
    781                               SharedArrayMappedSize(buffer->isWasm(), length),
    782                               MemoryUse::SharedArrayRawBuffer)) {
    783    return false;
    784  }
    785 
    786  setFixedSlot(RAWBUF_SLOT, PrivateValue(buffer));
    787  setFixedSlot(LENGTH_SLOT, PrivateValue(length));
    788  MOZ_ASSERT(isInitialized());
    789  return true;
    790 }
    791 
    792 void SharedArrayBufferObject::dropRawBuffer() {
    793  size_t length = byteLengthOrMaxByteLength();
    794  size_t size = SharedArrayMappedSize(isWasm(), length);
    795  zoneFromAnyThread()->removeSharedMemory(rawBufferObject(), size,
    796                                          MemoryUse::SharedArrayRawBuffer);
    797  rawBufferObject()->dropReference();
    798  setFixedSlot(RAWBUF_SLOT, UndefinedValue());
    799  MOZ_ASSERT(!isInitialized());
    800 }
    801 
    802 SharedArrayRawBuffer* SharedArrayBufferObject::rawBufferObject() const {
    803  Value v = getFixedSlot(RAWBUF_SLOT);
    804  MOZ_ASSERT(!v.isUndefined());
    805  return reinterpret_cast<SharedArrayRawBuffer*>(v.toPrivate());
    806 }
    807 
    808 void SharedArrayBufferObject::Finalize(JS::GCContext* gcx, JSObject* obj) {
    809  // Must be foreground finalizable so that we can account for the object.
    810  MOZ_ASSERT(gcx->onMainThread());
    811  gcx->runtime()->decSABCount();
    812 
    813  SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
    814 
    815  // Detect the case of failure during SharedArrayBufferObject creation,
    816  // which causes a SharedArrayRawBuffer to never be attached.
    817  Value v = buf.getFixedSlot(RAWBUF_SLOT);
    818  if (!v.isUndefined()) {
    819    buf.dropRawBuffer();
    820  }
    821 }
    822 
    823 /* static */
    824 void SharedArrayBufferObject::addSizeOfExcludingThis(
    825    JSObject* obj, mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info,
    826    JS::RuntimeSizes* runtimeSizes) {
    827  // Divide the buffer size by the refcount to get the fraction of the buffer
    828  // owned by this thread. It's conceivable that the refcount might change in
    829  // the middle of memory reporting, in which case the amount reported for
    830  // some threads might be to high (if the refcount goes up) or too low (if
    831  // the refcount goes down). But that's unlikely and hard to avoid, so we
    832  // just live with the risk.
    833  const SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
    834 
    835  if (MOZ_UNLIKELY(!buf.isInitialized())) {
    836    return;
    837  }
    838 
    839  size_t nbytes = buf.byteLengthOrMaxByteLength();
    840  size_t owned = nbytes / buf.rawBufferObject()->refcount();
    841  if (buf.isWasm()) {
    842    info->objectsNonHeapElementsWasmShared += owned;
    843    if (runtimeSizes) {
    844      size_t ownedGuardPages =
    845          (buf.wasmMappedSize() - nbytes) / buf.rawBufferObject()->refcount();
    846      runtimeSizes->wasmGuardPages += ownedGuardPages;
    847    }
    848  } else {
    849    info->objectsNonHeapElementsShared += owned;
    850  }
    851 }
    852 
    853 /* static */
    854 void SharedArrayBufferObject::copyData(ArrayBufferObjectMaybeShared* toBuffer,
    855                                       size_t toIndex,
    856                                       ArrayBufferObjectMaybeShared* fromBuffer,
    857                                       size_t fromIndex, size_t count) {
    858  MOZ_ASSERT(!toBuffer->isDetached());
    859  MOZ_ASSERT(toBuffer->byteLength() >= count);
    860  MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count);
    861  MOZ_ASSERT(!fromBuffer->isDetached());
    862  MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
    863  MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
    864 
    865  jit::AtomicOperations::memcpySafeWhenRacy(
    866      toBuffer->dataPointerEither() + toIndex,
    867      fromBuffer->dataPointerEither() + fromIndex, count);
    868 }
    869 
    870 SharedArrayBufferObject* SharedArrayBufferObject::createFromNewRawBuffer(
    871    JSContext* cx, WasmSharedArrayRawBuffer* buffer, size_t initialSize) {
    872  MOZ_ASSERT(cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
    873 
    874  AutoSetNewObjectMetadata metadata(cx);
    875  auto* obj = NewBuiltinClassInstance<FixedLengthSharedArrayBufferObject>(cx);
    876  if (!obj) {
    877    buffer->dropReference();
    878    return nullptr;
    879  }
    880 
    881  cx->runtime()->incSABCount();
    882 
    883  if (!obj->acceptRawBuffer(buffer, initialSize)) {
    884    buffer->dropReference();
    885    js::ReportOutOfMemory(cx);
    886    return nullptr;
    887  }
    888 
    889  return obj;
    890 }
    891 
    892 template <typename SharedArrayBufferType>
    893 SharedArrayBufferType* SharedArrayBufferObject::createFromWasmObject(
    894    JSContext* cx, Handle<SharedArrayBufferObject*> wasmBuffer) {
    895  MOZ_ASSERT(cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
    896  MOZ_ASSERT(wasmBuffer->isWasm());
    897 
    898  SharedArrayRawBuffer* rawBuffer = wasmBuffer->rawBufferObject();
    899  size_t byteLengthOrMaximum;
    900  if constexpr (std::is_same_v<SharedArrayBufferType,
    901                               GrowableSharedArrayBufferObject>) {
    902    byteLengthOrMaximum = rawBuffer->toWasmBuffer()->wasmClampedMaxByteLength();
    903  } else {
    904    static_assert(std::is_same_v<SharedArrayBufferType,
    905                                 FixedLengthSharedArrayBufferObject>);
    906    byteLengthOrMaximum = rawBuffer->volatileByteLength();
    907  }
    908 
    909  if (!rawBuffer->addReference()) {
    910    JS_ReportErrorASCII(cx, "Reference count overflow on SharedArrayBuffer");
    911    return nullptr;
    912  }
    913 
    914  SharedArrayBufferType* obj = NewWith<SharedArrayBufferType>(
    915      cx, rawBuffer, byteLengthOrMaximum, nullptr);
    916  if (!obj) {
    917    rawBuffer->dropReference();
    918    return nullptr;
    919  }
    920 
    921  return obj;
    922 }
    923 
    924 template FixedLengthSharedArrayBufferObject* SharedArrayBufferObject::
    925    createFromWasmObject<FixedLengthSharedArrayBufferObject>(
    926        JSContext* cx, Handle<SharedArrayBufferObject*> wasmBuffer);
    927 
    928 template GrowableSharedArrayBufferObject*
    929 SharedArrayBufferObject::createFromWasmObject<GrowableSharedArrayBufferObject>(
    930    JSContext* cx, Handle<SharedArrayBufferObject*> wasmBuffer);
    931 
    932 /* static */
    933 void SharedArrayBufferObject::wasmDiscard(Handle<SharedArrayBufferObject*> buf,
    934                                          uint64_t byteOffset,
    935                                          uint64_t byteLen) {
    936  MOZ_ASSERT(buf->isWasm());
    937  buf->rawWasmBufferObject()->discard(byteOffset, byteLen);
    938 }
    939 
    940 static const JSClassOps SharedArrayBufferObjectClassOps = {
    941    nullptr,                            // addProperty
    942    nullptr,                            // delProperty
    943    nullptr,                            // enumerate
    944    nullptr,                            // newEnumerate
    945    nullptr,                            // resolve
    946    nullptr,                            // mayResolve
    947    SharedArrayBufferObject::Finalize,  // finalize
    948    nullptr,                            // call
    949    nullptr,                            // construct
    950    nullptr,                            // trace
    951 };
    952 
    953 static const JSFunctionSpec sharedarray_functions[] = {
    954    JS_FS_END,
    955 };
    956 
    957 static const JSPropertySpec sharedarray_properties[] = {
    958    JS_SELF_HOSTED_SYM_GET(species, "$SharedArrayBufferSpecies", 0),
    959    JS_PS_END,
    960 };
    961 
    962 static const JSFunctionSpec sharedarray_proto_functions[] = {
    963    JS_FN("slice", SharedArrayBufferObject::slice, 2, 0),
    964    JS_FN("grow", SharedArrayBufferObject::grow, 1, 0),
    965    JS_FS_END,
    966 };
    967 
    968 static const JSPropertySpec sharedarray_proto_properties[] = {
    969    JS_INLINABLE_PSG("byteLength", SharedArrayBufferObject::byteLengthGetter, 0,
    970                     SharedArrayBufferByteLength),
    971    JS_PSG("maxByteLength", SharedArrayBufferObject::maxByteLengthGetter, 0),
    972    JS_PSG("growable", SharedArrayBufferObject::growableGetter, 0),
    973    JS_STRING_SYM_PS(toStringTag, "SharedArrayBuffer", JSPROP_READONLY),
    974    JS_PS_END,
    975 };
    976 
    977 static JSObject* CreateSharedArrayBufferPrototype(JSContext* cx,
    978                                                  JSProtoKey key) {
    979  return GlobalObject::createBlankPrototype(
    980      cx, cx->global(), &SharedArrayBufferObject::protoClass_);
    981 }
    982 
    983 static const ClassSpec SharedArrayBufferObjectClassSpec = {
    984    GenericCreateConstructor<SharedArrayBufferObject::class_constructor, 1,
    985                             gc::AllocKind::FUNCTION>,
    986    CreateSharedArrayBufferPrototype,
    987    sharedarray_functions,
    988    sharedarray_properties,
    989    sharedarray_proto_functions,
    990    sharedarray_proto_properties,
    991    GenericFinishInit<WhichHasRealmFuseProperty::ProtoAndCtor>,
    992 };
    993 
    994 const JSClass SharedArrayBufferObject::protoClass_ = {
    995    "SharedArrayBuffer.prototype",
    996    JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
    997    JS_NULL_CLASS_OPS,
    998    &SharedArrayBufferObjectClassSpec,
    999 };
   1000 
   1001 const JSClass FixedLengthSharedArrayBufferObject::class_ = {
   1002    "SharedArrayBuffer",
   1003    JSCLASS_DELAY_METADATA_BUILDER |
   1004        JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) |
   1005        JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer) |
   1006        JSCLASS_FOREGROUND_FINALIZE,
   1007    &SharedArrayBufferObjectClassOps,
   1008    &SharedArrayBufferObjectClassSpec,
   1009    JS_NULL_CLASS_EXT,
   1010 };
   1011 
   1012 const JSClass GrowableSharedArrayBufferObject::class_ = {
   1013    "SharedArrayBuffer",
   1014    JSCLASS_DELAY_METADATA_BUILDER |
   1015        JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) |
   1016        JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer) |
   1017        JSCLASS_FOREGROUND_FINALIZE,
   1018    &SharedArrayBufferObjectClassOps,
   1019    &SharedArrayBufferObjectClassSpec,
   1020    JS_NULL_CLASS_EXT,
   1021 };
   1022 
   1023 JS_PUBLIC_API size_t JS::GetSharedArrayBufferByteLength(JSObject* obj) {
   1024  auto* aobj = obj->maybeUnwrapAs<SharedArrayBufferObject>();
   1025  return aobj ? aobj->byteLength() : 0;
   1026 }
   1027 
   1028 JS_PUBLIC_API void JS::GetSharedArrayBufferLengthAndData(JSObject* obj,
   1029                                                         size_t* length,
   1030                                                         bool* isSharedMemory,
   1031                                                         uint8_t** data) {
   1032  MOZ_ASSERT(obj->is<SharedArrayBufferObject>());
   1033  *length = obj->as<SharedArrayBufferObject>().byteLength();
   1034  *data = obj->as<SharedArrayBufferObject>().dataPointerShared().unwrap(
   1035      /*safe - caller knows*/);
   1036  *isSharedMemory = true;
   1037 }
   1038 
   1039 JS_PUBLIC_API JSObject* JS::NewSharedArrayBuffer(JSContext* cx, size_t nbytes) {
   1040  MOZ_ASSERT(cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
   1041 
   1042  if (nbytes > ArrayBufferObject::ByteLengthLimit) {
   1043    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1044                              JSMSG_SHARED_ARRAY_BAD_LENGTH);
   1045    return nullptr;
   1046  }
   1047 
   1048  return SharedArrayBufferObject::New(cx, nbytes,
   1049                                      /* proto = */ nullptr);
   1050 }
   1051 
   1052 JS_PUBLIC_API bool JS::IsSharedArrayBufferObject(JSObject* obj) {
   1053  return obj->canUnwrapAs<SharedArrayBufferObject>();
   1054 }
   1055 
   1056 JS_PUBLIC_API uint8_t* JS::GetSharedArrayBufferData(
   1057    JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&) {
   1058  auto* aobj = obj->maybeUnwrapAs<SharedArrayBufferObject>();
   1059  if (!aobj) {
   1060    return nullptr;
   1061  }
   1062  *isSharedMemory = true;
   1063  return aobj->dataPointerShared().unwrap(/*safe - caller knows*/);
   1064 }
   1065 
   1066 JS_PUBLIC_API bool JS::ContainsSharedArrayBuffer(JSContext* cx) {
   1067  return cx->runtime()->hasLiveSABs();
   1068 }