|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
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 #ifndef vm_ArrayBufferObject_h |
|
8 #define vm_ArrayBufferObject_h |
|
9 |
|
10 #include "jsobj.h" |
|
11 |
|
12 #include "builtin/TypedObjectConstants.h" |
|
13 #include "vm/Runtime.h" |
|
14 |
|
15 typedef struct JSProperty JSProperty; |
|
16 |
|
17 namespace js { |
|
18 |
|
19 class ArrayBufferViewObject; |
|
20 |
|
21 // The inheritance hierarchy for the various classes relating to typed arrays |
|
22 // is as follows. |
|
23 // |
|
24 // - JSObject |
|
25 // - ArrayBufferObject |
|
26 // - SharedArrayBufferObject |
|
27 // - ArrayBufferViewObject |
|
28 // - DataViewObject |
|
29 // - TypedArrayObject (declared in vm/TypedArrayObject.h) |
|
30 // - TypedArrayObjectTemplate |
|
31 // - Int8ArrayObject |
|
32 // - Uint8ArrayObject |
|
33 // - ... |
|
34 // - TypedObject (declared in builtin/TypedObject.h) |
|
35 // |
|
36 // Note that |TypedArrayObjectTemplate| is just an implementation |
|
37 // detail that makes implementing its various subclasses easier. |
|
38 |
|
39 typedef Vector<ArrayBufferObject *, 0, SystemAllocPolicy> ArrayBufferVector; |
|
40 |
|
41 /* |
|
42 * ArrayBufferObject |
|
43 * |
|
44 * This class holds the underlying raw buffer that the various |
|
45 * ArrayBufferViewObject subclasses (DataViewObject and the TypedArrays) |
|
46 * access. It can be created explicitly and passed to an ArrayBufferViewObject |
|
47 * subclass, or can be created implicitly by constructing a TypedArrayObject |
|
48 * with a size. |
|
49 */ |
|
50 class ArrayBufferObject : public JSObject |
|
51 { |
|
52 static bool byteLengthGetterImpl(JSContext *cx, CallArgs args); |
|
53 static bool fun_slice_impl(JSContext *cx, CallArgs args); |
|
54 |
|
55 public: |
|
56 static const uint8_t DATA_SLOT = 0; |
|
57 static const uint8_t BYTE_LENGTH_SLOT = 1; |
|
58 static const uint8_t VIEW_LIST_SLOT = 2; |
|
59 static const uint8_t FLAGS_SLOT = 3; |
|
60 |
|
61 static const uint8_t RESERVED_SLOTS = 4; |
|
62 |
|
63 static const size_t ARRAY_BUFFER_ALIGNMENT = 8; |
|
64 |
|
65 static const Class class_; |
|
66 |
|
67 static const Class protoClass; |
|
68 static const JSFunctionSpec jsfuncs[]; |
|
69 static const JSFunctionSpec jsstaticfuncs[]; |
|
70 |
|
71 static bool byteLengthGetter(JSContext *cx, unsigned argc, Value *vp); |
|
72 |
|
73 static bool fun_slice(JSContext *cx, unsigned argc, Value *vp); |
|
74 |
|
75 static bool fun_isView(JSContext *cx, unsigned argc, Value *vp); |
|
76 |
|
77 static bool class_constructor(JSContext *cx, unsigned argc, Value *vp); |
|
78 |
|
79 static ArrayBufferObject *create(JSContext *cx, uint32_t nbytes, void *contents = nullptr, |
|
80 NewObjectKind newKind = GenericObject, bool mapped = false); |
|
81 |
|
82 static JSObject *createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer, |
|
83 uint32_t begin, uint32_t end); |
|
84 |
|
85 static bool createDataViewForThisImpl(JSContext *cx, CallArgs args); |
|
86 static bool createDataViewForThis(JSContext *cx, unsigned argc, Value *vp); |
|
87 |
|
88 template<typename T> |
|
89 static bool createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args); |
|
90 |
|
91 template<typename T> |
|
92 static bool createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp); |
|
93 |
|
94 static void obj_trace(JSTracer *trc, JSObject *obj); |
|
95 |
|
96 static void sweep(JSCompartment *rt); |
|
97 |
|
98 static void resetArrayBufferList(JSCompartment *rt); |
|
99 static bool saveArrayBufferList(JSCompartment *c, ArrayBufferVector &vector); |
|
100 static void restoreArrayBufferLists(ArrayBufferVector &vector); |
|
101 |
|
102 static void *stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer); |
|
103 |
|
104 bool hasStealableContents() const { |
|
105 // Inline elements strictly adhere to the corresponding buffer. |
|
106 if (!ownsData()) |
|
107 return false; |
|
108 |
|
109 // asm.js buffer contents are transferred by copying, just like inline |
|
110 // elements. |
|
111 if (isAsmJSArrayBuffer()) |
|
112 return false; |
|
113 |
|
114 // Neutered contents aren't transferrable because we want a neutered |
|
115 // array's contents to be backed by zeroed memory equal in length to |
|
116 // the original buffer contents. Transferring these contents would |
|
117 // allocate new ones based on the current byteLength, which is 0 for a |
|
118 // neutered array -- not the original byteLength. |
|
119 return !isNeutered(); |
|
120 } |
|
121 |
|
122 static void addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, |
|
123 JS::ObjectsExtraSizes *sizes); |
|
124 |
|
125 void addView(ArrayBufferViewObject *view); |
|
126 |
|
127 void setNewOwnedData(FreeOp* fop, void *newData); |
|
128 void changeContents(JSContext *cx, void *newData); |
|
129 |
|
130 /* |
|
131 * Ensure data is not stored inline in the object. Used when handing back a |
|
132 * GC-safe pointer. |
|
133 */ |
|
134 static bool ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer); |
|
135 |
|
136 bool canNeuter(JSContext *cx); |
|
137 |
|
138 /* Neuter this buffer and all its views. */ |
|
139 static void neuter(JSContext *cx, Handle<ArrayBufferObject*> buffer, void *newData); |
|
140 |
|
141 uint8_t *dataPointer() const; |
|
142 size_t byteLength() const; |
|
143 |
|
144 void releaseData(FreeOp *fop); |
|
145 |
|
146 /* |
|
147 * Check if the arrayBuffer contains any data. This will return false for |
|
148 * ArrayBuffer.prototype and neutered ArrayBuffers. |
|
149 */ |
|
150 bool hasData() const { |
|
151 return getClass() == &class_; |
|
152 } |
|
153 |
|
154 bool isAsmJSArrayBuffer() const { return flags() & ASMJS_BUFFER; } |
|
155 bool isSharedArrayBuffer() const { return flags() & SHARED_BUFFER; } |
|
156 bool isMappedArrayBuffer() const { return flags() & MAPPED_BUFFER; } |
|
157 bool isNeutered() const { return flags() & NEUTERED_BUFFER; } |
|
158 |
|
159 static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer); |
|
160 static bool canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer); |
|
161 |
|
162 static void finalize(FreeOp *fop, JSObject *obj); |
|
163 |
|
164 static void *createMappedContents(int fd, size_t offset, size_t length); |
|
165 |
|
166 static size_t flagsOffset() { |
|
167 return getFixedSlotOffset(FLAGS_SLOT); |
|
168 } |
|
169 |
|
170 static uint32_t neuteredFlag() { return NEUTERED_BUFFER; } |
|
171 |
|
172 protected: |
|
173 enum OwnsState { |
|
174 DoesntOwnData = 0, |
|
175 OwnsData = 1, |
|
176 }; |
|
177 |
|
178 void setDataPointer(void *data, OwnsState ownsState); |
|
179 void setByteLength(size_t length); |
|
180 |
|
181 ArrayBufferViewObject *viewList() const; |
|
182 void setViewList(ArrayBufferViewObject *viewsHead); |
|
183 void setViewListNoBarrier(ArrayBufferViewObject *viewsHead); |
|
184 |
|
185 enum ArrayBufferFlags { |
|
186 // In the gcLiveArrayBuffers list. |
|
187 IN_LIVE_LIST = 0x1, |
|
188 |
|
189 // The dataPointer() is owned by this buffer and should be released |
|
190 // when no longer in use. Releasing the pointer may be done by either |
|
191 // freeing or unmapping it, and how to do this is determined by the |
|
192 // buffer's other flags. |
|
193 OWNS_DATA = 0x2, |
|
194 |
|
195 ASMJS_BUFFER = 0x4, |
|
196 SHARED_BUFFER = 0x8, |
|
197 MAPPED_BUFFER = 0x10, |
|
198 NEUTERED_BUFFER = 0x20 |
|
199 }; |
|
200 |
|
201 uint32_t flags() const; |
|
202 void setFlags(uint32_t flags); |
|
203 |
|
204 bool inLiveList() const { return flags() & IN_LIVE_LIST; } |
|
205 void setInLiveList(bool value) { |
|
206 setFlags(value ? (flags() | IN_LIVE_LIST) : (flags() & ~IN_LIVE_LIST)); |
|
207 } |
|
208 |
|
209 bool ownsData() const { return flags() & OWNS_DATA; } |
|
210 void setOwnsData(OwnsState owns) { |
|
211 setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA)); |
|
212 } |
|
213 |
|
214 void setIsAsmJSArrayBuffer() { setFlags(flags() | ASMJS_BUFFER); } |
|
215 void setIsSharedArrayBuffer() { setFlags(flags() | SHARED_BUFFER); } |
|
216 void setIsMappedArrayBuffer() { setFlags(flags() | MAPPED_BUFFER); } |
|
217 void setIsNeutered() { setFlags(flags() | NEUTERED_BUFFER); } |
|
218 |
|
219 void initialize(size_t byteLength, void *data, OwnsState ownsState) { |
|
220 setByteLength(byteLength); |
|
221 setFlags(0); |
|
222 setViewListNoBarrier(nullptr); |
|
223 setDataPointer(data, ownsState); |
|
224 } |
|
225 |
|
226 void releaseAsmJSArray(FreeOp *fop); |
|
227 void releaseMappedArray(); |
|
228 }; |
|
229 |
|
230 /* |
|
231 * ArrayBufferViewObject |
|
232 * |
|
233 * Common definitions shared by all ArrayBufferViews. |
|
234 */ |
|
235 |
|
236 class ArrayBufferViewObject : public JSObject |
|
237 { |
|
238 protected: |
|
239 /* Offset of view in underlying ArrayBufferObject */ |
|
240 static const size_t BYTEOFFSET_SLOT = JS_TYPEDOBJ_SLOT_BYTEOFFSET; |
|
241 |
|
242 /* Byte length of view */ |
|
243 static const size_t BYTELENGTH_SLOT = JS_TYPEDOBJ_SLOT_BYTELENGTH; |
|
244 |
|
245 /* Underlying ArrayBufferObject */ |
|
246 static const size_t BUFFER_SLOT = JS_TYPEDOBJ_SLOT_OWNER; |
|
247 |
|
248 /* ArrayBufferObjects point to a linked list of views, chained through this slot */ |
|
249 static const size_t NEXT_VIEW_SLOT = JS_TYPEDOBJ_SLOT_NEXT_VIEW; |
|
250 |
|
251 public: |
|
252 static ArrayBufferObject *bufferObject(JSContext *cx, Handle<ArrayBufferViewObject *> obj); |
|
253 |
|
254 ArrayBufferViewObject *nextView() const { |
|
255 return static_cast<ArrayBufferViewObject*>(getFixedSlot(NEXT_VIEW_SLOT).toPrivate()); |
|
256 } |
|
257 |
|
258 inline void setNextView(ArrayBufferViewObject *view); |
|
259 |
|
260 void neuter(void *newData); |
|
261 |
|
262 static void trace(JSTracer *trc, JSObject *obj); |
|
263 |
|
264 uint8_t *dataPointer() { |
|
265 return static_cast<uint8_t *>(getPrivate()); |
|
266 } |
|
267 }; |
|
268 |
|
269 bool |
|
270 ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out); |
|
271 |
|
272 inline void |
|
273 PostBarrierTypedArrayObject(JSObject *obj) |
|
274 { |
|
275 #ifdef JSGC_GENERATIONAL |
|
276 JS_ASSERT(obj); |
|
277 JSRuntime *rt = obj->runtimeFromMainThread(); |
|
278 if (!rt->isHeapBusy() && !IsInsideNursery(rt, obj)) |
|
279 rt->gcStoreBuffer.putWholeCell(obj); |
|
280 #endif |
|
281 } |
|
282 |
|
283 inline void |
|
284 InitArrayBufferViewDataPointer(ArrayBufferViewObject *obj, ArrayBufferObject *buffer, size_t byteOffset) |
|
285 { |
|
286 /* |
|
287 * N.B. The base of the array's data is stored in the object's |
|
288 * private data rather than a slot to avoid alignment restrictions |
|
289 * on private Values. |
|
290 */ |
|
291 MOZ_ASSERT(buffer->dataPointer() != nullptr); |
|
292 obj->initPrivate(buffer->dataPointer() + byteOffset); |
|
293 |
|
294 PostBarrierTypedArrayObject(obj); |
|
295 } |
|
296 |
|
297 /* |
|
298 * Tests for either ArrayBufferObject or SharedArrayBufferObject. |
|
299 * For specific class testing, use e.g., obj->is<ArrayBufferObject>(). |
|
300 */ |
|
301 bool IsArrayBuffer(HandleValue v); |
|
302 bool IsArrayBuffer(HandleObject obj); |
|
303 bool IsArrayBuffer(JSObject *obj); |
|
304 ArrayBufferObject &AsArrayBuffer(HandleObject obj); |
|
305 ArrayBufferObject &AsArrayBuffer(JSObject *obj); |
|
306 |
|
307 inline void |
|
308 ArrayBufferViewObject::setNextView(ArrayBufferViewObject *view) |
|
309 { |
|
310 setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(view)); |
|
311 PostBarrierTypedArrayObject(this); |
|
312 } |
|
313 |
|
314 extern uint32_t JS_FASTCALL |
|
315 ClampDoubleToUint8(const double x); |
|
316 |
|
317 struct uint8_clamped { |
|
318 uint8_t val; |
|
319 |
|
320 uint8_clamped() { } |
|
321 uint8_clamped(const uint8_clamped& other) : val(other.val) { } |
|
322 |
|
323 // invoke our assignment helpers for constructor conversion |
|
324 uint8_clamped(uint8_t x) { *this = x; } |
|
325 uint8_clamped(uint16_t x) { *this = x; } |
|
326 uint8_clamped(uint32_t x) { *this = x; } |
|
327 uint8_clamped(int8_t x) { *this = x; } |
|
328 uint8_clamped(int16_t x) { *this = x; } |
|
329 uint8_clamped(int32_t x) { *this = x; } |
|
330 uint8_clamped(double x) { *this = x; } |
|
331 |
|
332 uint8_clamped& operator=(const uint8_clamped& x) { |
|
333 val = x.val; |
|
334 return *this; |
|
335 } |
|
336 |
|
337 uint8_clamped& operator=(uint8_t x) { |
|
338 val = x; |
|
339 return *this; |
|
340 } |
|
341 |
|
342 uint8_clamped& operator=(uint16_t x) { |
|
343 val = (x > 255) ? 255 : uint8_t(x); |
|
344 return *this; |
|
345 } |
|
346 |
|
347 uint8_clamped& operator=(uint32_t x) { |
|
348 val = (x > 255) ? 255 : uint8_t(x); |
|
349 return *this; |
|
350 } |
|
351 |
|
352 uint8_clamped& operator=(int8_t x) { |
|
353 val = (x >= 0) ? uint8_t(x) : 0; |
|
354 return *this; |
|
355 } |
|
356 |
|
357 uint8_clamped& operator=(int16_t x) { |
|
358 val = (x >= 0) |
|
359 ? ((x < 255) |
|
360 ? uint8_t(x) |
|
361 : 255) |
|
362 : 0; |
|
363 return *this; |
|
364 } |
|
365 |
|
366 uint8_clamped& operator=(int32_t x) { |
|
367 val = (x >= 0) |
|
368 ? ((x < 255) |
|
369 ? uint8_t(x) |
|
370 : 255) |
|
371 : 0; |
|
372 return *this; |
|
373 } |
|
374 |
|
375 uint8_clamped& operator=(const double x) { |
|
376 val = uint8_t(ClampDoubleToUint8(x)); |
|
377 return *this; |
|
378 } |
|
379 |
|
380 operator uint8_t() const { |
|
381 return val; |
|
382 } |
|
383 |
|
384 void staticAsserts() { |
|
385 static_assert(sizeof(uint8_clamped) == 1, |
|
386 "uint8_clamped must be layout-compatible with uint8_t"); |
|
387 } |
|
388 }; |
|
389 |
|
390 /* Note that we can't use std::numeric_limits here due to uint8_clamped. */ |
|
391 template<typename T> inline const bool TypeIsFloatingPoint() { return false; } |
|
392 template<> inline const bool TypeIsFloatingPoint<float>() { return true; } |
|
393 template<> inline const bool TypeIsFloatingPoint<double>() { return true; } |
|
394 |
|
395 template<typename T> inline const bool TypeIsUnsigned() { return false; } |
|
396 template<> inline const bool TypeIsUnsigned<uint8_t>() { return true; } |
|
397 template<> inline const bool TypeIsUnsigned<uint16_t>() { return true; } |
|
398 template<> inline const bool TypeIsUnsigned<uint32_t>() { return true; } |
|
399 |
|
400 } // namespace js |
|
401 |
|
402 #endif // vm_ArrayBufferObject_h |