tor-browser

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

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