tor-browser

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

ArrayBufferViewObject.cpp (22487B)


      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/ArrayBufferViewObject.h"
      8 
      9 #include "builtin/DataViewObject.h"
     10 #include "gc/Nursery.h"
     11 #include "js/ErrorReport.h"
     12 #include "js/experimental/TypedData.h"  // JS_GetArrayBufferView{Data,Buffer,Length,ByteOffset}, JS_GetObjectAsArrayBufferView, JS_IsArrayBufferViewObject
     13 #include "js/SharedArrayBuffer.h"
     14 #include "vm/Compartment.h"
     15 #include "vm/JSContext.h"
     16 #include "vm/TypedArrayObject.h"
     17 
     18 #include "gc/Nursery-inl.h"
     19 #include "vm/ArrayBufferObject-inl.h"
     20 #include "vm/NativeObject-inl.h"
     21 
     22 using namespace js;
     23 
     24 // This method is used to trace TypedArrayObjects and DataViewObjects. It
     25 // updates the object's data pointer if it points to inline data in an object
     26 // that was moved.
     27 /* static */
     28 void ArrayBufferViewObject::trace(JSTracer* trc, JSObject* obj) {
     29  ArrayBufferViewObject* view = &obj->as<ArrayBufferViewObject>();
     30 
     31  // Update view's data pointer if it moved.
     32  if (view->hasBuffer()) {
     33    JSObject* bufferObj = &view->bufferValue().toObject();
     34    ArrayBufferObject* buffer = nullptr;
     35    if (gc::MaybeForwardedObjectIs<FixedLengthArrayBufferObject>(bufferObj)) {
     36      buffer =
     37          &gc::MaybeForwardedObjectAs<FixedLengthArrayBufferObject>(bufferObj);
     38    } else if (gc::MaybeForwardedObjectIs<ResizableArrayBufferObject>(
     39                   bufferObj)) {
     40      buffer =
     41          &gc::MaybeForwardedObjectAs<ResizableArrayBufferObject>(bufferObj);
     42    } else if (gc::MaybeForwardedObjectIs<ImmutableArrayBufferObject>(
     43                   bufferObj)) {
     44      buffer =
     45          &gc::MaybeForwardedObjectAs<ImmutableArrayBufferObject>(bufferObj);
     46    }
     47    if (buffer) {
     48      size_t offset = view->dataPointerOffset();
     49      MOZ_ASSERT_IF(!buffer->dataPointer(), offset == 0);
     50 
     51      // The data may or may not be inline with the buffer. The buffer can only
     52      // move during a compacting GC, in which case its objectMoved hook has
     53      // already updated the buffer's data pointer.
     54      view->notifyBufferMoved(
     55          static_cast<uint8_t*>(view->dataPointerEither_()) - offset,
     56          buffer->dataPointer());
     57    }
     58  }
     59 }
     60 
     61 template <>
     62 bool JSObject::is<js::ArrayBufferViewObject>() const {
     63  return is<DataViewObject>() || is<TypedArrayObject>();
     64 }
     65 
     66 void ArrayBufferViewObject::notifyBufferDetached() {
     67  MOZ_ASSERT(!isSharedMemory());
     68  MOZ_ASSERT(hasBuffer());
     69  MOZ_ASSERT(!bufferUnshared()->isImmutable());
     70  MOZ_ASSERT(!bufferUnshared()->isLengthPinned());
     71 
     72  setFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0)));
     73  setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0)));
     74  setFixedSlot(DATA_SLOT, UndefinedValue());
     75 }
     76 
     77 void ArrayBufferViewObject::notifyBufferResized() {
     78  MOZ_ASSERT(!isSharedMemory());
     79  MOZ_ASSERT(hasBuffer());
     80  MOZ_ASSERT(!bufferUnshared()->isImmutable());
     81  MOZ_ASSERT(!bufferUnshared()->isLengthPinned());
     82  MOZ_ASSERT(bufferUnshared()->isResizable());
     83 
     84  computeResizableLengthAndByteOffset(bytesPerElement());
     85 }
     86 
     87 void ArrayBufferViewObject::notifyBufferMoved(uint8_t* srcBufStart,
     88                                              uint8_t* dstBufStart) {
     89  MOZ_ASSERT(!isSharedMemory());
     90  MOZ_ASSERT(hasBuffer());
     91 
     92  if (srcBufStart != dstBufStart) {
     93    void* data = dstBufStart + dataPointerOffset();
     94    setReservedSlotPrivateUnbarriered(DATA_SLOT, data);
     95  }
     96 }
     97 
     98 /* static */
     99 bool ArrayBufferViewObject::ensureNonInline(
    100    JSContext* cx, Handle<ArrayBufferViewObject*> view) {
    101  MOZ_ASSERT(!view->isSharedMemory());
    102  // Create an ArrayBuffer for the data if it was in the view.
    103  ArrayBufferObjectMaybeShared* buffer = ensureBufferObject(cx, view);
    104  if (!buffer) {
    105    return false;
    106  }
    107  Rooted<ArrayBufferObject*> unsharedBuffer(cx,
    108                                            &buffer->as<ArrayBufferObject>());
    109  return ArrayBufferObject::ensureNonInline(cx, unsharedBuffer);
    110 }
    111 
    112 /* static */
    113 ArrayBufferObjectMaybeShared* ArrayBufferViewObject::ensureBufferObject(
    114    JSContext* cx, Handle<ArrayBufferViewObject*> thisObject) {
    115  if (thisObject->is<TypedArrayObject>()) {
    116    auto typedArray = HandleObject(thisObject).as<TypedArrayObject>();
    117    if (!TypedArrayObject::ensureHasBuffer(cx, typedArray)) {
    118      return nullptr;
    119    }
    120  }
    121  auto* buffer = thisObject->bufferEither();
    122  if (!buffer) {
    123    MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "ABV has no buffer");
    124  }
    125  return buffer;
    126 }
    127 
    128 bool ArrayBufferViewObject::init(JSContext* cx,
    129                                 ArrayBufferObjectMaybeShared* buffer,
    130                                 size_t byteOffset, size_t length,
    131                                 uint32_t bytesPerElement) {
    132  MOZ_ASSERT_IF(!buffer, byteOffset == 0);
    133  MOZ_ASSERT_IF(buffer, !buffer->isDetached());
    134 
    135  MOZ_ASSERT(byteOffset <= ArrayBufferObject::ByteLengthLimit);
    136  MOZ_ASSERT(length <= ArrayBufferObject::ByteLengthLimit);
    137  MOZ_ASSERT(byteOffset + length <= ArrayBufferObject::ByteLengthLimit);
    138 
    139  MOZ_ASSERT_IF(is<TypedArrayObject>(),
    140                length <= TypedArrayObject::ByteLengthLimit / bytesPerElement);
    141 
    142  // The isSharedMemory property is invariant.  Self-hosting code that
    143  // sets BUFFER_SLOT or the private slot (if it does) must maintain it by
    144  // always setting those to reference shared memory.
    145  if (buffer && buffer->is<SharedArrayBufferObject>()) {
    146    setIsSharedMemory();
    147  }
    148 
    149  initFixedSlot(BYTEOFFSET_SLOT, PrivateValue(byteOffset));
    150  initFixedSlot(LENGTH_SLOT, PrivateValue(length));
    151  if (buffer) {
    152    initFixedSlot(BUFFER_SLOT, ObjectValue(*buffer));
    153  } else {
    154    MOZ_ASSERT(!isSharedMemory());
    155    initFixedSlot(BUFFER_SLOT, JS::FalseValue());
    156  }
    157 
    158  if (buffer) {
    159    SharedMem<uint8_t*> ptr = buffer->dataPointerEither();
    160    initDataPointer(ptr + byteOffset);
    161 
    162    // Only ArrayBuffers used for inline typed objects can have
    163    // nursery-allocated data and we shouldn't see such buffers here.
    164    MOZ_ASSERT_IF(buffer->byteLength() > 0, !cx->nursery().isInside(ptr));
    165  } else {
    166    MOZ_ASSERT(is<FixedLengthTypedArrayObject>());
    167    MOZ_ASSERT(length * bytesPerElement <=
    168               FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT);
    169    void* data = fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START);
    170    initReservedSlot(DATA_SLOT, PrivateValue(data));
    171    memset(data, 0, length * bytesPerElement);
    172 #ifdef DEBUG
    173    if (length == 0) {
    174      uint8_t* elements = static_cast<uint8_t*>(data);
    175      elements[0] = ZeroLengthArrayData;
    176    }
    177 #endif
    178  }
    179 
    180 #ifdef DEBUG
    181  if (buffer) {
    182    size_t viewByteLength = length * bytesPerElement;
    183    size_t viewByteOffset = byteOffset;
    184    size_t bufferByteLength = buffer->byteLength();
    185    // Unwraps are safe: both are for the pointer value.
    186    MOZ_ASSERT_IF(buffer->is<ArrayBufferObject>(),
    187                  buffer->dataPointerEither().unwrap(/*safe*/) <=
    188                      dataPointerEither().unwrap(/*safe*/));
    189    MOZ_ASSERT(bufferByteLength - viewByteOffset >= viewByteLength);
    190    MOZ_ASSERT(viewByteOffset <= bufferByteLength);
    191  }
    192 #endif
    193 
    194  // ArrayBufferObjects track their views to support detaching.
    195  if (buffer && buffer->is<ArrayBufferObject>()) {
    196    if (!buffer->as<ArrayBufferObject>().addView(cx, this)) {
    197      return false;
    198    }
    199  }
    200 
    201  return true;
    202 }
    203 
    204 bool ArrayBufferViewObject::initResizable(JSContext* cx,
    205                                          ArrayBufferObjectMaybeShared* buffer,
    206                                          size_t byteOffset, size_t length,
    207                                          uint32_t bytesPerElement,
    208                                          AutoLength autoLength) {
    209  MOZ_ASSERT(buffer->isResizable());
    210 
    211  initFixedSlot(AUTO_LENGTH_SLOT, BooleanValue(static_cast<bool>(autoLength)));
    212  initFixedSlot(INITIAL_LENGTH_SLOT, PrivateValue(length));
    213  initFixedSlot(INITIAL_BYTE_OFFSET_SLOT, PrivateValue(byteOffset));
    214 
    215  if (!init(cx, buffer, byteOffset, length, bytesPerElement)) {
    216    return false;
    217  }
    218 
    219  // Compute the actual byteLength and byteOffset for non-shared buffers.
    220  if (!isSharedMemory()) {
    221    computeResizableLengthAndByteOffset(bytesPerElement);
    222  }
    223 
    224  MOZ_ASSERT(!isOutOfBounds(), "can't create out-of-bounds views");
    225 
    226  return true;
    227 }
    228 
    229 void ArrayBufferViewObject::computeResizableLengthAndByteOffset(
    230    size_t bytesPerElement) {
    231  MOZ_ASSERT(!isSharedMemory());
    232  MOZ_ASSERT(hasBuffer());
    233  MOZ_ASSERT(bufferUnshared()->isResizable());
    234 
    235  size_t byteOffsetStart = initialByteOffset();
    236  size_t bufferByteLength = bufferUnshared()->byteLength();
    237 
    238  // Out-of-bounds if the byteOffset exceeds the buffer length.
    239  if (byteOffsetStart > bufferByteLength) {
    240    setFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0)));
    241    setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0)));
    242    return;
    243  }
    244 
    245  size_t length;
    246  if (isAutoLength()) {
    247    length = (bufferByteLength - byteOffsetStart) / bytesPerElement;
    248  } else {
    249    length = initialLength();
    250 
    251    // Out-of-bounds if the byteOffset end index exceeds the buffer length.
    252    size_t byteOffsetEnd = byteOffsetStart + length * bytesPerElement;
    253    if (byteOffsetEnd > bufferByteLength) {
    254      setFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0)));
    255      setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0)));
    256      return;
    257    }
    258  }
    259 
    260  setFixedSlot(LENGTH_SLOT, PrivateValue(length));
    261  setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(byteOffsetStart));
    262 }
    263 
    264 size_t ArrayBufferViewObject::bytesPerElement() const {
    265  if (is<TypedArrayObject>()) {
    266    return as<TypedArrayObject>().bytesPerElement();
    267  }
    268 
    269  MOZ_ASSERT(is<DataViewObject>());
    270  return 1;
    271 }
    272 
    273 bool ArrayBufferViewObject::hasResizableBuffer() const {
    274  if (auto* buffer = bufferEither()) {
    275    return buffer->isResizable();
    276  }
    277  return false;
    278 }
    279 
    280 bool ArrayBufferViewObject::hasImmutableBuffer() const {
    281  if (auto* buffer = bufferEither()) {
    282    return buffer->isImmutable();
    283  }
    284  return false;
    285 }
    286 
    287 size_t ArrayBufferViewObject::dataPointerOffset() const {
    288  // Views without a buffer have a zero offset.
    289  if (!hasBuffer()) {
    290    MOZ_ASSERT(byteOffsetSlotValue() == 0);
    291    return 0;
    292  }
    293 
    294  // Views on shared buffers store the offset in |byteOffset|.
    295  if (isSharedMemory()) {
    296    return byteOffsetSlotValue();
    297  }
    298 
    299  // Can be called during tracing, so the buffer is possibly forwarded.
    300  const auto* bufferObj = gc::MaybeForwarded(&bufferValue().toObject());
    301 
    302  // Three distinct classes are used for non-shared buffers.
    303  MOZ_ASSERT(
    304      gc::MaybeForwardedObjectIs<FixedLengthArrayBufferObject>(bufferObj) ||
    305      gc::MaybeForwardedObjectIs<ResizableArrayBufferObject>(bufferObj) ||
    306      gc::MaybeForwardedObjectIs<ImmutableArrayBufferObject>(bufferObj));
    307 
    308  // Ensure these three classes can be casted to ArrayBufferObject.
    309  static_assert(
    310      std::is_base_of_v<ArrayBufferObject, FixedLengthArrayBufferObject>);
    311  static_assert(
    312      std::is_base_of_v<ArrayBufferObject, ResizableArrayBufferObject>);
    313  static_assert(
    314      std::is_base_of_v<ArrayBufferObject, ImmutableArrayBufferObject>);
    315 
    316  // Manual cast necessary because the buffer is possibly forwarded.
    317  const auto* buffer = static_cast<const ArrayBufferObject*>(bufferObj);
    318 
    319  // Views on resizable buffers store the offset in |initialByteOffset|.
    320  if (buffer->isResizable() && !buffer->isDetached()) {
    321    return initialByteOffsetValue();
    322  }
    323 
    324  // Callers expect that this method returns zero for detached buffers.
    325  MOZ_ASSERT_IF(buffer->isDetached(), byteOffsetSlotValue() == 0);
    326 
    327  // Views on fixed-length buffers store the offset in |byteOffset|.
    328  return byteOffsetSlotValue();
    329 }
    330 
    331 mozilla::Maybe<size_t> ArrayBufferViewObject::byteOffset() const {
    332  // |byteOffset| is set to zero for detached or out-of-bounds views, so a
    333  // non-zero value indicates the view is in-bounds.
    334  size_t byteOffset = byteOffsetSlotValue();
    335  if (byteOffset > 0) {
    336    MOZ_ASSERT(!hasDetachedBuffer());
    337    MOZ_ASSERT_IF(hasResizableBuffer(), !isOutOfBounds());
    338    return mozilla::Some(byteOffset);
    339  }
    340  if (hasDetachedBufferOrIsOutOfBounds()) {
    341    return mozilla::Nothing{};
    342  }
    343  return mozilla::Some(0);
    344 }
    345 
    346 mozilla::Maybe<size_t> ArrayBufferViewObject::length() const {
    347  // |length| is set to zero for detached or out-of-bounds views, so a non-zero
    348  // value indicates the view is in-bounds.
    349  size_t length = lengthSlotValue();
    350  if (MOZ_LIKELY(length > 0)) {
    351    MOZ_ASSERT(!hasDetachedBuffer());
    352    MOZ_ASSERT_IF(hasResizableBuffer(), !isOutOfBounds());
    353    MOZ_ASSERT(!isSharedMemory() || !hasResizableBuffer() || !isAutoLength(),
    354               "length is zero for auto-length growable shared buffers");
    355    return mozilla::Some(length);
    356  }
    357 
    358  if (hasDetachedBufferOrIsOutOfBounds()) {
    359    return mozilla::Nothing{};
    360  }
    361 
    362  if (isSharedMemory()) {
    363    auto* buffer = bufferShared();
    364    MOZ_ASSERT(buffer, "shared memory doesn't use inline data");
    365 
    366    // Views backed by a growable SharedArrayBuffer can never get out-of-bounds,
    367    // but we have to dynamically compute the length when the auto-length flag
    368    // is set.
    369    if (buffer->isGrowable() && isAutoLength()) {
    370      size_t bufferByteLength = buffer->byteLength();
    371      size_t byteOffset = byteOffsetSlotValue();
    372      MOZ_ASSERT(byteOffset <= bufferByteLength);
    373      MOZ_ASSERT(byteOffset == initialByteOffset(),
    374                 "views on growable shared buffers can't get out-of-bounds");
    375 
    376      return mozilla::Some((bufferByteLength - byteOffset) / bytesPerElement());
    377    }
    378  }
    379  return mozilla::Some(0);
    380 }
    381 
    382 #if defined(DEBUG) || defined(JS_JITSPEW)
    383 void ArrayBufferViewObject::dumpOwnFields(js::JSONPrinter& json) const {
    384  json.formatProperty("length", "%zu",
    385                      size_t(getFixedSlot(LENGTH_SLOT).toPrivate()));
    386  json.formatProperty("byteOffset", "%zu",
    387                      size_t(getFixedSlot(BYTEOFFSET_SLOT).toPrivate()));
    388  void* data = dataPointerEither_();
    389  if (data) {
    390    json.formatProperty("data", "0x%p", data);
    391  } else {
    392    json.nullProperty("data");
    393  }
    394 }
    395 
    396 void ArrayBufferViewObject::dumpOwnStringContent(
    397    js::GenericPrinter& out) const {
    398  out.printf("length=%zu, byteOffset=%zu, ",
    399             size_t(getFixedSlot(LENGTH_SLOT).toPrivate()),
    400             size_t(getFixedSlot(BYTEOFFSET_SLOT).toPrivate()));
    401  void* data = dataPointerEither_();
    402  if (data) {
    403    out.printf("data=0x%p", data);
    404  } else {
    405    out.put("data=null");
    406  }
    407 }
    408 #endif
    409 
    410 /* JS Public API */
    411 
    412 JS_PUBLIC_API bool JS_IsArrayBufferViewObject(JSObject* obj) {
    413  return obj->canUnwrapAs<ArrayBufferViewObject>();
    414 }
    415 
    416 JS_PUBLIC_API JSObject* js::UnwrapArrayBufferView(JSObject* obj) {
    417  return obj->maybeUnwrapIf<ArrayBufferViewObject>();
    418 }
    419 
    420 JS_PUBLIC_API void* JS_GetArrayBufferViewData(JSObject* obj,
    421                                              bool* isSharedMemory,
    422                                              const JS::AutoRequireNoGC&) {
    423  ArrayBufferViewObject* view = obj->maybeUnwrapAs<ArrayBufferViewObject>();
    424  if (!view) {
    425    return nullptr;
    426  }
    427 
    428  *isSharedMemory = view->isSharedMemory();
    429  return view->dataPointerEither().unwrap(
    430      /*safe - caller sees isSharedMemory flag*/);
    431 }
    432 
    433 JS_PUBLIC_API uint8_t* JS_GetArrayBufferViewFixedData(JSObject* obj,
    434                                                      uint8_t* buffer,
    435                                                      size_t bufSize) {
    436  ArrayBufferViewObject* view = obj->maybeUnwrapAs<ArrayBufferViewObject>();
    437  if (!view) {
    438    return nullptr;
    439  }
    440 
    441  // Disallow shared memory until it is needed.
    442  if (view->isSharedMemory()) {
    443    return nullptr;
    444  }
    445 
    446  // TypedArrays (but not DataViews) can have inline data, in which case we
    447  // need to copy into the given buffer.
    448  if (view->is<FixedLengthTypedArrayObject>()) {
    449    auto* ta = &view->as<FixedLengthTypedArrayObject>();
    450    if (ta->hasInlineElements()) {
    451      size_t bytes = ta->byteLength();
    452      if (bytes > bufSize) {
    453        return nullptr;  // Does not fit.
    454      }
    455      memcpy(buffer, view->dataPointerUnshared(), bytes);
    456      return buffer;
    457    }
    458  }
    459 
    460  return static_cast<uint8_t*>(view->dataPointerUnshared());
    461 }
    462 
    463 JS_PUBLIC_API JSObject* JS_GetArrayBufferViewBuffer(JSContext* cx,
    464                                                    HandleObject obj,
    465                                                    bool* isSharedMemory) {
    466  AssertHeapIsIdle();
    467  CHECK_THREAD(cx);
    468 
    469  Rooted<ArrayBufferViewObject*> unwrappedView(
    470      cx, obj->maybeUnwrapAs<ArrayBufferViewObject>());
    471  if (!unwrappedView) {
    472    MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "access to buffer denied");
    473    ReportAccessDenied(cx);
    474    return nullptr;
    475  }
    476 
    477  ArrayBufferObjectMaybeShared* unwrappedBuffer;
    478  {
    479    AutoRealm ar(cx, unwrappedView);
    480    unwrappedBuffer =
    481        ArrayBufferViewObject::ensureBufferObject(cx, unwrappedView);
    482    if (!unwrappedBuffer) {
    483      return nullptr;
    484    }
    485  }
    486  *isSharedMemory = unwrappedBuffer->is<SharedArrayBufferObject>();
    487 
    488  RootedObject buffer(cx, unwrappedBuffer);
    489  if (!cx->compartment()->wrap(cx, &buffer)) {
    490    MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "wrapping buffer failed");
    491    return nullptr;
    492  }
    493 
    494  return buffer;
    495 }
    496 
    497 JS_PUBLIC_API size_t JS_GetArrayBufferViewByteLength(JSObject* obj) {
    498  obj = obj->maybeUnwrapAs<ArrayBufferViewObject>();
    499  if (!obj) {
    500    return 0;
    501  }
    502  size_t length = obj->is<DataViewObject>()
    503                      ? obj->as<DataViewObject>().byteLength().valueOr(0)
    504                      : obj->as<TypedArrayObject>().byteLength().valueOr(0);
    505  return length;
    506 }
    507 
    508 bool JS::ArrayBufferView::isDetached() const {
    509  MOZ_ASSERT(obj);
    510  return obj->as<ArrayBufferViewObject>().hasDetachedBuffer();
    511 }
    512 
    513 bool JS::ArrayBufferView::isResizable() const {
    514  MOZ_ASSERT(obj);
    515  return obj->as<ArrayBufferViewObject>().hasResizableBuffer();
    516 }
    517 
    518 bool JS::ArrayBufferView::isImmutable() const {
    519  MOZ_ASSERT(obj);
    520  return obj->as<ArrayBufferViewObject>().hasImmutableBuffer();
    521 }
    522 
    523 JS_PUBLIC_API size_t JS_GetArrayBufferViewByteOffset(JSObject* obj) {
    524  obj = obj->maybeUnwrapAs<ArrayBufferViewObject>();
    525  if (!obj) {
    526    return 0;
    527  }
    528  size_t offset = obj->is<DataViewObject>()
    529                      ? obj->as<DataViewObject>().byteOffset().valueOr(0)
    530                      : obj->as<TypedArrayObject>().byteOffset().valueOr(0);
    531  return offset;
    532 }
    533 
    534 JS_PUBLIC_API mozilla::Span<uint8_t> JS::ArrayBufferView::getData(
    535    bool* isSharedMemory, const AutoRequireNoGC&) {
    536  MOZ_ASSERT(obj->is<ArrayBufferViewObject>());
    537  size_t byteLength = obj->is<DataViewObject>()
    538                          ? obj->as<DataViewObject>().byteLength().valueOr(0)
    539                          : obj->as<TypedArrayObject>().byteLength().valueOr(0);
    540  ArrayBufferViewObject& view = obj->as<ArrayBufferViewObject>();
    541  *isSharedMemory = view.isSharedMemory();
    542  return {static_cast<uint8_t*>(view.dataPointerEither().unwrap(
    543              /*safe - caller sees isShared flag*/)),
    544          byteLength};
    545 }
    546 
    547 JS_PUBLIC_API JSObject* JS_GetObjectAsArrayBufferView(JSObject* obj,
    548                                                      size_t* length,
    549                                                      bool* isSharedMemory,
    550                                                      uint8_t** data) {
    551  obj = obj->maybeUnwrapIf<ArrayBufferViewObject>();
    552  if (!obj) {
    553    return nullptr;
    554  }
    555 
    556  js::GetArrayBufferViewLengthAndData(obj, length, isSharedMemory, data);
    557  return obj;
    558 }
    559 
    560 JS_PUBLIC_API void js::GetArrayBufferViewLengthAndData(JSObject* obj,
    561                                                       size_t* length,
    562                                                       bool* isSharedMemory,
    563                                                       uint8_t** data) {
    564  JS::AutoAssertNoGC nogc;
    565  auto span =
    566      JS::ArrayBufferView::fromObject(obj).getData(isSharedMemory, nogc);
    567  *data = span.data();
    568  *length = span.Length();
    569 }
    570 
    571 JS_PUBLIC_API bool JS::IsArrayBufferViewShared(JSObject* obj) {
    572  ArrayBufferViewObject* view = obj->maybeUnwrapAs<ArrayBufferViewObject>();
    573  if (!view) {
    574    return false;
    575  }
    576  return view->isSharedMemory();
    577 }
    578 
    579 JS_PUBLIC_API bool JS::IsLargeArrayBufferView(JSObject* obj) {
    580 #ifdef JS_64BIT
    581  obj = &obj->unwrapAs<ArrayBufferViewObject>();
    582  size_t len = obj->is<DataViewObject>()
    583                   ? obj->as<DataViewObject>().byteLength().valueOr(0)
    584                   : obj->as<TypedArrayObject>().byteLength().valueOr(0);
    585  return len > ArrayBufferObject::ByteLengthLimitForSmallBuffer;
    586 #else
    587  // Large ArrayBuffers are not supported on 32-bit.
    588  static_assert(ArrayBufferObject::ByteLengthLimit ==
    589                ArrayBufferObject::ByteLengthLimitForSmallBuffer);
    590  return false;
    591 #endif
    592 }
    593 
    594 JS_PUBLIC_API bool JS::IsResizableArrayBufferView(JSObject* obj) {
    595  auto* view = &obj->unwrapAs<ArrayBufferViewObject>();
    596  if (auto* buffer = view->bufferEither()) {
    597    return buffer->isResizable();
    598  }
    599  return false;
    600 }
    601 
    602 JS_PUBLIC_API bool JS::IsImmutableArrayBufferView(JSObject* obj) {
    603  auto* view = &obj->unwrapAs<ArrayBufferViewObject>();
    604  if (auto* buffer = view->bufferEither()) {
    605    return buffer->isImmutable();
    606  }
    607  return false;
    608 }
    609 
    610 JS_PUBLIC_API bool JS::PinArrayBufferOrViewLength(JSObject* obj, bool pin) {
    611  ArrayBufferObjectMaybeShared* buffer =
    612      obj->maybeUnwrapIf<ArrayBufferObjectMaybeShared>();
    613  if (buffer) {
    614    return buffer->pinLength(pin);
    615  }
    616 
    617  ArrayBufferViewObject* view = obj->maybeUnwrapAs<ArrayBufferViewObject>();
    618  if (view) {
    619    return view->pinLength(pin);
    620  }
    621 
    622  MOZ_DIAGNOSTIC_ASSERT(!js::TlsContext.get()->brittleMode,
    623                        "invalid type in PinABOVLength");
    624  return false;
    625 }
    626 
    627 JS_PUBLIC_API bool JS::EnsureNonInlineArrayBufferOrView(JSContext* cx,
    628                                                        JSObject* obj) {
    629  if (obj->is<SharedArrayBufferObject>()) {
    630    // Always locked and out of line.
    631    return true;
    632  }
    633 
    634  auto* buffer = obj->maybeUnwrapIf<ArrayBufferObject>();
    635  if (buffer) {
    636    Rooted<ArrayBufferObject*> rootedBuffer(cx, buffer);
    637    return ArrayBufferObject::ensureNonInline(cx, rootedBuffer);
    638  }
    639 
    640  auto* view = obj->maybeUnwrapIf<ArrayBufferViewObject>();
    641  if (view) {
    642    if (view->isSharedMemory()) {
    643      // Always locked and out of line.
    644      return true;
    645    }
    646    Rooted<ArrayBufferViewObject*> rootedView(cx, view);
    647    return ArrayBufferViewObject::ensureNonInline(cx, rootedView);
    648  }
    649 
    650  MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "unhandled type");
    651  JS_ReportErrorASCII(cx, "unhandled type");
    652  return false;
    653 }