Line data Source code
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 file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef mozilla_dom_TypedArray_h
8 : #define mozilla_dom_TypedArray_h
9 :
10 : #include "jsapi.h"
11 : #include "jsfriendapi.h"
12 : #include "js/RootingAPI.h"
13 : #include "js/TracingAPI.h"
14 : #include "mozilla/Attributes.h"
15 : #include "mozilla/Move.h"
16 : #include "mozilla/dom/BindingDeclarations.h"
17 : #include "nsWrapperCache.h"
18 :
19 : namespace mozilla {
20 : namespace dom {
21 :
22 : /*
23 : * Class that just handles the JSObject storage and tracing for typed arrays
24 : */
25 : struct TypedArrayObjectStorage : AllTypedArraysBase {
26 : protected:
27 : JSObject* mTypedObj;
28 : JSObject* mWrappedObj;
29 :
30 0 : TypedArrayObjectStorage()
31 0 : : mTypedObj(nullptr),
32 0 : mWrappedObj(nullptr)
33 : {
34 0 : }
35 :
36 : TypedArrayObjectStorage(TypedArrayObjectStorage&& aOther)
37 : : mTypedObj(aOther.mTypedObj),
38 : mWrappedObj(aOther.mWrappedObj)
39 : {
40 : aOther.mTypedObj = nullptr;
41 : aOther.mWrappedObj = nullptr;
42 : }
43 :
44 : public:
45 0 : inline void TraceSelf(JSTracer* trc)
46 : {
47 0 : JS::UnsafeTraceRoot(trc, &mTypedObj, "TypedArray.mTypedObj");
48 0 : JS::UnsafeTraceRoot(trc, &mWrappedObj, "TypedArray.mWrappedObj");
49 0 : }
50 :
51 : private:
52 : TypedArrayObjectStorage(const TypedArrayObjectStorage&) = delete;
53 : };
54 :
55 : /*
56 : * Various typed array classes for argument conversion. We have a base class
57 : * that has a way of initializing a TypedArray from an existing typed array, and
58 : * a subclass of the base class that supports creation of a relevant typed array
59 : * or array buffer object.
60 : */
61 : template<typename T,
62 : JSObject* UnwrapArray(JSObject*),
63 : void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**)>
64 : struct TypedArray_base : public TypedArrayObjectStorage {
65 : typedef T element_type;
66 :
67 0 : TypedArray_base()
68 : : mData(nullptr),
69 : mLength(0),
70 : mShared(false),
71 0 : mComputed(false)
72 : {
73 0 : }
74 :
75 : TypedArray_base(TypedArray_base&& aOther)
76 : : TypedArrayObjectStorage(Move(aOther)),
77 : mData(aOther.mData),
78 : mLength(aOther.mLength),
79 : mShared(aOther.mShared),
80 : mComputed(aOther.mComputed)
81 : {
82 : aOther.mData = nullptr;
83 : aOther.mLength = 0;
84 : aOther.mShared = false;
85 : aOther.mComputed = false;
86 : }
87 :
88 : private:
89 : mutable T* mData;
90 : mutable uint32_t mLength;
91 : mutable bool mShared;
92 : mutable bool mComputed;
93 :
94 : public:
95 0 : inline bool Init(JSObject* obj)
96 : {
97 0 : MOZ_ASSERT(!inited());
98 0 : mTypedObj = mWrappedObj = UnwrapArray(obj);
99 0 : return inited();
100 : }
101 :
102 0 : inline bool inited() const {
103 0 : return !!mTypedObj;
104 : }
105 :
106 : // About shared memory:
107 : //
108 : // Any DOM TypedArray as well as any DOM ArrayBufferView can map the
109 : // memory of either a JS ArrayBuffer or a JS SharedArrayBuffer. If
110 : // the TypedArray maps a SharedArrayBuffer the Length() and Data()
111 : // accessors on the DOM view will return zero and nullptr; to get
112 : // the actual length and data, call the LengthAllowShared() and
113 : // DataAllowShared() accessors instead.
114 : //
115 : // Two methods are available for determining if a DOM view maps
116 : // shared memory. The IsShared() method is cheap and can be called
117 : // if the view has been computed; the JS_GetTypedArraySharedness()
118 : // method is slightly more expensive and can be called on the Obj()
119 : // value if the view may not have been computed and if the value is
120 : // known to represent a JS TypedArray.
121 : //
122 : // (Just use JS_IsSharedArrayBuffer() to test if any object is of
123 : // that type.)
124 : //
125 : // Code that elects to allow views that map shared memory to be used
126 : // -- ie, code that "opts in to shared memory" -- should generally
127 : // not access the raw data buffer with standard C++ mechanisms as
128 : // that creates the possibility of C++ data races, which is
129 : // undefined behavior. The JS engine will eventually export (bug
130 : // 1225033) a suite of methods that avoid undefined behavior.
131 : //
132 : // Callers of Obj() that do not opt in to shared memory can produce
133 : // better diagnostics by checking whether the JSObject in fact maps
134 : // shared memory and throwing an error if it does. However, it is
135 : // safe to use the value of Obj() without such checks.
136 : //
137 : // The DOM TypedArray abstraction prevents the underlying buffer object
138 : // from being accessed directly, but JS_GetArrayBufferViewBuffer(Obj())
139 : // will obtain the buffer object. Code that calls that function must
140 : // not assume the returned buffer is an ArrayBuffer. That is guarded
141 : // against by an out parameter on that call that communicates the
142 : // sharedness of the buffer.
143 : //
144 : // Finally, note that the buffer memory of a SharedArrayBuffer is
145 : // not detachable.
146 :
147 0 : inline bool IsShared() const {
148 0 : MOZ_ASSERT(mComputed);
149 0 : return mShared;
150 : }
151 :
152 0 : inline T *Data() const {
153 0 : MOZ_ASSERT(mComputed);
154 0 : if (mShared)
155 0 : return nullptr;
156 0 : return mData;
157 : }
158 :
159 0 : inline T *DataAllowShared() const {
160 0 : MOZ_ASSERT(mComputed);
161 0 : return mData;
162 : }
163 :
164 0 : inline uint32_t Length() const {
165 0 : MOZ_ASSERT(mComputed);
166 0 : if (mShared)
167 0 : return 0;
168 0 : return mLength;
169 : }
170 :
171 0 : inline uint32_t LengthAllowShared() const {
172 0 : MOZ_ASSERT(mComputed);
173 0 : return mLength;
174 : }
175 :
176 0 : inline JSObject *Obj() const {
177 0 : MOZ_ASSERT(inited());
178 0 : return mWrappedObj;
179 : }
180 :
181 0 : inline bool WrapIntoNewCompartment(JSContext* cx)
182 : {
183 0 : return JS_WrapObject(cx,
184 0 : JS::MutableHandle<JSObject*>::fromMarkedLocation(&mWrappedObj));
185 : }
186 :
187 0 : inline void ComputeLengthAndData() const
188 : {
189 0 : MOZ_ASSERT(inited());
190 0 : MOZ_ASSERT(!mComputed);
191 0 : GetLengthAndDataAndSharedness(mTypedObj, &mLength, &mShared, &mData);
192 0 : mComputed = true;
193 0 : }
194 :
195 : private:
196 : TypedArray_base(const TypedArray_base&) = delete;
197 : };
198 :
199 : template<typename T,
200 : JSObject* UnwrapArray(JSObject*),
201 : T* GetData(JSObject*, bool* isShared, const JS::AutoCheckCannotGC&),
202 : void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**),
203 : JSObject* CreateNew(JSContext*, uint32_t)>
204 : struct TypedArray
205 : : public TypedArray_base<T, UnwrapArray, GetLengthAndDataAndSharedness>
206 : {
207 : private:
208 : typedef TypedArray_base<T, UnwrapArray, GetLengthAndDataAndSharedness> Base;
209 :
210 : public:
211 0 : TypedArray()
212 0 : : Base()
213 0 : {}
214 :
215 : TypedArray(TypedArray&& aOther)
216 : : Base(Move(aOther))
217 : {
218 : }
219 :
220 : static inline JSObject*
221 0 : Create(JSContext* cx, nsWrapperCache* creator, uint32_t length,
222 : const T* data = nullptr) {
223 0 : JS::Rooted<JSObject*> creatorWrapper(cx);
224 0 : Maybe<JSAutoCompartment> ac;
225 0 : if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) {
226 0 : ac.emplace(cx, creatorWrapper);
227 : }
228 :
229 0 : return CreateCommon(cx, length, data);
230 : }
231 :
232 : static inline JSObject*
233 0 : Create(JSContext* cx, uint32_t length, const T* data = nullptr) {
234 0 : return CreateCommon(cx, length, data);
235 : }
236 :
237 : private:
238 : static inline JSObject*
239 0 : CreateCommon(JSContext* cx, uint32_t length, const T* data) {
240 0 : JSObject* obj = CreateNew(cx, length);
241 0 : if (!obj) {
242 0 : return nullptr;
243 : }
244 0 : if (data) {
245 0 : JS::AutoCheckCannotGC nogc;
246 : bool isShared;
247 0 : T* buf = static_cast<T*>(GetData(obj, &isShared, nogc));
248 : // Data will not be shared, until a construction protocol exists
249 : // for constructing shared data.
250 0 : MOZ_ASSERT(!isShared);
251 0 : memcpy(buf, data, length*sizeof(T));
252 : }
253 0 : return obj;
254 : }
255 :
256 : TypedArray(const TypedArray&) = delete;
257 : };
258 :
259 : template<JSObject* UnwrapArray(JSObject*),
260 : void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*,
261 : uint8_t**),
262 : js::Scalar::Type GetViewType(JSObject*)>
263 : struct ArrayBufferView_base
264 : : public TypedArray_base<uint8_t, UnwrapArray, GetLengthAndDataAndSharedness>
265 : {
266 : private:
267 : typedef TypedArray_base<uint8_t, UnwrapArray, GetLengthAndDataAndSharedness>
268 : Base;
269 :
270 : public:
271 0 : ArrayBufferView_base()
272 0 : : Base()
273 : {
274 0 : }
275 :
276 : ArrayBufferView_base(ArrayBufferView_base&& aOther)
277 : : Base(Move(aOther)),
278 : mType(aOther.mType)
279 : {
280 : aOther.mType = js::Scalar::MaxTypedArrayViewType;
281 : }
282 :
283 : private:
284 : js::Scalar::Type mType;
285 :
286 : public:
287 0 : inline bool Init(JSObject* obj)
288 : {
289 0 : if (!Base::Init(obj)) {
290 0 : return false;
291 : }
292 :
293 0 : mType = GetViewType(this->Obj());
294 0 : return true;
295 : }
296 :
297 0 : inline js::Scalar::Type Type() const
298 : {
299 0 : MOZ_ASSERT(this->inited());
300 0 : return mType;
301 : }
302 : };
303 :
304 : typedef TypedArray<int8_t, js::UnwrapInt8Array, JS_GetInt8ArrayData,
305 : js::GetInt8ArrayLengthAndData, JS_NewInt8Array>
306 : Int8Array;
307 : typedef TypedArray<uint8_t, js::UnwrapUint8Array, JS_GetUint8ArrayData,
308 : js::GetUint8ArrayLengthAndData, JS_NewUint8Array>
309 : Uint8Array;
310 : typedef TypedArray<uint8_t, js::UnwrapUint8ClampedArray, JS_GetUint8ClampedArrayData,
311 : js::GetUint8ClampedArrayLengthAndData, JS_NewUint8ClampedArray>
312 : Uint8ClampedArray;
313 : typedef TypedArray<int16_t, js::UnwrapInt16Array, JS_GetInt16ArrayData,
314 : js::GetInt16ArrayLengthAndData, JS_NewInt16Array>
315 : Int16Array;
316 : typedef TypedArray<uint16_t, js::UnwrapUint16Array, JS_GetUint16ArrayData,
317 : js::GetUint16ArrayLengthAndData, JS_NewUint16Array>
318 : Uint16Array;
319 : typedef TypedArray<int32_t, js::UnwrapInt32Array, JS_GetInt32ArrayData,
320 : js::GetInt32ArrayLengthAndData, JS_NewInt32Array>
321 : Int32Array;
322 : typedef TypedArray<uint32_t, js::UnwrapUint32Array, JS_GetUint32ArrayData,
323 : js::GetUint32ArrayLengthAndData, JS_NewUint32Array>
324 : Uint32Array;
325 : typedef TypedArray<float, js::UnwrapFloat32Array, JS_GetFloat32ArrayData,
326 : js::GetFloat32ArrayLengthAndData, JS_NewFloat32Array>
327 : Float32Array;
328 : typedef TypedArray<double, js::UnwrapFloat64Array, JS_GetFloat64ArrayData,
329 : js::GetFloat64ArrayLengthAndData, JS_NewFloat64Array>
330 : Float64Array;
331 : typedef ArrayBufferView_base<js::UnwrapArrayBufferView,
332 : js::GetArrayBufferViewLengthAndData,
333 : JS_GetArrayBufferViewType>
334 : ArrayBufferView;
335 : typedef TypedArray<uint8_t, js::UnwrapArrayBuffer, JS_GetArrayBufferData,
336 : js::GetArrayBufferLengthAndData, JS_NewArrayBuffer>
337 : ArrayBuffer;
338 :
339 : typedef TypedArray<uint8_t, js::UnwrapSharedArrayBuffer, JS_GetSharedArrayBufferData,
340 : js::GetSharedArrayBufferLengthAndData, JS_NewSharedArrayBuffer>
341 : SharedArrayBuffer;
342 :
343 : // A class for converting an nsTArray to a TypedArray
344 : // Note: A TypedArrayCreator must not outlive the nsTArray it was created from.
345 : // So this is best used to pass from things that understand nsTArray to
346 : // things that understand TypedArray, as with ToJSValue.
347 : template<typename TypedArrayType>
348 : class TypedArrayCreator
349 : {
350 : typedef nsTArray<typename TypedArrayType::element_type> ArrayType;
351 :
352 : public:
353 0 : explicit TypedArrayCreator(const ArrayType& aArray)
354 0 : : mArray(aArray)
355 0 : {}
356 :
357 0 : JSObject* Create(JSContext* aCx) const
358 : {
359 0 : return TypedArrayType::Create(aCx, mArray.Length(), mArray.Elements());
360 : }
361 :
362 : private:
363 : const ArrayType& mArray;
364 : };
365 :
366 : // A class for rooting an existing TypedArray struct
367 : template<typename ArrayType>
368 0 : class MOZ_RAII TypedArrayRooter : private JS::CustomAutoRooter
369 : {
370 : public:
371 : template <typename CX>
372 0 : TypedArrayRooter(const CX& cx,
373 : ArrayType* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
374 0 : JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
375 0 : mArray(aArray)
376 : {
377 0 : }
378 :
379 0 : virtual void trace(JSTracer* trc) override
380 : {
381 0 : mArray->TraceSelf(trc);
382 0 : }
383 :
384 : private:
385 : TypedArrayObjectStorage* const mArray;
386 : };
387 :
388 : // And a specialization for dealing with nullable typed arrays
389 : template<typename Inner> struct Nullable;
390 : template<typename ArrayType>
391 0 : class MOZ_RAII TypedArrayRooter<Nullable<ArrayType> > :
392 : private JS::CustomAutoRooter
393 : {
394 : public:
395 : template <typename CX>
396 0 : TypedArrayRooter(const CX& cx,
397 : Nullable<ArrayType>* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
398 0 : JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
399 0 : mArray(aArray)
400 : {
401 0 : }
402 :
403 0 : virtual void trace(JSTracer* trc) override
404 : {
405 0 : if (!mArray->IsNull()) {
406 0 : mArray->Value().TraceSelf(trc);
407 : }
408 0 : }
409 :
410 : private:
411 : Nullable<ArrayType>* const mArray;
412 : };
413 :
414 : // Class for easily setting up a rooted typed array object on the stack
415 : template<typename ArrayType>
416 0 : class MOZ_RAII RootedTypedArray final : public ArrayType,
417 : private TypedArrayRooter<ArrayType>
418 : {
419 : public:
420 : template <typename CX>
421 0 : explicit RootedTypedArray(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
422 : ArrayType(),
423 : TypedArrayRooter<ArrayType>(cx, this
424 0 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
425 : {
426 0 : }
427 :
428 : template <typename CX>
429 : RootedTypedArray(const CX& cx, JSObject* obj MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
430 : ArrayType(obj),
431 : TypedArrayRooter<ArrayType>(cx, this
432 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
433 : {
434 : }
435 : };
436 :
437 : } // namespace dom
438 : } // namespace mozilla
439 :
440 : #endif /* mozilla_dom_TypedArray_h */
|