Bug 1908539 restrict MacOS platform audio processing to Nightly r=webrtc-reviewers...
[gecko.git] / js / src / vm / ArrayBufferViewObject.cpp
blobeec78ef3cdee1d691d15e74cbb566253b20de737
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/. */
7 #include "vm/ArrayBufferViewObject.h"
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"
18 #include "gc/Nursery-inl.h"
19 #include "vm/ArrayBufferObject-inl.h"
20 #include "vm/NativeObject-inl.h"
22 using namespace js;
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>();
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);
43 if (buffer) {
44 size_t offset = view->dataPointerOffset();
45 MOZ_ASSERT_IF(!buffer->dataPointer(), offset == 0);
47 // The data may or may not be inline with the buffer. The buffer can only
48 // move during a compacting GC, in which case its objectMoved hook has
49 // already updated the buffer's data pointer.
50 view->notifyBufferMoved(
51 static_cast<uint8_t*>(view->dataPointerEither_()) - offset,
52 buffer->dataPointer());
57 template <>
58 bool JSObject::is<js::ArrayBufferViewObject>() const {
59 return is<DataViewObject>() || is<TypedArrayObject>();
62 void ArrayBufferViewObject::notifyBufferDetached() {
63 MOZ_ASSERT(!isSharedMemory());
64 MOZ_ASSERT(hasBuffer());
65 MOZ_ASSERT(!bufferUnshared()->isLengthPinned());
67 setFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0)));
68 setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0)));
69 setFixedSlot(DATA_SLOT, UndefinedValue());
72 void ArrayBufferViewObject::notifyBufferResized() {
73 MOZ_ASSERT(!isSharedMemory());
74 MOZ_ASSERT(hasBuffer());
75 MOZ_ASSERT(!bufferUnshared()->isLengthPinned());
76 MOZ_ASSERT(bufferUnshared()->isResizable());
78 computeResizableLengthAndByteOffset(bytesPerElement());
81 void ArrayBufferViewObject::notifyBufferMoved(uint8_t* srcBufStart,
82 uint8_t* dstBufStart) {
83 MOZ_ASSERT(!isSharedMemory());
84 MOZ_ASSERT(hasBuffer());
86 if (srcBufStart != dstBufStart) {
87 void* data = dstBufStart + dataPointerOffset();
88 getFixedSlotRef(DATA_SLOT).unbarrieredSet(PrivateValue(data));
92 /* static */
93 bool ArrayBufferViewObject::ensureNonInline(
94 JSContext* cx, Handle<ArrayBufferViewObject*> view) {
95 MOZ_ASSERT(!view->isSharedMemory());
96 // Create an ArrayBuffer for the data if it was in the view.
97 ArrayBufferObjectMaybeShared* buffer = ensureBufferObject(cx, view);
98 if (!buffer) {
99 return false;
101 Rooted<ArrayBufferObject*> unsharedBuffer(cx,
102 &buffer->as<ArrayBufferObject>());
103 return ArrayBufferObject::ensureNonInline(cx, unsharedBuffer);
106 /* static */
107 ArrayBufferObjectMaybeShared* ArrayBufferViewObject::ensureBufferObject(
108 JSContext* cx, Handle<ArrayBufferViewObject*> thisObject) {
109 if (thisObject->is<TypedArrayObject>()) {
110 Rooted<TypedArrayObject*> typedArray(cx,
111 &thisObject->as<TypedArrayObject>());
112 if (!TypedArrayObject::ensureHasBuffer(cx, typedArray)) {
113 return nullptr;
116 auto* buffer = thisObject->bufferEither();
117 if (!buffer) {
118 MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "ABV has no buffer");
120 return buffer;
123 bool ArrayBufferViewObject::init(JSContext* cx,
124 ArrayBufferObjectMaybeShared* buffer,
125 size_t byteOffset, size_t length,
126 uint32_t bytesPerElement) {
127 MOZ_ASSERT_IF(!buffer, byteOffset == 0);
128 MOZ_ASSERT_IF(buffer, !buffer->isDetached());
130 MOZ_ASSERT(byteOffset <= ArrayBufferObject::ByteLengthLimit);
131 MOZ_ASSERT(length <= ArrayBufferObject::ByteLengthLimit);
132 MOZ_ASSERT(byteOffset + length <= ArrayBufferObject::ByteLengthLimit);
134 MOZ_ASSERT_IF(is<TypedArrayObject>(),
135 length <= TypedArrayObject::ByteLengthLimit / bytesPerElement);
137 // The isSharedMemory property is invariant. Self-hosting code that
138 // sets BUFFER_SLOT or the private slot (if it does) must maintain it by
139 // always setting those to reference shared memory.
140 if (buffer && buffer->is<SharedArrayBufferObject>()) {
141 setIsSharedMemory();
144 initFixedSlot(BYTEOFFSET_SLOT, PrivateValue(byteOffset));
145 initFixedSlot(LENGTH_SLOT, PrivateValue(length));
146 if (buffer) {
147 initFixedSlot(BUFFER_SLOT, ObjectValue(*buffer));
148 } else {
149 MOZ_ASSERT(!isSharedMemory());
150 initFixedSlot(BUFFER_SLOT, JS::FalseValue());
153 if (buffer) {
154 SharedMem<uint8_t*> ptr = buffer->dataPointerEither();
155 initDataPointer(ptr + byteOffset);
157 // Only ArrayBuffers used for inline typed objects can have
158 // nursery-allocated data and we shouldn't see such buffers here.
159 MOZ_ASSERT_IF(buffer->byteLength() > 0, !cx->nursery().isInside(ptr));
160 } else {
161 MOZ_ASSERT(is<FixedLengthTypedArrayObject>());
162 MOZ_ASSERT(length * bytesPerElement <=
163 FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT);
164 void* data = fixedData(FixedLengthTypedArrayObject::FIXED_DATA_START);
165 initReservedSlot(DATA_SLOT, PrivateValue(data));
166 memset(data, 0, length * bytesPerElement);
167 #ifdef DEBUG
168 if (length == 0) {
169 uint8_t* elements = static_cast<uint8_t*>(data);
170 elements[0] = ZeroLengthArrayData;
172 #endif
175 #ifdef DEBUG
176 if (buffer) {
177 size_t viewByteLength = length * bytesPerElement;
178 size_t viewByteOffset = byteOffset;
179 size_t bufferByteLength = buffer->byteLength();
180 // Unwraps are safe: both are for the pointer value.
181 MOZ_ASSERT_IF(buffer->is<ArrayBufferObject>(),
182 buffer->dataPointerEither().unwrap(/*safe*/) <=
183 dataPointerEither().unwrap(/*safe*/));
184 MOZ_ASSERT(bufferByteLength - viewByteOffset >= viewByteLength);
185 MOZ_ASSERT(viewByteOffset <= bufferByteLength);
187 #endif
189 // ArrayBufferObjects track their views to support detaching.
190 if (buffer && buffer->is<ArrayBufferObject>()) {
191 if (!buffer->as<ArrayBufferObject>().addView(cx, this)) {
192 return false;
196 return true;
199 bool ArrayBufferViewObject::initResizable(JSContext* cx,
200 ArrayBufferObjectMaybeShared* buffer,
201 size_t byteOffset, size_t length,
202 uint32_t bytesPerElement,
203 AutoLength autoLength) {
204 MOZ_ASSERT(buffer->isResizable());
206 if (!init(cx, buffer, byteOffset, length, bytesPerElement)) {
207 return false;
210 initFixedSlot(AUTO_LENGTH_SLOT, BooleanValue(static_cast<bool>(autoLength)));
211 initFixedSlot(INITIAL_LENGTH_SLOT, PrivateValue(length));
212 initFixedSlot(INITIAL_BYTE_OFFSET_SLOT, PrivateValue(byteOffset));
214 // Compute the actual byteLength and byteOffset for non-shared buffers.
215 if (!isSharedMemory()) {
216 computeResizableLengthAndByteOffset(bytesPerElement);
219 MOZ_ASSERT(!isOutOfBounds(), "can't create out-of-bounds views");
221 return true;
224 void ArrayBufferViewObject::computeResizableLengthAndByteOffset(
225 size_t bytesPerElement) {
226 MOZ_ASSERT(!isSharedMemory());
227 MOZ_ASSERT(hasBuffer());
228 MOZ_ASSERT(bufferUnshared()->isResizable());
230 size_t byteOffsetStart = initialByteOffset();
231 size_t bufferByteLength = bufferUnshared()->byteLength();
233 // Out-of-bounds if the byteOffset exceeds the buffer length.
234 if (byteOffsetStart > bufferByteLength) {
235 setFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0)));
236 setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0)));
237 return;
240 size_t length;
241 if (isAutoLength()) {
242 length = (bufferByteLength - byteOffsetStart) / bytesPerElement;
243 } else {
244 length = initialLength();
246 // Out-of-bounds if the byteOffset end index exceeds the buffer length.
247 size_t byteOffsetEnd = byteOffsetStart + length * bytesPerElement;
248 if (byteOffsetEnd > bufferByteLength) {
249 setFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0)));
250 setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0)));
251 return;
255 setFixedSlot(LENGTH_SLOT, PrivateValue(length));
256 setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(byteOffsetStart));
259 size_t ArrayBufferViewObject::bytesPerElement() const {
260 if (is<TypedArrayObject>()) {
261 return as<TypedArrayObject>().bytesPerElement();
264 MOZ_ASSERT(is<DataViewObject>());
265 return 1;
268 bool ArrayBufferViewObject::hasResizableBuffer() const {
269 if (auto* buffer = bufferEither()) {
270 return buffer->isResizable();
272 return false;
275 size_t ArrayBufferViewObject::dataPointerOffset() const {
276 // Views without a buffer have a zero offset.
277 if (!hasBuffer()) {
278 MOZ_ASSERT(byteOffsetSlotValue() == 0);
279 return 0;
282 // Views on shared buffers store the offset in |byteOffset|.
283 if (isSharedMemory()) {
284 return byteOffsetSlotValue();
287 // Can be called during tracing, so the buffer is possibly forwarded.
288 const auto* bufferObj = gc::MaybeForwarded(&bufferValue().toObject());
290 // Two distinct classes are used for non-shared buffers.
291 MOZ_ASSERT(
292 gc::MaybeForwardedObjectIs<FixedLengthArrayBufferObject>(bufferObj) ||
293 gc::MaybeForwardedObjectIs<ResizableArrayBufferObject>(bufferObj));
295 // Ensure these two classes can be casted to ArrayBufferObject.
296 static_assert(
297 std::is_base_of_v<ArrayBufferObject, FixedLengthArrayBufferObject>);
298 static_assert(
299 std::is_base_of_v<ArrayBufferObject, ResizableArrayBufferObject>);
301 // Manual cast necessary because the buffer is possibly forwarded.
302 const auto* buffer = static_cast<const ArrayBufferObject*>(bufferObj);
304 // Views on resizable buffers store the offset in |initialByteOffset|.
305 if (buffer->isResizable() && !buffer->isDetached()) {
306 return initialByteOffsetValue();
309 // Callers expect that this method returns zero for detached buffers.
310 MOZ_ASSERT_IF(buffer->isDetached(), byteOffsetSlotValue() == 0);
312 // Views on fixed-length buffers store the offset in |byteOffset|.
313 return byteOffsetSlotValue();
316 mozilla::Maybe<size_t> ArrayBufferViewObject::byteOffset() const {
317 // |byteOffset| is set to zero for detached or out-of-bounds views, so a
318 // non-zero value indicates the view is in-bounds.
319 size_t byteOffset = byteOffsetSlotValue();
320 if (byteOffset > 0) {
321 MOZ_ASSERT(!hasDetachedBuffer());
322 MOZ_ASSERT_IF(hasResizableBuffer(), !isOutOfBounds());
323 return mozilla::Some(byteOffset);
325 if (hasDetachedBufferOrIsOutOfBounds()) {
326 return mozilla::Nothing{};
328 return mozilla::Some(0);
331 mozilla::Maybe<size_t> ArrayBufferViewObject::length() const {
332 // |length| is set to zero for detached or out-of-bounds views, so a non-zero
333 // value indicates the view is in-bounds.
334 size_t length = lengthSlotValue();
335 if (MOZ_LIKELY(length > 0)) {
336 MOZ_ASSERT(!hasDetachedBuffer());
337 MOZ_ASSERT_IF(hasResizableBuffer(), !isOutOfBounds());
338 MOZ_ASSERT(!isSharedMemory() || !hasResizableBuffer() || !isAutoLength(),
339 "length is zero for auto-length growable shared buffers");
340 return mozilla::Some(length);
343 if (hasDetachedBufferOrIsOutOfBounds()) {
344 return mozilla::Nothing{};
347 if (isSharedMemory()) {
348 auto* buffer = bufferShared();
349 MOZ_ASSERT(buffer, "shared memory doesn't use inline data");
351 // Views backed by a growable SharedArrayBuffer can never get out-of-bounds,
352 // but we have to dynamically compute the length when the auto-length flag
353 // is set.
354 if (buffer->isGrowable() && isAutoLength()) {
355 size_t bufferByteLength = buffer->byteLength();
356 size_t byteOffset = byteOffsetSlotValue();
357 MOZ_ASSERT(byteOffset <= bufferByteLength);
358 MOZ_ASSERT(byteOffset == initialByteOffset(),
359 "views on growable shared buffers can't get out-of-bounds");
361 return mozilla::Some((bufferByteLength - byteOffset) / bytesPerElement());
364 return mozilla::Some(0);
367 #if defined(DEBUG) || defined(JS_JITSPEW)
368 void ArrayBufferViewObject::dumpOwnFields(js::JSONPrinter& json) const {
369 json.formatProperty("length", "%zu",
370 size_t(getFixedSlot(LENGTH_SLOT).toPrivate()));
371 json.formatProperty("byteOffset", "%zu",
372 size_t(getFixedSlot(BYTEOFFSET_SLOT).toPrivate()));
373 void* data = dataPointerEither_();
374 if (data) {
375 json.formatProperty("data", "0x%p", data);
376 } else {
377 json.nullProperty("data");
381 void ArrayBufferViewObject::dumpOwnStringContent(
382 js::GenericPrinter& out) const {
383 out.printf("length=%zu, byteOffset=%zu, ",
384 size_t(getFixedSlot(LENGTH_SLOT).toPrivate()),
385 size_t(getFixedSlot(BYTEOFFSET_SLOT).toPrivate()));
386 void* data = dataPointerEither_();
387 if (data) {
388 out.printf("data=0x%p", data);
389 } else {
390 out.put("data=null");
393 #endif
395 /* JS Public API */
397 JS_PUBLIC_API bool JS_IsArrayBufferViewObject(JSObject* obj) {
398 return obj->canUnwrapAs<ArrayBufferViewObject>();
401 JS_PUBLIC_API JSObject* js::UnwrapArrayBufferView(JSObject* obj) {
402 return obj->maybeUnwrapIf<ArrayBufferViewObject>();
405 JS_PUBLIC_API void* JS_GetArrayBufferViewData(JSObject* obj,
406 bool* isSharedMemory,
407 const JS::AutoRequireNoGC&) {
408 ArrayBufferViewObject* view = obj->maybeUnwrapAs<ArrayBufferViewObject>();
409 if (!view) {
410 return nullptr;
413 *isSharedMemory = view->isSharedMemory();
414 return view->dataPointerEither().unwrap(
415 /*safe - caller sees isSharedMemory flag*/);
418 JS_PUBLIC_API uint8_t* JS_GetArrayBufferViewFixedData(JSObject* obj,
419 uint8_t* buffer,
420 size_t bufSize) {
421 ArrayBufferViewObject* view = obj->maybeUnwrapAs<ArrayBufferViewObject>();
422 if (!view) {
423 return nullptr;
426 // Disallow shared memory until it is needed.
427 if (view->isSharedMemory()) {
428 return nullptr;
431 // TypedArrays (but not DataViews) can have inline data, in which case we
432 // need to copy into the given buffer.
433 if (view->is<FixedLengthTypedArrayObject>()) {
434 auto* ta = &view->as<FixedLengthTypedArrayObject>();
435 if (ta->hasInlineElements()) {
436 size_t bytes = ta->byteLength();
437 if (bytes > bufSize) {
438 return nullptr; // Does not fit.
440 memcpy(buffer, view->dataPointerUnshared(), bytes);
441 return buffer;
445 return static_cast<uint8_t*>(view->dataPointerUnshared());
448 JS_PUBLIC_API JSObject* JS_GetArrayBufferViewBuffer(JSContext* cx,
449 HandleObject obj,
450 bool* isSharedMemory) {
451 AssertHeapIsIdle();
452 CHECK_THREAD(cx);
453 cx->check(obj);
455 Rooted<ArrayBufferViewObject*> unwrappedView(
456 cx, obj->maybeUnwrapAs<ArrayBufferViewObject>());
457 if (!unwrappedView) {
458 MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "access to buffer denied");
459 ReportAccessDenied(cx);
460 return nullptr;
463 ArrayBufferObjectMaybeShared* unwrappedBuffer;
465 AutoRealm ar(cx, unwrappedView);
466 unwrappedBuffer =
467 ArrayBufferViewObject::ensureBufferObject(cx, unwrappedView);
468 if (!unwrappedBuffer) {
469 return nullptr;
472 *isSharedMemory = unwrappedBuffer->is<SharedArrayBufferObject>();
474 RootedObject buffer(cx, unwrappedBuffer);
475 if (!cx->compartment()->wrap(cx, &buffer)) {
476 MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "wrapping buffer failed");
477 return nullptr;
480 return buffer;
483 JS_PUBLIC_API size_t JS_GetArrayBufferViewByteLength(JSObject* obj) {
484 obj = obj->maybeUnwrapAs<ArrayBufferViewObject>();
485 if (!obj) {
486 return 0;
488 size_t length = obj->is<DataViewObject>()
489 ? obj->as<DataViewObject>().byteLength().valueOr(0)
490 : obj->as<TypedArrayObject>().byteLength().valueOr(0);
491 return length;
494 bool JS::ArrayBufferView::isDetached() const {
495 MOZ_ASSERT(obj);
496 return obj->as<ArrayBufferViewObject>().hasDetachedBuffer();
499 bool JS::ArrayBufferView::isResizable() const {
500 MOZ_ASSERT(obj);
501 return obj->as<ArrayBufferViewObject>().hasResizableBuffer();
504 JS_PUBLIC_API size_t JS_GetArrayBufferViewByteOffset(JSObject* obj) {
505 obj = obj->maybeUnwrapAs<ArrayBufferViewObject>();
506 if (!obj) {
507 return 0;
509 size_t offset = obj->is<DataViewObject>()
510 ? obj->as<DataViewObject>().byteOffset().valueOr(0)
511 : obj->as<TypedArrayObject>().byteOffset().valueOr(0);
512 return offset;
515 JS_PUBLIC_API mozilla::Span<uint8_t> JS::ArrayBufferView::getData(
516 bool* isSharedMemory, const AutoRequireNoGC&) {
517 MOZ_ASSERT(obj->is<ArrayBufferViewObject>());
518 size_t byteLength = obj->is<DataViewObject>()
519 ? obj->as<DataViewObject>().byteLength().valueOr(0)
520 : obj->as<TypedArrayObject>().byteLength().valueOr(0);
521 ArrayBufferViewObject& view = obj->as<ArrayBufferViewObject>();
522 *isSharedMemory = view.isSharedMemory();
523 return {static_cast<uint8_t*>(view.dataPointerEither().unwrap(
524 /*safe - caller sees isShared flag*/)),
525 byteLength};
528 JS_PUBLIC_API JSObject* JS_GetObjectAsArrayBufferView(JSObject* obj,
529 size_t* length,
530 bool* isSharedMemory,
531 uint8_t** data) {
532 obj = obj->maybeUnwrapIf<ArrayBufferViewObject>();
533 if (!obj) {
534 return nullptr;
537 js::GetArrayBufferViewLengthAndData(obj, length, isSharedMemory, data);
538 return obj;
541 JS_PUBLIC_API void js::GetArrayBufferViewLengthAndData(JSObject* obj,
542 size_t* length,
543 bool* isSharedMemory,
544 uint8_t** data) {
545 JS::AutoAssertNoGC nogc;
546 auto span =
547 JS::ArrayBufferView::fromObject(obj).getData(isSharedMemory, nogc);
548 *data = span.data();
549 *length = span.Length();
552 JS_PUBLIC_API bool JS::IsArrayBufferViewShared(JSObject* obj) {
553 ArrayBufferViewObject* view = obj->maybeUnwrapAs<ArrayBufferViewObject>();
554 if (!view) {
555 return false;
557 return view->isSharedMemory();
560 JS_PUBLIC_API bool JS::IsLargeArrayBufferView(JSObject* obj) {
561 #ifdef JS_64BIT
562 obj = &obj->unwrapAs<ArrayBufferViewObject>();
563 size_t len = obj->is<DataViewObject>()
564 ? obj->as<DataViewObject>().byteLength().valueOr(0)
565 : obj->as<TypedArrayObject>().byteLength().valueOr(0);
566 return len > ArrayBufferObject::ByteLengthLimitForSmallBuffer;
567 #else
568 // Large ArrayBuffers are not supported on 32-bit.
569 static_assert(ArrayBufferObject::ByteLengthLimit ==
570 ArrayBufferObject::ByteLengthLimitForSmallBuffer);
571 return false;
572 #endif
575 JS_PUBLIC_API bool JS::IsResizableArrayBufferView(JSObject* obj) {
576 auto* view = &obj->unwrapAs<ArrayBufferViewObject>();
577 if (auto* buffer = view->bufferEither()) {
578 return buffer->isResizable();
580 return false;
583 JS_PUBLIC_API bool JS::PinArrayBufferOrViewLength(JSObject* obj, bool pin) {
584 ArrayBufferObjectMaybeShared* buffer =
585 obj->maybeUnwrapIf<ArrayBufferObjectMaybeShared>();
586 if (buffer) {
587 return buffer->pinLength(pin);
590 ArrayBufferViewObject* view = obj->maybeUnwrapAs<ArrayBufferViewObject>();
591 if (view) {
592 return view->pinLength(pin);
595 MOZ_DIAGNOSTIC_ASSERT(!js::TlsContext.get()->brittleMode,
596 "invalid type in PinABOVLength");
597 return false;
600 JS_PUBLIC_API bool JS::EnsureNonInlineArrayBufferOrView(JSContext* cx,
601 JSObject* obj) {
602 if (obj->is<SharedArrayBufferObject>()) {
603 // Always locked and out of line.
604 return true;
607 auto* buffer = obj->maybeUnwrapIf<ArrayBufferObject>();
608 if (buffer) {
609 Rooted<ArrayBufferObject*> rootedBuffer(cx, buffer);
610 return ArrayBufferObject::ensureNonInline(cx, rootedBuffer);
613 auto* view = obj->maybeUnwrapIf<ArrayBufferViewObject>();
614 if (view) {
615 if (view->isSharedMemory()) {
616 // Always locked and out of line.
617 return true;
619 Rooted<ArrayBufferViewObject*> rootedView(cx, view);
620 return ArrayBufferViewObject::ensureNonInline(cx, rootedView);
623 MOZ_DIAGNOSTIC_ASSERT(!cx->brittleMode, "unhandled type");
624 JS_ReportErrorASCII(cx, "unhandled type");
625 return false;