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 }