|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ |
|
2 /* vim: set ts=2 sw=2 et tw=79: */ |
|
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* mObj; |
|
28 |
|
29 TypedArrayObjectStorage(JSObject *obj) : mObj(obj) |
|
30 { |
|
31 } |
|
32 |
|
33 explicit TypedArrayObjectStorage(TypedArrayObjectStorage&& aOther) |
|
34 : mObj(aOther.mObj) |
|
35 { |
|
36 aOther.mObj = nullptr; |
|
37 } |
|
38 |
|
39 public: |
|
40 inline void TraceSelf(JSTracer* trc) |
|
41 { |
|
42 if (mObj) { |
|
43 JS_CallObjectTracer(trc, &mObj, "TypedArray.mObj"); |
|
44 } |
|
45 } |
|
46 |
|
47 private: |
|
48 TypedArrayObjectStorage(const TypedArrayObjectStorage&) MOZ_DELETE; |
|
49 }; |
|
50 |
|
51 /* |
|
52 * Various typed array classes for argument conversion. We have a base class |
|
53 * that has a way of initializing a TypedArray from an existing typed array, and |
|
54 * a subclass of the base class that supports creation of a relevant typed array |
|
55 * or array buffer object. |
|
56 */ |
|
57 template<typename T, |
|
58 JSObject* UnwrapArray(JSObject*), |
|
59 void GetLengthAndData(JSObject*, uint32_t*, T**)> |
|
60 struct TypedArray_base : public TypedArrayObjectStorage { |
|
61 typedef T element_type; |
|
62 |
|
63 TypedArray_base(JSObject* obj) |
|
64 : TypedArrayObjectStorage(obj), |
|
65 mData(nullptr), |
|
66 mLength(0), |
|
67 mComputed(false) |
|
68 { |
|
69 MOZ_ASSERT(obj != nullptr); |
|
70 } |
|
71 |
|
72 TypedArray_base() |
|
73 : TypedArrayObjectStorage(nullptr), |
|
74 mData(nullptr), |
|
75 mLength(0), |
|
76 mComputed(false) |
|
77 { |
|
78 } |
|
79 |
|
80 explicit TypedArray_base(TypedArray_base&& aOther) |
|
81 : TypedArrayObjectStorage(Move(aOther)), |
|
82 mData(aOther.mData), |
|
83 mLength(aOther.mLength) |
|
84 { |
|
85 aOther.mData = nullptr; |
|
86 aOther.mLength = 0; |
|
87 } |
|
88 |
|
89 private: |
|
90 mutable T* mData; |
|
91 mutable uint32_t mLength; |
|
92 mutable bool mComputed; |
|
93 |
|
94 public: |
|
95 inline bool Init(JSObject* obj) |
|
96 { |
|
97 MOZ_ASSERT(!inited()); |
|
98 DoInit(obj); |
|
99 return inited(); |
|
100 } |
|
101 |
|
102 inline bool inited() const { |
|
103 return !!mObj; |
|
104 } |
|
105 |
|
106 inline T *Data() const { |
|
107 MOZ_ASSERT(mComputed); |
|
108 return mData; |
|
109 } |
|
110 |
|
111 inline uint32_t Length() const { |
|
112 MOZ_ASSERT(mComputed); |
|
113 return mLength; |
|
114 } |
|
115 |
|
116 inline JSObject *Obj() const { |
|
117 MOZ_ASSERT(inited()); |
|
118 return mObj; |
|
119 } |
|
120 |
|
121 inline bool WrapIntoNewCompartment(JSContext* cx) |
|
122 { |
|
123 return JS_WrapObject(cx, |
|
124 JS::MutableHandle<JSObject*>::fromMarkedLocation(&mObj)); |
|
125 } |
|
126 |
|
127 inline void ComputeLengthAndData() const |
|
128 { |
|
129 MOZ_ASSERT(inited()); |
|
130 MOZ_ASSERT(!mComputed); |
|
131 GetLengthAndData(mObj, &mLength, &mData); |
|
132 mComputed = true; |
|
133 } |
|
134 |
|
135 protected: |
|
136 inline void DoInit(JSObject* obj) |
|
137 { |
|
138 mObj = UnwrapArray(obj); |
|
139 } |
|
140 |
|
141 inline void ComputeData() const { |
|
142 MOZ_ASSERT(inited()); |
|
143 if (!mComputed) { |
|
144 GetLengthAndData(mObj, &mLength, &mData); |
|
145 mComputed = true; |
|
146 } |
|
147 } |
|
148 |
|
149 private: |
|
150 TypedArray_base(const TypedArray_base&) MOZ_DELETE; |
|
151 }; |
|
152 |
|
153 |
|
154 template<typename T, |
|
155 JSObject* UnwrapArray(JSObject*), |
|
156 T* GetData(JSObject*), |
|
157 void GetLengthAndData(JSObject*, uint32_t*, T**), |
|
158 JSObject* CreateNew(JSContext*, uint32_t)> |
|
159 struct TypedArray : public TypedArray_base<T, UnwrapArray, GetLengthAndData> { |
|
160 TypedArray(JSObject* obj) : |
|
161 TypedArray_base<T, UnwrapArray, GetLengthAndData>(obj) |
|
162 {} |
|
163 |
|
164 TypedArray() : |
|
165 TypedArray_base<T, UnwrapArray, GetLengthAndData>() |
|
166 {} |
|
167 |
|
168 explicit TypedArray(TypedArray&& aOther) |
|
169 : TypedArray_base<T, UnwrapArray, GetLengthAndData>(Move(aOther)) |
|
170 { |
|
171 } |
|
172 |
|
173 static inline JSObject* |
|
174 Create(JSContext* cx, nsWrapperCache* creator, uint32_t length, |
|
175 const T* data = nullptr) { |
|
176 JS::Rooted<JSObject*> creatorWrapper(cx); |
|
177 Maybe<JSAutoCompartment> ac; |
|
178 if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) { |
|
179 ac.construct(cx, creatorWrapper); |
|
180 } |
|
181 |
|
182 return CreateCommon(cx, length, data); |
|
183 } |
|
184 |
|
185 static inline JSObject* |
|
186 Create(JSContext* cx, uint32_t length, const T* data = nullptr) { |
|
187 return CreateCommon(cx, length, data); |
|
188 } |
|
189 |
|
190 private: |
|
191 static inline JSObject* |
|
192 CreateCommon(JSContext* cx, uint32_t length, const T* data) { |
|
193 JSObject* obj = CreateNew(cx, length); |
|
194 if (!obj) { |
|
195 return nullptr; |
|
196 } |
|
197 if (data) { |
|
198 T* buf = static_cast<T*>(GetData(obj)); |
|
199 memcpy(buf, data, length*sizeof(T)); |
|
200 } |
|
201 return obj; |
|
202 } |
|
203 |
|
204 TypedArray(const TypedArray&) MOZ_DELETE; |
|
205 }; |
|
206 |
|
207 typedef TypedArray<int8_t, js::UnwrapInt8Array, JS_GetInt8ArrayData, |
|
208 js::GetInt8ArrayLengthAndData, JS_NewInt8Array> |
|
209 Int8Array; |
|
210 typedef TypedArray<uint8_t, js::UnwrapUint8Array, JS_GetUint8ArrayData, |
|
211 js::GetUint8ArrayLengthAndData, JS_NewUint8Array> |
|
212 Uint8Array; |
|
213 typedef TypedArray<uint8_t, js::UnwrapUint8ClampedArray, JS_GetUint8ClampedArrayData, |
|
214 js::GetUint8ClampedArrayLengthAndData, JS_NewUint8ClampedArray> |
|
215 Uint8ClampedArray; |
|
216 typedef TypedArray<int16_t, js::UnwrapInt16Array, JS_GetInt16ArrayData, |
|
217 js::GetInt16ArrayLengthAndData, JS_NewInt16Array> |
|
218 Int16Array; |
|
219 typedef TypedArray<uint16_t, js::UnwrapUint16Array, JS_GetUint16ArrayData, |
|
220 js::GetUint16ArrayLengthAndData, JS_NewUint16Array> |
|
221 Uint16Array; |
|
222 typedef TypedArray<int32_t, js::UnwrapInt32Array, JS_GetInt32ArrayData, |
|
223 js::GetInt32ArrayLengthAndData, JS_NewInt32Array> |
|
224 Int32Array; |
|
225 typedef TypedArray<uint32_t, js::UnwrapUint32Array, JS_GetUint32ArrayData, |
|
226 js::GetUint32ArrayLengthAndData, JS_NewUint32Array> |
|
227 Uint32Array; |
|
228 typedef TypedArray<float, js::UnwrapFloat32Array, JS_GetFloat32ArrayData, |
|
229 js::GetFloat32ArrayLengthAndData, JS_NewFloat32Array> |
|
230 Float32Array; |
|
231 typedef TypedArray<double, js::UnwrapFloat64Array, JS_GetFloat64ArrayData, |
|
232 js::GetFloat64ArrayLengthAndData, JS_NewFloat64Array> |
|
233 Float64Array; |
|
234 typedef TypedArray_base<uint8_t, js::UnwrapArrayBufferView, js::GetArrayBufferViewLengthAndData> |
|
235 ArrayBufferView; |
|
236 typedef TypedArray<uint8_t, js::UnwrapArrayBuffer, JS_GetArrayBufferData, |
|
237 js::GetArrayBufferLengthAndData, JS_NewArrayBuffer> |
|
238 ArrayBuffer; |
|
239 |
|
240 // A class for converting an nsTArray to a TypedArray |
|
241 // Note: A TypedArrayCreator must not outlive the nsTArray it was created from. |
|
242 // So this is best used to pass from things that understand nsTArray to |
|
243 // things that understand TypedArray, as with Promise::ArgumentToJSValue. |
|
244 template<typename TypedArrayType> |
|
245 class TypedArrayCreator |
|
246 { |
|
247 typedef nsTArray<typename TypedArrayType::element_type> ArrayType; |
|
248 |
|
249 public: |
|
250 TypedArrayCreator(const ArrayType& aArray) |
|
251 : mArray(aArray) |
|
252 {} |
|
253 |
|
254 JSObject* Create(JSContext* aCx) const |
|
255 { |
|
256 return TypedArrayType::Create(aCx, mArray.Length(), mArray.Elements()); |
|
257 } |
|
258 |
|
259 private: |
|
260 const ArrayType& mArray; |
|
261 }; |
|
262 |
|
263 // A class for rooting an existing TypedArray struct |
|
264 template<typename ArrayType> |
|
265 class MOZ_STACK_CLASS TypedArrayRooter : private JS::CustomAutoRooter |
|
266 { |
|
267 public: |
|
268 TypedArrayRooter(JSContext* cx, |
|
269 ArrayType* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : |
|
270 JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), |
|
271 mArray(aArray) |
|
272 { |
|
273 } |
|
274 |
|
275 virtual void trace(JSTracer* trc) MOZ_OVERRIDE |
|
276 { |
|
277 mArray->TraceSelf(trc); |
|
278 } |
|
279 |
|
280 private: |
|
281 TypedArrayObjectStorage* const mArray; |
|
282 }; |
|
283 |
|
284 // And a specialization for dealing with nullable typed arrays |
|
285 template<typename Inner> struct Nullable; |
|
286 template<typename ArrayType> |
|
287 class MOZ_STACK_CLASS TypedArrayRooter<Nullable<ArrayType> > : |
|
288 private JS::CustomAutoRooter |
|
289 { |
|
290 public: |
|
291 TypedArrayRooter(JSContext* cx, |
|
292 Nullable<ArrayType>* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : |
|
293 JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), |
|
294 mArray(aArray) |
|
295 { |
|
296 } |
|
297 |
|
298 virtual void trace(JSTracer* trc) MOZ_OVERRIDE |
|
299 { |
|
300 if (!mArray->IsNull()) { |
|
301 mArray->Value().TraceSelf(trc); |
|
302 } |
|
303 } |
|
304 |
|
305 private: |
|
306 Nullable<ArrayType>* const mArray; |
|
307 }; |
|
308 |
|
309 // Class for easily setting up a rooted typed array object on the stack |
|
310 template<typename ArrayType> |
|
311 class MOZ_STACK_CLASS RootedTypedArray : public ArrayType, |
|
312 private TypedArrayRooter<ArrayType> |
|
313 { |
|
314 public: |
|
315 RootedTypedArray(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : |
|
316 ArrayType(), |
|
317 TypedArrayRooter<ArrayType>(cx, |
|
318 MOZ_THIS_IN_INITIALIZER_LIST() |
|
319 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) |
|
320 { |
|
321 } |
|
322 |
|
323 RootedTypedArray(JSContext* cx, JSObject* obj MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : |
|
324 ArrayType(obj), |
|
325 TypedArrayRooter<ArrayType>(cx, |
|
326 MOZ_THIS_IN_INITIALIZER_LIST() |
|
327 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) |
|
328 { |
|
329 } |
|
330 }; |
|
331 |
|
332 } // namespace dom |
|
333 } // namespace mozilla |
|
334 |
|
335 #endif /* mozilla_dom_TypedArray_h */ |