diff options
Diffstat (limited to 'dom/bindings/TypedArray.h')
-rw-r--r-- | dom/bindings/TypedArray.h | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h new file mode 100644 index 000000000..a86abcd9d --- /dev/null +++ b/dom/bindings/TypedArray.h @@ -0,0 +1,441 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_TypedArray_h +#define mozilla_dom_TypedArray_h + +#include "jsapi.h" +#include "jsfriendapi.h" +#include "js/RootingAPI.h" +#include "js/TracingAPI.h" +#include "mozilla/Attributes.h" +#include "mozilla/Move.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "nsWrapperCache.h" + +namespace mozilla { +namespace dom { + +/* + * Class that just handles the JSObject storage and tracing for typed arrays + */ +struct TypedArrayObjectStorage : AllTypedArraysBase { +protected: + JSObject* mTypedObj; + JSObject* mWrappedObj; + + TypedArrayObjectStorage() + : mTypedObj(nullptr), + mWrappedObj(nullptr) + { + } + + TypedArrayObjectStorage(TypedArrayObjectStorage&& aOther) + : mTypedObj(aOther.mTypedObj), + mWrappedObj(aOther.mWrappedObj) + { + aOther.mTypedObj = nullptr; + aOther.mWrappedObj = nullptr; + } + +public: + inline void TraceSelf(JSTracer* trc) + { + JS::UnsafeTraceRoot(trc, &mTypedObj, "TypedArray.mTypedObj"); + JS::UnsafeTraceRoot(trc, &mWrappedObj, "TypedArray.mWrappedObj"); + } + +private: + TypedArrayObjectStorage(const TypedArrayObjectStorage&) = delete; +}; + +/* + * Various typed array classes for argument conversion. We have a base class + * that has a way of initializing a TypedArray from an existing typed array, and + * a subclass of the base class that supports creation of a relevant typed array + * or array buffer object. + */ +template<typename T, + JSObject* UnwrapArray(JSObject*), + void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**)> +struct TypedArray_base : public TypedArrayObjectStorage { + typedef T element_type; + + TypedArray_base() + : mData(nullptr), + mLength(0), + mShared(false), + mComputed(false) + { + } + + TypedArray_base(TypedArray_base&& aOther) + : TypedArrayObjectStorage(Move(aOther)), + mData(aOther.mData), + mLength(aOther.mLength), + mShared(aOther.mShared), + mComputed(aOther.mComputed) + { + aOther.mData = nullptr; + aOther.mLength = 0; + aOther.mShared = false; + aOther.mComputed = false; + } + +private: + mutable T* mData; + mutable uint32_t mLength; + mutable bool mShared; + mutable bool mComputed; + +public: + inline bool Init(JSObject* obj) + { + MOZ_ASSERT(!inited()); + mTypedObj = mWrappedObj = UnwrapArray(obj); + return inited(); + } + + inline bool inited() const { + return !!mTypedObj; + } + + // About shared memory: + // + // Any DOM TypedArray as well as any DOM ArrayBufferView that does + // not represent a JS DataView can map the memory of either a JS + // ArrayBuffer or a JS SharedArrayBuffer. (DataView cannot view + // shared memory.) If the TypedArray maps a SharedArrayBuffer the + // Length() and Data() accessors on the DOM view will return zero + // and nullptr; to get the actual length and data, call the + // LengthAllowShared() and DataAllowShared() accessors instead. + // + // Two methods are available for determining if a DOM view maps + // shared memory. The IsShared() method is cheap and can be called + // if the view has been computed; the JS_GetTypedArraySharedness() + // method is slightly more expensive and can be called on the Obj() + // value if the view may not have been computed and if the value is + // known to represent a JS TypedArray. + // + // (Just use JS_IsSharedArrayBuffer() to test if any object is of + // that type.) + // + // Code that elects to allow views that map shared memory to be used + // -- ie, code that "opts in to shared memory" -- should generally + // not access the raw data buffer with standard C++ mechanisms as + // that creates the possibility of C++ data races, which is + // undefined behavior. The JS engine will eventually export (bug + // 1225033) a suite of methods that avoid undefined behavior. + // + // Callers of Obj() that do not opt in to shared memory can produce + // better diagnostics by checking whether the JSObject in fact maps + // shared memory and throwing an error if it does. However, it is + // safe to use the value of Obj() without such checks. + // + // The DOM TypedArray abstraction prevents the underlying buffer object + // from being accessed directly, but JS_GetArrayBufferViewBuffer(Obj()) + // will obtain the buffer object. Code that calls that function must + // not assume the returned buffer is an ArrayBuffer. That is guarded + // against by an out parameter on that call that communicates the + // sharedness of the buffer. + // + // Finally, note that the buffer memory of a SharedArrayBuffer is + // not detachable. + + inline bool IsShared() const { + MOZ_ASSERT(mComputed); + return mShared; + } + + inline T *Data() const { + MOZ_ASSERT(mComputed); + if (mShared) + return nullptr; + return mData; + } + + inline T *DataAllowShared() const { + MOZ_ASSERT(mComputed); + return mData; + } + + inline uint32_t Length() const { + MOZ_ASSERT(mComputed); + if (mShared) + return 0; + return mLength; + } + + inline uint32_t LengthAllowShared() const { + MOZ_ASSERT(mComputed); + return mLength; + } + + inline JSObject *Obj() const { + MOZ_ASSERT(inited()); + return mWrappedObj; + } + + inline bool WrapIntoNewCompartment(JSContext* cx) + { + return JS_WrapObject(cx, + JS::MutableHandle<JSObject*>::fromMarkedLocation(&mWrappedObj)); + } + + inline void ComputeLengthAndData() const + { + MOZ_ASSERT(inited()); + MOZ_ASSERT(!mComputed); + GetLengthAndDataAndSharedness(mTypedObj, &mLength, &mShared, &mData); + mComputed = true; + } + +private: + TypedArray_base(const TypedArray_base&) = delete; +}; + +template<typename T, + JSObject* UnwrapArray(JSObject*), + T* GetData(JSObject*, bool* isShared, const JS::AutoCheckCannotGC&), + void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**), + JSObject* CreateNew(JSContext*, uint32_t)> +struct TypedArray + : public TypedArray_base<T, UnwrapArray, GetLengthAndDataAndSharedness> +{ +private: + typedef TypedArray_base<T, UnwrapArray, GetLengthAndDataAndSharedness> Base; + +public: + TypedArray() + : Base() + {} + + TypedArray(TypedArray&& aOther) + : Base(Move(aOther)) + { + } + + static inline JSObject* + Create(JSContext* cx, nsWrapperCache* creator, uint32_t length, + const T* data = nullptr) { + JS::Rooted<JSObject*> creatorWrapper(cx); + Maybe<JSAutoCompartment> ac; + if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) { + ac.emplace(cx, creatorWrapper); + } + + return CreateCommon(cx, length, data); + } + + static inline JSObject* + Create(JSContext* cx, uint32_t length, const T* data = nullptr) { + return CreateCommon(cx, length, data); + } + +private: + static inline JSObject* + CreateCommon(JSContext* cx, uint32_t length, const T* data) { + JSObject* obj = CreateNew(cx, length); + if (!obj) { + return nullptr; + } + if (data) { + JS::AutoCheckCannotGC nogc; + bool isShared; + T* buf = static_cast<T*>(GetData(obj, &isShared, nogc)); + // Data will not be shared, until a construction protocol exists + // for constructing shared data. + MOZ_ASSERT(!isShared); + memcpy(buf, data, length*sizeof(T)); + } + return obj; + } + + TypedArray(const TypedArray&) = delete; +}; + +template<JSObject* UnwrapArray(JSObject*), + void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, + uint8_t**), + js::Scalar::Type GetViewType(JSObject*)> +struct ArrayBufferView_base + : public TypedArray_base<uint8_t, UnwrapArray, GetLengthAndDataAndSharedness> +{ +private: + typedef TypedArray_base<uint8_t, UnwrapArray, GetLengthAndDataAndSharedness> + Base; + +public: + ArrayBufferView_base() + : Base() + { + } + + ArrayBufferView_base(ArrayBufferView_base&& aOther) + : Base(Move(aOther)), + mType(aOther.mType) + { + aOther.mType = js::Scalar::MaxTypedArrayViewType; + } + +private: + js::Scalar::Type mType; + +public: + inline bool Init(JSObject* obj) + { + if (!Base::Init(obj)) { + return false; + } + + mType = GetViewType(this->Obj()); + return true; + } + + inline js::Scalar::Type Type() const + { + MOZ_ASSERT(this->inited()); + return mType; + } +}; + +typedef TypedArray<int8_t, js::UnwrapInt8Array, JS_GetInt8ArrayData, + js::GetInt8ArrayLengthAndData, JS_NewInt8Array> + Int8Array; +typedef TypedArray<uint8_t, js::UnwrapUint8Array, JS_GetUint8ArrayData, + js::GetUint8ArrayLengthAndData, JS_NewUint8Array> + Uint8Array; +typedef TypedArray<uint8_t, js::UnwrapUint8ClampedArray, JS_GetUint8ClampedArrayData, + js::GetUint8ClampedArrayLengthAndData, JS_NewUint8ClampedArray> + Uint8ClampedArray; +typedef TypedArray<int16_t, js::UnwrapInt16Array, JS_GetInt16ArrayData, + js::GetInt16ArrayLengthAndData, JS_NewInt16Array> + Int16Array; +typedef TypedArray<uint16_t, js::UnwrapUint16Array, JS_GetUint16ArrayData, + js::GetUint16ArrayLengthAndData, JS_NewUint16Array> + Uint16Array; +typedef TypedArray<int32_t, js::UnwrapInt32Array, JS_GetInt32ArrayData, + js::GetInt32ArrayLengthAndData, JS_NewInt32Array> + Int32Array; +typedef TypedArray<uint32_t, js::UnwrapUint32Array, JS_GetUint32ArrayData, + js::GetUint32ArrayLengthAndData, JS_NewUint32Array> + Uint32Array; +typedef TypedArray<float, js::UnwrapFloat32Array, JS_GetFloat32ArrayData, + js::GetFloat32ArrayLengthAndData, JS_NewFloat32Array> + Float32Array; +typedef TypedArray<double, js::UnwrapFloat64Array, JS_GetFloat64ArrayData, + js::GetFloat64ArrayLengthAndData, JS_NewFloat64Array> + Float64Array; +typedef ArrayBufferView_base<js::UnwrapArrayBufferView, + js::GetArrayBufferViewLengthAndData, + JS_GetArrayBufferViewType> + ArrayBufferView; +typedef TypedArray<uint8_t, js::UnwrapArrayBuffer, JS_GetArrayBufferData, + js::GetArrayBufferLengthAndData, JS_NewArrayBuffer> + ArrayBuffer; + +typedef TypedArray<uint8_t, js::UnwrapSharedArrayBuffer, JS_GetSharedArrayBufferData, + js::GetSharedArrayBufferLengthAndData, JS_NewSharedArrayBuffer> + SharedArrayBuffer; + +// A class for converting an nsTArray to a TypedArray +// Note: A TypedArrayCreator must not outlive the nsTArray it was created from. +// So this is best used to pass from things that understand nsTArray to +// things that understand TypedArray, as with Promise::ArgumentToJSValue. +template<typename TypedArrayType> +class TypedArrayCreator +{ + typedef nsTArray<typename TypedArrayType::element_type> ArrayType; + + public: + explicit TypedArrayCreator(const ArrayType& aArray) + : mArray(aArray) + {} + + JSObject* Create(JSContext* aCx) const + { + return TypedArrayType::Create(aCx, mArray.Length(), mArray.Elements()); + } + + private: + const ArrayType& mArray; +}; + +// A class for rooting an existing TypedArray struct +template<typename ArrayType> +class MOZ_RAII TypedArrayRooter : private JS::CustomAutoRooter +{ +public: + template <typename CX> + TypedArrayRooter(const CX& cx, + ArrayType* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : + JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), + mArray(aArray) + { + } + + virtual void trace(JSTracer* trc) override + { + mArray->TraceSelf(trc); + } + +private: + TypedArrayObjectStorage* const mArray; +}; + +// And a specialization for dealing with nullable typed arrays +template<typename Inner> struct Nullable; +template<typename ArrayType> +class MOZ_RAII TypedArrayRooter<Nullable<ArrayType> > : + private JS::CustomAutoRooter +{ +public: + template <typename CX> + TypedArrayRooter(const CX& cx, + Nullable<ArrayType>* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : + JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), + mArray(aArray) + { + } + + virtual void trace(JSTracer* trc) override + { + if (!mArray->IsNull()) { + mArray->Value().TraceSelf(trc); + } + } + +private: + Nullable<ArrayType>* const mArray; +}; + +// Class for easily setting up a rooted typed array object on the stack +template<typename ArrayType> +class MOZ_RAII RootedTypedArray final : public ArrayType, + private TypedArrayRooter<ArrayType> +{ +public: + template <typename CX> + explicit RootedTypedArray(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : + ArrayType(), + TypedArrayRooter<ArrayType>(cx, this + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) + { + } + + template <typename CX> + RootedTypedArray(const CX& cx, JSObject* obj MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : + ArrayType(obj), + TypedArrayRooter<ArrayType>(cx, this + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) + { + } +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_TypedArray_h */ |