TypedData.h (29446B)
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 /* 8 * Typed array, ArrayBuffer, and DataView creation, predicate, and accessor 9 * functions. 10 */ 11 12 #ifndef js_experimental_TypedData_h 13 #define js_experimental_TypedData_h 14 15 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_CRASH 16 #include "mozilla/Span.h" 17 18 #include <stddef.h> // size_t 19 #include <stdint.h> // {,u}int8_t, {,u}int16_t, {,u}int32_t 20 21 #include "jstypes.h" // JS_PUBLIC_API 22 23 #include "js/Object.h" // JS::GetClass, JS::GetReservedSlot, JS::GetMaybePtrFromReservedSlot 24 #include "js/RootingAPI.h" // JS::Handle, JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE 25 #include "js/ScalarType.h" // JS::Scalar::Type 26 #include "js/Wrapper.h" // js::CheckedUnwrapStatic 27 28 struct JSClass; 29 class JS_PUBLIC_API JSObject; 30 31 namespace JS { 32 33 class JS_PUBLIC_API AutoRequireNoGC; 34 35 } // namespace JS 36 37 // JS_FOR_EACH_TYPED_ARRAY(MACRO) expands MACRO once for each specific 38 // typed array subtype (Int8Array, Float64Array, ...), passing arguments 39 // as MACRO(ExternalT, NativeT, Name) where 40 // 41 // ExternalT - externally-exposed element type (eg uint8_t) 42 // 43 // NativeT - element type used for the implementation (eg js::uint8_clamped_t) 44 // Note that this type is not exposed publicly. Internal files need to 45 // #include <vm/Uint8Clamped.h> to see it. 46 // 47 // Name - a name usable as both a JS::Scalar::Type value (eg 48 // JS::Scalar::Uint8Clamped) or the stem of a full typed array name (eg 49 // Uint8ClampedArray) 50 // 51 #define JS_FOR_EACH_TYPED_ARRAY(MACRO) \ 52 MACRO(int8_t, int8_t, Int8) \ 53 MACRO(uint8_t, uint8_t, Uint8) \ 54 MACRO(int16_t, int16_t, Int16) \ 55 MACRO(uint16_t, uint16_t, Uint16) \ 56 MACRO(int32_t, int32_t, Int32) \ 57 MACRO(uint32_t, uint32_t, Uint32) \ 58 MACRO(float, float, Float32) \ 59 MACRO(double, double, Float64) \ 60 MACRO(uint8_t, js::uint8_clamped, Uint8Clamped) \ 61 MACRO(int64_t, int64_t, BigInt64) \ 62 MACRO(uint64_t, uint64_t, BigUint64) \ 63 MACRO(uint16_t, js::float16, Float16) 64 65 /* 66 * JS_New(type)Array: 67 * 68 * Create a new typed array with nelements elements. 69 * 70 * These functions (except the WithBuffer variants) fill in the array with 71 * zeros. 72 * 73 * JS_New(type)ArrayFromArray: 74 * 75 * Create a new typed array and copy in values from the given object. The 76 * object is used as if it were an array; that is, the new array (if 77 * successfully created) will have length given by array.length, and its 78 * elements will be those specified by array[0], array[1], and so on, after 79 * conversion to the typed array element type. 80 * 81 * JS_New(type)ArrayWithBuffer: 82 * 83 * Create a new typed array using the given ArrayBuffer or 84 * SharedArrayBuffer for storage. The length value is optional; if -1 85 * is passed, enough elements to use up the remainder of the byte 86 * array is used as the default value. 87 */ 88 89 #define DECLARE_TYPED_ARRAY_CREATION_API(ExternalType, NativeType, Name) \ 90 extern JS_PUBLIC_API JSObject* JS_New##Name##Array(JSContext* cx, \ 91 size_t nelements); \ 92 extern JS_PUBLIC_API JSObject* JS_New##Name##ArrayFromArray( \ 93 JSContext* cx, JS::Handle<JSObject*> array); \ 94 extern JS_PUBLIC_API JSObject* JS_New##Name##ArrayWithBuffer( \ 95 JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset, \ 96 int64_t length); 97 98 JS_FOR_EACH_TYPED_ARRAY(DECLARE_TYPED_ARRAY_CREATION_API) 99 #undef DECLARE_TYPED_ARRAY_CREATION_API 100 101 /** 102 * Check whether obj supports JS_GetTypedArray* APIs. Note that this may return 103 * false if a security wrapper is encountered that denies the unwrapping. If 104 * this test or one of the JS_Is*Array tests succeeds, then it is safe to call 105 * the various accessor JSAPI calls defined below. 106 */ 107 extern JS_PUBLIC_API bool JS_IsTypedArrayObject(JSObject* obj); 108 109 /** 110 * Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may 111 * return false if a security wrapper is encountered that denies the 112 * unwrapping. If this test or one of the more specific tests succeeds, then it 113 * is safe to call the various ArrayBufferView accessor JSAPI calls defined 114 * below. 115 */ 116 extern JS_PUBLIC_API bool JS_IsArrayBufferViewObject(JSObject* obj); 117 118 /** 119 * Return the isShared flag of a typed array, which denotes whether 120 * the underlying buffer is a SharedArrayBuffer. 121 * 122 * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow 123 * be known that it would pass such a test: it is a typed array or a wrapper of 124 * a typed array, and the unwrapping will succeed. 125 */ 126 extern JS_PUBLIC_API bool JS_GetTypedArraySharedness(JSObject* obj); 127 128 /* 129 * Test for specific typed array types (ArrayBufferView subtypes) and return 130 * the unwrapped object if so, else nullptr. Never throws. 131 */ 132 133 namespace js { 134 135 extern JS_PUBLIC_API JSObject* UnwrapArrayBufferView(JSObject* obj); 136 137 namespace detail { 138 139 constexpr size_t TypedArrayLengthSlot = 1; 140 constexpr size_t TypedArrayDataSlot = 3; 141 142 } // namespace detail 143 144 // This one isn't inlined because it's rather tricky (by dint of having to deal 145 // with a dozen-plus classes and varying slot layouts. 146 extern JS_PUBLIC_API void GetArrayBufferViewLengthAndData(JSObject* obj, 147 size_t* length, 148 bool* isSharedMemory, 149 uint8_t** data); 150 151 } // namespace js 152 153 /* 154 * JS_GetObjectAs(type)Array(JSObject* maybeWrapped, size_t* length, bool* 155 * isSharedMemory, element_type** data) 156 * 157 * Unwrap Typed arrays all at once. Return nullptr without throwing if the 158 * object cannot be viewed as the correct typed array, or the typed array 159 * object on success, filling both outparameters. 160 */ 161 #define DECLARE_GET_OBJECT_AS(ExternalType, NativeType, Name) \ 162 extern JS_PUBLIC_API JSObject* JS_GetObjectAs##Name##Array( \ 163 JSObject* maybeWrapped, size_t* length, bool* isSharedMemory, \ 164 ExternalType** data); 165 JS_FOR_EACH_TYPED_ARRAY(DECLARE_GET_OBJECT_AS) 166 #undef DECLARE_GET_OBJECT_AS 167 168 extern JS_PUBLIC_API JSObject* JS_GetObjectAsArrayBufferView( 169 JSObject* obj, size_t* length, bool* isSharedMemory, uint8_t** data); 170 171 /* 172 * Get the type of elements in a typed array, or MaxTypedArrayViewType if a 173 * DataView. 174 * 175 * |obj| must have passed a JS_IsArrayBufferView/JS_Is*Array test, or somehow 176 * be known that it would pass such a test: it is an ArrayBufferView or a 177 * wrapper of an ArrayBufferView, and the unwrapping will succeed. 178 */ 179 extern JS_PUBLIC_API JS::Scalar::Type JS_GetArrayBufferViewType(JSObject* obj); 180 181 /** 182 * Return the number of elements in a typed array. 183 * 184 * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow 185 * be known that it would pass such a test: it is a typed array or a wrapper of 186 * a typed array, and the unwrapping will succeed. 187 */ 188 extern JS_PUBLIC_API size_t JS_GetTypedArrayLength(JSObject* obj); 189 190 /** 191 * Return the byte offset from the start of an ArrayBuffer to the start of a 192 * typed array view. 193 * 194 * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow 195 * be known that it would pass such a test: it is a typed array or a wrapper of 196 * a typed array, and the unwrapping will succeed. 197 */ 198 extern JS_PUBLIC_API size_t JS_GetTypedArrayByteOffset(JSObject* obj); 199 200 /** 201 * Return the byte length of a typed array. 202 * 203 * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow 204 * be known that it would pass such a test: it is a typed array or a wrapper of 205 * a typed array, and the unwrapping will succeed. 206 */ 207 extern JS_PUBLIC_API size_t JS_GetTypedArrayByteLength(JSObject* obj); 208 209 /** 210 * More generic name for JS_GetTypedArrayByteLength to cover DataViews as well 211 */ 212 extern JS_PUBLIC_API size_t JS_GetArrayBufferViewByteLength(JSObject* obj); 213 214 /** 215 * More generic name for JS_GetTypedArrayByteOffset to cover DataViews as well 216 */ 217 extern JS_PUBLIC_API size_t JS_GetArrayBufferViewByteOffset(JSObject* obj); 218 219 /** 220 * Same as above, but for any kind of ArrayBufferView. Prefer the type-specific 221 * versions when possible. 222 */ 223 extern JS_PUBLIC_API void* JS_GetArrayBufferViewData( 224 JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&); 225 226 /** 227 * Return a "fixed" pointer (one that will not move during a GC) to the 228 * ArrayBufferView's data. Note that this will not keep the object alive; the 229 * holding object should be rooted or traced. If the view is storing the data 230 * inline, this will copy the data to the provided buffer, returning nullptr if 231 * bufSize is inadequate. 232 * 233 * Avoid using this unless necessary. JS_GetArrayBufferViewData is simpler and 234 * more efficient because it requires the caller to ensure that a GC will not 235 * occur and thus does not need to handle movable data. 236 */ 237 extern JS_PUBLIC_API uint8_t* JS_GetArrayBufferViewFixedData(JSObject* obj, 238 uint8_t* buffer, 239 size_t bufSize); 240 241 /** 242 * If the bufSize passed to JS_GetArrayBufferViewFixedData is at least this 243 * many bytes, then any copied data is guaranteed to fit into the provided 244 * buffer. 245 */ 246 extern JS_PUBLIC_API size_t JS_MaxMovableTypedArraySize(); 247 248 /** 249 * Return the ArrayBuffer or SharedArrayBuffer underlying an ArrayBufferView. 250 * This may return a detached buffer. |obj| must be an object that would 251 * return true for JS_IsArrayBufferViewObject(). 252 */ 253 extern JS_PUBLIC_API JSObject* JS_GetArrayBufferViewBuffer( 254 JSContext* cx, JS::Handle<JSObject*> obj, bool* isSharedMemory); 255 256 /** 257 * Create a new DataView using the given buffer for storage. The given buffer 258 * must be an ArrayBuffer or SharedArrayBuffer (or a cross-compartment wrapper 259 * of either type), and the offset and length must fit within the bounds of the 260 * buffer. Currently, nullptr will be returned and an exception will be thrown 261 * if these conditions do not hold, but do not depend on that behavior. 262 */ 263 JS_PUBLIC_API JSObject* JS_NewDataView(JSContext* cx, 264 JS::Handle<JSObject*> buffer, 265 size_t byteOffset, size_t byteLength); 266 267 namespace JS { 268 269 /* 270 * Returns whether the passed array buffer view is 'large': its byteLength >= 2 271 * GB. 272 * 273 * |obj| must pass a JS_IsArrayBufferViewObject test. 274 */ 275 JS_PUBLIC_API bool IsLargeArrayBufferView(JSObject* obj); 276 277 /* 278 * Returns whether the passed array buffer view has a resizable or growable 279 * array buffer. 280 * 281 * |obj| must pass a JS_IsArrayBufferViewObject test. 282 */ 283 JS_PUBLIC_API bool IsResizableArrayBufferView(JSObject* obj); 284 285 /* 286 * Returns whether the passed array buffer view has an immutable array buffer. 287 * 288 * |obj| must pass a JS_IsArrayBufferViewObject test. 289 */ 290 JS_PUBLIC_API bool IsImmutableArrayBufferView(JSObject* obj); 291 292 /* 293 * Given an ArrayBuffer or view, prevent the length of the underlying 294 * ArrayBuffer from changing (with pin=true) until unfrozen (with 295 * pin=false). Note that some objects (eg SharedArrayBuffers) cannot change 296 * length to begin with, and are treated as always pinned. 297 * 298 * ArrayBuffers and their views can change length by being detached, or 299 * if they are ResizableArrayBuffers or (shared) GrowableArrayBuffers. 300 * 301 * Returns whether the pinned status changed. 302 */ 303 JS_PUBLIC_API bool PinArrayBufferOrViewLength(JSObject* obj, bool pin); 304 305 /* 306 * Given an ArrayBuffer or view, make sure its contents are not stored inline 307 * so that the data is safe for use even if a GC moves the owning object. 308 * 309 * Note that this by itself does not make it safe to use the data pointer 310 * if JS can run or the ArrayBuffer can be detached in any way. Consider using 311 * this in conjunction with PinArrayBufferOrViewLength, which will cause any 312 * potentially invalidating operations to fail. 313 */ 314 JS_PUBLIC_API bool EnsureNonInlineArrayBufferOrView(JSContext* cx, 315 JSObject* obj); 316 317 namespace detail { 318 319 // Map from eg Uint8Clamped -> uint8_t, Uint8 -> uint8_t, or Float64 -> 320 // double. Used as the DataType within a JS::TypedArray specialization. 321 template <JS::Scalar::Type ArrayType> 322 struct ExternalTypeOf {}; 323 324 #define DEFINE_ELEMENT_TYPES(ExternalT, NativeT, Name) \ 325 template <> \ 326 struct ExternalTypeOf<JS::Scalar::Name> { \ 327 using Type = ExternalT; \ 328 }; 329 JS_FOR_EACH_TYPED_ARRAY(DEFINE_ELEMENT_TYPES) 330 #undef DEFINE_ELEMENT_TYPES 331 332 template <JS::Scalar::Type ArrayType> 333 using ExternalTypeOf_t = typename ExternalTypeOf<ArrayType>::Type; 334 335 } // namespace detail 336 337 // A class holding a JSObject referring to a buffer of data. Either an 338 // ArrayBufferObject or some sort of ArrayBufferViewObject (see below). 339 // Note that this will always hold an unwrapped object. 340 class JS_PUBLIC_API ArrayBufferOrView { 341 public: 342 // Typed Arrays will set this to their specific element type. 343 // Everything else just claims to expose things as uint8_t*. 344 using DataType = uint8_t; 345 346 protected: 347 JSObject* obj; 348 349 explicit ArrayBufferOrView(JSObject* unwrapped) : obj(unwrapped) {} 350 351 public: 352 // ArrayBufferOrView subclasses will set `obj` to nullptr if wrapping an 353 // object of the wrong type. So this allows: 354 // 355 // auto view = JS::TypedArray<JS::Scalar::Int8>::fromObject(obj); 356 // if (!view) { ... } 357 // 358 explicit operator bool() const { return !!obj; } 359 360 // `obj` must be an unwrapped ArrayBuffer or view, or nullptr. 361 static inline ArrayBufferOrView fromObject(JSObject* unwrapped); 362 363 // Unwrap an ArrayBuffer or view. Returns ArrayBufferOrView(nullptr) if 364 // `maybeWrapped` is the wrong type or fails unwrapping. Never throw. 365 static ArrayBufferOrView unwrap(JSObject* maybeWrapped); 366 367 // Allow use as Rooted<JS::ArrayBufferOrView>. 368 void trace(JSTracer* trc) { 369 if (obj) { 370 js::gc::TraceExternalEdge(trc, &obj, "ArrayBufferOrView object"); 371 } 372 } 373 374 bool isDetached() const; 375 bool isResizable() const; 376 bool isImmutable() const; 377 378 void exposeToActiveJS() const { 379 if (obj) { 380 js::BarrierMethods<JSObject*>::exposeToJS(obj); 381 } 382 } 383 384 JSObject* asObject() const { 385 exposeToActiveJS(); 386 return obj; 387 } 388 389 JSObject* asObjectUnbarriered() const { return obj; } 390 391 JSObject** addressOfObject() { return &obj; } 392 393 bool operator==(const ArrayBufferOrView& other) const { 394 return obj == other.asObjectUnbarriered(); 395 } 396 bool operator!=(const ArrayBufferOrView& other) const { 397 return obj != other.asObjectUnbarriered(); 398 } 399 }; 400 401 class JS_PUBLIC_API ArrayBuffer : public ArrayBufferOrView { 402 static const JSClass* const FixedLengthUnsharedClass; 403 static const JSClass* const ResizableUnsharedClass; 404 static const JSClass* const ImmutableUnsharedClass; 405 static const JSClass* const FixedLengthSharedClass; 406 static const JSClass* const GrowableSharedClass; 407 408 protected: 409 explicit ArrayBuffer(JSObject* unwrapped) : ArrayBufferOrView(unwrapped) {} 410 411 public: 412 static ArrayBuffer fromObject(JSObject* unwrapped) { 413 if (unwrapped) { 414 const JSClass* clasp = GetClass(unwrapped); 415 if (clasp == FixedLengthUnsharedClass || 416 clasp == ResizableUnsharedClass || clasp == ImmutableUnsharedClass || 417 clasp == FixedLengthSharedClass || clasp == GrowableSharedClass) { 418 return ArrayBuffer(unwrapped); 419 } 420 } 421 return ArrayBuffer(nullptr); 422 } 423 static ArrayBuffer unwrap(JSObject* maybeWrapped); 424 425 static ArrayBuffer create(JSContext* cx, size_t nbytes); 426 427 mozilla::Span<uint8_t> getData(bool* isSharedMemory, 428 const JS::AutoRequireNoGC&); 429 }; 430 431 // A view into an ArrayBuffer, either a DataViewObject or a Typed Array variant. 432 class JS_PUBLIC_API ArrayBufferView : public ArrayBufferOrView { 433 protected: 434 explicit ArrayBufferView(JSObject* unwrapped) 435 : ArrayBufferOrView(unwrapped) {} 436 437 public: 438 static inline ArrayBufferView fromObject(JSObject* unwrapped); 439 static ArrayBufferView unwrap(JSObject* maybeWrapped) { 440 if (!maybeWrapped) { 441 return ArrayBufferView(nullptr); 442 } 443 ArrayBufferView view = fromObject(maybeWrapped); 444 if (view) { 445 return view; 446 } 447 return fromObject(js::CheckedUnwrapStatic(maybeWrapped)); 448 } 449 450 bool isDetached() const; 451 bool isResizable() const; 452 bool isImmutable() const; 453 454 mozilla::Span<uint8_t> getData(bool* isSharedMemory, 455 const JS::AutoRequireNoGC&); 456 457 // Must only be called if !isDetached(). 458 size_t getByteLength(const JS::AutoRequireNoGC&); 459 }; 460 461 class JS_PUBLIC_API DataView : public ArrayBufferView { 462 static const JSClass* const FixedLengthClassPtr; 463 static const JSClass* const ResizableClassPtr; 464 static const JSClass* const ImmutableClassPtr; 465 466 protected: 467 explicit DataView(JSObject* unwrapped) : ArrayBufferView(unwrapped) {} 468 469 public: 470 static DataView fromObject(JSObject* unwrapped) { 471 if (unwrapped) { 472 const JSClass* clasp = GetClass(unwrapped); 473 if (clasp == FixedLengthClassPtr || clasp == ResizableClassPtr || 474 clasp == ImmutableClassPtr) { 475 return DataView(unwrapped); 476 } 477 } 478 return DataView(nullptr); 479 } 480 481 static DataView unwrap(JSObject* maybeWrapped) { 482 if (!maybeWrapped) { 483 return DataView(nullptr); 484 } 485 DataView view = fromObject(maybeWrapped); 486 if (view) { 487 return view; 488 } 489 return fromObject(js::CheckedUnwrapStatic(maybeWrapped)); 490 } 491 }; 492 493 // Base type of all Typed Array variants. 494 class JS_PUBLIC_API TypedArray_base : public ArrayBufferView { 495 protected: 496 explicit TypedArray_base(JSObject* unwrapped) : ArrayBufferView(unwrapped) {} 497 498 static const JSClass* const fixedLengthClasses; 499 static const JSClass* const resizableClasses; 500 static const JSClass* const immutableClasses; 501 502 public: 503 static TypedArray_base fromObject(JSObject* unwrapped); 504 505 static TypedArray_base unwrap(JSObject* maybeWrapped) { 506 if (!maybeWrapped) { 507 return TypedArray_base(nullptr); 508 } 509 TypedArray_base view = fromObject(maybeWrapped); 510 if (view) { 511 return view; 512 } 513 return fromObject(js::CheckedUnwrapStatic(maybeWrapped)); 514 } 515 }; 516 517 template <JS::Scalar::Type TypedArrayElementType> 518 class JS_PUBLIC_API TypedArray : public TypedArray_base { 519 // This cannot be a static data member because on Windows, 520 // __declspec(dllexport) causes the class to be instantiated immediately, 521 // leading to errors when later explicit specializations of inline member 522 // functions are encountered ("error: explicit specialization of 'ClassPtr' 523 // after instantiation"). And those inlines need to be defined outside of the 524 // class due to order dependencies. This is the only way I could get it to 525 // work on both Windows and POSIX. 526 static const JSClass* fixedLengthClasp() { 527 return &TypedArray_base::fixedLengthClasses[static_cast<int>( 528 TypedArrayElementType)]; 529 } 530 static const JSClass* resizableClasp() { 531 return &TypedArray_base::resizableClasses[static_cast<int>( 532 TypedArrayElementType)]; 533 } 534 static const JSClass* immutableClasp() { 535 return &TypedArray_base::immutableClasses[static_cast<int>( 536 TypedArrayElementType)]; 537 } 538 539 protected: 540 explicit TypedArray(JSObject* unwrapped) : TypedArray_base(unwrapped) {} 541 542 public: 543 using DataType = detail::ExternalTypeOf_t<TypedArrayElementType>; 544 545 static constexpr JS::Scalar::Type Scalar = TypedArrayElementType; 546 547 static TypedArray create(JSContext* cx, size_t nelements); 548 static TypedArray fromArray(JSContext* cx, HandleObject other); 549 static TypedArray fromBuffer(JSContext* cx, HandleObject arrayBuffer, 550 size_t byteOffset, int64_t length); 551 552 // Return an interface wrapper around `obj`, or around nullptr if `obj` is not 553 // an unwrapped typed array of the correct type. 554 static TypedArray fromObject(JSObject* unwrapped) { 555 if (unwrapped) { 556 const JSClass* clasp = GetClass(unwrapped); 557 if (clasp == fixedLengthClasp() || clasp == resizableClasp() || 558 clasp == immutableClasp()) { 559 return TypedArray(unwrapped); 560 } 561 } 562 return TypedArray(nullptr); 563 } 564 565 static TypedArray unwrap(JSObject* maybeWrapped) { 566 if (!maybeWrapped) { 567 return TypedArray(nullptr); 568 } 569 TypedArray view = fromObject(maybeWrapped); 570 if (view) { 571 return view; 572 } 573 return fromObject(js::CheckedUnwrapStatic(maybeWrapped)); 574 } 575 576 // Return a pointer to the start of the data referenced by a typed array. The 577 // data is still owned by the typed array, and should not be modified on 578 // another thread. Furthermore, the pointer can become invalid on GC (if the 579 // data is small and fits inside the array's GC header), so callers must take 580 // care not to hold on across anything that could GC. 581 // 582 // |obj| must have passed a JS_Is*Array test, or somehow be known that it 583 // would pass such a test: it is a typed array or a wrapper of a typed array, 584 // and the unwrapping will succeed. 585 // 586 // |*isSharedMemory| will be set to true if the typed array maps a 587 // SharedArrayBuffer, otherwise to false. 588 // 589 mozilla::Span<DataType> getData(bool* isSharedMemory, 590 const JS::AutoRequireNoGC& nogc); 591 }; 592 593 ArrayBufferOrView ArrayBufferOrView::fromObject(JSObject* unwrapped) { 594 if (ArrayBuffer::fromObject(unwrapped) || 595 ArrayBufferView::fromObject(unwrapped)) { 596 return ArrayBufferOrView(unwrapped); 597 } 598 return ArrayBufferOrView(nullptr); 599 } 600 601 ArrayBufferView ArrayBufferView::fromObject(JSObject* unwrapped) { 602 if (TypedArray_base::fromObject(unwrapped) || 603 DataView::fromObject(unwrapped)) { 604 return ArrayBufferView(unwrapped); 605 } 606 return ArrayBufferView(nullptr); 607 } 608 609 } /* namespace JS */ 610 611 /* 612 * JS_Get(type)ArrayData(JSObject* obj, 613 * bool* isSharedMemory, 614 * const JS::AutoRequireNoGC&) 615 * 616 * js::Get(type)ArrayLengthAndData(JSObject* obj, 617 * size_t* length, 618 * bool* isSharedMemory, 619 * const JS::AutoRequireNoGC&) 620 * 621 * Return a pointer to the start of the data referenced by a typed array. The 622 * data is still owned by the typed array, and should not be modified on 623 * another thread. Furthermore, the pointer can become invalid on GC (if the 624 * data is small and fits inside the array's GC header), so callers must take 625 * care not to hold on across anything that could GC. 626 * 627 * |obj| must have passed a JS_Is*Array test, or somehow be known that it would 628 * pass such a test: it is a typed array or a wrapper of a typed array, and the 629 * unwrapping will succeed. 630 * 631 * |*isSharedMemory| will be set to true if the typed array maps a 632 * SharedArrayBuffer, otherwise to false. 633 */ 634 635 #define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(ExternalType, NativeType, Name) \ 636 extern JS_PUBLIC_API ExternalType* JS_Get##Name##ArrayData( \ 637 JSObject* maybeWrapped, bool* isSharedMemory, \ 638 const JS::AutoRequireNoGC&); \ 639 \ 640 namespace js { \ 641 inline void Get##Name##ArrayLengthAndData(JSObject* unwrapped, \ 642 size_t* length, \ 643 bool* isSharedMemory, \ 644 ExternalType** data) { \ 645 MOZ_ASSERT(JS::TypedArray<JS::Scalar::Name>::fromObject(unwrapped)); \ 646 const JS::Value& lenSlot = \ 647 JS::GetReservedSlot(unwrapped, detail::TypedArrayLengthSlot); \ 648 *length = size_t(lenSlot.toPrivate()); \ 649 *isSharedMemory = JS_GetTypedArraySharedness(unwrapped); \ 650 *data = JS::GetMaybePtrFromReservedSlot<ExternalType>( \ 651 unwrapped, detail::TypedArrayDataSlot); \ 652 } \ 653 \ 654 JS_PUBLIC_API JSObject* Unwrap##Name##Array(JSObject* maybeWrapped); \ 655 } /* namespace js */ 656 657 JS_FOR_EACH_TYPED_ARRAY(JS_DEFINE_DATA_AND_LENGTH_ACCESSOR) 658 #undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR 659 660 namespace JS { 661 662 #define IMPL_TYPED_ARRAY_CLASS(ExternalType, NativeType, Name) \ 663 template <> \ 664 inline JS::TypedArray<JS::Scalar::Name> \ 665 JS::TypedArray<JS::Scalar::Name>::create(JSContext* cx, size_t nelements) { \ 666 return fromObject(JS_New##Name##Array(cx, nelements)); \ 667 }; \ 668 \ 669 template <> \ 670 inline JS::TypedArray<JS::Scalar::Name> \ 671 JS::TypedArray<JS::Scalar::Name>::fromArray(JSContext* cx, \ 672 HandleObject other) { \ 673 return fromObject(JS_New##Name##ArrayFromArray(cx, other)); \ 674 }; \ 675 \ 676 template <> \ 677 inline JS::TypedArray<JS::Scalar::Name> \ 678 JS::TypedArray<JS::Scalar::Name>::fromBuffer( \ 679 JSContext* cx, HandleObject arrayBuffer, size_t byteOffset, \ 680 int64_t length) { \ 681 return fromObject( \ 682 JS_New##Name##ArrayWithBuffer(cx, arrayBuffer, byteOffset, length)); \ 683 }; 684 685 JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS) 686 #undef IMPL_TYPED_ARRAY_CLASS 687 688 // Create simple names like Int8Array, Float32Array, etc. 689 #define JS_DECLARE_CLASS_ALIAS(ExternalType, NativeType, Name) \ 690 using Name##Array = TypedArray<js::Scalar::Name>; 691 JS_FOR_EACH_TYPED_ARRAY(JS_DECLARE_CLASS_ALIAS) 692 #undef JS_DECLARE_CLASS_ALIAS 693 694 } // namespace JS 695 696 namespace js { 697 698 template <typename T> 699 using EnableIfABOVType = 700 std::enable_if_t<std::is_base_of_v<JS::ArrayBufferOrView, T>>; 701 702 template <typename T, typename Wrapper> 703 class WrappedPtrOperations<T, Wrapper, EnableIfABOVType<T>> { 704 auto get() const { return static_cast<const Wrapper*>(this)->get(); } 705 706 public: 707 explicit operator bool() const { return bool(get()); } 708 JSObject* asObject() const { return get().asObject(); } 709 bool isDetached() const { return get().isDetached(); } 710 bool isSharedMemory() const { return get().isSharedMemory(); } 711 712 mozilla::Span<typename T::DataType> getData(bool* isSharedMemory, 713 const JS::AutoRequireNoGC& nogc) { 714 return get().getData(isSharedMemory, nogc); 715 } 716 }; 717 718 // Allow usage within Heap<T>. 719 template <typename T> 720 struct IsHeapConstructibleType<T, EnableIfABOVType<T>> : public std::true_type { 721 }; 722 723 template <typename T> 724 struct BarrierMethods<T, EnableIfABOVType<T>> { 725 static gc::Cell* asGCThingOrNull(T view) { 726 return reinterpret_cast<gc::Cell*>(view.asObjectUnbarriered()); 727 } 728 static void writeBarriers(T* viewp, T prev, T next) { 729 BarrierMethods<JSObject*>::writeBarriers(viewp->addressOfObject(), 730 prev.asObjectUnbarriered(), 731 next.asObjectUnbarriered()); 732 } 733 static void postWriteBarrier(T* viewp, T prev, T next) { 734 BarrierMethods<JSObject*>::postWriteBarrier(viewp->addressOfObject(), 735 prev.asObjectUnbarriered(), 736 next.asObjectUnbarriered()); 737 } 738 static void exposeToJS(T view) { view.exposeToActiveJS(); } 739 static void readBarrier(T view) { 740 JSObject* obj = view.asObjectUnbarriered(); 741 if (obj) { 742 js::gc::IncrementalReadBarrier(JS::GCCellPtr(obj)); 743 } 744 } 745 }; 746 747 } // namespace js 748 749 namespace JS { 750 template <typename T> 751 struct SafelyInitialized<T, js::EnableIfABOVType<T>> { 752 static T create() { return T::fromObject(nullptr); } 753 }; 754 } // namespace JS 755 756 /* 757 * JS_Is(type)Array(JSObject* maybeWrapped) 758 * 759 * Test for specific typed array types. 760 */ 761 762 #define DECLARE_IS_ARRAY_TEST(_1, _2, Name) \ 763 inline JS_PUBLIC_API bool JS_Is##Name##Array(JSObject* maybeWrapped) { \ 764 return JS::TypedArray<js::Scalar::Name>::unwrap(maybeWrapped).asObject(); \ 765 } 766 JS_FOR_EACH_TYPED_ARRAY(DECLARE_IS_ARRAY_TEST) 767 #undef DECLARE_IS_ARRAY_TEST 768 769 #endif // js_experimental_TypedData_h