|
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 #include "vm/TypedArrayObject.h" |
|
8 |
|
9 #include "mozilla/Alignment.h" |
|
10 #include "mozilla/FloatingPoint.h" |
|
11 #include "mozilla/PodOperations.h" |
|
12 |
|
13 #include <string.h> |
|
14 #ifndef XP_WIN |
|
15 # include <sys/mman.h> |
|
16 #endif |
|
17 |
|
18 #include "jsapi.h" |
|
19 #include "jsarray.h" |
|
20 #include "jscntxt.h" |
|
21 #include "jscpucfg.h" |
|
22 #include "jsnum.h" |
|
23 #include "jsobj.h" |
|
24 #include "jstypes.h" |
|
25 #include "jsutil.h" |
|
26 #ifdef XP_WIN |
|
27 # include "jswin.h" |
|
28 #endif |
|
29 #include "jswrapper.h" |
|
30 |
|
31 #include "gc/Barrier.h" |
|
32 #include "gc/Marking.h" |
|
33 #include "jit/AsmJS.h" |
|
34 #include "jit/AsmJSModule.h" |
|
35 #include "vm/ArrayBufferObject.h" |
|
36 #include "vm/GlobalObject.h" |
|
37 #include "vm/Interpreter.h" |
|
38 #include "vm/NumericConversions.h" |
|
39 #include "vm/SharedArrayObject.h" |
|
40 #include "vm/WrapperObject.h" |
|
41 |
|
42 #include "jsatominlines.h" |
|
43 #include "jsinferinlines.h" |
|
44 #include "jsobjinlines.h" |
|
45 |
|
46 #include "vm/Shape-inl.h" |
|
47 |
|
48 using namespace js; |
|
49 using namespace js::gc; |
|
50 using namespace js::types; |
|
51 |
|
52 using mozilla::IsNaN; |
|
53 using mozilla::NegativeInfinity; |
|
54 using mozilla::PodCopy; |
|
55 using mozilla::PositiveInfinity; |
|
56 using JS::CanonicalizeNaN; |
|
57 using JS::GenericNaN; |
|
58 |
|
59 static bool |
|
60 ValueIsLength(const Value &v, uint32_t *len) |
|
61 { |
|
62 if (v.isInt32()) { |
|
63 int32_t i = v.toInt32(); |
|
64 if (i < 0) |
|
65 return false; |
|
66 *len = i; |
|
67 return true; |
|
68 } |
|
69 |
|
70 if (v.isDouble()) { |
|
71 double d = v.toDouble(); |
|
72 if (IsNaN(d)) |
|
73 return false; |
|
74 |
|
75 uint32_t length = uint32_t(d); |
|
76 if (d != double(length)) |
|
77 return false; |
|
78 |
|
79 *len = length; |
|
80 return true; |
|
81 } |
|
82 |
|
83 return false; |
|
84 } |
|
85 |
|
86 /* |
|
87 * TypedArrayObject |
|
88 * |
|
89 * The non-templated base class for the specific typed implementations. |
|
90 * This class holds all the member variables that are used by |
|
91 * the subclasses. |
|
92 */ |
|
93 |
|
94 void |
|
95 TypedArrayObject::neuter(void *newData) |
|
96 { |
|
97 setSlot(LENGTH_SLOT, Int32Value(0)); |
|
98 setSlot(BYTELENGTH_SLOT, Int32Value(0)); |
|
99 setSlot(BYTEOFFSET_SLOT, Int32Value(0)); |
|
100 setPrivate(newData); |
|
101 } |
|
102 |
|
103 ArrayBufferObject * |
|
104 TypedArrayObject::sharedBuffer() const |
|
105 { |
|
106 return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<SharedArrayBufferObject>(); |
|
107 } |
|
108 |
|
109 /* static */ bool |
|
110 TypedArrayObject::ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray) |
|
111 { |
|
112 if (tarray->buffer()) |
|
113 return true; |
|
114 |
|
115 Rooted<ArrayBufferObject *> buffer(cx, ArrayBufferObject::create(cx, tarray->byteLength())); |
|
116 if (!buffer) |
|
117 return false; |
|
118 |
|
119 buffer->addView(tarray); |
|
120 |
|
121 memcpy(buffer->dataPointer(), tarray->viewData(), tarray->byteLength()); |
|
122 InitArrayBufferViewDataPointer(tarray, buffer, 0); |
|
123 |
|
124 tarray->setSlot(BUFFER_SLOT, ObjectValue(*buffer)); |
|
125 return true; |
|
126 } |
|
127 |
|
128 /* static */ int |
|
129 TypedArrayObject::lengthOffset() |
|
130 { |
|
131 return JSObject::getFixedSlotOffset(LENGTH_SLOT); |
|
132 } |
|
133 |
|
134 /* static */ int |
|
135 TypedArrayObject::dataOffset() |
|
136 { |
|
137 return JSObject::getPrivateDataOffset(DATA_SLOT); |
|
138 } |
|
139 |
|
140 /* Helper clamped uint8_t type */ |
|
141 |
|
142 uint32_t JS_FASTCALL |
|
143 js::ClampDoubleToUint8(const double x) |
|
144 { |
|
145 // Not < so that NaN coerces to 0 |
|
146 if (!(x >= 0)) |
|
147 return 0; |
|
148 |
|
149 if (x > 255) |
|
150 return 255; |
|
151 |
|
152 double toTruncate = x + 0.5; |
|
153 uint8_t y = uint8_t(toTruncate); |
|
154 |
|
155 /* |
|
156 * now val is rounded to nearest, ties rounded up. We want |
|
157 * rounded to nearest ties to even, so check whether we had a |
|
158 * tie. |
|
159 */ |
|
160 if (y == toTruncate) { |
|
161 /* |
|
162 * It was a tie (since adding 0.5 gave us the exact integer |
|
163 * we want). Since we rounded up, we either already have an |
|
164 * even number or we have an odd number but the number we |
|
165 * want is one less. So just unconditionally masking out the |
|
166 * ones bit should do the trick to get us the value we |
|
167 * want. |
|
168 */ |
|
169 return y & ~1; |
|
170 } |
|
171 |
|
172 return y; |
|
173 } |
|
174 |
|
175 template<typename NativeType> static inline const int TypeIDOfType(); |
|
176 template<> inline const int TypeIDOfType<int8_t>() { return ScalarTypeDescr::TYPE_INT8; } |
|
177 template<> inline const int TypeIDOfType<uint8_t>() { return ScalarTypeDescr::TYPE_UINT8; } |
|
178 template<> inline const int TypeIDOfType<int16_t>() { return ScalarTypeDescr::TYPE_INT16; } |
|
179 template<> inline const int TypeIDOfType<uint16_t>() { return ScalarTypeDescr::TYPE_UINT16; } |
|
180 template<> inline const int TypeIDOfType<int32_t>() { return ScalarTypeDescr::TYPE_INT32; } |
|
181 template<> inline const int TypeIDOfType<uint32_t>() { return ScalarTypeDescr::TYPE_UINT32; } |
|
182 template<> inline const int TypeIDOfType<float>() { return ScalarTypeDescr::TYPE_FLOAT32; } |
|
183 template<> inline const int TypeIDOfType<double>() { return ScalarTypeDescr::TYPE_FLOAT64; } |
|
184 template<> inline const int TypeIDOfType<uint8_clamped>() { return ScalarTypeDescr::TYPE_UINT8_CLAMPED; } |
|
185 |
|
186 template<typename ElementType> |
|
187 static inline JSObject * |
|
188 NewArray(JSContext *cx, uint32_t nelements); |
|
189 |
|
190 namespace { |
|
191 |
|
192 template<typename NativeType> |
|
193 class TypedArrayObjectTemplate : public TypedArrayObject |
|
194 { |
|
195 public: |
|
196 typedef NativeType ThisType; |
|
197 typedef TypedArrayObjectTemplate<NativeType> ThisTypedArrayObject; |
|
198 static const int ArrayTypeID() { return TypeIDOfType<NativeType>(); } |
|
199 static const bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); } |
|
200 static const bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); } |
|
201 |
|
202 static const size_t BYTES_PER_ELEMENT = sizeof(ThisType); |
|
203 |
|
204 static inline const Class *protoClass() |
|
205 { |
|
206 return &TypedArrayObject::protoClasses[ArrayTypeID()]; |
|
207 } |
|
208 |
|
209 static inline const Class *instanceClass() |
|
210 { |
|
211 return &TypedArrayObject::classes[ArrayTypeID()]; |
|
212 } |
|
213 |
|
214 static bool is(HandleValue v) { |
|
215 return v.isObject() && v.toObject().hasClass(instanceClass()); |
|
216 } |
|
217 |
|
218 static void |
|
219 setIndexValue(TypedArrayObject &tarray, uint32_t index, double d) |
|
220 { |
|
221 // If the array is an integer array, we only handle up to |
|
222 // 32-bit ints from this point on. if we want to handle |
|
223 // 64-bit ints, we'll need some changes. |
|
224 |
|
225 // Assign based on characteristics of the destination type |
|
226 if (ArrayTypeIsFloatingPoint()) { |
|
227 setIndex(tarray, index, NativeType(d)); |
|
228 } else if (ArrayTypeIsUnsigned()) { |
|
229 JS_ASSERT(sizeof(NativeType) <= 4); |
|
230 uint32_t n = ToUint32(d); |
|
231 setIndex(tarray, index, NativeType(n)); |
|
232 } else if (ArrayTypeID() == ScalarTypeDescr::TYPE_UINT8_CLAMPED) { |
|
233 // The uint8_clamped type has a special rounding converter |
|
234 // for doubles. |
|
235 setIndex(tarray, index, NativeType(d)); |
|
236 } else { |
|
237 JS_ASSERT(sizeof(NativeType) <= 4); |
|
238 int32_t n = ToInt32(d); |
|
239 setIndex(tarray, index, NativeType(n)); |
|
240 } |
|
241 } |
|
242 |
|
243 static TypedArrayObject * |
|
244 makeProtoInstance(JSContext *cx, HandleObject proto, AllocKind allocKind) |
|
245 { |
|
246 JS_ASSERT(proto); |
|
247 |
|
248 RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind)); |
|
249 if (!obj) |
|
250 return nullptr; |
|
251 |
|
252 types::TypeObject *type = cx->getNewType(obj->getClass(), proto.get()); |
|
253 if (!type) |
|
254 return nullptr; |
|
255 obj->setType(type); |
|
256 |
|
257 return &obj->as<TypedArrayObject>(); |
|
258 } |
|
259 |
|
260 static TypedArrayObject * |
|
261 makeTypedInstance(JSContext *cx, uint32_t len, AllocKind allocKind) |
|
262 { |
|
263 if (len * sizeof(NativeType) >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) { |
|
264 return &NewBuiltinClassInstance(cx, instanceClass(), allocKind, |
|
265 SingletonObject)->as<TypedArrayObject>(); |
|
266 } |
|
267 |
|
268 jsbytecode *pc; |
|
269 RootedScript script(cx, cx->currentScript(&pc)); |
|
270 NewObjectKind newKind = script |
|
271 ? UseNewTypeForInitializer(script, pc, instanceClass()) |
|
272 : GenericObject; |
|
273 RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind, newKind)); |
|
274 if (!obj) |
|
275 return nullptr; |
|
276 |
|
277 if (script) { |
|
278 if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) |
|
279 return nullptr; |
|
280 } |
|
281 |
|
282 return &obj->as<TypedArrayObject>(); |
|
283 } |
|
284 |
|
285 static JSObject * |
|
286 makeInstance(JSContext *cx, Handle<ArrayBufferObject *> buffer, uint32_t byteOffset, uint32_t len, |
|
287 HandleObject proto) |
|
288 { |
|
289 JS_ASSERT_IF(!buffer, byteOffset == 0); |
|
290 |
|
291 gc::AllocKind allocKind = buffer |
|
292 ? GetGCObjectKind(instanceClass()) |
|
293 : AllocKindForLazyBuffer(len * sizeof(NativeType)); |
|
294 |
|
295 Rooted<TypedArrayObject*> obj(cx); |
|
296 if (proto) |
|
297 obj = makeProtoInstance(cx, proto, allocKind); |
|
298 else |
|
299 obj = makeTypedInstance(cx, len, allocKind); |
|
300 if (!obj) |
|
301 return nullptr; |
|
302 |
|
303 obj->setSlot(TYPE_SLOT, Int32Value(ArrayTypeID())); |
|
304 obj->setSlot(BUFFER_SLOT, ObjectOrNullValue(buffer)); |
|
305 |
|
306 if (buffer) { |
|
307 InitArrayBufferViewDataPointer(obj, buffer, byteOffset); |
|
308 } else { |
|
309 void *data = obj->fixedData(FIXED_DATA_START); |
|
310 obj->initPrivate(data); |
|
311 memset(data, 0, len * sizeof(NativeType)); |
|
312 } |
|
313 |
|
314 obj->setSlot(LENGTH_SLOT, Int32Value(len)); |
|
315 obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset)); |
|
316 obj->setSlot(BYTELENGTH_SLOT, Int32Value(len * sizeof(NativeType))); |
|
317 obj->setSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr)); |
|
318 |
|
319 #ifdef DEBUG |
|
320 if (buffer) { |
|
321 uint32_t arrayByteLength = obj->byteLength(); |
|
322 uint32_t arrayByteOffset = obj->byteOffset(); |
|
323 uint32_t bufferByteLength = buffer->byteLength(); |
|
324 JS_ASSERT_IF(!buffer->isNeutered(), buffer->dataPointer() <= obj->viewData()); |
|
325 JS_ASSERT(bufferByteLength - arrayByteOffset >= arrayByteLength); |
|
326 JS_ASSERT(arrayByteOffset <= bufferByteLength); |
|
327 } |
|
328 |
|
329 // Verify that the private slot is at the expected place |
|
330 JS_ASSERT(obj->numFixedSlots() == DATA_SLOT); |
|
331 #endif |
|
332 |
|
333 if (buffer) |
|
334 buffer->addView(obj); |
|
335 |
|
336 return obj; |
|
337 } |
|
338 |
|
339 static JSObject * |
|
340 makeInstance(JSContext *cx, Handle<ArrayBufferObject *> bufobj, uint32_t byteOffset, uint32_t len) |
|
341 { |
|
342 RootedObject nullproto(cx, nullptr); |
|
343 return makeInstance(cx, bufobj, byteOffset, len, nullproto); |
|
344 } |
|
345 |
|
346 /* |
|
347 * new [Type]Array(length) |
|
348 * new [Type]Array(otherTypedArray) |
|
349 * new [Type]Array(JSArray) |
|
350 * new [Type]Array(ArrayBuffer, [optional] byteOffset, [optional] length) |
|
351 */ |
|
352 static bool |
|
353 class_constructor(JSContext *cx, unsigned argc, Value *vp) |
|
354 { |
|
355 /* N.B. this is a constructor for protoClass, not instanceClass! */ |
|
356 CallArgs args = CallArgsFromVp(argc, vp); |
|
357 JSObject *obj = create(cx, args); |
|
358 if (!obj) |
|
359 return false; |
|
360 args.rval().setObject(*obj); |
|
361 return true; |
|
362 } |
|
363 |
|
364 static JSObject * |
|
365 create(JSContext *cx, const CallArgs& args) |
|
366 { |
|
367 /* () or (number) */ |
|
368 uint32_t len = 0; |
|
369 if (args.length() == 0 || ValueIsLength(args[0], &len)) |
|
370 return fromLength(cx, len); |
|
371 |
|
372 /* (not an object) */ |
|
373 if (!args[0].isObject()) { |
|
374 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
375 return nullptr; |
|
376 } |
|
377 |
|
378 RootedObject dataObj(cx, &args.get(0).toObject()); |
|
379 |
|
380 /* |
|
381 * (typedArray) |
|
382 * (type[] array) |
|
383 * |
|
384 * Otherwise create a new typed array and copy elements 0..len-1 |
|
385 * properties from the object, treating it as some sort of array. |
|
386 * Note that offset and length will be ignored |
|
387 */ |
|
388 if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>() && |
|
389 !UncheckedUnwrap(dataObj)->is<SharedArrayBufferObject>()) |
|
390 { |
|
391 return fromArray(cx, dataObj); |
|
392 } |
|
393 |
|
394 /* (ArrayBuffer, [byteOffset, [length]]) */ |
|
395 int32_t byteOffset = 0; |
|
396 int32_t length = -1; |
|
397 |
|
398 if (args.length() > 1) { |
|
399 if (!ToInt32(cx, args[1], &byteOffset)) |
|
400 return nullptr; |
|
401 if (byteOffset < 0) { |
|
402 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
|
403 JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1"); |
|
404 return nullptr; |
|
405 } |
|
406 |
|
407 if (args.length() > 2) { |
|
408 if (!ToInt32(cx, args[2], &length)) |
|
409 return nullptr; |
|
410 if (length < 0) { |
|
411 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
|
412 JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2"); |
|
413 return nullptr; |
|
414 } |
|
415 } |
|
416 } |
|
417 |
|
418 Rooted<JSObject*> proto(cx, nullptr); |
|
419 return fromBuffer(cx, dataObj, byteOffset, length, proto); |
|
420 } |
|
421 |
|
422 static bool IsThisClass(HandleValue v) { |
|
423 return v.isObject() && v.toObject().hasClass(instanceClass()); |
|
424 } |
|
425 |
|
426 template<Value ValueGetter(TypedArrayObject *tarr)> |
|
427 static bool |
|
428 GetterImpl(JSContext *cx, CallArgs args) |
|
429 { |
|
430 JS_ASSERT(IsThisClass(args.thisv())); |
|
431 args.rval().set(ValueGetter(&args.thisv().toObject().as<TypedArrayObject>())); |
|
432 return true; |
|
433 } |
|
434 |
|
435 // ValueGetter is a function that takes an unwrapped typed array object and |
|
436 // returns a Value. Given such a function, Getter<> is a native that |
|
437 // retrieves a given Value, probably from a slot on the object. |
|
438 template<Value ValueGetter(TypedArrayObject *tarr)> |
|
439 static bool |
|
440 Getter(JSContext *cx, unsigned argc, Value *vp) |
|
441 { |
|
442 CallArgs args = CallArgsFromVp(argc, vp); |
|
443 return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass, |
|
444 ThisTypedArrayObject::GetterImpl<ValueGetter> >(cx, args); |
|
445 } |
|
446 |
|
447 static bool |
|
448 BufferGetterImpl(JSContext *cx, CallArgs args) |
|
449 { |
|
450 JS_ASSERT(IsThisClass(args.thisv())); |
|
451 Rooted<TypedArrayObject *> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>()); |
|
452 if (!ensureHasBuffer(cx, tarray)) |
|
453 return false; |
|
454 args.rval().set(bufferValue(tarray)); |
|
455 return true; |
|
456 } |
|
457 |
|
458 // BufferGetter is a function that lazily constructs the array buffer for a |
|
459 // typed array before fetching it. |
|
460 static bool |
|
461 BufferGetter(JSContext *cx, unsigned argc, Value *vp) |
|
462 { |
|
463 CallArgs args = CallArgsFromVp(argc, vp); |
|
464 return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass, |
|
465 ThisTypedArrayObject::BufferGetterImpl>(cx, args); |
|
466 } |
|
467 |
|
468 // Define an accessor for a read-only property that invokes a native getter |
|
469 static bool |
|
470 DefineGetter(JSContext *cx, HandleObject proto, PropertyName *name, Native native) |
|
471 { |
|
472 RootedId id(cx, NameToId(name)); |
|
473 unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT; |
|
474 |
|
475 Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
|
476 JSObject *getter = NewFunction(cx, NullPtr(), native, 0, |
|
477 JSFunction::NATIVE_FUN, global, NullPtr()); |
|
478 if (!getter) |
|
479 return false; |
|
480 |
|
481 return DefineNativeProperty(cx, proto, id, UndefinedHandleValue, |
|
482 JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, |
|
483 attrs); |
|
484 } |
|
485 |
|
486 static |
|
487 bool defineGetters(JSContext *cx, HandleObject proto) |
|
488 { |
|
489 if (!DefineGetter(cx, proto, cx->names().length, Getter<lengthValue>)) |
|
490 return false; |
|
491 |
|
492 if (!DefineGetter(cx, proto, cx->names().buffer, BufferGetter)) |
|
493 return false; |
|
494 |
|
495 if (!DefineGetter(cx, proto, cx->names().byteLength, Getter<byteLengthValue>)) |
|
496 return false; |
|
497 |
|
498 if (!DefineGetter(cx, proto, cx->names().byteOffset, Getter<byteOffsetValue>)) |
|
499 return false; |
|
500 |
|
501 return true; |
|
502 } |
|
503 |
|
504 /* subarray(start[, end]) */ |
|
505 static bool |
|
506 fun_subarray_impl(JSContext *cx, CallArgs args) |
|
507 { |
|
508 JS_ASSERT(IsThisClass(args.thisv())); |
|
509 Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>()); |
|
510 |
|
511 // these are the default values |
|
512 uint32_t length = tarray->length(); |
|
513 uint32_t begin = 0, end = length; |
|
514 |
|
515 if (args.length() > 0) { |
|
516 if (!ToClampedIndex(cx, args[0], length, &begin)) |
|
517 return false; |
|
518 |
|
519 if (args.length() > 1) { |
|
520 if (!ToClampedIndex(cx, args[1], length, &end)) |
|
521 return false; |
|
522 } |
|
523 } |
|
524 |
|
525 if (begin > end) |
|
526 begin = end; |
|
527 |
|
528 JSObject *nobj = createSubarray(cx, tarray, begin, end); |
|
529 if (!nobj) |
|
530 return false; |
|
531 args.rval().setObject(*nobj); |
|
532 return true; |
|
533 } |
|
534 |
|
535 static bool |
|
536 fun_subarray(JSContext *cx, unsigned argc, Value *vp) |
|
537 { |
|
538 CallArgs args = CallArgsFromVp(argc, vp); |
|
539 return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass, |
|
540 ThisTypedArrayObject::fun_subarray_impl>(cx, args); |
|
541 } |
|
542 |
|
543 /* move(begin, end, dest) */ |
|
544 static bool |
|
545 fun_move_impl(JSContext *cx, CallArgs args) |
|
546 { |
|
547 JS_ASSERT(IsThisClass(args.thisv())); |
|
548 Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>()); |
|
549 |
|
550 if (args.length() < 3) { |
|
551 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
552 return false; |
|
553 } |
|
554 |
|
555 uint32_t srcBegin; |
|
556 uint32_t srcEnd; |
|
557 uint32_t dest; |
|
558 |
|
559 uint32_t originalLength = tarray->length(); |
|
560 if (!ToClampedIndex(cx, args[0], originalLength, &srcBegin) || |
|
561 !ToClampedIndex(cx, args[1], originalLength, &srcEnd) || |
|
562 !ToClampedIndex(cx, args[2], originalLength, &dest)) |
|
563 { |
|
564 return false; |
|
565 } |
|
566 |
|
567 if (srcBegin > srcEnd) { |
|
568 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX); |
|
569 return false; |
|
570 } |
|
571 |
|
572 uint32_t lengthDuringMove = tarray->length(); // beware ToClampedIndex |
|
573 uint32_t nelts = srcEnd - srcBegin; |
|
574 |
|
575 MOZ_ASSERT(dest <= INT32_MAX, "size limited to 2**31"); |
|
576 MOZ_ASSERT(nelts <= INT32_MAX, "size limited to 2**31"); |
|
577 if (dest + nelts > lengthDuringMove || srcEnd > lengthDuringMove) { |
|
578 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
579 return false; |
|
580 } |
|
581 |
|
582 uint32_t byteDest = dest * sizeof(NativeType); |
|
583 uint32_t byteSrc = srcBegin * sizeof(NativeType); |
|
584 uint32_t byteSize = nelts * sizeof(NativeType); |
|
585 |
|
586 #ifdef DEBUG |
|
587 uint32_t viewByteLength = tarray->byteLength(); |
|
588 JS_ASSERT(byteDest <= viewByteLength); |
|
589 JS_ASSERT(byteSrc <= viewByteLength); |
|
590 JS_ASSERT(byteDest + byteSize <= viewByteLength); |
|
591 JS_ASSERT(byteSrc + byteSize <= viewByteLength); |
|
592 |
|
593 // Should not overflow because size is limited to 2^31 |
|
594 JS_ASSERT(byteDest + byteSize >= byteDest); |
|
595 JS_ASSERT(byteSrc + byteSize >= byteSrc); |
|
596 #endif |
|
597 |
|
598 uint8_t *data = static_cast<uint8_t*>(tarray->viewData()); |
|
599 memmove(&data[byteDest], &data[byteSrc], byteSize); |
|
600 args.rval().setUndefined(); |
|
601 return true; |
|
602 } |
|
603 |
|
604 static bool |
|
605 fun_move(JSContext *cx, unsigned argc, Value *vp) |
|
606 { |
|
607 CallArgs args = CallArgsFromVp(argc, vp); |
|
608 return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass, |
|
609 ThisTypedArrayObject::fun_move_impl>(cx, args); |
|
610 } |
|
611 |
|
612 /* set(array[, offset]) */ |
|
613 static bool |
|
614 fun_set_impl(JSContext *cx, CallArgs args) |
|
615 { |
|
616 JS_ASSERT(IsThisClass(args.thisv())); |
|
617 Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>()); |
|
618 |
|
619 // first arg must be either a typed array or a JS array |
|
620 if (args.length() == 0 || !args[0].isObject()) { |
|
621 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
622 return false; |
|
623 } |
|
624 |
|
625 int32_t offset = 0; |
|
626 if (args.length() > 1) { |
|
627 if (!ToInt32(cx, args[1], &offset)) |
|
628 return false; |
|
629 |
|
630 if (offset < 0 || uint32_t(offset) > tarray->length()) { |
|
631 // the given offset is bogus |
|
632 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
|
633 JSMSG_TYPED_ARRAY_BAD_INDEX, "2"); |
|
634 return false; |
|
635 } |
|
636 } |
|
637 |
|
638 if (!args[0].isObject()) { |
|
639 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
640 return false; |
|
641 } |
|
642 |
|
643 RootedObject arg0(cx, args[0].toObjectOrNull()); |
|
644 if (arg0->is<TypedArrayObject>()) { |
|
645 if (arg0->as<TypedArrayObject>().length() > tarray->length() - offset) { |
|
646 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); |
|
647 return false; |
|
648 } |
|
649 |
|
650 if (!copyFromTypedArray(cx, tarray, arg0, offset)) |
|
651 return false; |
|
652 } else { |
|
653 uint32_t len; |
|
654 if (!GetLengthProperty(cx, arg0, &len)) |
|
655 return false; |
|
656 |
|
657 if (uint32_t(offset) > tarray->length() || len > tarray->length() - offset) { |
|
658 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); |
|
659 return false; |
|
660 } |
|
661 |
|
662 if (!copyFromArray(cx, tarray, arg0, len, offset)) |
|
663 return false; |
|
664 } |
|
665 |
|
666 args.rval().setUndefined(); |
|
667 return true; |
|
668 } |
|
669 |
|
670 static bool |
|
671 fun_set(JSContext *cx, unsigned argc, Value *vp) |
|
672 { |
|
673 CallArgs args = CallArgsFromVp(argc, vp); |
|
674 return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass, |
|
675 ThisTypedArrayObject::fun_set_impl>(cx, args); |
|
676 } |
|
677 |
|
678 public: |
|
679 static JSObject * |
|
680 fromBuffer(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt, |
|
681 HandleObject proto) |
|
682 { |
|
683 if (!ObjectClassIs(bufobj, ESClass_ArrayBuffer, cx)) { |
|
684 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
685 return nullptr; // must be arrayBuffer |
|
686 } |
|
687 |
|
688 JS_ASSERT(IsArrayBuffer(bufobj) || bufobj->is<ProxyObject>()); |
|
689 if (bufobj->is<ProxyObject>()) { |
|
690 /* |
|
691 * Normally, NonGenericMethodGuard handles the case of transparent |
|
692 * wrappers. However, we have a peculiar situation: we want to |
|
693 * construct the new typed array in the compartment of the buffer, |
|
694 * so that the typed array can point directly at their buffer's |
|
695 * data without crossing compartment boundaries. So we use the |
|
696 * machinery underlying NonGenericMethodGuard directly to proxy the |
|
697 * native call. We will end up with a wrapper in the origin |
|
698 * compartment for a view in the target compartment referencing the |
|
699 * ArrayBufferObject in that same compartment. |
|
700 */ |
|
701 JSObject *wrapped = CheckedUnwrap(bufobj); |
|
702 if (!wrapped) { |
|
703 JS_ReportError(cx, "Permission denied to access object"); |
|
704 return nullptr; |
|
705 } |
|
706 if (IsArrayBuffer(wrapped)) { |
|
707 /* |
|
708 * And for even more fun, the new view's prototype should be |
|
709 * set to the origin compartment's prototype object, not the |
|
710 * target's (specifically, the actual view in the target |
|
711 * compartment will use as its prototype a wrapper around the |
|
712 * origin compartment's view.prototype object). |
|
713 * |
|
714 * Rather than hack some crazy solution together, implement |
|
715 * this all using a private helper function, created when |
|
716 * ArrayBufferObject was initialized and cached in the global. |
|
717 * This reuses all the existing cross-compartment crazy so we |
|
718 * don't have to do anything *uniquely* crazy here. |
|
719 */ |
|
720 |
|
721 Rooted<JSObject*> proto(cx); |
|
722 if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &proto)) |
|
723 return nullptr; |
|
724 |
|
725 InvokeArgs args(cx); |
|
726 if (!args.init(3)) |
|
727 return nullptr; |
|
728 |
|
729 args.setCallee(cx->compartment()->maybeGlobal()->createArrayFromBuffer<NativeType>()); |
|
730 args.setThis(ObjectValue(*bufobj)); |
|
731 args[0].setNumber(byteOffset); |
|
732 args[1].setInt32(lengthInt); |
|
733 args[2].setObject(*proto); |
|
734 |
|
735 if (!Invoke(cx, args)) |
|
736 return nullptr; |
|
737 return &args.rval().toObject(); |
|
738 } |
|
739 } |
|
740 |
|
741 if (!IsArrayBuffer(bufobj)) { |
|
742 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
743 return nullptr; // must be arrayBuffer |
|
744 } |
|
745 |
|
746 Rooted<ArrayBufferObject *> buffer(cx, &AsArrayBuffer(bufobj)); |
|
747 |
|
748 if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) { |
|
749 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
750 return nullptr; // invalid byteOffset |
|
751 } |
|
752 |
|
753 uint32_t len; |
|
754 if (lengthInt == -1) { |
|
755 len = (buffer->byteLength() - byteOffset) / sizeof(NativeType); |
|
756 if (len * sizeof(NativeType) != buffer->byteLength() - byteOffset) { |
|
757 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
|
758 JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
759 return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N |
|
760 } |
|
761 } else { |
|
762 len = uint32_t(lengthInt); |
|
763 } |
|
764 |
|
765 // Go slowly and check for overflow. |
|
766 uint32_t arrayByteLength = len * sizeof(NativeType); |
|
767 if (len >= INT32_MAX / sizeof(NativeType) || byteOffset >= INT32_MAX - arrayByteLength) { |
|
768 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
769 return nullptr; // overflow when calculating byteOffset + len * sizeof(NativeType) |
|
770 } |
|
771 |
|
772 if (arrayByteLength + byteOffset > buffer->byteLength()) { |
|
773 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
774 return nullptr; // byteOffset + len is too big for the arraybuffer |
|
775 } |
|
776 |
|
777 return makeInstance(cx, buffer, byteOffset, len, proto); |
|
778 } |
|
779 |
|
780 static bool |
|
781 maybeCreateArrayBuffer(JSContext *cx, uint32_t nelements, MutableHandle<ArrayBufferObject *> buffer) |
|
782 { |
|
783 // Make sure that array elements evenly divide into the inline buffer's |
|
784 // size, for the test below. |
|
785 JS_STATIC_ASSERT((INLINE_BUFFER_LIMIT / sizeof(NativeType)) * sizeof(NativeType) == INLINE_BUFFER_LIMIT); |
|
786 |
|
787 if (nelements <= INLINE_BUFFER_LIMIT / sizeof(NativeType)) { |
|
788 // The array's data can be inline, and the buffer created lazily. |
|
789 return true; |
|
790 } |
|
791 |
|
792 if (nelements >= INT32_MAX / sizeof(NativeType)) { |
|
793 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
|
794 JSMSG_NEED_DIET, "size and count"); |
|
795 return false; |
|
796 } |
|
797 |
|
798 buffer.set(ArrayBufferObject::create(cx, nelements * sizeof(NativeType))); |
|
799 return !!buffer; |
|
800 } |
|
801 |
|
802 static JSObject * |
|
803 fromLength(JSContext *cx, uint32_t nelements) |
|
804 { |
|
805 Rooted<ArrayBufferObject *> buffer(cx); |
|
806 if (!maybeCreateArrayBuffer(cx, nelements, &buffer)) |
|
807 return nullptr; |
|
808 return makeInstance(cx, buffer, 0, nelements); |
|
809 } |
|
810 |
|
811 static JSObject * |
|
812 fromArray(JSContext *cx, HandleObject other) |
|
813 { |
|
814 uint32_t len; |
|
815 if (other->is<TypedArrayObject>()) { |
|
816 len = other->as<TypedArrayObject>().length(); |
|
817 } else if (!GetLengthProperty(cx, other, &len)) { |
|
818 return nullptr; |
|
819 } |
|
820 |
|
821 Rooted<ArrayBufferObject *> buffer(cx); |
|
822 if (!maybeCreateArrayBuffer(cx, len, &buffer)) |
|
823 return nullptr; |
|
824 |
|
825 RootedObject obj(cx, makeInstance(cx, buffer, 0, len)); |
|
826 if (!obj || !copyFromArray(cx, obj, other, len)) |
|
827 return nullptr; |
|
828 return obj; |
|
829 } |
|
830 |
|
831 static const NativeType |
|
832 getIndex(JSObject *obj, uint32_t index) |
|
833 { |
|
834 TypedArrayObject &tarray = obj->as<TypedArrayObject>(); |
|
835 MOZ_ASSERT(index < tarray.length()); |
|
836 return static_cast<const NativeType*>(tarray.viewData())[index]; |
|
837 } |
|
838 |
|
839 static void |
|
840 setIndex(TypedArrayObject &tarray, uint32_t index, NativeType val) |
|
841 { |
|
842 MOZ_ASSERT(index < tarray.length()); |
|
843 static_cast<NativeType*>(tarray.viewData())[index] = val; |
|
844 } |
|
845 |
|
846 static Value getIndexValue(JSObject *tarray, uint32_t index); |
|
847 |
|
848 static JSObject * |
|
849 createSubarray(JSContext *cx, HandleObject tarrayArg, uint32_t begin, uint32_t end) |
|
850 { |
|
851 Rooted<TypedArrayObject*> tarray(cx, &tarrayArg->as<TypedArrayObject>()); |
|
852 |
|
853 if (begin > tarray->length() || end > tarray->length() || begin > end) { |
|
854 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX); |
|
855 return nullptr; |
|
856 } |
|
857 |
|
858 if (!ensureHasBuffer(cx, tarray)) |
|
859 return nullptr; |
|
860 |
|
861 Rooted<ArrayBufferObject *> bufobj(cx, tarray->buffer()); |
|
862 JS_ASSERT(bufobj); |
|
863 |
|
864 uint32_t length = end - begin; |
|
865 |
|
866 JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType)); |
|
867 uint32_t arrayByteOffset = tarray->byteOffset(); |
|
868 JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= arrayByteOffset); |
|
869 uint32_t byteOffset = arrayByteOffset + begin * sizeof(NativeType); |
|
870 |
|
871 return makeInstance(cx, bufobj, byteOffset, length); |
|
872 } |
|
873 |
|
874 protected: |
|
875 static NativeType |
|
876 doubleToNative(double d) |
|
877 { |
|
878 if (TypeIsFloatingPoint<NativeType>()) { |
|
879 #ifdef JS_MORE_DETERMINISTIC |
|
880 // The JS spec doesn't distinguish among different NaN values, and |
|
881 // it deliberately doesn't specify the bit pattern written to a |
|
882 // typed array when NaN is written into it. This bit-pattern |
|
883 // inconsistency could confuse deterministic testing, so always |
|
884 // canonicalize NaN values in more-deterministic builds. |
|
885 d = CanonicalizeNaN(d); |
|
886 #endif |
|
887 return NativeType(d); |
|
888 } |
|
889 if (MOZ_UNLIKELY(IsNaN(d))) |
|
890 return NativeType(0); |
|
891 if (TypeIsUnsigned<NativeType>()) |
|
892 return NativeType(ToUint32(d)); |
|
893 return NativeType(ToInt32(d)); |
|
894 } |
|
895 |
|
896 static bool |
|
897 canConvertInfallibly(const Value &v) |
|
898 { |
|
899 return v.isNumber() || v.isBoolean() || v.isNull() || v.isUndefined(); |
|
900 } |
|
901 |
|
902 static NativeType |
|
903 infallibleValueToNative(const Value &v) |
|
904 { |
|
905 if (v.isInt32()) |
|
906 return v.toInt32(); |
|
907 if (v.isDouble()) |
|
908 return doubleToNative(v.toDouble()); |
|
909 if (v.isBoolean()) |
|
910 return v.toBoolean(); |
|
911 if (v.isNull()) |
|
912 return 0; |
|
913 |
|
914 MOZ_ASSERT(v.isUndefined()); |
|
915 return ArrayTypeIsFloatingPoint() ? NativeType(GenericNaN()) : NativeType(0); |
|
916 } |
|
917 |
|
918 static bool |
|
919 valueToNative(JSContext *cx, const Value &v, NativeType *result) |
|
920 { |
|
921 MOZ_ASSERT(!v.isMagic()); |
|
922 |
|
923 if (MOZ_LIKELY(canConvertInfallibly(v))) { |
|
924 *result = infallibleValueToNative(v); |
|
925 return true; |
|
926 } |
|
927 |
|
928 double d; |
|
929 MOZ_ASSERT(v.isString() || v.isObject()); |
|
930 if (!(v.isString() ? StringToNumber(cx, v.toString(), &d) : ToNumber(cx, v, &d))) |
|
931 return false; |
|
932 |
|
933 *result = doubleToNative(d); |
|
934 return true; |
|
935 } |
|
936 |
|
937 static bool |
|
938 copyFromArray(JSContext *cx, HandleObject thisTypedArrayObj, |
|
939 HandleObject source, uint32_t len, uint32_t offset = 0) |
|
940 { |
|
941 Rooted<TypedArrayObject*> thisTypedArray(cx, &thisTypedArrayObj->as<TypedArrayObject>()); |
|
942 JS_ASSERT(offset <= thisTypedArray->length()); |
|
943 JS_ASSERT(len <= thisTypedArray->length() - offset); |
|
944 if (source->is<TypedArrayObject>()) |
|
945 return copyFromTypedArray(cx, thisTypedArray, source, offset); |
|
946 |
|
947 uint32_t i = 0; |
|
948 if (source->isNative()) { |
|
949 // Attempt fast-path infallible conversion of dense elements up to |
|
950 // the first potentially side-effectful lookup or conversion. |
|
951 uint32_t bound = Min(source->getDenseInitializedLength(), len); |
|
952 |
|
953 NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset; |
|
954 |
|
955 const Value *srcValues = source->getDenseElements(); |
|
956 for (; i < bound; i++) { |
|
957 // Note: holes don't convert infallibly. |
|
958 if (!canConvertInfallibly(srcValues[i])) |
|
959 break; |
|
960 dest[i] = infallibleValueToNative(srcValues[i]); |
|
961 } |
|
962 if (i == len) |
|
963 return true; |
|
964 } |
|
965 |
|
966 // Convert and copy any remaining elements generically. |
|
967 RootedValue v(cx); |
|
968 for (; i < len; i++) { |
|
969 if (!JSObject::getElement(cx, source, source, i, &v)) |
|
970 return false; |
|
971 |
|
972 NativeType n; |
|
973 if (!valueToNative(cx, v, &n)) |
|
974 return false; |
|
975 |
|
976 len = Min(len, thisTypedArray->length()); |
|
977 if (i >= len) |
|
978 break; |
|
979 |
|
980 // Compute every iteration in case getElement acts wacky. |
|
981 void *data = thisTypedArray->viewData(); |
|
982 static_cast<NativeType*>(data)[offset + i] = n; |
|
983 } |
|
984 |
|
985 return true; |
|
986 } |
|
987 |
|
988 static bool |
|
989 copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarrayObj, |
|
990 uint32_t offset) |
|
991 { |
|
992 TypedArrayObject *thisTypedArray = &thisTypedArrayObj->as<TypedArrayObject>(); |
|
993 TypedArrayObject *tarray = &tarrayObj->as<TypedArrayObject>(); |
|
994 JS_ASSERT(offset <= thisTypedArray->length()); |
|
995 JS_ASSERT(tarray->length() <= thisTypedArray->length() - offset); |
|
996 if (tarray->buffer() == thisTypedArray->buffer()) |
|
997 return copyFromWithOverlap(cx, thisTypedArray, tarray, offset); |
|
998 |
|
999 NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset; |
|
1000 |
|
1001 if (tarray->type() == thisTypedArray->type()) { |
|
1002 js_memcpy(dest, tarray->viewData(), tarray->byteLength()); |
|
1003 return true; |
|
1004 } |
|
1005 |
|
1006 unsigned srclen = tarray->length(); |
|
1007 switch (tarray->type()) { |
|
1008 case ScalarTypeDescr::TYPE_INT8: { |
|
1009 int8_t *src = static_cast<int8_t*>(tarray->viewData()); |
|
1010 for (unsigned i = 0; i < srclen; ++i) |
|
1011 *dest++ = NativeType(*src++); |
|
1012 break; |
|
1013 } |
|
1014 case ScalarTypeDescr::TYPE_UINT8: |
|
1015 case ScalarTypeDescr::TYPE_UINT8_CLAMPED: { |
|
1016 uint8_t *src = static_cast<uint8_t*>(tarray->viewData()); |
|
1017 for (unsigned i = 0; i < srclen; ++i) |
|
1018 *dest++ = NativeType(*src++); |
|
1019 break; |
|
1020 } |
|
1021 case ScalarTypeDescr::TYPE_INT16: { |
|
1022 int16_t *src = static_cast<int16_t*>(tarray->viewData()); |
|
1023 for (unsigned i = 0; i < srclen; ++i) |
|
1024 *dest++ = NativeType(*src++); |
|
1025 break; |
|
1026 } |
|
1027 case ScalarTypeDescr::TYPE_UINT16: { |
|
1028 uint16_t *src = static_cast<uint16_t*>(tarray->viewData()); |
|
1029 for (unsigned i = 0; i < srclen; ++i) |
|
1030 *dest++ = NativeType(*src++); |
|
1031 break; |
|
1032 } |
|
1033 case ScalarTypeDescr::TYPE_INT32: { |
|
1034 int32_t *src = static_cast<int32_t*>(tarray->viewData()); |
|
1035 for (unsigned i = 0; i < srclen; ++i) |
|
1036 *dest++ = NativeType(*src++); |
|
1037 break; |
|
1038 } |
|
1039 case ScalarTypeDescr::TYPE_UINT32: { |
|
1040 uint32_t *src = static_cast<uint32_t*>(tarray->viewData()); |
|
1041 for (unsigned i = 0; i < srclen; ++i) |
|
1042 *dest++ = NativeType(*src++); |
|
1043 break; |
|
1044 } |
|
1045 case ScalarTypeDescr::TYPE_FLOAT32: { |
|
1046 float *src = static_cast<float*>(tarray->viewData()); |
|
1047 for (unsigned i = 0; i < srclen; ++i) |
|
1048 *dest++ = NativeType(*src++); |
|
1049 break; |
|
1050 } |
|
1051 case ScalarTypeDescr::TYPE_FLOAT64: { |
|
1052 double *src = static_cast<double*>(tarray->viewData()); |
|
1053 for (unsigned i = 0; i < srclen; ++i) |
|
1054 *dest++ = NativeType(*src++); |
|
1055 break; |
|
1056 } |
|
1057 default: |
|
1058 MOZ_ASSUME_UNREACHABLE("copyFrom with a TypedArrayObject of unknown type"); |
|
1059 } |
|
1060 |
|
1061 return true; |
|
1062 } |
|
1063 |
|
1064 static bool |
|
1065 copyFromWithOverlap(JSContext *cx, JSObject *selfObj, JSObject *tarrayObj, uint32_t offset) |
|
1066 { |
|
1067 TypedArrayObject *self = &selfObj->as<TypedArrayObject>(); |
|
1068 TypedArrayObject *tarray = &tarrayObj->as<TypedArrayObject>(); |
|
1069 |
|
1070 JS_ASSERT(offset <= self->length()); |
|
1071 |
|
1072 NativeType *dest = static_cast<NativeType*>(self->viewData()) + offset; |
|
1073 uint32_t byteLength = tarray->byteLength(); |
|
1074 |
|
1075 if (tarray->type() == self->type()) { |
|
1076 memmove(dest, tarray->viewData(), byteLength); |
|
1077 return true; |
|
1078 } |
|
1079 |
|
1080 // We have to make a copy of the source array here, since |
|
1081 // there's overlap, and we have to convert types. |
|
1082 void *srcbuf = cx->malloc_(byteLength); |
|
1083 if (!srcbuf) |
|
1084 return false; |
|
1085 js_memcpy(srcbuf, tarray->viewData(), byteLength); |
|
1086 |
|
1087 uint32_t len = tarray->length(); |
|
1088 switch (tarray->type()) { |
|
1089 case ScalarTypeDescr::TYPE_INT8: { |
|
1090 int8_t *src = (int8_t*) srcbuf; |
|
1091 for (unsigned i = 0; i < len; ++i) |
|
1092 *dest++ = NativeType(*src++); |
|
1093 break; |
|
1094 } |
|
1095 case ScalarTypeDescr::TYPE_UINT8: |
|
1096 case ScalarTypeDescr::TYPE_UINT8_CLAMPED: { |
|
1097 uint8_t *src = (uint8_t*) srcbuf; |
|
1098 for (unsigned i = 0; i < len; ++i) |
|
1099 *dest++ = NativeType(*src++); |
|
1100 break; |
|
1101 } |
|
1102 case ScalarTypeDescr::TYPE_INT16: { |
|
1103 int16_t *src = (int16_t*) srcbuf; |
|
1104 for (unsigned i = 0; i < len; ++i) |
|
1105 *dest++ = NativeType(*src++); |
|
1106 break; |
|
1107 } |
|
1108 case ScalarTypeDescr::TYPE_UINT16: { |
|
1109 uint16_t *src = (uint16_t*) srcbuf; |
|
1110 for (unsigned i = 0; i < len; ++i) |
|
1111 *dest++ = NativeType(*src++); |
|
1112 break; |
|
1113 } |
|
1114 case ScalarTypeDescr::TYPE_INT32: { |
|
1115 int32_t *src = (int32_t*) srcbuf; |
|
1116 for (unsigned i = 0; i < len; ++i) |
|
1117 *dest++ = NativeType(*src++); |
|
1118 break; |
|
1119 } |
|
1120 case ScalarTypeDescr::TYPE_UINT32: { |
|
1121 uint32_t *src = (uint32_t*) srcbuf; |
|
1122 for (unsigned i = 0; i < len; ++i) |
|
1123 *dest++ = NativeType(*src++); |
|
1124 break; |
|
1125 } |
|
1126 case ScalarTypeDescr::TYPE_FLOAT32: { |
|
1127 float *src = (float*) srcbuf; |
|
1128 for (unsigned i = 0; i < len; ++i) |
|
1129 *dest++ = NativeType(*src++); |
|
1130 break; |
|
1131 } |
|
1132 case ScalarTypeDescr::TYPE_FLOAT64: { |
|
1133 double *src = (double*) srcbuf; |
|
1134 for (unsigned i = 0; i < len; ++i) |
|
1135 *dest++ = NativeType(*src++); |
|
1136 break; |
|
1137 } |
|
1138 default: |
|
1139 MOZ_ASSUME_UNREACHABLE("copyFromWithOverlap with a TypedArrayObject of unknown type"); |
|
1140 } |
|
1141 |
|
1142 js_free(srcbuf); |
|
1143 return true; |
|
1144 } |
|
1145 }; |
|
1146 |
|
1147 class Int8ArrayObject : public TypedArrayObjectTemplate<int8_t> { |
|
1148 public: |
|
1149 enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT8 }; |
|
1150 static const JSProtoKey key = JSProto_Int8Array; |
|
1151 static const JSFunctionSpec jsfuncs[]; |
|
1152 }; |
|
1153 class Uint8ArrayObject : public TypedArrayObjectTemplate<uint8_t> { |
|
1154 public: |
|
1155 enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT8 }; |
|
1156 static const JSProtoKey key = JSProto_Uint8Array; |
|
1157 static const JSFunctionSpec jsfuncs[]; |
|
1158 }; |
|
1159 class Int16ArrayObject : public TypedArrayObjectTemplate<int16_t> { |
|
1160 public: |
|
1161 enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT16 }; |
|
1162 static const JSProtoKey key = JSProto_Int16Array; |
|
1163 static const JSFunctionSpec jsfuncs[]; |
|
1164 }; |
|
1165 class Uint16ArrayObject : public TypedArrayObjectTemplate<uint16_t> { |
|
1166 public: |
|
1167 enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT16 }; |
|
1168 static const JSProtoKey key = JSProto_Uint16Array; |
|
1169 static const JSFunctionSpec jsfuncs[]; |
|
1170 }; |
|
1171 class Int32ArrayObject : public TypedArrayObjectTemplate<int32_t> { |
|
1172 public: |
|
1173 enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT32 }; |
|
1174 static const JSProtoKey key = JSProto_Int32Array; |
|
1175 static const JSFunctionSpec jsfuncs[]; |
|
1176 }; |
|
1177 class Uint32ArrayObject : public TypedArrayObjectTemplate<uint32_t> { |
|
1178 public: |
|
1179 enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT32 }; |
|
1180 static const JSProtoKey key = JSProto_Uint32Array; |
|
1181 static const JSFunctionSpec jsfuncs[]; |
|
1182 }; |
|
1183 class Float32ArrayObject : public TypedArrayObjectTemplate<float> { |
|
1184 public: |
|
1185 enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_FLOAT32 }; |
|
1186 static const JSProtoKey key = JSProto_Float32Array; |
|
1187 static const JSFunctionSpec jsfuncs[]; |
|
1188 }; |
|
1189 class Float64ArrayObject : public TypedArrayObjectTemplate<double> { |
|
1190 public: |
|
1191 enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_FLOAT64 }; |
|
1192 static const JSProtoKey key = JSProto_Float64Array; |
|
1193 static const JSFunctionSpec jsfuncs[]; |
|
1194 }; |
|
1195 class Uint8ClampedArrayObject : public TypedArrayObjectTemplate<uint8_clamped> { |
|
1196 public: |
|
1197 enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT8_CLAMPED }; |
|
1198 static const JSProtoKey key = JSProto_Uint8ClampedArray; |
|
1199 static const JSFunctionSpec jsfuncs[]; |
|
1200 }; |
|
1201 |
|
1202 } /* anonymous namespace */ |
|
1203 |
|
1204 template<typename T> |
|
1205 bool |
|
1206 ArrayBufferObject::createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args) |
|
1207 { |
|
1208 typedef TypedArrayObjectTemplate<T> ArrayType; |
|
1209 JS_ASSERT(IsArrayBuffer(args.thisv())); |
|
1210 JS_ASSERT(args.length() == 3); |
|
1211 |
|
1212 Rooted<JSObject*> buffer(cx, &args.thisv().toObject()); |
|
1213 Rooted<JSObject*> proto(cx, &args[2].toObject()); |
|
1214 |
|
1215 Rooted<JSObject*> obj(cx); |
|
1216 double byteOffset = args[0].toNumber(); |
|
1217 MOZ_ASSERT(0 <= byteOffset); |
|
1218 MOZ_ASSERT(byteOffset <= UINT32_MAX); |
|
1219 MOZ_ASSERT(byteOffset == uint32_t(byteOffset)); |
|
1220 obj = ArrayType::fromBuffer(cx, buffer, uint32_t(byteOffset), args[1].toInt32(), proto); |
|
1221 if (!obj) |
|
1222 return false; |
|
1223 args.rval().setObject(*obj); |
|
1224 return true; |
|
1225 } |
|
1226 |
|
1227 template<typename T> |
|
1228 bool |
|
1229 ArrayBufferObject::createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp) |
|
1230 { |
|
1231 CallArgs args = CallArgsFromVp(argc, vp); |
|
1232 return CallNonGenericMethod<IsArrayBuffer, createTypedArrayFromBufferImpl<T> >(cx, args); |
|
1233 } |
|
1234 |
|
1235 // this default implementation is only valid for integer types |
|
1236 // less than 32-bits in size. |
|
1237 template<typename NativeType> |
|
1238 Value |
|
1239 TypedArrayObjectTemplate<NativeType>::getIndexValue(JSObject *tarray, uint32_t index) |
|
1240 { |
|
1241 JS_STATIC_ASSERT(sizeof(NativeType) < 4); |
|
1242 |
|
1243 return Int32Value(getIndex(tarray, index)); |
|
1244 } |
|
1245 |
|
1246 namespace { |
|
1247 |
|
1248 // and we need to specialize for 32-bit integers and floats |
|
1249 template<> |
|
1250 Value |
|
1251 TypedArrayObjectTemplate<int32_t>::getIndexValue(JSObject *tarray, uint32_t index) |
|
1252 { |
|
1253 return Int32Value(getIndex(tarray, index)); |
|
1254 } |
|
1255 |
|
1256 template<> |
|
1257 Value |
|
1258 TypedArrayObjectTemplate<uint32_t>::getIndexValue(JSObject *tarray, uint32_t index) |
|
1259 { |
|
1260 uint32_t val = getIndex(tarray, index); |
|
1261 return NumberValue(val); |
|
1262 } |
|
1263 |
|
1264 template<> |
|
1265 Value |
|
1266 TypedArrayObjectTemplate<float>::getIndexValue(JSObject *tarray, uint32_t index) |
|
1267 { |
|
1268 float val = getIndex(tarray, index); |
|
1269 double dval = val; |
|
1270 |
|
1271 /* |
|
1272 * Doubles in typed arrays could be typed-punned arrays of integers. This |
|
1273 * could allow user code to break the engine-wide invariant that only |
|
1274 * canonical nans are stored into jsvals, which means user code could |
|
1275 * confuse the engine into interpreting a double-typed jsval as an |
|
1276 * object-typed jsval. |
|
1277 * |
|
1278 * This could be removed for platforms/compilers known to convert a 32-bit |
|
1279 * non-canonical nan to a 64-bit canonical nan. |
|
1280 */ |
|
1281 return DoubleValue(CanonicalizeNaN(dval)); |
|
1282 } |
|
1283 |
|
1284 template<> |
|
1285 Value |
|
1286 TypedArrayObjectTemplate<double>::getIndexValue(JSObject *tarray, uint32_t index) |
|
1287 { |
|
1288 double val = getIndex(tarray, index); |
|
1289 |
|
1290 /* |
|
1291 * Doubles in typed arrays could be typed-punned arrays of integers. This |
|
1292 * could allow user code to break the engine-wide invariant that only |
|
1293 * canonical nans are stored into jsvals, which means user code could |
|
1294 * confuse the engine into interpreting a double-typed jsval as an |
|
1295 * object-typed jsval. |
|
1296 */ |
|
1297 return DoubleValue(CanonicalizeNaN(val)); |
|
1298 } |
|
1299 |
|
1300 } /* anonymous namespace */ |
|
1301 |
|
1302 static NewObjectKind |
|
1303 DataViewNewObjectKind(JSContext *cx, uint32_t byteLength, JSObject *proto) |
|
1304 { |
|
1305 if (!proto && byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) |
|
1306 return SingletonObject; |
|
1307 jsbytecode *pc; |
|
1308 JSScript *script = cx->currentScript(&pc); |
|
1309 if (!script) |
|
1310 return GenericObject; |
|
1311 return types::UseNewTypeForInitializer(script, pc, &DataViewObject::class_); |
|
1312 } |
|
1313 |
|
1314 inline DataViewObject * |
|
1315 DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, |
|
1316 Handle<ArrayBufferObject*> arrayBuffer, JSObject *protoArg) |
|
1317 { |
|
1318 JS_ASSERT(byteOffset <= INT32_MAX); |
|
1319 JS_ASSERT(byteLength <= INT32_MAX); |
|
1320 |
|
1321 RootedObject proto(cx, protoArg); |
|
1322 RootedObject obj(cx); |
|
1323 |
|
1324 // This is overflow-safe: 2 * INT32_MAX is still a valid uint32_t. |
|
1325 if (byteOffset + byteLength > arrayBuffer->byteLength()) { |
|
1326 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); |
|
1327 return nullptr; |
|
1328 |
|
1329 } |
|
1330 |
|
1331 NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto); |
|
1332 obj = NewBuiltinClassInstance(cx, &class_, newKind); |
|
1333 if (!obj) |
|
1334 return nullptr; |
|
1335 |
|
1336 if (proto) { |
|
1337 types::TypeObject *type = cx->getNewType(&class_, TaggedProto(proto)); |
|
1338 if (!type) |
|
1339 return nullptr; |
|
1340 obj->setType(type); |
|
1341 } else if (byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) { |
|
1342 JS_ASSERT(obj->hasSingletonType()); |
|
1343 } else { |
|
1344 jsbytecode *pc; |
|
1345 RootedScript script(cx, cx->currentScript(&pc)); |
|
1346 if (script) { |
|
1347 if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) |
|
1348 return nullptr; |
|
1349 } |
|
1350 } |
|
1351 |
|
1352 DataViewObject &dvobj = obj->as<DataViewObject>(); |
|
1353 dvobj.setFixedSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset)); |
|
1354 dvobj.setFixedSlot(BYTELENGTH_SLOT, Int32Value(byteLength)); |
|
1355 dvobj.setFixedSlot(BUFFER_SLOT, ObjectValue(*arrayBuffer)); |
|
1356 dvobj.setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr)); |
|
1357 InitArrayBufferViewDataPointer(&dvobj, arrayBuffer, byteOffset); |
|
1358 JS_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength()); |
|
1359 |
|
1360 // Verify that the private slot is at the expected place |
|
1361 JS_ASSERT(dvobj.numFixedSlots() == DATA_SLOT); |
|
1362 |
|
1363 arrayBuffer->addView(&dvobj); |
|
1364 |
|
1365 return &dvobj; |
|
1366 } |
|
1367 |
|
1368 bool |
|
1369 DataViewObject::construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, HandleObject proto) |
|
1370 { |
|
1371 if (!IsArrayBuffer(bufobj)) { |
|
1372 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, |
|
1373 "DataView", "ArrayBuffer", bufobj->getClass()->name); |
|
1374 return false; |
|
1375 } |
|
1376 |
|
1377 Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj)); |
|
1378 uint32_t bufferLength = buffer->byteLength(); |
|
1379 uint32_t byteOffset = 0; |
|
1380 uint32_t byteLength = bufferLength; |
|
1381 |
|
1382 if (args.length() > 1) { |
|
1383 if (!ToUint32(cx, args[1], &byteOffset)) |
|
1384 return false; |
|
1385 if (byteOffset > INT32_MAX) { |
|
1386 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
|
1387 JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); |
|
1388 return false; |
|
1389 } |
|
1390 |
|
1391 if (args.length() > 2) { |
|
1392 if (!ToUint32(cx, args[2], &byteLength)) |
|
1393 return false; |
|
1394 if (byteLength > INT32_MAX) { |
|
1395 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
|
1396 JSMSG_ARG_INDEX_OUT_OF_RANGE, "2"); |
|
1397 return false; |
|
1398 } |
|
1399 } else { |
|
1400 if (byteOffset > bufferLength) { |
|
1401 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
|
1402 JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); |
|
1403 return false; |
|
1404 } |
|
1405 |
|
1406 byteLength = bufferLength - byteOffset; |
|
1407 } |
|
1408 } |
|
1409 |
|
1410 /* The sum of these cannot overflow a uint32_t */ |
|
1411 JS_ASSERT(byteOffset <= INT32_MAX); |
|
1412 JS_ASSERT(byteLength <= INT32_MAX); |
|
1413 |
|
1414 if (byteOffset + byteLength > bufferLength) { |
|
1415 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); |
|
1416 return false; |
|
1417 } |
|
1418 |
|
1419 JSObject *obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto); |
|
1420 if (!obj) |
|
1421 return false; |
|
1422 args.rval().setObject(*obj); |
|
1423 return true; |
|
1424 } |
|
1425 |
|
1426 bool |
|
1427 DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp) |
|
1428 { |
|
1429 CallArgs args = CallArgsFromVp(argc, vp); |
|
1430 |
|
1431 RootedObject bufobj(cx); |
|
1432 if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj)) |
|
1433 return false; |
|
1434 |
|
1435 if (bufobj->is<WrapperObject>() && IsArrayBuffer(UncheckedUnwrap(bufobj))) { |
|
1436 Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
|
1437 Rooted<JSObject*> proto(cx, global->getOrCreateDataViewPrototype(cx)); |
|
1438 if (!proto) |
|
1439 return false; |
|
1440 |
|
1441 InvokeArgs args2(cx); |
|
1442 if (!args2.init(args.length() + 1)) |
|
1443 return false; |
|
1444 args2.setCallee(global->createDataViewForThis()); |
|
1445 args2.setThis(ObjectValue(*bufobj)); |
|
1446 PodCopy(args2.array(), args.array(), args.length()); |
|
1447 args2[args.length()].setObject(*proto); |
|
1448 if (!Invoke(cx, args2)) |
|
1449 return false; |
|
1450 args.rval().set(args2.rval()); |
|
1451 return true; |
|
1452 } |
|
1453 |
|
1454 return construct(cx, bufobj, args, NullPtr()); |
|
1455 } |
|
1456 |
|
1457 template <typename NativeType> |
|
1458 /* static */ uint8_t * |
|
1459 DataViewObject::getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, uint32_t offset) |
|
1460 { |
|
1461 const size_t TypeSize = sizeof(NativeType); |
|
1462 if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) { |
|
1463 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); |
|
1464 return nullptr; |
|
1465 } |
|
1466 |
|
1467 return static_cast<uint8_t*>(obj->dataPointer()) + offset; |
|
1468 } |
|
1469 |
|
1470 static inline bool |
|
1471 needToSwapBytes(bool littleEndian) |
|
1472 { |
|
1473 #if IS_LITTLE_ENDIAN |
|
1474 return !littleEndian; |
|
1475 #else |
|
1476 return littleEndian; |
|
1477 #endif |
|
1478 } |
|
1479 |
|
1480 static inline uint8_t |
|
1481 swapBytes(uint8_t x) |
|
1482 { |
|
1483 return x; |
|
1484 } |
|
1485 |
|
1486 static inline uint16_t |
|
1487 swapBytes(uint16_t x) |
|
1488 { |
|
1489 return ((x & 0xff) << 8) | (x >> 8); |
|
1490 } |
|
1491 |
|
1492 static inline uint32_t |
|
1493 swapBytes(uint32_t x) |
|
1494 { |
|
1495 return ((x & 0xff) << 24) | |
|
1496 ((x & 0xff00) << 8) | |
|
1497 ((x & 0xff0000) >> 8) | |
|
1498 ((x & 0xff000000) >> 24); |
|
1499 } |
|
1500 |
|
1501 static inline uint64_t |
|
1502 swapBytes(uint64_t x) |
|
1503 { |
|
1504 uint32_t a = x & UINT32_MAX; |
|
1505 uint32_t b = x >> 32; |
|
1506 return (uint64_t(swapBytes(a)) << 32) | swapBytes(b); |
|
1507 } |
|
1508 |
|
1509 template <typename DataType> struct DataToRepType { typedef DataType result; }; |
|
1510 template <> struct DataToRepType<int8_t> { typedef uint8_t result; }; |
|
1511 template <> struct DataToRepType<uint8_t> { typedef uint8_t result; }; |
|
1512 template <> struct DataToRepType<int16_t> { typedef uint16_t result; }; |
|
1513 template <> struct DataToRepType<uint16_t> { typedef uint16_t result; }; |
|
1514 template <> struct DataToRepType<int32_t> { typedef uint32_t result; }; |
|
1515 template <> struct DataToRepType<uint32_t> { typedef uint32_t result; }; |
|
1516 template <> struct DataToRepType<float> { typedef uint32_t result; }; |
|
1517 template <> struct DataToRepType<double> { typedef uint64_t result; }; |
|
1518 |
|
1519 template <typename DataType> |
|
1520 struct DataViewIO |
|
1521 { |
|
1522 typedef typename DataToRepType<DataType>::result ReadWriteType; |
|
1523 |
|
1524 static void fromBuffer(DataType *dest, const uint8_t *unalignedBuffer, bool wantSwap) |
|
1525 { |
|
1526 JS_ASSERT((reinterpret_cast<uintptr_t>(dest) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0); |
|
1527 memcpy((void *) dest, unalignedBuffer, sizeof(ReadWriteType)); |
|
1528 if (wantSwap) { |
|
1529 ReadWriteType *rwDest = reinterpret_cast<ReadWriteType *>(dest); |
|
1530 *rwDest = swapBytes(*rwDest); |
|
1531 } |
|
1532 } |
|
1533 |
|
1534 static void toBuffer(uint8_t *unalignedBuffer, const DataType *src, bool wantSwap) |
|
1535 { |
|
1536 JS_ASSERT((reinterpret_cast<uintptr_t>(src) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0); |
|
1537 ReadWriteType temp = *reinterpret_cast<const ReadWriteType *>(src); |
|
1538 if (wantSwap) |
|
1539 temp = swapBytes(temp); |
|
1540 memcpy(unalignedBuffer, (void *) &temp, sizeof(ReadWriteType)); |
|
1541 } |
|
1542 }; |
|
1543 |
|
1544 template<typename NativeType> |
|
1545 /* static */ bool |
|
1546 DataViewObject::read(JSContext *cx, Handle<DataViewObject*> obj, |
|
1547 CallArgs &args, NativeType *val, const char *method) |
|
1548 { |
|
1549 if (args.length() < 1) { |
|
1550 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
|
1551 JSMSG_MORE_ARGS_NEEDED, method, "0", "s"); |
|
1552 return false; |
|
1553 } |
|
1554 |
|
1555 uint32_t offset; |
|
1556 if (!ToUint32(cx, args[0], &offset)) |
|
1557 return false; |
|
1558 |
|
1559 bool fromLittleEndian = args.length() >= 2 && ToBoolean(args[1]); |
|
1560 |
|
1561 uint8_t *data = DataViewObject::getDataPointer<NativeType>(cx, obj, offset); |
|
1562 if (!data) |
|
1563 return false; |
|
1564 |
|
1565 DataViewIO<NativeType>::fromBuffer(val, data, needToSwapBytes(fromLittleEndian)); |
|
1566 return true; |
|
1567 } |
|
1568 |
|
1569 template <typename NativeType> |
|
1570 static inline bool |
|
1571 WebIDLCast(JSContext *cx, HandleValue value, NativeType *out) |
|
1572 { |
|
1573 int32_t temp; |
|
1574 if (!ToInt32(cx, value, &temp)) |
|
1575 return false; |
|
1576 // Technically, the behavior of assigning an out of range value to a signed |
|
1577 // variable is undefined. In practice, compilers seem to do what we want |
|
1578 // without issuing any warnings. |
|
1579 *out = static_cast<NativeType>(temp); |
|
1580 return true; |
|
1581 } |
|
1582 |
|
1583 template <> |
|
1584 inline bool |
|
1585 WebIDLCast<float>(JSContext *cx, HandleValue value, float *out) |
|
1586 { |
|
1587 double temp; |
|
1588 if (!ToNumber(cx, value, &temp)) |
|
1589 return false; |
|
1590 *out = static_cast<float>(temp); |
|
1591 return true; |
|
1592 } |
|
1593 |
|
1594 template <> |
|
1595 inline bool |
|
1596 WebIDLCast<double>(JSContext *cx, HandleValue value, double *out) |
|
1597 { |
|
1598 return ToNumber(cx, value, out); |
|
1599 } |
|
1600 |
|
1601 template<typename NativeType> |
|
1602 /* static */ bool |
|
1603 DataViewObject::write(JSContext *cx, Handle<DataViewObject*> obj, |
|
1604 CallArgs &args, const char *method) |
|
1605 { |
|
1606 if (args.length() < 2) { |
|
1607 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
|
1608 JSMSG_MORE_ARGS_NEEDED, method, "1", ""); |
|
1609 return false; |
|
1610 } |
|
1611 |
|
1612 uint32_t offset; |
|
1613 if (!ToUint32(cx, args[0], &offset)) |
|
1614 return false; |
|
1615 |
|
1616 NativeType value; |
|
1617 if (!WebIDLCast(cx, args[1], &value)) |
|
1618 return false; |
|
1619 |
|
1620 bool toLittleEndian = args.length() >= 3 && ToBoolean(args[2]); |
|
1621 |
|
1622 uint8_t *data = DataViewObject::getDataPointer<NativeType>(cx, obj, offset); |
|
1623 if (!data) |
|
1624 return false; |
|
1625 |
|
1626 DataViewIO<NativeType>::toBuffer(data, &value, needToSwapBytes(toLittleEndian)); |
|
1627 return true; |
|
1628 } |
|
1629 |
|
1630 bool |
|
1631 DataViewObject::getInt8Impl(JSContext *cx, CallArgs args) |
|
1632 { |
|
1633 JS_ASSERT(is(args.thisv())); |
|
1634 |
|
1635 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1636 |
|
1637 int8_t val; |
|
1638 if (!read(cx, thisView, args, &val, "getInt8")) |
|
1639 return false; |
|
1640 args.rval().setInt32(val); |
|
1641 return true; |
|
1642 } |
|
1643 |
|
1644 bool |
|
1645 DataViewObject::fun_getInt8(JSContext *cx, unsigned argc, Value *vp) |
|
1646 { |
|
1647 CallArgs args = CallArgsFromVp(argc, vp); |
|
1648 return CallNonGenericMethod<is, getInt8Impl>(cx, args); |
|
1649 } |
|
1650 |
|
1651 bool |
|
1652 DataViewObject::getUint8Impl(JSContext *cx, CallArgs args) |
|
1653 { |
|
1654 JS_ASSERT(is(args.thisv())); |
|
1655 |
|
1656 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1657 |
|
1658 uint8_t val; |
|
1659 if (!read(cx, thisView, args, &val, "getUint8")) |
|
1660 return false; |
|
1661 args.rval().setInt32(val); |
|
1662 return true; |
|
1663 } |
|
1664 |
|
1665 bool |
|
1666 DataViewObject::fun_getUint8(JSContext *cx, unsigned argc, Value *vp) |
|
1667 { |
|
1668 CallArgs args = CallArgsFromVp(argc, vp); |
|
1669 return CallNonGenericMethod<is, getUint8Impl>(cx, args); |
|
1670 } |
|
1671 |
|
1672 bool |
|
1673 DataViewObject::getInt16Impl(JSContext *cx, CallArgs args) |
|
1674 { |
|
1675 JS_ASSERT(is(args.thisv())); |
|
1676 |
|
1677 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1678 |
|
1679 int16_t val; |
|
1680 if (!read(cx, thisView, args, &val, "getInt16")) |
|
1681 return false; |
|
1682 args.rval().setInt32(val); |
|
1683 return true; |
|
1684 } |
|
1685 |
|
1686 bool |
|
1687 DataViewObject::fun_getInt16(JSContext *cx, unsigned argc, Value *vp) |
|
1688 { |
|
1689 CallArgs args = CallArgsFromVp(argc, vp); |
|
1690 return CallNonGenericMethod<is, getInt16Impl>(cx, args); |
|
1691 } |
|
1692 |
|
1693 bool |
|
1694 DataViewObject::getUint16Impl(JSContext *cx, CallArgs args) |
|
1695 { |
|
1696 JS_ASSERT(is(args.thisv())); |
|
1697 |
|
1698 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1699 |
|
1700 uint16_t val; |
|
1701 if (!read(cx, thisView, args, &val, "getUint16")) |
|
1702 return false; |
|
1703 args.rval().setInt32(val); |
|
1704 return true; |
|
1705 } |
|
1706 |
|
1707 bool |
|
1708 DataViewObject::fun_getUint16(JSContext *cx, unsigned argc, Value *vp) |
|
1709 { |
|
1710 CallArgs args = CallArgsFromVp(argc, vp); |
|
1711 return CallNonGenericMethod<is, getUint16Impl>(cx, args); |
|
1712 } |
|
1713 |
|
1714 bool |
|
1715 DataViewObject::getInt32Impl(JSContext *cx, CallArgs args) |
|
1716 { |
|
1717 JS_ASSERT(is(args.thisv())); |
|
1718 |
|
1719 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1720 |
|
1721 int32_t val; |
|
1722 if (!read(cx, thisView, args, &val, "getInt32")) |
|
1723 return false; |
|
1724 args.rval().setInt32(val); |
|
1725 return true; |
|
1726 } |
|
1727 |
|
1728 bool |
|
1729 DataViewObject::fun_getInt32(JSContext *cx, unsigned argc, Value *vp) |
|
1730 { |
|
1731 CallArgs args = CallArgsFromVp(argc, vp); |
|
1732 return CallNonGenericMethod<is, getInt32Impl>(cx, args); |
|
1733 } |
|
1734 |
|
1735 bool |
|
1736 DataViewObject::getUint32Impl(JSContext *cx, CallArgs args) |
|
1737 { |
|
1738 JS_ASSERT(is(args.thisv())); |
|
1739 |
|
1740 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1741 |
|
1742 uint32_t val; |
|
1743 if (!read(cx, thisView, args, &val, "getUint32")) |
|
1744 return false; |
|
1745 args.rval().setNumber(val); |
|
1746 return true; |
|
1747 } |
|
1748 |
|
1749 bool |
|
1750 DataViewObject::fun_getUint32(JSContext *cx, unsigned argc, Value *vp) |
|
1751 { |
|
1752 CallArgs args = CallArgsFromVp(argc, vp); |
|
1753 return CallNonGenericMethod<is, getUint32Impl>(cx, args); |
|
1754 } |
|
1755 |
|
1756 bool |
|
1757 DataViewObject::getFloat32Impl(JSContext *cx, CallArgs args) |
|
1758 { |
|
1759 JS_ASSERT(is(args.thisv())); |
|
1760 |
|
1761 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1762 |
|
1763 float val; |
|
1764 if (!read(cx, thisView, args, &val, "getFloat32")) |
|
1765 return false; |
|
1766 |
|
1767 args.rval().setDouble(CanonicalizeNaN(val)); |
|
1768 return true; |
|
1769 } |
|
1770 |
|
1771 bool |
|
1772 DataViewObject::fun_getFloat32(JSContext *cx, unsigned argc, Value *vp) |
|
1773 { |
|
1774 CallArgs args = CallArgsFromVp(argc, vp); |
|
1775 return CallNonGenericMethod<is, getFloat32Impl>(cx, args); |
|
1776 } |
|
1777 |
|
1778 bool |
|
1779 DataViewObject::getFloat64Impl(JSContext *cx, CallArgs args) |
|
1780 { |
|
1781 JS_ASSERT(is(args.thisv())); |
|
1782 |
|
1783 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1784 |
|
1785 double val; |
|
1786 if (!read(cx, thisView, args, &val, "getFloat64")) |
|
1787 return false; |
|
1788 |
|
1789 args.rval().setDouble(CanonicalizeNaN(val)); |
|
1790 return true; |
|
1791 } |
|
1792 |
|
1793 bool |
|
1794 DataViewObject::fun_getFloat64(JSContext *cx, unsigned argc, Value *vp) |
|
1795 { |
|
1796 CallArgs args = CallArgsFromVp(argc, vp); |
|
1797 return CallNonGenericMethod<is, getFloat64Impl>(cx, args); |
|
1798 } |
|
1799 |
|
1800 bool |
|
1801 DataViewObject::setInt8Impl(JSContext *cx, CallArgs args) |
|
1802 { |
|
1803 JS_ASSERT(is(args.thisv())); |
|
1804 |
|
1805 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1806 |
|
1807 if (!write<int8_t>(cx, thisView, args, "setInt8")) |
|
1808 return false; |
|
1809 args.rval().setUndefined(); |
|
1810 return true; |
|
1811 } |
|
1812 |
|
1813 bool |
|
1814 DataViewObject::fun_setInt8(JSContext *cx, unsigned argc, Value *vp) |
|
1815 { |
|
1816 CallArgs args = CallArgsFromVp(argc, vp); |
|
1817 return CallNonGenericMethod<is, setInt8Impl>(cx, args); |
|
1818 } |
|
1819 |
|
1820 bool |
|
1821 DataViewObject::setUint8Impl(JSContext *cx, CallArgs args) |
|
1822 { |
|
1823 JS_ASSERT(is(args.thisv())); |
|
1824 |
|
1825 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1826 |
|
1827 if (!write<uint8_t>(cx, thisView, args, "setUint8")) |
|
1828 return false; |
|
1829 args.rval().setUndefined(); |
|
1830 return true; |
|
1831 } |
|
1832 |
|
1833 bool |
|
1834 DataViewObject::fun_setUint8(JSContext *cx, unsigned argc, Value *vp) |
|
1835 { |
|
1836 CallArgs args = CallArgsFromVp(argc, vp); |
|
1837 return CallNonGenericMethod<is, setUint8Impl>(cx, args); |
|
1838 } |
|
1839 |
|
1840 bool |
|
1841 DataViewObject::setInt16Impl(JSContext *cx, CallArgs args) |
|
1842 { |
|
1843 JS_ASSERT(is(args.thisv())); |
|
1844 |
|
1845 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1846 |
|
1847 if (!write<int16_t>(cx, thisView, args, "setInt16")) |
|
1848 return false; |
|
1849 args.rval().setUndefined(); |
|
1850 return true; |
|
1851 } |
|
1852 |
|
1853 bool |
|
1854 DataViewObject::fun_setInt16(JSContext *cx, unsigned argc, Value *vp) |
|
1855 { |
|
1856 CallArgs args = CallArgsFromVp(argc, vp); |
|
1857 return CallNonGenericMethod<is, setInt16Impl>(cx, args); |
|
1858 } |
|
1859 |
|
1860 bool |
|
1861 DataViewObject::setUint16Impl(JSContext *cx, CallArgs args) |
|
1862 { |
|
1863 JS_ASSERT(is(args.thisv())); |
|
1864 |
|
1865 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1866 |
|
1867 if (!write<uint16_t>(cx, thisView, args, "setUint16")) |
|
1868 return false; |
|
1869 args.rval().setUndefined(); |
|
1870 return true; |
|
1871 } |
|
1872 |
|
1873 bool |
|
1874 DataViewObject::fun_setUint16(JSContext *cx, unsigned argc, Value *vp) |
|
1875 { |
|
1876 CallArgs args = CallArgsFromVp(argc, vp); |
|
1877 return CallNonGenericMethod<is, setUint16Impl>(cx, args); |
|
1878 } |
|
1879 |
|
1880 bool |
|
1881 DataViewObject::setInt32Impl(JSContext *cx, CallArgs args) |
|
1882 { |
|
1883 JS_ASSERT(is(args.thisv())); |
|
1884 |
|
1885 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1886 |
|
1887 if (!write<int32_t>(cx, thisView, args, "setInt32")) |
|
1888 return false; |
|
1889 args.rval().setUndefined(); |
|
1890 return true; |
|
1891 } |
|
1892 |
|
1893 bool |
|
1894 DataViewObject::fun_setInt32(JSContext *cx, unsigned argc, Value *vp) |
|
1895 { |
|
1896 CallArgs args = CallArgsFromVp(argc, vp); |
|
1897 return CallNonGenericMethod<is, setInt32Impl>(cx, args); |
|
1898 } |
|
1899 |
|
1900 bool |
|
1901 DataViewObject::setUint32Impl(JSContext *cx, CallArgs args) |
|
1902 { |
|
1903 JS_ASSERT(is(args.thisv())); |
|
1904 |
|
1905 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1906 |
|
1907 if (!write<uint32_t>(cx, thisView, args, "setUint32")) |
|
1908 return false; |
|
1909 args.rval().setUndefined(); |
|
1910 return true; |
|
1911 } |
|
1912 |
|
1913 bool |
|
1914 DataViewObject::fun_setUint32(JSContext *cx, unsigned argc, Value *vp) |
|
1915 { |
|
1916 CallArgs args = CallArgsFromVp(argc, vp); |
|
1917 return CallNonGenericMethod<is, setUint32Impl>(cx, args); |
|
1918 } |
|
1919 |
|
1920 bool |
|
1921 DataViewObject::setFloat32Impl(JSContext *cx, CallArgs args) |
|
1922 { |
|
1923 JS_ASSERT(is(args.thisv())); |
|
1924 |
|
1925 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1926 |
|
1927 if (!write<float>(cx, thisView, args, "setFloat32")) |
|
1928 return false; |
|
1929 args.rval().setUndefined(); |
|
1930 return true; |
|
1931 } |
|
1932 |
|
1933 bool |
|
1934 DataViewObject::fun_setFloat32(JSContext *cx, unsigned argc, Value *vp) |
|
1935 { |
|
1936 CallArgs args = CallArgsFromVp(argc, vp); |
|
1937 return CallNonGenericMethod<is, setFloat32Impl>(cx, args); |
|
1938 } |
|
1939 |
|
1940 bool |
|
1941 DataViewObject::setFloat64Impl(JSContext *cx, CallArgs args) |
|
1942 { |
|
1943 JS_ASSERT(is(args.thisv())); |
|
1944 |
|
1945 Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
|
1946 |
|
1947 if (!write<double>(cx, thisView, args, "setFloat64")) |
|
1948 return false; |
|
1949 args.rval().setUndefined(); |
|
1950 return true; |
|
1951 } |
|
1952 |
|
1953 bool |
|
1954 DataViewObject::fun_setFloat64(JSContext *cx, unsigned argc, Value *vp) |
|
1955 { |
|
1956 CallArgs args = CallArgsFromVp(argc, vp); |
|
1957 return CallNonGenericMethod<is, setFloat64Impl>(cx, args); |
|
1958 } |
|
1959 |
|
1960 Value |
|
1961 TypedArrayObject::getElement(uint32_t index) |
|
1962 { |
|
1963 switch (type()) { |
|
1964 case ScalarTypeDescr::TYPE_INT8: |
|
1965 return TypedArrayObjectTemplate<int8_t>::getIndexValue(this, index); |
|
1966 break; |
|
1967 case ScalarTypeDescr::TYPE_UINT8: |
|
1968 return TypedArrayObjectTemplate<uint8_t>::getIndexValue(this, index); |
|
1969 break; |
|
1970 case ScalarTypeDescr::TYPE_UINT8_CLAMPED: |
|
1971 return TypedArrayObjectTemplate<uint8_clamped>::getIndexValue(this, index); |
|
1972 break; |
|
1973 case ScalarTypeDescr::TYPE_INT16: |
|
1974 return TypedArrayObjectTemplate<int16_t>::getIndexValue(this, index); |
|
1975 break; |
|
1976 case ScalarTypeDescr::TYPE_UINT16: |
|
1977 return TypedArrayObjectTemplate<uint16_t>::getIndexValue(this, index); |
|
1978 break; |
|
1979 case ScalarTypeDescr::TYPE_INT32: |
|
1980 return TypedArrayObjectTemplate<int32_t>::getIndexValue(this, index); |
|
1981 break; |
|
1982 case ScalarTypeDescr::TYPE_UINT32: |
|
1983 return TypedArrayObjectTemplate<uint32_t>::getIndexValue(this, index); |
|
1984 break; |
|
1985 case ScalarTypeDescr::TYPE_FLOAT32: |
|
1986 return TypedArrayObjectTemplate<float>::getIndexValue(this, index); |
|
1987 break; |
|
1988 case ScalarTypeDescr::TYPE_FLOAT64: |
|
1989 return TypedArrayObjectTemplate<double>::getIndexValue(this, index); |
|
1990 break; |
|
1991 default: |
|
1992 MOZ_ASSUME_UNREACHABLE("Unknown TypedArray type"); |
|
1993 break; |
|
1994 } |
|
1995 } |
|
1996 |
|
1997 void |
|
1998 TypedArrayObject::setElement(TypedArrayObject &obj, uint32_t index, double d) |
|
1999 { |
|
2000 MOZ_ASSERT(index < obj.length()); |
|
2001 |
|
2002 switch (obj.type()) { |
|
2003 case ScalarTypeDescr::TYPE_INT8: |
|
2004 TypedArrayObjectTemplate<int8_t>::setIndexValue(obj, index, d); |
|
2005 break; |
|
2006 case ScalarTypeDescr::TYPE_UINT8: |
|
2007 TypedArrayObjectTemplate<uint8_t>::setIndexValue(obj, index, d); |
|
2008 break; |
|
2009 case ScalarTypeDescr::TYPE_UINT8_CLAMPED: |
|
2010 TypedArrayObjectTemplate<uint8_clamped>::setIndexValue(obj, index, d); |
|
2011 break; |
|
2012 case ScalarTypeDescr::TYPE_INT16: |
|
2013 TypedArrayObjectTemplate<int16_t>::setIndexValue(obj, index, d); |
|
2014 break; |
|
2015 case ScalarTypeDescr::TYPE_UINT16: |
|
2016 TypedArrayObjectTemplate<uint16_t>::setIndexValue(obj, index, d); |
|
2017 break; |
|
2018 case ScalarTypeDescr::TYPE_INT32: |
|
2019 TypedArrayObjectTemplate<int32_t>::setIndexValue(obj, index, d); |
|
2020 break; |
|
2021 case ScalarTypeDescr::TYPE_UINT32: |
|
2022 TypedArrayObjectTemplate<uint32_t>::setIndexValue(obj, index, d); |
|
2023 break; |
|
2024 case ScalarTypeDescr::TYPE_FLOAT32: |
|
2025 TypedArrayObjectTemplate<float>::setIndexValue(obj, index, d); |
|
2026 break; |
|
2027 case ScalarTypeDescr::TYPE_FLOAT64: |
|
2028 TypedArrayObjectTemplate<double>::setIndexValue(obj, index, d); |
|
2029 break; |
|
2030 default: |
|
2031 MOZ_ASSUME_UNREACHABLE("Unknown TypedArray type"); |
|
2032 break; |
|
2033 } |
|
2034 } |
|
2035 |
|
2036 /*** |
|
2037 *** JS impl |
|
2038 ***/ |
|
2039 |
|
2040 /* |
|
2041 * TypedArrayObject boilerplate |
|
2042 */ |
|
2043 |
|
2044 #ifndef RELEASE_BUILD |
|
2045 # define IMPL_TYPED_ARRAY_STATICS(_typedArray) \ |
|
2046 const JSFunctionSpec _typedArray##Object::jsfuncs[] = { \ |
|
2047 JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0), \ |
|
2048 JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \ |
|
2049 JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE), \ |
|
2050 JS_FN("move", _typedArray##Object::fun_move, 3, JSFUN_GENERIC_NATIVE), \ |
|
2051 JS_FS_END \ |
|
2052 } |
|
2053 #else |
|
2054 # define IMPL_TYPED_ARRAY_STATICS(_typedArray) \ |
|
2055 const JSFunctionSpec _typedArray##Object::jsfuncs[] = { \ |
|
2056 JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0), \ |
|
2057 JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \ |
|
2058 JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE), \ |
|
2059 JS_FS_END \ |
|
2060 } |
|
2061 #endif |
|
2062 |
|
2063 #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \ |
|
2064 JS_FRIEND_API(JSObject *) JS_New ## Name ## Array(JSContext *cx, uint32_t nelements) \ |
|
2065 { \ |
|
2066 return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements); \ |
|
2067 } \ |
|
2068 JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, HandleObject other) \ |
|
2069 { \ |
|
2070 return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other); \ |
|
2071 } \ |
|
2072 JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayWithBuffer(JSContext *cx, \ |
|
2073 HandleObject arrayBuffer, uint32_t byteOffset, int32_t length) \ |
|
2074 { \ |
|
2075 return TypedArrayObjectTemplate<NativeType>::fromBuffer(cx, arrayBuffer, byteOffset, \ |
|
2076 length, js::NullPtr()); \ |
|
2077 } \ |
|
2078 JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject *obj) \ |
|
2079 { \ |
|
2080 if (!(obj = CheckedUnwrap(obj))) \ |
|
2081 return false; \ |
|
2082 const Class *clasp = obj->getClass(); \ |
|
2083 return clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]; \ |
|
2084 } \ |
|
2085 JS_FRIEND_API(JSObject *) js::Unwrap ## Name ## Array(JSObject *obj) \ |
|
2086 { \ |
|
2087 obj = CheckedUnwrap(obj); \ |
|
2088 if (!obj) \ |
|
2089 return nullptr; \ |
|
2090 const Class *clasp = obj->getClass(); \ |
|
2091 if (clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]) \ |
|
2092 return obj; \ |
|
2093 return nullptr; \ |
|
2094 } \ |
|
2095 const js::Class* const js::detail::Name ## ArrayClassPtr = \ |
|
2096 &js::TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]; |
|
2097 |
|
2098 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t) |
|
2099 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t) |
|
2100 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8Clamped, uint8_clamped) |
|
2101 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int16, int16_t) |
|
2102 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint16, uint16_t) |
|
2103 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int32, int32_t) |
|
2104 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint32, uint32_t) |
|
2105 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float32, float) |
|
2106 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double) |
|
2107 |
|
2108 #define IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType) \ |
|
2109 JS_FRIEND_API(JSObject *) JS_GetObjectAs ## Name ## Array(JSObject *obj, \ |
|
2110 uint32_t *length, \ |
|
2111 ExternalType **data) \ |
|
2112 { \ |
|
2113 if (!(obj = CheckedUnwrap(obj))) \ |
|
2114 return nullptr; \ |
|
2115 \ |
|
2116 const Class *clasp = obj->getClass(); \ |
|
2117 if (clasp != &TypedArrayObject::classes[TypedArrayObjectTemplate<InternalType>::ArrayTypeID()]) \ |
|
2118 return nullptr; \ |
|
2119 \ |
|
2120 TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); \ |
|
2121 *length = tarr->length(); \ |
|
2122 *data = static_cast<ExternalType *>(tarr->viewData()); \ |
|
2123 \ |
|
2124 return obj; \ |
|
2125 } |
|
2126 |
|
2127 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int8, int8_t, int8_t) |
|
2128 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8, uint8_t, uint8_t) |
|
2129 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8Clamped, uint8_t, uint8_clamped) |
|
2130 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int16, int16_t, int16_t) |
|
2131 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint16, uint16_t, uint16_t) |
|
2132 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int32, int32_t, int32_t) |
|
2133 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t) |
|
2134 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float) |
|
2135 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double) |
|
2136 |
|
2137 #define IMPL_TYPED_ARRAY_PROTO_CLASS(_typedArray) \ |
|
2138 { \ |
|
2139 #_typedArray "Prototype", \ |
|
2140 JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) | \ |
|
2141 JSCLASS_HAS_PRIVATE | \ |
|
2142 JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \ |
|
2143 JS_PropertyStub, /* addProperty */ \ |
|
2144 JS_DeletePropertyStub, /* delProperty */ \ |
|
2145 JS_PropertyStub, /* getProperty */ \ |
|
2146 JS_StrictPropertyStub, /* setProperty */ \ |
|
2147 JS_EnumerateStub, \ |
|
2148 JS_ResolveStub, \ |
|
2149 JS_ConvertStub \ |
|
2150 } |
|
2151 |
|
2152 #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray) \ |
|
2153 { \ |
|
2154 #_typedArray, \ |
|
2155 JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) | \ |
|
2156 JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \ |
|
2157 JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \ |
|
2158 JS_PropertyStub, /* addProperty */ \ |
|
2159 JS_DeletePropertyStub, /* delProperty */ \ |
|
2160 JS_PropertyStub, /* getProperty */ \ |
|
2161 JS_StrictPropertyStub, /* setProperty */ \ |
|
2162 JS_EnumerateStub, \ |
|
2163 JS_ResolveStub, \ |
|
2164 JS_ConvertStub, \ |
|
2165 nullptr, /* finalize */ \ |
|
2166 nullptr, /* call */ \ |
|
2167 nullptr, /* hasInstance */ \ |
|
2168 nullptr, /* construct */ \ |
|
2169 ArrayBufferViewObject::trace, /* trace */ \ |
|
2170 } |
|
2171 |
|
2172 template<class ArrayType> |
|
2173 static inline bool |
|
2174 InitTypedArrayClass(JSContext *cx) |
|
2175 { |
|
2176 Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
|
2177 if (global->isStandardClassResolved(ArrayType::key)) |
|
2178 return true; |
|
2179 |
|
2180 RootedObject proto(cx, global->createBlankPrototype(cx, ArrayType::protoClass())); |
|
2181 if (!proto) |
|
2182 return false; |
|
2183 |
|
2184 RootedFunction ctor(cx); |
|
2185 ctor = global->createConstructor(cx, ArrayType::class_constructor, |
|
2186 ClassName(ArrayType::key, cx), 3); |
|
2187 if (!ctor) |
|
2188 return false; |
|
2189 |
|
2190 if (!LinkConstructorAndPrototype(cx, ctor, proto)) |
|
2191 return false; |
|
2192 |
|
2193 RootedValue bytesValue(cx, Int32Value(ArrayType::BYTES_PER_ELEMENT)); |
|
2194 |
|
2195 if (!JSObject::defineProperty(cx, ctor, |
|
2196 cx->names().BYTES_PER_ELEMENT, bytesValue, |
|
2197 JS_PropertyStub, JS_StrictPropertyStub, |
|
2198 JSPROP_PERMANENT | JSPROP_READONLY) || |
|
2199 !JSObject::defineProperty(cx, proto, |
|
2200 cx->names().BYTES_PER_ELEMENT, bytesValue, |
|
2201 JS_PropertyStub, JS_StrictPropertyStub, |
|
2202 JSPROP_PERMANENT | JSPROP_READONLY)) |
|
2203 { |
|
2204 return false; |
|
2205 } |
|
2206 |
|
2207 if (!ArrayType::defineGetters(cx, proto)) |
|
2208 return false; |
|
2209 |
|
2210 if (!JS_DefineFunctions(cx, proto, ArrayType::jsfuncs)) |
|
2211 return false; |
|
2212 |
|
2213 RootedFunction fun(cx); |
|
2214 fun = |
|
2215 NewFunction(cx, NullPtr(), |
|
2216 ArrayBufferObject::createTypedArrayFromBuffer<typename ArrayType::ThisType>, |
|
2217 0, JSFunction::NATIVE_FUN, global, NullPtr()); |
|
2218 if (!fun) |
|
2219 return false; |
|
2220 |
|
2221 if (!GlobalObject::initBuiltinConstructor(cx, global, ArrayType::key, ctor, proto)) |
|
2222 return false; |
|
2223 |
|
2224 global->setCreateArrayFromBuffer<typename ArrayType::ThisType>(fun); |
|
2225 |
|
2226 return true; |
|
2227 } |
|
2228 |
|
2229 IMPL_TYPED_ARRAY_STATICS(Int8Array); |
|
2230 IMPL_TYPED_ARRAY_STATICS(Uint8Array); |
|
2231 IMPL_TYPED_ARRAY_STATICS(Int16Array); |
|
2232 IMPL_TYPED_ARRAY_STATICS(Uint16Array); |
|
2233 IMPL_TYPED_ARRAY_STATICS(Int32Array); |
|
2234 IMPL_TYPED_ARRAY_STATICS(Uint32Array); |
|
2235 IMPL_TYPED_ARRAY_STATICS(Float32Array); |
|
2236 IMPL_TYPED_ARRAY_STATICS(Float64Array); |
|
2237 IMPL_TYPED_ARRAY_STATICS(Uint8ClampedArray); |
|
2238 |
|
2239 const Class TypedArrayObject::classes[ScalarTypeDescr::TYPE_MAX] = { |
|
2240 IMPL_TYPED_ARRAY_FAST_CLASS(Int8Array), |
|
2241 IMPL_TYPED_ARRAY_FAST_CLASS(Uint8Array), |
|
2242 IMPL_TYPED_ARRAY_FAST_CLASS(Int16Array), |
|
2243 IMPL_TYPED_ARRAY_FAST_CLASS(Uint16Array), |
|
2244 IMPL_TYPED_ARRAY_FAST_CLASS(Int32Array), |
|
2245 IMPL_TYPED_ARRAY_FAST_CLASS(Uint32Array), |
|
2246 IMPL_TYPED_ARRAY_FAST_CLASS(Float32Array), |
|
2247 IMPL_TYPED_ARRAY_FAST_CLASS(Float64Array), |
|
2248 IMPL_TYPED_ARRAY_FAST_CLASS(Uint8ClampedArray) |
|
2249 }; |
|
2250 |
|
2251 const Class TypedArrayObject::protoClasses[ScalarTypeDescr::TYPE_MAX] = { |
|
2252 IMPL_TYPED_ARRAY_PROTO_CLASS(Int8Array), |
|
2253 IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Array), |
|
2254 IMPL_TYPED_ARRAY_PROTO_CLASS(Int16Array), |
|
2255 IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16Array), |
|
2256 IMPL_TYPED_ARRAY_PROTO_CLASS(Int32Array), |
|
2257 IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32Array), |
|
2258 IMPL_TYPED_ARRAY_PROTO_CLASS(Float32Array), |
|
2259 IMPL_TYPED_ARRAY_PROTO_CLASS(Float64Array), |
|
2260 IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8ClampedArray) |
|
2261 }; |
|
2262 |
|
2263 #define CHECK(t, a) { if (t == a::IsThisClass) return true; } |
|
2264 JS_FRIEND_API(bool) |
|
2265 js::IsTypedArrayThisCheck(JS::IsAcceptableThis test) |
|
2266 { |
|
2267 CHECK(test, Int8ArrayObject); |
|
2268 CHECK(test, Uint8ArrayObject); |
|
2269 CHECK(test, Int16ArrayObject); |
|
2270 CHECK(test, Uint16ArrayObject); |
|
2271 CHECK(test, Int32ArrayObject); |
|
2272 CHECK(test, Uint32ArrayObject); |
|
2273 CHECK(test, Float32ArrayObject); |
|
2274 CHECK(test, Float64ArrayObject); |
|
2275 CHECK(test, Uint8ClampedArrayObject); |
|
2276 return false; |
|
2277 } |
|
2278 #undef CHECK |
|
2279 |
|
2280 static JSObject * |
|
2281 InitArrayBufferClass(JSContext *cx) |
|
2282 { |
|
2283 Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
|
2284 if (global->isStandardClassResolved(JSProto_ArrayBuffer)) |
|
2285 return &global->getPrototype(JSProto_ArrayBuffer).toObject(); |
|
2286 |
|
2287 RootedObject arrayBufferProto(cx, global->createBlankPrototype(cx, &ArrayBufferObject::protoClass)); |
|
2288 if (!arrayBufferProto) |
|
2289 return nullptr; |
|
2290 |
|
2291 RootedFunction ctor(cx, global->createConstructor(cx, ArrayBufferObject::class_constructor, |
|
2292 cx->names().ArrayBuffer, 1)); |
|
2293 if (!ctor) |
|
2294 return nullptr; |
|
2295 |
|
2296 if (!LinkConstructorAndPrototype(cx, ctor, arrayBufferProto)) |
|
2297 return nullptr; |
|
2298 |
|
2299 RootedId byteLengthId(cx, NameToId(cx->names().byteLength)); |
|
2300 unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT; |
|
2301 JSObject *getter = NewFunction(cx, NullPtr(), ArrayBufferObject::byteLengthGetter, 0, |
|
2302 JSFunction::NATIVE_FUN, global, NullPtr()); |
|
2303 if (!getter) |
|
2304 return nullptr; |
|
2305 |
|
2306 if (!DefineNativeProperty(cx, arrayBufferProto, byteLengthId, UndefinedHandleValue, |
|
2307 JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs)) |
|
2308 return nullptr; |
|
2309 |
|
2310 if (!JS_DefineFunctions(cx, ctor, ArrayBufferObject::jsstaticfuncs)) |
|
2311 return nullptr; |
|
2312 |
|
2313 if (!JS_DefineFunctions(cx, arrayBufferProto, ArrayBufferObject::jsfuncs)) |
|
2314 return nullptr; |
|
2315 |
|
2316 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_ArrayBuffer, |
|
2317 ctor, arrayBufferProto)) |
|
2318 { |
|
2319 return nullptr; |
|
2320 } |
|
2321 |
|
2322 return arrayBufferProto; |
|
2323 } |
|
2324 |
|
2325 const Class DataViewObject::protoClass = { |
|
2326 "DataViewPrototype", |
|
2327 JSCLASS_HAS_PRIVATE | |
|
2328 JSCLASS_HAS_RESERVED_SLOTS(DataViewObject::RESERVED_SLOTS) | |
|
2329 JSCLASS_HAS_CACHED_PROTO(JSProto_DataView), |
|
2330 JS_PropertyStub, /* addProperty */ |
|
2331 JS_DeletePropertyStub, /* delProperty */ |
|
2332 JS_PropertyStub, /* getProperty */ |
|
2333 JS_StrictPropertyStub, /* setProperty */ |
|
2334 JS_EnumerateStub, |
|
2335 JS_ResolveStub, |
|
2336 JS_ConvertStub |
|
2337 }; |
|
2338 |
|
2339 const Class DataViewObject::class_ = { |
|
2340 "DataView", |
|
2341 JSCLASS_HAS_PRIVATE | |
|
2342 JSCLASS_IMPLEMENTS_BARRIERS | |
|
2343 JSCLASS_HAS_RESERVED_SLOTS(DataViewObject::RESERVED_SLOTS) | |
|
2344 JSCLASS_HAS_CACHED_PROTO(JSProto_DataView), |
|
2345 JS_PropertyStub, /* addProperty */ |
|
2346 JS_DeletePropertyStub, /* delProperty */ |
|
2347 JS_PropertyStub, /* getProperty */ |
|
2348 JS_StrictPropertyStub, /* setProperty */ |
|
2349 JS_EnumerateStub, |
|
2350 JS_ResolveStub, |
|
2351 JS_ConvertStub, |
|
2352 nullptr, /* finalize */ |
|
2353 nullptr, /* call */ |
|
2354 nullptr, /* hasInstance */ |
|
2355 nullptr, /* construct */ |
|
2356 ArrayBufferViewObject::trace, /* trace */ |
|
2357 }; |
|
2358 |
|
2359 const JSFunctionSpec DataViewObject::jsfuncs[] = { |
|
2360 JS_FN("getInt8", DataViewObject::fun_getInt8, 1,0), |
|
2361 JS_FN("getUint8", DataViewObject::fun_getUint8, 1,0), |
|
2362 JS_FN("getInt16", DataViewObject::fun_getInt16, 2,0), |
|
2363 JS_FN("getUint16", DataViewObject::fun_getUint16, 2,0), |
|
2364 JS_FN("getInt32", DataViewObject::fun_getInt32, 2,0), |
|
2365 JS_FN("getUint32", DataViewObject::fun_getUint32, 2,0), |
|
2366 JS_FN("getFloat32", DataViewObject::fun_getFloat32, 2,0), |
|
2367 JS_FN("getFloat64", DataViewObject::fun_getFloat64, 2,0), |
|
2368 JS_FN("setInt8", DataViewObject::fun_setInt8, 2,0), |
|
2369 JS_FN("setUint8", DataViewObject::fun_setUint8, 2,0), |
|
2370 JS_FN("setInt16", DataViewObject::fun_setInt16, 3,0), |
|
2371 JS_FN("setUint16", DataViewObject::fun_setUint16, 3,0), |
|
2372 JS_FN("setInt32", DataViewObject::fun_setInt32, 3,0), |
|
2373 JS_FN("setUint32", DataViewObject::fun_setUint32, 3,0), |
|
2374 JS_FN("setFloat32", DataViewObject::fun_setFloat32, 3,0), |
|
2375 JS_FN("setFloat64", DataViewObject::fun_setFloat64, 3,0), |
|
2376 JS_FS_END |
|
2377 }; |
|
2378 |
|
2379 template<Value ValueGetter(DataViewObject *view)> |
|
2380 bool |
|
2381 DataViewObject::getterImpl(JSContext *cx, CallArgs args) |
|
2382 { |
|
2383 args.rval().set(ValueGetter(&args.thisv().toObject().as<DataViewObject>())); |
|
2384 return true; |
|
2385 } |
|
2386 |
|
2387 template<Value ValueGetter(DataViewObject *view)> |
|
2388 bool |
|
2389 DataViewObject::getter(JSContext *cx, unsigned argc, Value *vp) |
|
2390 { |
|
2391 CallArgs args = CallArgsFromVp(argc, vp); |
|
2392 return CallNonGenericMethod<is, getterImpl<ValueGetter> >(cx, args); |
|
2393 } |
|
2394 |
|
2395 template<Value ValueGetter(DataViewObject *view)> |
|
2396 bool |
|
2397 DataViewObject::defineGetter(JSContext *cx, PropertyName *name, HandleObject proto) |
|
2398 { |
|
2399 RootedId id(cx, NameToId(name)); |
|
2400 unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT; |
|
2401 |
|
2402 Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
|
2403 JSObject *getter = NewFunction(cx, NullPtr(), DataViewObject::getter<ValueGetter>, 0, |
|
2404 JSFunction::NATIVE_FUN, global, NullPtr()); |
|
2405 if (!getter) |
|
2406 return false; |
|
2407 |
|
2408 return DefineNativeProperty(cx, proto, id, UndefinedHandleValue, |
|
2409 JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs); |
|
2410 } |
|
2411 |
|
2412 /* static */ bool |
|
2413 DataViewObject::initClass(JSContext *cx) |
|
2414 { |
|
2415 Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
|
2416 if (global->isStandardClassResolved(JSProto_DataView)) |
|
2417 return true; |
|
2418 |
|
2419 RootedObject proto(cx, global->createBlankPrototype(cx, &DataViewObject::protoClass)); |
|
2420 if (!proto) |
|
2421 return false; |
|
2422 |
|
2423 RootedFunction ctor(cx, global->createConstructor(cx, DataViewObject::class_constructor, |
|
2424 cx->names().DataView, 3)); |
|
2425 if (!ctor) |
|
2426 return false; |
|
2427 |
|
2428 if (!LinkConstructorAndPrototype(cx, ctor, proto)) |
|
2429 return false; |
|
2430 |
|
2431 if (!defineGetter<bufferValue>(cx, cx->names().buffer, proto)) |
|
2432 return false; |
|
2433 |
|
2434 if (!defineGetter<byteLengthValue>(cx, cx->names().byteLength, proto)) |
|
2435 return false; |
|
2436 |
|
2437 if (!defineGetter<byteOffsetValue>(cx, cx->names().byteOffset, proto)) |
|
2438 return false; |
|
2439 |
|
2440 if (!JS_DefineFunctions(cx, proto, DataViewObject::jsfuncs)) |
|
2441 return false; |
|
2442 |
|
2443 /* |
|
2444 * Create a helper function to implement the craziness of |
|
2445 * |new DataView(new otherWindow.ArrayBuffer())|, and install it in the |
|
2446 * global for use by the DataViewObject constructor. |
|
2447 */ |
|
2448 RootedFunction fun(cx, NewFunction(cx, NullPtr(), ArrayBufferObject::createDataViewForThis, |
|
2449 0, JSFunction::NATIVE_FUN, global, NullPtr())); |
|
2450 if (!fun) |
|
2451 return false; |
|
2452 |
|
2453 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_DataView, ctor, proto)) |
|
2454 return false; |
|
2455 |
|
2456 global->setCreateDataViewForThis(fun); |
|
2457 |
|
2458 return true; |
|
2459 } |
|
2460 |
|
2461 void |
|
2462 DataViewObject::neuter(void *newData) |
|
2463 { |
|
2464 setSlot(BYTELENGTH_SLOT, Int32Value(0)); |
|
2465 setSlot(BYTEOFFSET_SLOT, Int32Value(0)); |
|
2466 setPrivate(newData); |
|
2467 } |
|
2468 |
|
2469 JSObject * |
|
2470 js_InitTypedArrayClasses(JSContext *cx, HandleObject obj) |
|
2471 { |
|
2472 if (!InitTypedArrayClass<Int8ArrayObject>(cx) || |
|
2473 !InitTypedArrayClass<Uint8ArrayObject>(cx) || |
|
2474 !InitTypedArrayClass<Int16ArrayObject>(cx) || |
|
2475 !InitTypedArrayClass<Uint16ArrayObject>(cx) || |
|
2476 !InitTypedArrayClass<Int32ArrayObject>(cx) || |
|
2477 !InitTypedArrayClass<Uint32ArrayObject>(cx) || |
|
2478 !InitTypedArrayClass<Float32ArrayObject>(cx) || |
|
2479 !InitTypedArrayClass<Float64ArrayObject>(cx) || |
|
2480 !InitTypedArrayClass<Uint8ClampedArrayObject>(cx) || |
|
2481 !DataViewObject::initClass(cx)) |
|
2482 { |
|
2483 return nullptr; |
|
2484 } |
|
2485 |
|
2486 return InitArrayBufferClass(cx); |
|
2487 } |
|
2488 |
|
2489 bool |
|
2490 js::IsTypedArrayConstructor(HandleValue v, uint32_t type) |
|
2491 { |
|
2492 switch (type) { |
|
2493 case ScalarTypeDescr::TYPE_INT8: |
|
2494 return IsNativeFunction(v, Int8ArrayObject::class_constructor); |
|
2495 case ScalarTypeDescr::TYPE_UINT8: |
|
2496 return IsNativeFunction(v, Uint8ArrayObject::class_constructor); |
|
2497 case ScalarTypeDescr::TYPE_INT16: |
|
2498 return IsNativeFunction(v, Int16ArrayObject::class_constructor); |
|
2499 case ScalarTypeDescr::TYPE_UINT16: |
|
2500 return IsNativeFunction(v, Uint16ArrayObject::class_constructor); |
|
2501 case ScalarTypeDescr::TYPE_INT32: |
|
2502 return IsNativeFunction(v, Int32ArrayObject::class_constructor); |
|
2503 case ScalarTypeDescr::TYPE_UINT32: |
|
2504 return IsNativeFunction(v, Uint32ArrayObject::class_constructor); |
|
2505 case ScalarTypeDescr::TYPE_FLOAT32: |
|
2506 return IsNativeFunction(v, Float32ArrayObject::class_constructor); |
|
2507 case ScalarTypeDescr::TYPE_FLOAT64: |
|
2508 return IsNativeFunction(v, Float64ArrayObject::class_constructor); |
|
2509 case ScalarTypeDescr::TYPE_UINT8_CLAMPED: |
|
2510 return IsNativeFunction(v, Uint8ClampedArrayObject::class_constructor); |
|
2511 } |
|
2512 MOZ_ASSUME_UNREACHABLE("unexpected typed array type"); |
|
2513 } |
|
2514 |
|
2515 bool |
|
2516 js::IsTypedArrayBuffer(HandleValue v) |
|
2517 { |
|
2518 return v.isObject() && |
|
2519 (v.toObject().is<ArrayBufferObject>() || |
|
2520 v.toObject().is<SharedArrayBufferObject>()); |
|
2521 } |
|
2522 |
|
2523 ArrayBufferObject & |
|
2524 js::AsTypedArrayBuffer(HandleValue v) |
|
2525 { |
|
2526 JS_ASSERT(IsTypedArrayBuffer(v)); |
|
2527 if (v.toObject().is<ArrayBufferObject>()) |
|
2528 return v.toObject().as<ArrayBufferObject>(); |
|
2529 return v.toObject().as<SharedArrayBufferObject>(); |
|
2530 } |
|
2531 |
|
2532 bool |
|
2533 js::StringIsTypedArrayIndex(JSLinearString *str, uint64_t *indexp) |
|
2534 { |
|
2535 const jschar *s = str->chars(); |
|
2536 const jschar *end = s + str->length(); |
|
2537 |
|
2538 if (s == end) |
|
2539 return false; |
|
2540 |
|
2541 bool negative = false; |
|
2542 if (*s == '-') { |
|
2543 negative = true; |
|
2544 if (++s == end) |
|
2545 return false; |
|
2546 } |
|
2547 |
|
2548 if (!JS7_ISDEC(*s)) |
|
2549 return false; |
|
2550 |
|
2551 uint64_t index = 0; |
|
2552 uint32_t digit = JS7_UNDEC(*s++); |
|
2553 |
|
2554 /* Don't allow leading zeros. */ |
|
2555 if (digit == 0 && s != end) |
|
2556 return false; |
|
2557 |
|
2558 index = digit; |
|
2559 |
|
2560 for (; s < end; s++) { |
|
2561 if (!JS7_ISDEC(*s)) |
|
2562 return false; |
|
2563 |
|
2564 digit = JS7_UNDEC(*s); |
|
2565 |
|
2566 /* Watch for overflows. */ |
|
2567 if ((UINT64_MAX - digit) / 10 < index) |
|
2568 index = UINT64_MAX; |
|
2569 else |
|
2570 index = 10 * index + digit; |
|
2571 } |
|
2572 |
|
2573 if (negative) |
|
2574 *indexp = UINT64_MAX; |
|
2575 else |
|
2576 *indexp = index; |
|
2577 return true; |
|
2578 } |
|
2579 |
|
2580 /* JS Friend API */ |
|
2581 |
|
2582 JS_FRIEND_API(bool) |
|
2583 JS_IsTypedArrayObject(JSObject *obj) |
|
2584 { |
|
2585 obj = CheckedUnwrap(obj); |
|
2586 return obj ? obj->is<TypedArrayObject>() : false; |
|
2587 } |
|
2588 |
|
2589 JS_FRIEND_API(uint32_t) |
|
2590 JS_GetTypedArrayLength(JSObject *obj) |
|
2591 { |
|
2592 obj = CheckedUnwrap(obj); |
|
2593 if (!obj) |
|
2594 return 0; |
|
2595 return obj->as<TypedArrayObject>().length(); |
|
2596 } |
|
2597 |
|
2598 JS_FRIEND_API(uint32_t) |
|
2599 JS_GetTypedArrayByteOffset(JSObject *obj) |
|
2600 { |
|
2601 obj = CheckedUnwrap(obj); |
|
2602 if (!obj) |
|
2603 return 0; |
|
2604 return obj->as<TypedArrayObject>().byteOffset(); |
|
2605 } |
|
2606 |
|
2607 JS_FRIEND_API(uint32_t) |
|
2608 JS_GetTypedArrayByteLength(JSObject *obj) |
|
2609 { |
|
2610 obj = CheckedUnwrap(obj); |
|
2611 if (!obj) |
|
2612 return 0; |
|
2613 return obj->as<TypedArrayObject>().byteLength(); |
|
2614 } |
|
2615 |
|
2616 JS_FRIEND_API(JSArrayBufferViewType) |
|
2617 JS_GetArrayBufferViewType(JSObject *obj) |
|
2618 { |
|
2619 obj = CheckedUnwrap(obj); |
|
2620 if (!obj) |
|
2621 return ArrayBufferView::TYPE_MAX; |
|
2622 |
|
2623 if (obj->is<TypedArrayObject>()) |
|
2624 return static_cast<JSArrayBufferViewType>(obj->as<TypedArrayObject>().type()); |
|
2625 else if (obj->is<DataViewObject>()) |
|
2626 return ArrayBufferView::TYPE_DATAVIEW; |
|
2627 MOZ_ASSUME_UNREACHABLE("invalid ArrayBufferView type"); |
|
2628 } |
|
2629 |
|
2630 JS_FRIEND_API(int8_t *) |
|
2631 JS_GetInt8ArrayData(JSObject *obj) |
|
2632 { |
|
2633 obj = CheckedUnwrap(obj); |
|
2634 if (!obj) |
|
2635 return nullptr; |
|
2636 TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
|
2637 JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT8); |
|
2638 return static_cast<int8_t *>(tarr->viewData()); |
|
2639 } |
|
2640 |
|
2641 JS_FRIEND_API(uint8_t *) |
|
2642 JS_GetUint8ArrayData(JSObject *obj) |
|
2643 { |
|
2644 obj = CheckedUnwrap(obj); |
|
2645 if (!obj) |
|
2646 return nullptr; |
|
2647 TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
|
2648 JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT8); |
|
2649 return static_cast<uint8_t *>(tarr->viewData()); |
|
2650 } |
|
2651 |
|
2652 JS_FRIEND_API(uint8_t *) |
|
2653 JS_GetUint8ClampedArrayData(JSObject *obj) |
|
2654 { |
|
2655 obj = CheckedUnwrap(obj); |
|
2656 if (!obj) |
|
2657 return nullptr; |
|
2658 TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
|
2659 JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT8_CLAMPED); |
|
2660 return static_cast<uint8_t *>(tarr->viewData()); |
|
2661 } |
|
2662 |
|
2663 JS_FRIEND_API(int16_t *) |
|
2664 JS_GetInt16ArrayData(JSObject *obj) |
|
2665 { |
|
2666 obj = CheckedUnwrap(obj); |
|
2667 if (!obj) |
|
2668 return nullptr; |
|
2669 TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
|
2670 JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT16); |
|
2671 return static_cast<int16_t *>(tarr->viewData()); |
|
2672 } |
|
2673 |
|
2674 JS_FRIEND_API(uint16_t *) |
|
2675 JS_GetUint16ArrayData(JSObject *obj) |
|
2676 { |
|
2677 obj = CheckedUnwrap(obj); |
|
2678 if (!obj) |
|
2679 return nullptr; |
|
2680 TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
|
2681 JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT16); |
|
2682 return static_cast<uint16_t *>(tarr->viewData()); |
|
2683 } |
|
2684 |
|
2685 JS_FRIEND_API(int32_t *) |
|
2686 JS_GetInt32ArrayData(JSObject *obj) |
|
2687 { |
|
2688 obj = CheckedUnwrap(obj); |
|
2689 if (!obj) |
|
2690 return nullptr; |
|
2691 TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
|
2692 JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT32); |
|
2693 return static_cast<int32_t *>(tarr->viewData()); |
|
2694 } |
|
2695 |
|
2696 JS_FRIEND_API(uint32_t *) |
|
2697 JS_GetUint32ArrayData(JSObject *obj) |
|
2698 { |
|
2699 obj = CheckedUnwrap(obj); |
|
2700 if (!obj) |
|
2701 return nullptr; |
|
2702 TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
|
2703 JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT32); |
|
2704 return static_cast<uint32_t *>(tarr->viewData()); |
|
2705 } |
|
2706 |
|
2707 JS_FRIEND_API(float *) |
|
2708 JS_GetFloat32ArrayData(JSObject *obj) |
|
2709 { |
|
2710 obj = CheckedUnwrap(obj); |
|
2711 if (!obj) |
|
2712 return nullptr; |
|
2713 TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
|
2714 JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_FLOAT32); |
|
2715 return static_cast<float *>(tarr->viewData()); |
|
2716 } |
|
2717 |
|
2718 JS_FRIEND_API(double *) |
|
2719 JS_GetFloat64ArrayData(JSObject *obj) |
|
2720 { |
|
2721 obj = CheckedUnwrap(obj); |
|
2722 if (!obj) |
|
2723 return nullptr; |
|
2724 TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
|
2725 JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_FLOAT64); |
|
2726 return static_cast<double *>(tarr->viewData()); |
|
2727 } |
|
2728 |
|
2729 JS_FRIEND_API(bool) |
|
2730 JS_IsDataViewObject(JSObject *obj) |
|
2731 { |
|
2732 obj = CheckedUnwrap(obj); |
|
2733 return obj ? obj->is<DataViewObject>() : false; |
|
2734 } |
|
2735 |
|
2736 JS_FRIEND_API(uint32_t) |
|
2737 JS_GetDataViewByteOffset(JSObject *obj) |
|
2738 { |
|
2739 obj = CheckedUnwrap(obj); |
|
2740 if (!obj) |
|
2741 return 0; |
|
2742 return obj->as<DataViewObject>().byteOffset(); |
|
2743 } |
|
2744 |
|
2745 JS_FRIEND_API(void *) |
|
2746 JS_GetDataViewData(JSObject *obj) |
|
2747 { |
|
2748 obj = CheckedUnwrap(obj); |
|
2749 if (!obj) |
|
2750 return nullptr; |
|
2751 return obj->as<DataViewObject>().dataPointer(); |
|
2752 } |
|
2753 |
|
2754 JS_FRIEND_API(uint32_t) |
|
2755 JS_GetDataViewByteLength(JSObject *obj) |
|
2756 { |
|
2757 obj = CheckedUnwrap(obj); |
|
2758 if (!obj) |
|
2759 return 0; |
|
2760 return obj->as<DataViewObject>().byteLength(); |
|
2761 } |