js/src/vm/TypedArrayObject.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:83c65ded0074
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 }

mercurial