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