summaryrefslogtreecommitdiffstats
path: root/js/public/experimental/TypedData.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/public/experimental/TypedData.h
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/public/experimental/TypedData.h')
-rw-r--r--js/public/experimental/TypedData.h719
1 files changed, 719 insertions, 0 deletions
diff --git a/js/public/experimental/TypedData.h b/js/public/experimental/TypedData.h
new file mode 100644
index 0000000000..9ba14c65bc
--- /dev/null
+++ b/js/public/experimental/TypedData.h
@@ -0,0 +1,719 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Typed array, ArrayBuffer, and DataView creation, predicate, and accessor
+ * functions.
+ */
+
+#ifndef js_experimental_TypedData_h
+#define js_experimental_TypedData_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_CRASH
+#include "mozilla/Casting.h" // mozilla::AssertedCast
+
+#include <stddef.h> // size_t
+#include <stdint.h> // {,u}int8_t, {,u}int16_t, {,u}int32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Object.h" // JS::GetClass, JS::GetReservedSlot, JS::GetMaybePtrFromReservedSlot
+#include "js/RootingAPI.h" // JS::Handle, JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE
+#include "js/ScalarType.h" // JS::Scalar::Type
+#include "js/Wrapper.h" // js::CheckedUnwrapStatic
+
+struct JSClass;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+class JS_PUBLIC_API AutoRequireNoGC;
+
+} // namespace JS
+
+// JS_FOR_EACH_TYPED_ARRAY(MACRO) expands MACRO once for each specific
+// typed array subtype (Int8Array, Float64Array, ...), passing arguments
+// as MACRO(ExternalT, NativeT, Name) where
+//
+// ExternalT - externally-exposed element type (eg uint8_t)
+//
+// NativeT - element type used for the implementation (eg js::uint8_clamped_t)
+// Note that this type is not exposed publicly. Internal files need to
+// #include <vm/Uint8Clamped.h> to see it.
+//
+// Name - a name usable as both a JS::Scalar::Type value (eg
+// JS::Scalar::Uint8Clamped) or the stem of a full typed array name (eg
+// Uint8ClampedArray)
+//
+#define JS_FOR_EACH_TYPED_ARRAY(MACRO) \
+ MACRO(int8_t, int8_t, Int8) \
+ MACRO(uint8_t, uint8_t, Uint8) \
+ MACRO(int16_t, int16_t, Int16) \
+ MACRO(uint16_t, uint16_t, Uint16) \
+ MACRO(int32_t, int32_t, Int32) \
+ MACRO(uint32_t, uint32_t, Uint32) \
+ MACRO(float, float, Float32) \
+ MACRO(double, double, Float64) \
+ MACRO(uint8_t, js::uint8_clamped, Uint8Clamped) \
+ MACRO(int64_t, int64_t, BigInt64) \
+ MACRO(uint64_t, uint64_t, BigUint64)
+
+/*
+ * JS_New(type)Array:
+ *
+ * Create a new typed array with nelements elements.
+ *
+ * These functions (except the WithBuffer variants) fill in the array with
+ * zeros.
+ *
+ * JS_New(type)ArrayFromArray:
+ *
+ * Create a new typed array and copy in values from the given object. The
+ * object is used as if it were an array; that is, the new array (if
+ * successfully created) will have length given by array.length, and its
+ * elements will be those specified by array[0], array[1], and so on, after
+ * conversion to the typed array element type.
+ *
+ * JS_New(type)ArrayWithBuffer:
+ *
+ * Create a new typed array using the given ArrayBuffer or
+ * SharedArrayBuffer for storage. The length value is optional; if -1
+ * is passed, enough elements to use up the remainder of the byte
+ * array is used as the default value.
+ */
+
+#define DECLARE_TYPED_ARRAY_CREATION_API(ExternalType, NativeType, Name) \
+ extern JS_PUBLIC_API JSObject* JS_New##Name##Array(JSContext* cx, \
+ size_t nelements); \
+ extern JS_PUBLIC_API JSObject* JS_New##Name##ArrayFromArray( \
+ JSContext* cx, JS::Handle<JSObject*> array); \
+ extern JS_PUBLIC_API JSObject* JS_New##Name##ArrayWithBuffer( \
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset, \
+ int64_t length);
+
+JS_FOR_EACH_TYPED_ARRAY(DECLARE_TYPED_ARRAY_CREATION_API)
+#undef DECLARE_TYPED_ARRAY_CREATION_API
+
+/**
+ * Check whether obj supports JS_GetTypedArray* APIs. Note that this may return
+ * false if a security wrapper is encountered that denies the unwrapping. If
+ * this test or one of the JS_Is*Array tests succeeds, then it is safe to call
+ * the various accessor JSAPI calls defined below.
+ */
+extern JS_PUBLIC_API bool JS_IsTypedArrayObject(JSObject* obj);
+
+/**
+ * Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may
+ * return false if a security wrapper is encountered that denies the
+ * unwrapping. If this test or one of the more specific tests succeeds, then it
+ * is safe to call the various ArrayBufferView accessor JSAPI calls defined
+ * below.
+ */
+extern JS_PUBLIC_API bool JS_IsArrayBufferViewObject(JSObject* obj);
+
+/**
+ * Return the isShared flag of a typed array, which denotes whether
+ * the underlying buffer is a SharedArrayBuffer.
+ *
+ * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
+ * be known that it would pass such a test: it is a typed array or a wrapper of
+ * a typed array, and the unwrapping will succeed.
+ */
+extern JS_PUBLIC_API bool JS_GetTypedArraySharedness(JSObject* obj);
+
+/*
+ * Test for specific typed array types (ArrayBufferView subtypes) and return
+ * the unwrapped object if so, else nullptr. Never throws.
+ */
+
+namespace js {
+
+extern JS_PUBLIC_API JSObject* UnwrapArrayBufferView(JSObject* obj);
+
+extern JS_PUBLIC_API JSObject* UnwrapReadableStream(JSObject* obj);
+
+namespace detail {
+
+constexpr size_t TypedArrayLengthSlot = 1;
+constexpr size_t TypedArrayDataSlot = 3;
+
+} // namespace detail
+
+// This one isn't inlined because it's rather tricky (by dint of having to deal
+// with a dozen-plus classes and varying slot layouts.
+extern JS_PUBLIC_API void GetArrayBufferViewLengthAndData(JSObject* obj,
+ size_t* length,
+ bool* isSharedMemory,
+ uint8_t** data);
+
+} // namespace js
+
+/*
+ * JS_GetObjectAs(type)Array(JSObject* maybeWrapped, size_t* length, bool*
+ * isSharedMemory, element_type** data)
+ *
+ * Unwrap Typed arrays all at once. Return nullptr without throwing if the
+ * object cannot be viewed as the correct typed array, or the typed array
+ * object on success, filling both outparameters.
+ */
+#define DECLARE_GET_OBJECT_AS(ExternalType, NativeType, Name) \
+ extern JS_PUBLIC_API JSObject* JS_GetObjectAs##Name##Array( \
+ JSObject* maybeWrapped, size_t* length, bool* isSharedMemory, \
+ ExternalType** data);
+JS_FOR_EACH_TYPED_ARRAY(DECLARE_GET_OBJECT_AS)
+#undef DECLARE_GET_OBJECT_AS
+
+extern JS_PUBLIC_API JSObject* JS_GetObjectAsArrayBufferView(
+ JSObject* obj, size_t* length, bool* isSharedMemory, uint8_t** data);
+
+/*
+ * Get the type of elements in a typed array, or MaxTypedArrayViewType if a
+ * DataView.
+ *
+ * |obj| must have passed a JS_IsArrayBufferView/JS_Is*Array test, or somehow
+ * be known that it would pass such a test: it is an ArrayBufferView or a
+ * wrapper of an ArrayBufferView, and the unwrapping will succeed.
+ */
+extern JS_PUBLIC_API JS::Scalar::Type JS_GetArrayBufferViewType(JSObject* obj);
+
+/**
+ * Return the number of elements in a typed array.
+ *
+ * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
+ * be known that it would pass such a test: it is a typed array or a wrapper of
+ * a typed array, and the unwrapping will succeed.
+ */
+extern JS_PUBLIC_API size_t JS_GetTypedArrayLength(JSObject* obj);
+
+/**
+ * Return the byte offset from the start of an ArrayBuffer to the start of a
+ * typed array view.
+ *
+ * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
+ * be known that it would pass such a test: it is a typed array or a wrapper of
+ * a typed array, and the unwrapping will succeed.
+ */
+extern JS_PUBLIC_API size_t JS_GetTypedArrayByteOffset(JSObject* obj);
+
+/**
+ * Return the byte length of a typed array.
+ *
+ * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
+ * be known that it would pass such a test: it is a typed array or a wrapper of
+ * a typed array, and the unwrapping will succeed.
+ */
+extern JS_PUBLIC_API size_t JS_GetTypedArrayByteLength(JSObject* obj);
+
+/**
+ * More generic name for JS_GetTypedArrayByteLength to cover DataViews as well
+ */
+extern JS_PUBLIC_API size_t JS_GetArrayBufferViewByteLength(JSObject* obj);
+
+/**
+ * More generic name for JS_GetTypedArrayByteOffset to cover DataViews as well
+ */
+extern JS_PUBLIC_API size_t JS_GetArrayBufferViewByteOffset(JSObject* obj);
+
+/**
+ * Same as above, but for any kind of ArrayBufferView. Prefer the type-specific
+ * versions when possible.
+ */
+extern JS_PUBLIC_API void* JS_GetArrayBufferViewData(
+ JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&);
+
+/**
+ * Return a "fixed" pointer (one that will not move during a GC) to the
+ * ArrayBufferView's data. Note that this will not keep the object alive; the
+ * holding object should be rooted or traced. If the view is storing the data
+ * inline, this will copy the data to the provided buffer, returning nullptr if
+ * bufSize is inadequate.
+ *
+ * Avoid using this unless necessary. JS_GetArrayBufferViewData is simpler and
+ * more efficient because it requires the caller to ensure that a GC will not
+ * occur and thus does not need to handle movable data.
+ */
+extern JS_PUBLIC_API uint8_t* JS_GetArrayBufferViewFixedData(JSObject* obj,
+ uint8_t* buffer,
+ size_t bufSize);
+
+/**
+ * If the bufSize passed to JS_GetArrayBufferViewFixedData is at least this
+ * many bytes, then any copied data is guaranteed to fit into the provided
+ * buffer.
+ */
+extern JS_PUBLIC_API size_t JS_MaxMovableTypedArraySize();
+
+/**
+ * Return the ArrayBuffer or SharedArrayBuffer underlying an ArrayBufferView.
+ * This may return a detached buffer. |obj| must be an object that would
+ * return true for JS_IsArrayBufferViewObject().
+ */
+extern JS_PUBLIC_API JSObject* JS_GetArrayBufferViewBuffer(
+ JSContext* cx, JS::Handle<JSObject*> obj, bool* isSharedMemory);
+
+/**
+ * Create a new DataView using the given buffer for storage. The given buffer
+ * must be an ArrayBuffer or SharedArrayBuffer (or a cross-compartment wrapper
+ * of either type), and the offset and length must fit within the bounds of the
+ * buffer. Currently, nullptr will be returned and an exception will be thrown
+ * if these conditions do not hold, but do not depend on that behavior.
+ */
+JS_PUBLIC_API JSObject* JS_NewDataView(JSContext* cx,
+ JS::Handle<JSObject*> buffer,
+ size_t byteOffset, size_t byteLength);
+
+namespace JS {
+
+/*
+ * Returns whether the passed array buffer view is 'large': its byteLength >= 2
+ * GB.
+ *
+ * |obj| must pass a JS_IsArrayBufferViewObject test.
+ */
+JS_PUBLIC_API bool IsLargeArrayBufferView(JSObject* obj);
+
+namespace detail {
+
+// Map from eg Uint8Clamped -> uint8_t, Uint8 -> uint8_t, or Float64 ->
+// double. Used as the DataType within a JS::TypedArray specialization.
+template <JS::Scalar::Type ArrayType>
+struct ExternalTypeOf {};
+
+#define DEFINE_ELEMENT_TYPES(ExternalT, NativeT, Name) \
+ template <> \
+ struct ExternalTypeOf<JS::Scalar::Name> { \
+ using Type = ExternalT; \
+ };
+JS_FOR_EACH_TYPED_ARRAY(DEFINE_ELEMENT_TYPES)
+#undef DEFINE_ELEMENT_TYPES
+
+template <JS::Scalar::Type ArrayType>
+using ExternalTypeOf_t = typename ExternalTypeOf<ArrayType>::Type;
+
+} // namespace detail
+
+// A class holding a JSObject referring to a buffer of data. Either an
+// ArrayBufferObject or some sort of ArrayBufferViewObject (see below).
+// Note that this will always hold an unwrapped object.
+class JS_PUBLIC_API ArrayBufferOrView {
+ public:
+ // Typed Arrays will set this to their specific element type.
+ // Everything else just claims to expose things as uint8_t*.
+ using DataType = uint8_t;
+
+ protected:
+ JSObject* obj;
+
+ explicit ArrayBufferOrView(JSObject* unwrapped) : obj(unwrapped) {}
+
+ public:
+ // ArrayBufferOrView subclasses will set `obj` to nullptr if wrapping an
+ // object of the wrong type. So this allows:
+ //
+ // auto view = JS::TypedArray<JS::Scalar::Int8>::fromObject(obj);
+ // if (!view) { ... }
+ //
+ explicit operator bool() const { return !!obj; }
+
+ // `obj` must be an unwrapped ArrayBuffer or view, or nullptr.
+ static inline ArrayBufferOrView fromObject(JSObject* unwrapped);
+
+ // Unwrap an ArrayBuffer or view. Returns ArrayBufferOrView(nullptr) if
+ // `maybeWrapped` is the wrong type or fails unwrapping. Never throw.
+ static ArrayBufferOrView unwrap(JSObject* maybeWrapped);
+
+ // Allow use as Rooted<JS::ArrayBufferOrView>.
+ void trace(JSTracer* trc) {
+ if (obj) {
+ js::gc::TraceExternalEdge(trc, &obj, "ArrayBufferOrView object");
+ }
+ }
+
+ bool isDetached() const;
+
+ void exposeToActiveJS() const {
+ if (obj) {
+ js::BarrierMethods<JSObject*>::exposeToJS(obj);
+ }
+ }
+
+ JSObject* asObject() const {
+ exposeToActiveJS();
+ return obj;
+ }
+
+ JSObject* asObjectUnbarriered() const { return obj; }
+
+ JSObject** addressOfObject() { return &obj; }
+
+ bool operator==(const ArrayBufferOrView& other) const {
+ return obj == other.asObjectUnbarriered();
+ }
+ bool operator!=(const ArrayBufferOrView& other) const {
+ return obj != other.asObjectUnbarriered();
+ }
+};
+
+class JS_PUBLIC_API ArrayBuffer : public ArrayBufferOrView {
+ protected:
+ explicit ArrayBuffer(JSObject* unwrapped) : ArrayBufferOrView(unwrapped) {}
+
+ public:
+ static const JSClass* const UnsharedClass;
+ static const JSClass* const SharedClass;
+
+ static ArrayBuffer fromObject(JSObject* unwrapped) {
+ if (unwrapped) {
+ const JSClass* clasp = GetClass(unwrapped);
+ if (clasp == UnsharedClass || clasp == SharedClass) {
+ return ArrayBuffer(unwrapped);
+ }
+ }
+ return ArrayBuffer(nullptr);
+ }
+ static ArrayBuffer unwrap(JSObject* maybeWrapped);
+
+ static ArrayBuffer create(JSContext* cx, size_t nbytes);
+
+ bool isDetached() const;
+ bool isSharedMemory() const;
+
+ uint8_t* getLengthAndData(size_t* length, bool* isSharedMemory,
+ const JS::AutoRequireNoGC&);
+
+ uint8_t* getData(bool* isSharedMemory, const JS::AutoRequireNoGC& nogc) {
+ size_t length;
+ return getLengthAndData(&length, isSharedMemory, nogc);
+ }
+};
+
+// A view into an ArrayBuffer, either a DataViewObject or a Typed Array variant.
+class JS_PUBLIC_API ArrayBufferView : public ArrayBufferOrView {
+ protected:
+ explicit ArrayBufferView(JSObject* unwrapped)
+ : ArrayBufferOrView(unwrapped) {}
+
+ public:
+ static inline ArrayBufferView fromObject(JSObject* unwrapped);
+ static ArrayBufferView unwrap(JSObject* maybeWrapped) {
+ if (!maybeWrapped) {
+ return ArrayBufferView(nullptr);
+ }
+ ArrayBufferView view = fromObject(maybeWrapped);
+ if (view) {
+ return view;
+ }
+ return fromObject(js::CheckedUnwrapStatic(maybeWrapped));
+ }
+
+ bool isDetached() const;
+ bool isSharedMemory() const;
+
+ uint8_t* getLengthAndData(size_t* length, bool* isSharedMemory,
+ const JS::AutoRequireNoGC&);
+ uint8_t* getData(bool* isSharedMemory, const JS::AutoRequireNoGC& nogc) {
+ size_t length;
+ return getLengthAndData(&length, isSharedMemory, nogc);
+ }
+
+ // Must only be called if !isDetached().
+ size_t getByteLength(const JS::AutoRequireNoGC&);
+};
+
+class JS_PUBLIC_API DataView : public ArrayBufferView {
+ protected:
+ explicit DataView(JSObject* unwrapped) : ArrayBufferView(unwrapped) {}
+
+ public:
+ static const JSClass* const ClassPtr;
+
+ static DataView fromObject(JSObject* unwrapped) {
+ if (unwrapped && GetClass(unwrapped) == ClassPtr) {
+ return DataView(unwrapped);
+ }
+ return DataView(nullptr);
+ }
+
+ static DataView unwrap(JSObject* maybeWrapped) {
+ if (!maybeWrapped) {
+ return DataView(nullptr);
+ }
+ DataView view = fromObject(maybeWrapped);
+ if (view) {
+ return view;
+ }
+ return fromObject(js::CheckedUnwrapStatic(maybeWrapped));
+ }
+};
+
+// Base type of all Typed Array variants.
+class JS_PUBLIC_API TypedArray_base : public ArrayBufferView {
+ protected:
+ explicit TypedArray_base(JSObject* unwrapped) : ArrayBufferView(unwrapped) {}
+
+ static const JSClass* const classes;
+
+ public:
+ static TypedArray_base fromObject(JSObject* unwrapped);
+
+ static TypedArray_base unwrap(JSObject* maybeWrapped) {
+ if (!maybeWrapped) {
+ return TypedArray_base(nullptr);
+ }
+ TypedArray_base view = fromObject(maybeWrapped);
+ if (view) {
+ return view;
+ }
+ return fromObject(js::CheckedUnwrapStatic(maybeWrapped));
+ }
+};
+
+template <JS::Scalar::Type TypedArrayElementType>
+class JS_PUBLIC_API TypedArray : public TypedArray_base {
+ protected:
+ explicit TypedArray(JSObject* unwrapped) : TypedArray_base(unwrapped) {}
+
+ public:
+ using DataType = detail::ExternalTypeOf_t<TypedArrayElementType>;
+
+ static constexpr JS::Scalar::Type Scalar = TypedArrayElementType;
+
+ // This cannot be a static data member because on Windows,
+ // __declspec(dllexport) causes the class to be instantiated immediately,
+ // leading to errors when later explicit specializations of inline member
+ // functions are encountered ("error: explicit specialization of 'ClassPtr'
+ // after instantiation"). And those inlines need to be defined outside of the
+ // class due to order dependencies. This is the only way I could get it to
+ // work on both Windows and POSIX.
+ static const JSClass* clasp() {
+ return &TypedArray_base::classes[static_cast<int>(TypedArrayElementType)];
+ }
+
+ static TypedArray create(JSContext* cx, size_t nelements);
+ static TypedArray fromArray(JSContext* cx, HandleObject other);
+ static TypedArray fromBuffer(JSContext* cx, HandleObject arrayBuffer,
+ size_t byteOffset, int64_t length);
+
+ // Return an interface wrapper around `obj`, or around nullptr if `obj` is not
+ // an unwrapped typed array of the correct type.
+ static TypedArray fromObject(JSObject* unwrapped) {
+ if (unwrapped && GetClass(unwrapped) == clasp()) {
+ return TypedArray(unwrapped);
+ }
+ return TypedArray(nullptr);
+ }
+
+ static TypedArray unwrap(JSObject* maybeWrapped) {
+ if (!maybeWrapped) {
+ return TypedArray(nullptr);
+ }
+ TypedArray view = fromObject(maybeWrapped);
+ if (view) {
+ return view;
+ }
+ return fromObject(js::CheckedUnwrapStatic(maybeWrapped));
+ }
+
+ // Return a pointer to the start of the data referenced by a typed array. The
+ // data is still owned by the typed array, and should not be modified on
+ // another thread. Furthermore, the pointer can become invalid on GC (if the
+ // data is small and fits inside the array's GC header), so callers must take
+ // care not to hold on across anything that could GC.
+ //
+ // |obj| must have passed a JS_Is*Array test, or somehow be known that it
+ // would pass such a test: it is a typed array or a wrapper of a typed array,
+ // and the unwrapping will succeed.
+ //
+ // |*isSharedMemory| will be set to true if the typed array maps a
+ // SharedArrayBuffer, otherwise to false.
+ //
+ DataType* getLengthAndData(size_t* length, bool* isSharedMemory,
+ const JS::AutoRequireNoGC& nogc);
+
+ DataType* getData(bool* isSharedMemory, const JS::AutoRequireNoGC& nogc) {
+ size_t length;
+ return getLengthAndData(&length, isSharedMemory, nogc);
+ }
+};
+
+ArrayBufferOrView ArrayBufferOrView::fromObject(JSObject* unwrapped) {
+ if (ArrayBuffer::fromObject(unwrapped) ||
+ ArrayBufferView::fromObject(unwrapped)) {
+ return ArrayBufferOrView(unwrapped);
+ }
+ return ArrayBufferOrView(nullptr);
+}
+
+ArrayBufferView ArrayBufferView::fromObject(JSObject* unwrapped) {
+ if (TypedArray_base::fromObject(unwrapped) ||
+ DataView::fromObject(unwrapped)) {
+ return ArrayBufferView(unwrapped);
+ }
+ return ArrayBufferView(nullptr);
+}
+
+} /* namespace JS */
+
+/*
+ * JS_Get(type)ArrayData(JSObject* obj,
+ * bool* isSharedMemory,
+ * const JS::AutoRequireNoGC&)
+ *
+ * js::Get(type)ArrayLengthAndData(JSObject* obj,
+ * size_t* length,
+ * bool* isSharedMemory,
+ * const JS::AutoRequireNoGC&)
+ *
+ * Return a pointer to the start of the data referenced by a typed array. The
+ * data is still owned by the typed array, and should not be modified on
+ * another thread. Furthermore, the pointer can become invalid on GC (if the
+ * data is small and fits inside the array's GC header), so callers must take
+ * care not to hold on across anything that could GC.
+ *
+ * |obj| must have passed a JS_Is*Array test, or somehow be known that it would
+ * pass such a test: it is a typed array or a wrapper of a typed array, and the
+ * unwrapping will succeed.
+ *
+ * |*isSharedMemory| will be set to true if the typed array maps a
+ * SharedArrayBuffer, otherwise to false.
+ */
+
+#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(ExternalType, NativeType, Name) \
+ extern JS_PUBLIC_API ExternalType* JS_Get##Name##ArrayData( \
+ JSObject* maybeWrapped, bool* isSharedMemory, \
+ const JS::AutoRequireNoGC&); \
+ \
+ namespace js { \
+ inline void Get##Name##ArrayLengthAndData(JSObject* unwrapped, \
+ size_t* length, \
+ bool* isSharedMemory, \
+ ExternalType** data) { \
+ MOZ_ASSERT(JS::GetClass(unwrapped) == \
+ JS::TypedArray<JS::Scalar::Name>::clasp()); \
+ const JS::Value& lenSlot = \
+ JS::GetReservedSlot(unwrapped, detail::TypedArrayLengthSlot); \
+ *length = size_t(lenSlot.toPrivate()); \
+ *isSharedMemory = JS_GetTypedArraySharedness(unwrapped); \
+ *data = JS::GetMaybePtrFromReservedSlot<ExternalType>( \
+ unwrapped, detail::TypedArrayDataSlot); \
+ } \
+ \
+ JS_PUBLIC_API JSObject* Unwrap##Name##Array(JSObject* maybeWrapped); \
+ } /* namespace js */
+
+JS_FOR_EACH_TYPED_ARRAY(JS_DEFINE_DATA_AND_LENGTH_ACCESSOR)
+#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR
+
+namespace JS {
+
+#define IMPL_TYPED_ARRAY_CLASS(ExternalType, NativeType, Name) \
+ template <> \
+ inline JS::TypedArray<JS::Scalar::Name> \
+ JS::TypedArray<JS::Scalar::Name>::create(JSContext* cx, size_t nelements) { \
+ return fromObject(JS_New##Name##Array(cx, nelements)); \
+ }; \
+ \
+ template <> \
+ inline JS::TypedArray<JS::Scalar::Name> \
+ JS::TypedArray<JS::Scalar::Name>::fromArray(JSContext* cx, \
+ HandleObject other) { \
+ return fromObject(JS_New##Name##ArrayFromArray(cx, other)); \
+ }; \
+ \
+ template <> \
+ inline JS::TypedArray<JS::Scalar::Name> \
+ JS::TypedArray<JS::Scalar::Name>::fromBuffer( \
+ JSContext* cx, HandleObject arrayBuffer, size_t byteOffset, \
+ int64_t length) { \
+ return fromObject( \
+ JS_New##Name##ArrayWithBuffer(cx, arrayBuffer, byteOffset, length)); \
+ };
+
+JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS)
+#undef IMPL_TYPED_ARRAY_CLASS
+
+// Create simple names like Int8Array, Float32Array, etc.
+#define JS_DECLARE_CLASS_ALIAS(ExternalType, NativeType, Name) \
+ using Name##Array = TypedArray<js::Scalar::Name>;
+JS_FOR_EACH_TYPED_ARRAY(JS_DECLARE_CLASS_ALIAS)
+#undef JS_DECLARE_CLASS_ALIAS
+
+} // namespace JS
+
+namespace js {
+
+template <typename T>
+using EnableIfABOVType =
+ std::enable_if_t<std::is_base_of_v<JS::ArrayBufferOrView, T>>;
+
+template <typename T, typename Wrapper>
+class WrappedPtrOperations<T, Wrapper, EnableIfABOVType<T>> {
+ auto get() const { return static_cast<const Wrapper*>(this)->get(); }
+
+ public:
+ explicit operator bool() const { return bool(get()); }
+ JSObject* asObject() const { return get().asObject(); }
+ bool isDetached() const { return get().isDetached(); }
+ bool isSharedMemory() const { return get().isSharedMemory(); }
+
+ typename T::DataType* getLengthAndData(size_t* length, bool* isSharedMemory,
+ const JS::AutoRequireNoGC& nogc) {
+ return get().getLengthAndData(length, isSharedMemory, nogc);
+ }
+
+ typename T::DataType* getData(bool* isSharedMemory,
+ const JS::AutoRequireNoGC& nogc) {
+ return get().getData(isSharedMemory, nogc);
+ }
+};
+
+// Allow usage within Heap<T>.
+template <typename T>
+struct IsHeapConstructibleType<T, EnableIfABOVType<T>> : public std::true_type {
+};
+
+template <typename T>
+struct BarrierMethods<T, EnableIfABOVType<T>> {
+ static gc::Cell* asGCThingOrNull(T view) {
+ return reinterpret_cast<gc::Cell*>(view.asObjectUnbarriered());
+ }
+ static void postWriteBarrier(T* viewp, T prev, T next) {
+ BarrierMethods<JSObject*>::postWriteBarrier(viewp->addressOfObject(),
+ prev.asObjectUnbarriered(),
+ next.asObjectUnbarriered());
+ }
+ static void exposeToJS(T view) { view.exposeToActiveJS(); }
+ static void readBarrier(T view) {
+ JSObject* obj = view.asObjectUnbarriered();
+ if (obj) {
+ js::gc::IncrementalReadBarrier(JS::GCCellPtr(obj));
+ }
+ }
+};
+
+} // namespace js
+
+namespace JS {
+template <typename T>
+struct SafelyInitialized<T, js::EnableIfABOVType<T>> {
+ static T create() { return T::fromObject(nullptr); }
+};
+} // namespace JS
+
+/*
+ * JS_Is(type)Array(JSObject* maybeWrapped)
+ *
+ * Test for specific typed array types.
+ */
+
+#define DECLARE_IS_ARRAY_TEST(_1, _2, Name) \
+ inline JS_PUBLIC_API bool JS_Is##Name##Array(JSObject* maybeWrapped) { \
+ return JS::TypedArray<js::Scalar::Name>::unwrap(maybeWrapped).asObject(); \
+ }
+JS_FOR_EACH_TYPED_ARRAY(DECLARE_IS_ARRAY_TEST)
+#undef DECLARE_IS_ARRAY_TEST
+
+#endif // js_experimental_TypedData_h