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