|
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_TypedArrayObject_h |
|
8 #define vm_TypedArrayObject_h |
|
9 |
|
10 #include "jsobj.h" |
|
11 |
|
12 #include "builtin/TypedObject.h" |
|
13 #include "gc/Barrier.h" |
|
14 #include "js/Class.h" |
|
15 #include "vm/ArrayBufferObject.h" |
|
16 |
|
17 typedef struct JSProperty JSProperty; |
|
18 |
|
19 namespace js { |
|
20 |
|
21 /* |
|
22 * TypedArrayObject |
|
23 * |
|
24 * The non-templated base class for the specific typed implementations. |
|
25 * This class holds all the member variables that are used by |
|
26 * the subclasses. |
|
27 */ |
|
28 |
|
29 class TypedArrayObject : public ArrayBufferViewObject |
|
30 { |
|
31 protected: |
|
32 // Typed array properties stored in slots, beyond those shared by all |
|
33 // ArrayBufferViews. |
|
34 static const size_t LENGTH_SLOT = JS_TYPEDOBJ_SLOT_LENGTH; |
|
35 static const size_t TYPE_SLOT = JS_TYPEDOBJ_SLOT_TYPE_DESCR; |
|
36 static const size_t RESERVED_SLOTS = JS_TYPEDOBJ_SLOTS; |
|
37 static const size_t DATA_SLOT = JS_TYPEDOBJ_SLOT_DATA; |
|
38 |
|
39 static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT, |
|
40 "bad inlined constant in jsfriendapi.h"); |
|
41 |
|
42 public: |
|
43 static const Class classes[ScalarTypeDescr::TYPE_MAX]; |
|
44 static const Class protoClasses[ScalarTypeDescr::TYPE_MAX]; |
|
45 |
|
46 static const size_t FIXED_DATA_START = DATA_SLOT + 1; |
|
47 |
|
48 // For typed arrays which can store their data inline, the array buffer |
|
49 // object is created lazily. |
|
50 static const uint32_t INLINE_BUFFER_LIMIT = |
|
51 (JSObject::MAX_FIXED_SLOTS - FIXED_DATA_START) * sizeof(Value); |
|
52 |
|
53 static gc::AllocKind |
|
54 AllocKindForLazyBuffer(size_t nbytes) |
|
55 { |
|
56 JS_ASSERT(nbytes <= INLINE_BUFFER_LIMIT); |
|
57 /* For GGC we need at least one slot in which to store a forwarding pointer. */ |
|
58 size_t dataSlots = Max(size_t(1), AlignBytes(nbytes, sizeof(Value)) / sizeof(Value)); |
|
59 JS_ASSERT(nbytes <= dataSlots * sizeof(Value)); |
|
60 return gc::GetGCObjectKind(FIXED_DATA_START + dataSlots); |
|
61 } |
|
62 |
|
63 static Value bufferValue(TypedArrayObject *tarr) { |
|
64 return tarr->getFixedSlot(BUFFER_SLOT); |
|
65 } |
|
66 static Value byteOffsetValue(TypedArrayObject *tarr) { |
|
67 return tarr->getFixedSlot(BYTEOFFSET_SLOT); |
|
68 } |
|
69 static Value byteLengthValue(TypedArrayObject *tarr) { |
|
70 return tarr->getFixedSlot(BYTELENGTH_SLOT); |
|
71 } |
|
72 static Value lengthValue(TypedArrayObject *tarr) { |
|
73 return tarr->getFixedSlot(LENGTH_SLOT); |
|
74 } |
|
75 |
|
76 static bool |
|
77 ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray); |
|
78 |
|
79 ArrayBufferObject *sharedBuffer() const; |
|
80 ArrayBufferObject *buffer() const { |
|
81 JSObject *obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull(); |
|
82 if (!obj) |
|
83 return nullptr; |
|
84 if (obj->is<ArrayBufferObject>()) |
|
85 return &obj->as<ArrayBufferObject>(); |
|
86 return sharedBuffer(); |
|
87 } |
|
88 uint32_t byteOffset() const { |
|
89 return byteOffsetValue(const_cast<TypedArrayObject*>(this)).toInt32(); |
|
90 } |
|
91 uint32_t byteLength() const { |
|
92 return byteLengthValue(const_cast<TypedArrayObject*>(this)).toInt32(); |
|
93 } |
|
94 uint32_t length() const { |
|
95 return lengthValue(const_cast<TypedArrayObject*>(this)).toInt32(); |
|
96 } |
|
97 |
|
98 uint32_t type() const { |
|
99 return getFixedSlot(TYPE_SLOT).toInt32(); |
|
100 } |
|
101 void *viewData() const { |
|
102 // Keep synced with js::Get<Type>ArrayLengthAndData in jsfriendapi.h! |
|
103 return static_cast<void*>(getPrivate(DATA_SLOT)); |
|
104 } |
|
105 |
|
106 Value getElement(uint32_t index); |
|
107 static void setElement(TypedArrayObject &obj, uint32_t index, double d); |
|
108 |
|
109 void neuter(void *newData); |
|
110 |
|
111 static uint32_t slotWidth(int atype) { |
|
112 switch (atype) { |
|
113 case ScalarTypeDescr::TYPE_INT8: |
|
114 case ScalarTypeDescr::TYPE_UINT8: |
|
115 case ScalarTypeDescr::TYPE_UINT8_CLAMPED: |
|
116 return 1; |
|
117 case ScalarTypeDescr::TYPE_INT16: |
|
118 case ScalarTypeDescr::TYPE_UINT16: |
|
119 return 2; |
|
120 case ScalarTypeDescr::TYPE_INT32: |
|
121 case ScalarTypeDescr::TYPE_UINT32: |
|
122 case ScalarTypeDescr::TYPE_FLOAT32: |
|
123 return 4; |
|
124 case ScalarTypeDescr::TYPE_FLOAT64: |
|
125 return 8; |
|
126 default: |
|
127 MOZ_ASSUME_UNREACHABLE("invalid typed array type"); |
|
128 } |
|
129 } |
|
130 |
|
131 int slotWidth() { |
|
132 return slotWidth(type()); |
|
133 } |
|
134 |
|
135 /* |
|
136 * Byte length above which created typed arrays and data views will have |
|
137 * singleton types regardless of the context in which they are created. |
|
138 */ |
|
139 static const uint32_t SINGLETON_TYPE_BYTE_LENGTH = 1024 * 1024 * 10; |
|
140 |
|
141 static int lengthOffset(); |
|
142 static int dataOffset(); |
|
143 }; |
|
144 |
|
145 inline bool |
|
146 IsTypedArrayClass(const Class *clasp) |
|
147 { |
|
148 return &TypedArrayObject::classes[0] <= clasp && |
|
149 clasp < &TypedArrayObject::classes[ScalarTypeDescr::TYPE_MAX]; |
|
150 } |
|
151 |
|
152 inline bool |
|
153 IsTypedArrayProtoClass(const Class *clasp) |
|
154 { |
|
155 return &TypedArrayObject::protoClasses[0] <= clasp && |
|
156 clasp < &TypedArrayObject::protoClasses[ScalarTypeDescr::TYPE_MAX]; |
|
157 } |
|
158 |
|
159 bool |
|
160 IsTypedArrayConstructor(HandleValue v, uint32_t type); |
|
161 |
|
162 bool |
|
163 IsTypedArrayBuffer(HandleValue v); |
|
164 |
|
165 ArrayBufferObject & |
|
166 AsTypedArrayBuffer(HandleValue v); |
|
167 |
|
168 // Return value is whether the string is some integer. If the string is an |
|
169 // integer which is not representable as a uint64_t, the return value is true |
|
170 // and the resulting index is UINT64_MAX. |
|
171 bool |
|
172 StringIsTypedArrayIndex(JSLinearString *str, uint64_t *indexp); |
|
173 |
|
174 inline bool |
|
175 IsTypedArrayIndex(jsid id, uint64_t *indexp) |
|
176 { |
|
177 if (JSID_IS_INT(id)) { |
|
178 int32_t i = JSID_TO_INT(id); |
|
179 JS_ASSERT(i >= 0); |
|
180 *indexp = (double)i; |
|
181 return true; |
|
182 } |
|
183 |
|
184 if (MOZ_UNLIKELY(!JSID_IS_STRING(id))) |
|
185 return false; |
|
186 |
|
187 JSAtom *atom = JSID_TO_ATOM(id); |
|
188 |
|
189 jschar c = atom->chars()[0]; |
|
190 if (!JS7_ISDEC(c) && c != '-') |
|
191 return false; |
|
192 |
|
193 return StringIsTypedArrayIndex(atom, indexp); |
|
194 } |
|
195 |
|
196 static inline unsigned |
|
197 TypedArrayShift(ArrayBufferView::ViewType viewType) |
|
198 { |
|
199 switch (viewType) { |
|
200 case ArrayBufferView::TYPE_INT8: |
|
201 case ArrayBufferView::TYPE_UINT8: |
|
202 case ArrayBufferView::TYPE_UINT8_CLAMPED: |
|
203 return 0; |
|
204 case ArrayBufferView::TYPE_INT16: |
|
205 case ArrayBufferView::TYPE_UINT16: |
|
206 return 1; |
|
207 case ArrayBufferView::TYPE_INT32: |
|
208 case ArrayBufferView::TYPE_UINT32: |
|
209 case ArrayBufferView::TYPE_FLOAT32: |
|
210 return 2; |
|
211 case ArrayBufferView::TYPE_FLOAT64: |
|
212 return 3; |
|
213 default:; |
|
214 } |
|
215 MOZ_ASSUME_UNREACHABLE("Unexpected array type"); |
|
216 } |
|
217 |
|
218 class DataViewObject : public ArrayBufferViewObject |
|
219 { |
|
220 static const size_t RESERVED_SLOTS = JS_DATAVIEW_SLOTS; |
|
221 static const size_t DATA_SLOT = JS_TYPEDOBJ_SLOT_DATA; |
|
222 |
|
223 private: |
|
224 static const Class protoClass; |
|
225 |
|
226 static bool is(HandleValue v) { |
|
227 return v.isObject() && v.toObject().hasClass(&class_); |
|
228 } |
|
229 |
|
230 template <typename NativeType> |
|
231 static uint8_t * |
|
232 getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, uint32_t offset); |
|
233 |
|
234 template<Value ValueGetter(DataViewObject *view)> |
|
235 static bool |
|
236 getterImpl(JSContext *cx, CallArgs args); |
|
237 |
|
238 template<Value ValueGetter(DataViewObject *view)> |
|
239 static bool |
|
240 getter(JSContext *cx, unsigned argc, Value *vp); |
|
241 |
|
242 template<Value ValueGetter(DataViewObject *view)> |
|
243 static bool |
|
244 defineGetter(JSContext *cx, PropertyName *name, HandleObject proto); |
|
245 |
|
246 public: |
|
247 static const Class class_; |
|
248 |
|
249 static Value byteOffsetValue(DataViewObject *view) { |
|
250 Value v = view->getReservedSlot(BYTEOFFSET_SLOT); |
|
251 JS_ASSERT(v.toInt32() >= 0); |
|
252 return v; |
|
253 } |
|
254 |
|
255 static Value byteLengthValue(DataViewObject *view) { |
|
256 Value v = view->getReservedSlot(BYTELENGTH_SLOT); |
|
257 JS_ASSERT(v.toInt32() >= 0); |
|
258 return v; |
|
259 } |
|
260 |
|
261 static Value bufferValue(DataViewObject *view) { |
|
262 return view->getReservedSlot(BUFFER_SLOT); |
|
263 } |
|
264 |
|
265 uint32_t byteOffset() const { |
|
266 return byteOffsetValue(const_cast<DataViewObject*>(this)).toInt32(); |
|
267 } |
|
268 |
|
269 uint32_t byteLength() const { |
|
270 return byteLengthValue(const_cast<DataViewObject*>(this)).toInt32(); |
|
271 } |
|
272 |
|
273 ArrayBufferObject &arrayBuffer() const { |
|
274 return bufferValue(const_cast<DataViewObject*>(this)).toObject().as<ArrayBufferObject>(); |
|
275 } |
|
276 |
|
277 void *dataPointer() const { |
|
278 return getPrivate(); |
|
279 } |
|
280 |
|
281 static bool class_constructor(JSContext *cx, unsigned argc, Value *vp); |
|
282 static bool constructWithProto(JSContext *cx, unsigned argc, Value *vp); |
|
283 static bool construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, |
|
284 HandleObject proto); |
|
285 |
|
286 static inline DataViewObject * |
|
287 create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, |
|
288 Handle<ArrayBufferObject*> arrayBuffer, JSObject *proto); |
|
289 |
|
290 static bool getInt8Impl(JSContext *cx, CallArgs args); |
|
291 static bool fun_getInt8(JSContext *cx, unsigned argc, Value *vp); |
|
292 |
|
293 static bool getUint8Impl(JSContext *cx, CallArgs args); |
|
294 static bool fun_getUint8(JSContext *cx, unsigned argc, Value *vp); |
|
295 |
|
296 static bool getInt16Impl(JSContext *cx, CallArgs args); |
|
297 static bool fun_getInt16(JSContext *cx, unsigned argc, Value *vp); |
|
298 |
|
299 static bool getUint16Impl(JSContext *cx, CallArgs args); |
|
300 static bool fun_getUint16(JSContext *cx, unsigned argc, Value *vp); |
|
301 |
|
302 static bool getInt32Impl(JSContext *cx, CallArgs args); |
|
303 static bool fun_getInt32(JSContext *cx, unsigned argc, Value *vp); |
|
304 |
|
305 static bool getUint32Impl(JSContext *cx, CallArgs args); |
|
306 static bool fun_getUint32(JSContext *cx, unsigned argc, Value *vp); |
|
307 |
|
308 static bool getFloat32Impl(JSContext *cx, CallArgs args); |
|
309 static bool fun_getFloat32(JSContext *cx, unsigned argc, Value *vp); |
|
310 |
|
311 static bool getFloat64Impl(JSContext *cx, CallArgs args); |
|
312 static bool fun_getFloat64(JSContext *cx, unsigned argc, Value *vp); |
|
313 |
|
314 static bool setInt8Impl(JSContext *cx, CallArgs args); |
|
315 static bool fun_setInt8(JSContext *cx, unsigned argc, Value *vp); |
|
316 |
|
317 static bool setUint8Impl(JSContext *cx, CallArgs args); |
|
318 static bool fun_setUint8(JSContext *cx, unsigned argc, Value *vp); |
|
319 |
|
320 static bool setInt16Impl(JSContext *cx, CallArgs args); |
|
321 static bool fun_setInt16(JSContext *cx, unsigned argc, Value *vp); |
|
322 |
|
323 static bool setUint16Impl(JSContext *cx, CallArgs args); |
|
324 static bool fun_setUint16(JSContext *cx, unsigned argc, Value *vp); |
|
325 |
|
326 static bool setInt32Impl(JSContext *cx, CallArgs args); |
|
327 static bool fun_setInt32(JSContext *cx, unsigned argc, Value *vp); |
|
328 |
|
329 static bool setUint32Impl(JSContext *cx, CallArgs args); |
|
330 static bool fun_setUint32(JSContext *cx, unsigned argc, Value *vp); |
|
331 |
|
332 static bool setFloat32Impl(JSContext *cx, CallArgs args); |
|
333 static bool fun_setFloat32(JSContext *cx, unsigned argc, Value *vp); |
|
334 |
|
335 static bool setFloat64Impl(JSContext *cx, CallArgs args); |
|
336 static bool fun_setFloat64(JSContext *cx, unsigned argc, Value *vp); |
|
337 |
|
338 static bool initClass(JSContext *cx); |
|
339 static void neuter(JSObject *view); |
|
340 template<typename NativeType> |
|
341 static bool read(JSContext *cx, Handle<DataViewObject*> obj, |
|
342 CallArgs &args, NativeType *val, const char *method); |
|
343 template<typename NativeType> |
|
344 static bool write(JSContext *cx, Handle<DataViewObject*> obj, |
|
345 CallArgs &args, const char *method); |
|
346 |
|
347 void neuter(void *newData); |
|
348 |
|
349 private: |
|
350 static const JSFunctionSpec jsfuncs[]; |
|
351 }; |
|
352 |
|
353 static inline int32_t |
|
354 ClampIntForUint8Array(int32_t x) |
|
355 { |
|
356 if (x < 0) |
|
357 return 0; |
|
358 if (x > 255) |
|
359 return 255; |
|
360 return x; |
|
361 } |
|
362 |
|
363 } // namespace js |
|
364 |
|
365 template <> |
|
366 inline bool |
|
367 JSObject::is<js::TypedArrayObject>() const |
|
368 { |
|
369 return js::IsTypedArrayClass(getClass()); |
|
370 } |
|
371 |
|
372 template <> |
|
373 inline bool |
|
374 JSObject::is<js::ArrayBufferViewObject>() const |
|
375 { |
|
376 return is<js::DataViewObject>() || is<js::TypedArrayObject>() || |
|
377 IsTypedObjectClass(getClass()); |
|
378 } |
|
379 |
|
380 #endif /* vm_TypedArrayObject_h */ |