Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jstypedarray.cpp
1 /* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
2 /* vim: set ts=40 sw=4 et tw=99: */
3 /* ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla WebGL impl
17  *
18  * The Initial Developer of the Original Code is
19  *   Mozilla Foundation
20  * Portions created by the Initial Developer are Copyright (C) 2009
21  * the Initial Developer. All Rights Reserved.
22  *
23  * Contributor(s):
24  *   Vladimir Vukicevic <vladimir@pobox.com>
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 #include <string.h>
41
42 #include "jstypes.h"
43 #include "jsstdint.h"
44 #include "jsutil.h"
45 #include "jshash.h"
46 #include "jsprf.h"
47 #include "jsapi.h"
48 #include "jsarray.h"
49 #include "jsatom.h"
50 #include "jsbool.h"
51 #include "jsbuiltins.h"
52 #include "jscntxt.h"
53 #include "jsversion.h"
54 #include "jsgc.h"
55 #include "jsinterp.h"
56 #include "jslock.h"
57 #include "jsnum.h"
58 #include "jsobj.h"
59 #include "jsstaticcheck.h"
60 #include "jsbit.h"
61 #include "jsvector.h"
62 #include "jstypedarray.h"
63
64 #include "jsobjinlines.h"
65
66 using namespace js;
67 using namespace js::gc;
68
69 static bool
70 ValueIsLength(JSContext *cx, const Value &v, jsuint *len)
71 {
72     if (v.isInt32()) {
73         int32_t i = v.toInt32();
74         if (i < 0)
75             return false;
76         *len = i;
77         return true;
78     }
79
80     if (v.isDouble()) {
81         jsdouble d = v.toDouble();
82         if (JSDOUBLE_IS_NaN(d))
83             return false;
84
85         jsuint length = jsuint(d);
86         if (d != jsdouble(length))
87             return false;
88
89         *len = length;
90         return true;
91     }
92
93     return false;
94 }
95
96 /*
97  * ArrayBuffer
98  *
99  * This class holds the underlying raw buffer that the TypedArray classes
100  * access.  It can be created explicitly and passed to a TypedArray, or
101  * can be created implicitly by constructing a TypedArray with a size.
102  */
103 ArrayBuffer *
104 ArrayBuffer::fromJSObject(JSObject *obj)
105 {
106     while (!js_IsArrayBuffer(obj))
107         obj = obj->getProto();
108     return reinterpret_cast<ArrayBuffer*>(obj->getPrivate());
109 }
110
111 JSBool
112 ArrayBuffer::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
113 {
114     ArrayBuffer *abuf = ArrayBuffer::fromJSObject(obj);
115     if (abuf)
116         vp->setInt32(jsint(abuf->byteLength));
117     return true;
118 }
119
120 void
121 ArrayBuffer::class_finalize(JSContext *cx, JSObject *obj)
122 {
123     ArrayBuffer *abuf = ArrayBuffer::fromJSObject(obj);
124     if (abuf) {
125         abuf->freeStorage(cx);
126         cx->destroy<ArrayBuffer>(abuf);
127     }
128 }
129
130 /*
131  * new ArrayBuffer(byteLength)
132  */
133 JSBool
134 ArrayBuffer::class_constructor(JSContext *cx, uintN argc, Value *vp)
135 {
136     int32 nbytes = 0;
137     if (argc > 0 && !ValueToECMAInt32(cx, vp[2], &nbytes))
138         return false;
139
140     JSObject *bufobj = create(cx, nbytes);
141     if (!bufobj)
142         return false;
143     vp->setObject(*bufobj);
144     return true;
145 }
146
147 JSObject *
148 ArrayBuffer::create(JSContext *cx, int32 nbytes)
149 {
150     JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBuffer::jsclass);
151     if (!obj)
152         return NULL;
153
154     if (nbytes < 0) {
155         /*
156          * We're just not going to support arrays that are bigger than what will fit
157          * as an integer value; if someone actually ever complains (validly), then we
158          * can fix.
159          */
160         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
161         return NULL;
162     }
163
164     ArrayBuffer *abuf = cx->create<ArrayBuffer>();
165     if (!abuf)
166         return NULL;
167
168     if (!abuf->allocateStorage(cx, nbytes)) {
169         cx->destroy<ArrayBuffer>(abuf);
170         return NULL;
171     }
172
173     obj->setPrivate(abuf);
174     return obj;
175 }
176
177 bool
178 ArrayBuffer::allocateStorage(JSContext *cx, uint32 nbytes)
179 {
180     JS_ASSERT(data == 0);
181
182     if (nbytes) {
183         data = cx->calloc(nbytes);
184         if (!data)
185             return false;
186     }
187
188     byteLength = nbytes;
189     return true;
190 }
191
192 void
193 ArrayBuffer::freeStorage(JSContext *cx)
194 {
195     if (data) {
196         cx->free(data);
197 #ifdef DEBUG
198         // the destructor asserts that data is 0 in debug builds
199         data = NULL;
200 #endif
201     }
202 }
203
204 ArrayBuffer::~ArrayBuffer()
205 {
206     JS_ASSERT(data == NULL);
207 }
208
209 /*
210  * TypedArray
211  *
212  * The non-templated base class for the specific typed implementations.
213  * This class holds all the member variables that are used by
214  * the subclasses.
215  */
216
217 TypedArray *
218 TypedArray::fromJSObject(JSObject *obj)
219 {
220     while (!js_IsTypedArray(obj))
221         obj = obj->getProto();
222     return reinterpret_cast<TypedArray*>(obj->getPrivate());
223 }
224
225 inline bool
226 TypedArray::isArrayIndex(JSContext *cx, jsid id, jsuint *ip)
227 {
228     jsuint index;
229     if (js_IdIsIndex(id, &index) && index < length) {
230         if (ip)
231             *ip = index;
232         return true;
233     }
234
235     return false;
236 }
237
238 typedef Value (* TypedArrayPropertyGetter)(TypedArray *tarray);
239
240 template <TypedArrayPropertyGetter Get>
241 class TypedArrayGetter {
242   public:
243     static inline bool get(JSContext *cx, JSObject *obj, jsid id, Value *vp) {
244         do {
245             if (js_IsTypedArray(obj)) {
246                 TypedArray *tarray = TypedArray::fromJSObject(obj);
247                 if (tarray)
248                     *vp = Get(tarray);
249                 return true;
250             }
251         } while ((obj = obj->getProto()) != NULL);
252         return true;
253     }
254 };
255
256 inline Value
257 getBuffer(TypedArray *tarray)
258 {
259     return ObjectValue(*tarray->bufferJS);
260 }
261
262 JSBool
263 TypedArray::prop_getBuffer(JSContext *cx, JSObject *obj, jsid id, Value *vp)
264 {
265     return TypedArrayGetter<getBuffer>::get(cx, obj, id, vp);
266 }
267
268 inline Value
269 getByteOffset(TypedArray *tarray)
270 {
271     return Int32Value(tarray->byteOffset);
272 }
273
274 JSBool
275 TypedArray::prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value *vp)
276 {
277     return TypedArrayGetter<getByteOffset>::get(cx, obj, id, vp);
278 }
279
280 inline Value
281 getByteLength(TypedArray *tarray)
282 {
283     return Int32Value(tarray->byteLength);
284 }
285
286 JSBool
287 TypedArray::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
288 {
289     return TypedArrayGetter<getByteLength>::get(cx, obj, id, vp);
290 }
291
292 inline Value
293 getLength(TypedArray *tarray)
294 {
295     return Int32Value(tarray->length);
296 }
297
298 JSBool
299 TypedArray::prop_getLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
300 {
301     return TypedArrayGetter<getLength>::get(cx, obj, id, vp);
302 }
303
304 JSBool
305 TypedArray::obj_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
306                                JSObject **objp, JSProperty **propp)
307 {
308     TypedArray *tarray = fromJSObject(obj);
309     JS_ASSERT(tarray);
310
311     if (tarray->isArrayIndex(cx, id)) {
312         *propp = (JSProperty *) 1;  /* non-null to indicate found */
313         *objp = obj;
314         return true;
315     }
316
317     JSObject *proto = obj->getProto();
318     if (!proto) {
319         *objp = NULL;
320         *propp = NULL;
321         return true;
322     }
323
324     return proto->lookupProperty(cx, id, objp, propp);
325 }
326
327 void
328 TypedArray::obj_trace(JSTracer *trc, JSObject *obj)
329 {
330     TypedArray *tarray = fromJSObject(obj);
331     JS_ASSERT(tarray);
332     MarkObject(trc, *tarray->bufferJS, "typedarray.buffer");
333 }
334
335 JSBool
336 TypedArray::obj_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
337 {
338     *attrsp = (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
339               ? JSPROP_PERMANENT | JSPROP_READONLY
340               : JSPROP_PERMANENT | JSPROP_ENUMERATE;
341     return true;
342 }
343
344 JSBool
345 TypedArray::obj_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
346 {
347     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
348                          JSMSG_CANT_SET_ARRAY_ATTRS);
349     return false;
350 }
351
352 /* Helper clamped uint8 type */
353
354 int32 JS_FASTCALL
355 js_TypedArray_uint8_clamp_double(const double x)
356 {
357     // Not < so that NaN coerces to 0
358     if (!(x >= 0))
359         return 0;
360
361     if (x > 255)
362         return 255;
363
364     jsdouble toTruncate = x + 0.5;
365     JSUint8 y = JSUint8(toTruncate);
366
367     /*
368      * now val is rounded to nearest, ties rounded up.  We want
369      * rounded to nearest ties to even, so check whether we had a
370      * tie.
371      */
372     if (y == toTruncate) {
373         /*
374          * It was a tie (since adding 0.5 gave us the exact integer
375          * we want).  Since we rounded up, we either already have an
376          * even number or we have an odd number but the number we
377          * want is one less.  So just unconditionally masking out the
378          * ones bit should do the trick to get us the value we
379          * want.
380          */
381         return (y & ~1);
382     }
383
384     return y;
385 }
386
387 JS_DEFINE_CALLINFO_1(extern, INT32, js_TypedArray_uint8_clamp_double, DOUBLE,
388                      1, nanojit::ACCSET_NONE)
389
390
391 struct uint8_clamped {
392     uint8 val;
393
394     uint8_clamped() { }
395     uint8_clamped(const uint8_clamped& other) : val(other.val) { }
396
397     // invoke our assignment helpers for constructor conversion
398     uint8_clamped(uint8 x)    { *this = x; }
399     uint8_clamped(uint16 x)   { *this = x; }
400     uint8_clamped(uint32 x)   { *this = x; }
401     uint8_clamped(int8 x)     { *this = x; }
402     uint8_clamped(int16 x)    { *this = x; }
403     uint8_clamped(int32 x)    { *this = x; }
404     uint8_clamped(jsdouble x) { *this = x; }
405
406     inline uint8_clamped& operator= (const uint8_clamped& x) {
407         val = x.val;
408         return *this;
409     }
410
411     inline uint8_clamped& operator= (uint8 x) {
412         val = x;
413         return *this;
414     }
415
416     inline uint8_clamped& operator= (uint16 x) {
417         val = (x > 255) ? 255 : uint8(x);
418         return *this;
419     }
420
421     inline uint8_clamped& operator= (uint32 x) {
422         val = (x > 255) ? 255 : uint8(x);
423         return *this;
424     }
425
426     inline uint8_clamped& operator= (int8 x) {
427         val = (x >= 0) ? uint8(x) : 0;
428         return *this;
429     }
430
431     inline uint8_clamped& operator= (int16 x) {
432         val = (x >= 0)
433               ? ((x < 255)
434                  ? uint8(x)
435                  : 255)
436               : 0;
437         return *this;
438     }
439
440     inline uint8_clamped& operator= (int32 x) { 
441         val = (x >= 0)
442               ? ((x < 255)
443                  ? uint8(x)
444                  : 255)
445               : 0;
446         return *this;
447     }
448
449     inline uint8_clamped& operator= (const jsdouble x) { 
450         val = uint8(js_TypedArray_uint8_clamp_double(x));
451         return *this;
452     }
453
454     inline operator uint8() const {
455         return val;
456     }
457 };
458
459 /* Make sure the compiler isn't doing some funky stuff */
460 JS_STATIC_ASSERT(sizeof(uint8_clamped) == 1);
461
462 template<typename NativeType> static inline const int TypeIDOfType();
463 template<> inline const int TypeIDOfType<int8>() { return TypedArray::TYPE_INT8; }
464 template<> inline const int TypeIDOfType<uint8>() { return TypedArray::TYPE_UINT8; }
465 template<> inline const int TypeIDOfType<int16>() { return TypedArray::TYPE_INT16; }
466 template<> inline const int TypeIDOfType<uint16>() { return TypedArray::TYPE_UINT16; }
467 template<> inline const int TypeIDOfType<int32>() { return TypedArray::TYPE_INT32; }
468 template<> inline const int TypeIDOfType<uint32>() { return TypedArray::TYPE_UINT32; }
469 template<> inline const int TypeIDOfType<float>() { return TypedArray::TYPE_FLOAT32; }
470 template<> inline const int TypeIDOfType<double>() { return TypedArray::TYPE_FLOAT64; }
471 template<> inline const int TypeIDOfType<uint8_clamped>() { return TypedArray::TYPE_UINT8_CLAMPED; }
472
473 template<typename NativeType> static inline const bool TypeIsUnsigned() { return false; }
474 template<> inline const bool TypeIsUnsigned<uint8>() { return true; }
475 template<> inline const bool TypeIsUnsigned<uint16>() { return true; }
476 template<> inline const bool TypeIsUnsigned<uint32>() { return true; }
477
478 template<typename NativeType> static inline const bool TypeIsFloatingPoint() { return false; }
479 template<> inline const bool TypeIsFloatingPoint<float>() { return true; }
480 template<> inline const bool TypeIsFloatingPoint<double>() { return true; }
481
482 template<typename NativeType> class TypedArrayTemplate;
483
484 typedef TypedArrayTemplate<int8> Int8Array;
485 typedef TypedArrayTemplate<uint8> Uint8Array;
486 typedef TypedArrayTemplate<int16> Int16Array;
487 typedef TypedArrayTemplate<uint16> Uint16Array;
488 typedef TypedArrayTemplate<int32> Int32Array;
489 typedef TypedArrayTemplate<uint32> Uint32Array;
490 typedef TypedArrayTemplate<float> Float32Array;
491 typedef TypedArrayTemplate<double> Float64Array;
492 typedef TypedArrayTemplate<uint8_clamped> Uint8ClampedArray;
493
494 template<typename NativeType>
495 class TypedArrayTemplate
496   : public TypedArray
497 {
498   public:
499     typedef NativeType ThisType;
500     typedef TypedArrayTemplate<NativeType> ThisTypeArray;
501     static const int ArrayTypeID() { return TypeIDOfType<NativeType>(); }
502     static const bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
503     static const bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
504
505     static JSFunctionSpec jsfuncs[];
506
507     static inline Class *slowClass()
508     {
509         return &TypedArray::slowClasses[ArrayTypeID()];
510     }
511
512     static inline Class *fastClass()
513     {
514         return &TypedArray::fastClasses[ArrayTypeID()];
515     }
516
517     static JSBool
518     obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
519     {
520         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
521         JS_ASSERT(tarray);
522
523         if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
524             vp->setNumber(tarray->length);
525             return true;
526         }
527
528         jsuint index;
529         if (tarray->isArrayIndex(cx, id, &index)) {
530             // this inline function is specialized for each type
531             tarray->copyIndexToValue(cx, index, vp);
532         } else {
533             JSObject *obj2;
534             JSProperty *prop;
535             const Shape *shape;
536
537             JSObject *proto = obj->getProto();
538             if (!proto) {
539                 vp->setUndefined();
540                 return true;
541             }
542
543             vp->setUndefined();
544             if (js_LookupPropertyWithFlags(cx, proto, id, cx->resolveFlags, &obj2, &prop) < 0)
545                 return false;
546
547             if (prop) {
548                 if (obj2->isNative()) {
549                     shape = (Shape *) prop;
550                     if (!js_NativeGet(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, vp))
551                         return false;
552                 }
553             }
554         }
555
556         return true;
557     }
558
559     static JSBool
560     obj_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
561     {
562         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
563         JS_ASSERT(tarray);
564
565         if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
566             vp->setNumber(tarray->length);
567             return true;
568         }
569
570         jsuint index;
571         // We can't just chain to js_SetProperty, because we're not a normal object.
572         if (!tarray->isArrayIndex(cx, id, &index)) {
573 #if 0
574             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
575                                  JSMSG_TYPED_ARRAY_BAD_INDEX);
576             return false;
577 #endif
578             // Silent ignore is better than an exception here, because
579             // at some point we may want to support other properties on
580             // these objects.  This is especially true when these arrays
581             // are used to implement HTML Canvas 2D's PixelArray objects,
582             // which used to be plain old arrays.
583             vp->setUndefined();
584             return true;
585         }
586
587         if (vp->isInt32()) {
588             tarray->setIndex(index, NativeType(vp->toInt32()));
589             return true;
590         }
591
592         jsdouble d;
593
594         if (vp->isDouble()) {
595             d = vp->toDouble();
596         } else if (vp->isNull()) {
597             d = 0.0f;
598         } else if (vp->isPrimitive()) {
599             JS_ASSERT(vp->isString() || vp->isUndefined() || vp->isBoolean());
600             if (vp->isString()) {
601                 // note that ValueToNumber will always succeed with a string arg
602                 ValueToNumber(cx, *vp, &d);
603             } else if (vp->isUndefined()) {
604                 d = js_NaN;
605             } else {
606                 d = (double) vp->toBoolean();
607             }
608         } else {
609             // non-primitive assignments become NaN or 0 (for float/int arrays)
610             d = js_NaN;
611         }
612
613         // If the array is an integer array, we only handle up to
614         // 32-bit ints from this point on.  if we want to handle
615         // 64-bit ints, we'll need some changes.
616
617         // Assign based on characteristics of the destination type
618         if (ArrayTypeIsFloatingPoint()) {
619             tarray->setIndex(index, NativeType(d));
620         } else if (ArrayTypeIsUnsigned()) {
621             JS_ASSERT(sizeof(NativeType) <= 4);
622             uint32 n = js_DoubleToECMAUint32(d);
623             tarray->setIndex(index, NativeType(n));
624         } else if (ArrayTypeID() == TypedArray::TYPE_UINT8_CLAMPED) {
625             // The uint8_clamped type has a special rounding converter
626             // for doubles.
627             tarray->setIndex(index, NativeType(d));
628         } else {
629             JS_ASSERT(sizeof(NativeType) <= 4);
630             int32 n = js_DoubleToECMAInt32(d);
631             tarray->setIndex(index, NativeType(n));
632         }
633
634         return true;
635     }
636
637     static JSBool
638     obj_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v,
639                        PropertyOp getter, StrictPropertyOp setter, uintN attrs)
640     {
641         if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
642             return true;
643
644         Value tmp = *v;
645         return obj_setProperty(cx, obj, id, &tmp, false);
646     }
647
648     static JSBool
649     obj_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
650     {
651         if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
652             rval->setBoolean(false);
653             return true;
654         }
655
656         TypedArray *tarray = TypedArray::fromJSObject(obj);
657         JS_ASSERT(tarray);
658
659         if (tarray->isArrayIndex(cx, id)) {
660             rval->setBoolean(false);
661             return true;
662         }
663
664         rval->setBoolean(true);
665         return true;
666     }
667
668     static JSBool
669     obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
670                   Value *statep, jsid *idp)
671     {
672         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
673         JS_ASSERT(tarray);
674
675         /*
676          * Iteration is "length" (if JSENUMERATE_INIT_ALL), then [0, length).
677          * *statep is JSVAL_TRUE if enumerating "length" and
678          * JSVAL_TO_INT(index) when enumerating index.
679          */
680         switch (enum_op) {
681           case JSENUMERATE_INIT_ALL:
682             statep->setBoolean(true);
683             if (idp)
684                 *idp = INT_TO_JSID(tarray->length + 1);
685             break;
686
687           case JSENUMERATE_INIT:
688             statep->setInt32(0);
689             if (idp)
690                 *idp = INT_TO_JSID(tarray->length);
691             break;
692
693           case JSENUMERATE_NEXT:
694             if (statep->isTrue()) {
695                 *idp = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
696                 statep->setInt32(0);
697             } else {
698                 uint32 index = statep->toInt32();
699                 if (index < uint32(tarray->length)) {
700                     *idp = INT_TO_JSID(index);
701                     statep->setInt32(index + 1);
702                 } else {
703                     JS_ASSERT(index == tarray->length);
704                     statep->setNull();
705                 }
706             }
707             break;
708
709           case JSENUMERATE_DESTROY:
710             statep->setNull();
711             break;
712         }
713
714         return true;
715     }
716
717     static JSType
718     obj_typeOf(JSContext *cx, JSObject *obj)
719     {
720         return JSTYPE_OBJECT;
721     }
722
723     static JSObject *
724     createTypedArray(JSContext *cx, JSObject *bufobj, uint32 byteOffset, uint32 len)
725     {
726         JSObject *obj = NewBuiltinClassInstance(cx, slowClass());
727         if (!obj)
728             return NULL;
729
730         ThisTypeArray *tarray = cx->create<ThisTypeArray>(bufobj, byteOffset, len);
731         if (!tarray)
732             return NULL;
733
734         JS_ASSERT(obj->getClass() == slowClass());
735         obj->setSharedNonNativeMap();
736         obj->clasp = fastClass();
737         obj->setPrivate(tarray);
738
739         // FIXME Bug 599008: make it ok to call preventExtensions here.
740         obj->flags |= JSObject::NOT_EXTENSIBLE;
741
742         return obj;
743     }
744
745     /*
746      * new [Type]Array(length)
747      * new [Type]Array(otherTypedArray)
748      * new [Type]Array(JSArray)
749      * new [Type]Array(ArrayBuffer, [optional] byteOffset, [optional] length)
750      */
751     static JSBool
752     class_constructor(JSContext *cx, uintN argc, Value *vp)
753     {
754         /* N.B. this is a constructor for slowClass, not fastClass! */
755         JSObject *obj = create(cx, argc, JS_ARGV(cx, vp));
756         if (!obj)
757             return false;
758         vp->setObject(*obj);
759         return true;
760     }
761
762     static JSObject *
763     create(JSContext *cx, uintN argc, Value *argv)
764     {
765         /* N.B. there may not be an argv[-2]/argv[-1]. */
766
767         /* () or (number) */
768         jsuint len = 0;
769         if (argc == 0 || ValueIsLength(cx, argv[0], &len)) {
770             JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
771             if (!bufobj)
772                 return NULL;
773
774             return createTypedArray(cx, bufobj, 0, len);
775         }
776
777         /* (not an object) */
778         if (!argv[0].isObject()) {
779             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
780                                  JSMSG_TYPED_ARRAY_BAD_ARGS);
781             return NULL;
782         }
783
784         JSObject *dataObj = &argv[0].toObject();
785
786         /* (typedArray) */
787         if (js_IsTypedArray(dataObj)) {
788             TypedArray *otherTypedArray = TypedArray::fromJSObject(dataObj);
789             JS_ASSERT(otherTypedArray);
790
791             uint32 len = otherTypedArray->length;
792             JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
793             if (!bufobj)
794                 return NULL;
795
796             JSObject *obj = createTypedArray(cx, bufobj, 0, len);
797             if (!obj || !copyFrom(cx, obj, otherTypedArray, 0))
798                 return NULL;
799             return obj;
800         }
801
802         /* (obj, byteOffset, length). */
803         int32_t byteOffset = -1;
804         int32_t length = -1;
805
806         if (argc > 1) {
807             if (!ValueToInt32(cx, argv[1], &byteOffset))
808                 return NULL;
809             if (byteOffset < 0) {
810                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
811                                      JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1");
812                 return NULL;
813             }
814
815             if (argc > 2) {
816                 if (!ValueToInt32(cx, argv[2], &length))
817                     return NULL;
818                 if (length < 0) {
819                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
820                                          JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2");
821                     return NULL;
822                 }
823             }
824         }
825
826         /* (obj, byteOffset, length) */
827         return createTypedArrayWithOffsetLength(cx, dataObj, byteOffset, length);
828     }
829
830     static void
831     class_finalize(JSContext *cx, JSObject *obj)
832     {
833         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
834         if (tarray)
835             cx->destroy<ThisTypeArray>(tarray);
836     }
837
838     /* subarray(start[, end]) */
839     static JSBool
840     fun_subarray(JSContext *cx, uintN argc, Value *vp)
841     {
842         JSObject *obj = ToObject(cx, &vp[1]);
843         if (!obj)
844             return false;
845
846         if (!InstanceOf(cx, obj, ThisTypeArray::fastClass(), vp + 2))
847             return false;
848
849         if (obj->getClass() != fastClass()) {
850             // someone tried to apply this subarray() to the wrong class
851             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
852                                  JSMSG_INCOMPATIBLE_METHOD,
853                                  fastClass()->name, "subarray", obj->getClass()->name);
854             return false;
855         }
856
857         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
858         if (!tarray)
859             return true;
860
861         // these are the default values
862         int32_t begin = 0, end = tarray->length;
863         int32_t length = int32(tarray->length);
864
865         if (argc > 0) {
866             Value *argv = JS_ARGV(cx, vp);
867             if (!ValueToInt32(cx, argv[0], &begin))
868                 return false;
869             if (begin < 0) {
870                 begin += length;
871                 if (begin < 0)
872                     begin = 0;
873             } else if (begin > length) {
874                 begin = length;
875             }
876
877             if (argc > 1) {
878                 if (!ValueToInt32(cx, argv[1], &end))
879                     return false;
880                 if (end < 0) {
881                     end += length;
882                     if (end < 0)
883                         end = 0;
884                 } else if (end > length) {
885                     end = length;
886                 }
887             }
888         }
889
890         if (begin > end)
891             begin = end;
892
893         JSObject *nobj = createSubarray(cx, tarray, begin, end);
894         if (!nobj)
895             return false;
896         vp->setObject(*nobj);
897         return true;
898     }
899
900     /* set(array[, offset]) */
901     static JSBool
902     fun_set(JSContext *cx, uintN argc, Value *vp)
903     {
904         JSObject *obj = ToObject(cx, &vp[1]);
905         if (!obj)
906             return false;
907
908         if (!InstanceOf(cx, obj, ThisTypeArray::fastClass(), vp + 2))
909             return false;
910
911         if (obj->getClass() != fastClass()) {
912             // someone tried to apply this set() to the wrong class
913             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
914                                  JSMSG_INCOMPATIBLE_METHOD,
915                                  fastClass()->name, "set", obj->getClass()->name);
916             return false;
917         }
918
919         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
920         if (!tarray)
921             return true;
922
923         // these are the default values
924         int32_t off = 0;
925
926         Value *argv = JS_ARGV(cx, vp);
927         if (argc > 1) {
928             if (!ValueToInt32(cx, argv[1], &off))
929                 return false;
930
931             if (off < 0 || uint32_t(off) > tarray->length) {
932                 // the given offset is bogus
933                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
934                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
935                 return false;
936             }
937         }
938
939         uint32 offset(off);
940
941         // first arg must be either a typed array or a JS array
942         if (argc == 0 || !argv[0].isObject()) {
943             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
944                                  JSMSG_TYPED_ARRAY_BAD_ARGS);
945             return false;
946         }
947
948         JSObject *arg0 = argv[0].toObjectOrNull();
949         if (js_IsTypedArray(arg0)) {
950             TypedArray *src = TypedArray::fromJSObject(arg0);
951             if (!src ||
952                 src->length > tarray->length - offset)
953             {
954                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
955                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
956                 return false;
957             }
958
959             if (!copyFrom(cx, obj, src, offset))
960                 return false;
961         } else {
962             jsuint len;
963             if (!js_GetLengthProperty(cx, arg0, &len))
964                 return false;
965
966             // avoid overflow; we know that offset <= length
967             if (len > tarray->length - offset) {
968                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
969                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
970                 return false;
971             }
972
973             if (!copyFrom(cx, obj, arg0, len, offset))
974                 return false;
975         }
976
977         vp->setUndefined();
978         return true;
979     }
980
981     static ThisTypeArray *
982     fromJSObject(JSObject *obj)
983     {
984         JS_ASSERT(obj->getClass() == fastClass());
985         return reinterpret_cast<ThisTypeArray*>(obj->getPrivate());
986     }
987
988   public:
989     TypedArrayTemplate(JSObject *bufobj, uint32 byteOffset, uint32 len)
990     {
991         JS_ASSERT(bufobj->getClass() == &ArrayBuffer::jsclass);
992
993         type = ArrayTypeID();
994         bufferJS = bufobj;
995         buffer = ArrayBuffer::fromJSObject(bufobj);
996
997         this->byteOffset = byteOffset;
998
999         JS_ASSERT(byteOffset <= buffer->byteLength);
1000         this->data = buffer->offsetData(byteOffset);
1001         JS_ASSERT(buffer->data <= this->data);
1002         JS_ASSERT(this->data <= buffer->offsetData(buffer->byteLength));
1003
1004         this->byteLength = len * sizeof(NativeType);
1005         JS_ASSERT(buffer->byteLength - byteOffset >= this->byteLength);
1006
1007         this->length = len;
1008     }
1009
1010     static JSObject *
1011     createTypedArrayWithOffsetLength(JSContext *cx, JSObject *other,
1012                                      int32 byteOffsetInt, int32 lengthInt)
1013     {
1014         JS_ASSERT(!js_IsTypedArray(other));
1015
1016         /* Handle creation from an ArrayBuffer not ArrayBuffer.prototype. */
1017         ArrayBuffer *abuf;
1018         if (other->getClass() == &ArrayBuffer::jsclass &&
1019                    ((abuf = ArrayBuffer::fromJSObject(other)) != NULL)) {
1020             uint32 boffset = (byteOffsetInt < 0) ? 0 : uint32(byteOffsetInt);
1021
1022             if (boffset > abuf->byteLength || boffset % sizeof(NativeType) != 0) {
1023                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1024                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
1025                 return NULL; // invalid byteOffset
1026             }
1027
1028             uint32 len;
1029             if (lengthInt < 0) {
1030                 len = (abuf->byteLength - boffset) / sizeof(NativeType);
1031                 if (len * sizeof(NativeType) != (abuf->byteLength - boffset)) {
1032                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1033                                          JSMSG_TYPED_ARRAY_BAD_ARGS);
1034                     return NULL; // given byte array doesn't map exactly to sizeof(NativeType)*N
1035                 }
1036             } else {
1037                 len = (uint32) lengthInt;
1038             }
1039
1040             // Go slowly and check for overflow.
1041             uint32 arrayByteLength = len*sizeof(NativeType);
1042             if (uint32(len) >= INT32_MAX / sizeof(NativeType) ||
1043                 uint32(boffset) >= INT32_MAX - arrayByteLength)
1044             {
1045                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1046                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
1047                 return NULL; // overflow occurred along the way when calculating boffset+len*sizeof(NativeType)
1048             }
1049
1050             if (arrayByteLength + boffset > abuf->byteLength) {
1051                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1052                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
1053                 return NULL; // boffset+len is too big for the arraybuffer
1054             }
1055
1056             return createTypedArray(cx, other, boffset, len);
1057         }
1058
1059         /*
1060          * Otherwise create a new typed array and copy len properties from the
1061          * object.
1062          */
1063         jsuint len;
1064         if (!js_GetLengthProperty(cx, other, &len))
1065             return NULL;
1066
1067         JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
1068         if (!bufobj)
1069             return NULL;
1070
1071         JSObject *obj = createTypedArray(cx, bufobj, 0, len);
1072         if (!obj || !copyFrom(cx, obj, other, len))
1073             return NULL;
1074         return obj;
1075     }
1076
1077     const NativeType
1078     getIndex(uint32 index) const
1079     {
1080         return *(static_cast<const NativeType*>(data) + index);
1081     }
1082
1083     void
1084     setIndex(uint32 index, NativeType val)
1085     {
1086         *(static_cast<NativeType*>(data) + index) = val;
1087     }
1088
1089     inline void copyIndexToValue(JSContext *cx, uint32 index, Value *vp);
1090
1091     static JSObject *
1092     createSubarray(JSContext *cx, ThisTypeArray *tarray, uint32 begin, uint32 end)
1093     {
1094         JS_ASSERT(tarray);
1095
1096         JS_ASSERT(0 <= begin);
1097         JS_ASSERT(begin <= tarray->length);
1098         JS_ASSERT(0 <= end);
1099         JS_ASSERT(end <= tarray->length);
1100
1101         JSObject *bufobj = tarray->bufferJS;
1102         JS_ASSERT(bufobj);
1103
1104         JS_ASSERT(begin <= end);
1105         uint32 length = end - begin;
1106
1107         JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
1108         uint32 byteOffset = begin * sizeof(NativeType);
1109
1110         return createTypedArray(cx, bufobj, byteOffset, length);
1111     }
1112
1113   protected:
1114     static NativeType
1115     nativeFromValue(JSContext *cx, const Value &v)
1116     {
1117         if (v.isInt32())
1118             return NativeType(v.toInt32());
1119
1120         if (v.isDouble()) {
1121             double d = v.toDouble();
1122             if (!ArrayTypeIsFloatingPoint() && JS_UNLIKELY(JSDOUBLE_IS_NaN(d)))
1123                 return NativeType(int32(0));
1124             if (TypeIsFloatingPoint<NativeType>())
1125                 return NativeType(d);
1126             if (TypeIsUnsigned<NativeType>())
1127                 return NativeType(js_DoubleToECMAUint32(d));
1128             return NativeType(js_DoubleToECMAInt32(d));
1129         }
1130
1131         if (v.isPrimitive() && !v.isMagic()) {
1132             jsdouble dval;
1133             ValueToNumber(cx, v, &dval);
1134             return NativeType(dval);
1135         }
1136
1137         if (ArrayTypeIsFloatingPoint())
1138             return NativeType(js_NaN);
1139
1140         return NativeType(int32(0));
1141     }
1142     
1143     static bool
1144     copyFrom(JSContext *cx, JSObject *thisTypedArrayObj,
1145              JSObject *ar, jsuint len, jsuint offset = 0)
1146     {
1147         ThisTypeArray *thisTypedArray = fromJSObject(thisTypedArrayObj);
1148         JS_ASSERT(thisTypedArray);
1149
1150         JS_ASSERT(offset <= thisTypedArray->length);
1151         JS_ASSERT(len <= thisTypedArray->length - offset);
1152         NativeType *dest = static_cast<NativeType*>(thisTypedArray->data) + offset;
1153
1154         if (ar->isDenseArray() && ar->getDenseArrayCapacity() >= len) {
1155             JS_ASSERT(ar->getArrayLength() == len);
1156
1157             Value *src = ar->getDenseArrayElements();
1158
1159             for (uintN i = 0; i < len; ++i)
1160                 *dest++ = nativeFromValue(cx, *src++);
1161         } else {
1162             // slow path
1163             Value v;
1164
1165             for (uintN i = 0; i < len; ++i) {
1166                 if (!ar->getProperty(cx, INT_TO_JSID(i), &v))
1167                     return false;
1168                 *dest++ = nativeFromValue(cx, v);
1169             }
1170         }
1171
1172         return true;
1173     }
1174
1175     static bool
1176     copyFrom(JSContext *cx, JSObject *thisTypedArrayObj, TypedArray *tarray, jsuint offset)
1177     {
1178         ThisTypeArray *thisTypedArray = fromJSObject(thisTypedArrayObj);
1179         JS_ASSERT(thisTypedArray);
1180
1181         JS_ASSERT(offset <= thisTypedArray->length);
1182         JS_ASSERT(tarray->length <= thisTypedArray->length - offset);
1183         if (tarray->buffer == thisTypedArray->buffer)
1184             return thisTypedArray->copyFromWithOverlap(cx, tarray, offset);
1185
1186         NativeType *dest = static_cast<NativeType*>(thisTypedArray->data) + offset;
1187
1188         if (tarray->type == thisTypedArray->type) {
1189             memcpy(dest, tarray->data, tarray->byteLength);
1190             return true;
1191         }
1192
1193         uintN srclen = tarray->length;
1194         switch (tarray->type) {
1195           case TypedArray::TYPE_INT8: {
1196             int8 *src = static_cast<int8*>(tarray->data);
1197             for (uintN i = 0; i < srclen; ++i)
1198                 *dest++ = NativeType(*src++);
1199             break;
1200           }
1201           case TypedArray::TYPE_UINT8:
1202           case TypedArray::TYPE_UINT8_CLAMPED: {
1203             uint8 *src = static_cast<uint8*>(tarray->data);
1204             for (uintN i = 0; i < srclen; ++i)
1205                 *dest++ = NativeType(*src++);
1206             break;
1207           }
1208           case TypedArray::TYPE_INT16: {
1209             int16 *src = static_cast<int16*>(tarray->data);
1210             for (uintN i = 0; i < srclen; ++i)
1211                 *dest++ = NativeType(*src++);
1212             break;
1213           }
1214           case TypedArray::TYPE_UINT16: {
1215             uint16 *src = static_cast<uint16*>(tarray->data);
1216             for (uintN i = 0; i < srclen; ++i)
1217                 *dest++ = NativeType(*src++);
1218             break;
1219           }
1220           case TypedArray::TYPE_INT32: {
1221             int32 *src = static_cast<int32*>(tarray->data);
1222             for (uintN i = 0; i < srclen; ++i)
1223                 *dest++ = NativeType(*src++);
1224             break;
1225           }
1226           case TypedArray::TYPE_UINT32: {
1227             uint32 *src = static_cast<uint32*>(tarray->data);
1228             for (uintN i = 0; i < srclen; ++i)
1229                 *dest++ = NativeType(*src++);
1230             break;
1231           }
1232           case TypedArray::TYPE_FLOAT32: {
1233             float *src = static_cast<float*>(tarray->data);
1234             for (uintN i = 0; i < srclen; ++i)
1235                 *dest++ = NativeType(*src++);
1236             break;
1237           }
1238           case TypedArray::TYPE_FLOAT64: {
1239             double *src = static_cast<double*>(tarray->data);
1240             for (uintN i = 0; i < srclen; ++i)
1241                 *dest++ = NativeType(*src++);
1242             break;
1243           }
1244           default:
1245             JS_NOT_REACHED("copyFrom with a TypedArray of unknown type");
1246             break;
1247         }
1248
1249         return true;
1250     }
1251
1252     bool
1253     copyFromWithOverlap(JSContext *cx, TypedArray *tarray, jsuint offset)
1254     {
1255         JS_ASSERT(offset <= length);
1256
1257         NativeType *dest = static_cast<NativeType*>(data) + offset;
1258
1259         if (tarray->type == type) {
1260             memmove(dest, tarray->data, tarray->byteLength);
1261             return true;
1262         }
1263
1264         // We have to make a copy of the source array here, since
1265         // there's overlap, and we have to convert types.
1266         void *srcbuf = cx->malloc(tarray->byteLength);
1267         if (!srcbuf)
1268             return false;
1269         memcpy(srcbuf, tarray->data, tarray->byteLength);
1270
1271         switch (tarray->type) {
1272           case TypedArray::TYPE_INT8: {
1273             int8 *src = (int8*) srcbuf;
1274             for (uintN i = 0; i < tarray->length; ++i)
1275                 *dest++ = NativeType(*src++);
1276             break;
1277           }
1278           case TypedArray::TYPE_UINT8:
1279           case TypedArray::TYPE_UINT8_CLAMPED: {
1280             uint8 *src = (uint8*) srcbuf;
1281             for (uintN i = 0; i < tarray->length; ++i)
1282                 *dest++ = NativeType(*src++);
1283             break;
1284           }
1285           case TypedArray::TYPE_INT16: {
1286             int16 *src = (int16*) srcbuf;
1287             for (uintN i = 0; i < tarray->length; ++i)
1288                 *dest++ = NativeType(*src++);
1289             break;
1290           }
1291           case TypedArray::TYPE_UINT16: {
1292             uint16 *src = (uint16*) srcbuf;
1293             for (uintN i = 0; i < tarray->length; ++i)
1294                 *dest++ = NativeType(*src++);
1295             break;
1296           }
1297           case TypedArray::TYPE_INT32: {
1298             int32 *src = (int32*) srcbuf;
1299             for (uintN i = 0; i < tarray->length; ++i)
1300                 *dest++ = NativeType(*src++);
1301             break;
1302           }
1303           case TypedArray::TYPE_UINT32: {
1304             uint32 *src = (uint32*) srcbuf;
1305             for (uintN i = 0; i < tarray->length; ++i)
1306                 *dest++ = NativeType(*src++);
1307             break;
1308           }
1309           case TypedArray::TYPE_FLOAT32: {
1310             float *src = (float*) srcbuf;
1311             for (uintN i = 0; i < tarray->length; ++i)
1312                 *dest++ = NativeType(*src++);
1313             break;
1314           }
1315           case TypedArray::TYPE_FLOAT64: {
1316             double *src = (double*) srcbuf;
1317             for (uintN i = 0; i < tarray->length; ++i)
1318                 *dest++ = NativeType(*src++);
1319             break;
1320           }
1321           default:
1322             JS_NOT_REACHED("copyFromWithOverlap with a TypedArray of unknown type");
1323             break;
1324         }
1325
1326         js_free(srcbuf);
1327         return true;
1328     }
1329
1330     static JSObject *
1331     createBufferWithSizeAndCount(JSContext *cx, uint32 count)
1332     {
1333         size_t size = sizeof(NativeType);
1334         if (size != 0 && count >= INT32_MAX / size) {
1335             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1336                                  JSMSG_NEED_DIET, "size and count");
1337             return false;
1338         }
1339
1340         int32 bytelen = size * count;
1341         return ArrayBuffer::create(cx, bytelen);
1342     }
1343 };
1344
1345 // this default implementation is only valid for integer types
1346 // less than 32-bits in size.
1347 template<typename NativeType>
1348 void
1349 TypedArrayTemplate<NativeType>::copyIndexToValue(JSContext *cx, uint32 index, Value *vp)
1350 {
1351     JS_STATIC_ASSERT(sizeof(NativeType) < 4);
1352
1353     vp->setInt32(getIndex(index));
1354 }
1355
1356 // and we need to specialize for 32-bit integers and floats
1357 template<>
1358 void
1359 TypedArrayTemplate<int32>::copyIndexToValue(JSContext *cx, uint32 index, Value *vp)
1360 {
1361     int32 val = getIndex(index);
1362     vp->setInt32(val);
1363 }
1364
1365 template<>
1366 void
1367 TypedArrayTemplate<uint32>::copyIndexToValue(JSContext *cx, uint32 index, Value *vp)
1368 {
1369     uint32 val = getIndex(index);
1370     vp->setNumber(val);
1371 }
1372
1373 template<>
1374 void
1375 TypedArrayTemplate<float>::copyIndexToValue(JSContext *cx, uint32 index, Value *vp)
1376 {
1377     float val = getIndex(index);
1378     double dval = val;
1379
1380     /*
1381      * Doubles in typed arrays could be typed-punned arrays of integers. This
1382      * could allow user code to break the engine-wide invariant that only
1383      * canonical nans are stored into jsvals, which means user code could
1384      * confuse the engine into interpreting a double-typed jsval as an
1385      * object-typed jsval.
1386      *
1387      * This could be removed for platforms/compilers known to convert a 32-bit
1388      * non-canonical nan to a 64-bit canonical nan.
1389      */
1390     if (JS_UNLIKELY(JSDOUBLE_IS_NaN(dval)))
1391         dval = js_NaN;
1392
1393     vp->setDouble(dval);
1394 }
1395
1396 template<>
1397 void
1398 TypedArrayTemplate<double>::copyIndexToValue(JSContext *cx, uint32 index, Value *vp)
1399 {
1400     double val = getIndex(index);
1401
1402     /*
1403      * Doubles in typed arrays could be typed-punned arrays of integers. This
1404      * could allow user code to break the engine-wide invariant that only
1405      * canonical nans are stored into jsvals, which means user code could
1406      * confuse the engine into interpreting a double-typed jsval as an
1407      * object-typed jsval.
1408      */
1409     if (JS_UNLIKELY(JSDOUBLE_IS_NaN(val)))
1410         val = js_NaN;
1411
1412     vp->setDouble(val);
1413 }
1414
1415 /***
1416  *** JS impl
1417  ***/
1418
1419 /*
1420  * ArrayBuffer (base)
1421  */
1422
1423 Class ArrayBuffer::jsclass = {
1424     "ArrayBuffer",
1425     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
1426     PropertyStub,         /* addProperty */
1427     PropertyStub,         /* delProperty */
1428     PropertyStub,         /* getProperty */
1429     StrictPropertyStub,   /* setProperty */
1430     EnumerateStub,
1431     ResolveStub,
1432     ConvertStub,
1433     ArrayBuffer::class_finalize,
1434 };
1435
1436 JSPropertySpec ArrayBuffer::jsprops[] = {
1437     { "byteLength",
1438       -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
1439       Jsvalify(ArrayBuffer::prop_getByteLength), JS_StrictPropertyStub },
1440     {0,0,0,0,0}
1441 };
1442
1443 /*
1444  * shared TypedArray
1445  */
1446
1447 JSPropertySpec TypedArray::jsprops[] = {
1448     { js_length_str,
1449       -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
1450       Jsvalify(TypedArray::prop_getLength), JS_StrictPropertyStub },
1451     { "byteLength",
1452       -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
1453       Jsvalify(TypedArray::prop_getByteLength), JS_StrictPropertyStub },
1454     { "byteOffset",
1455       -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
1456       Jsvalify(TypedArray::prop_getByteOffset), JS_StrictPropertyStub },
1457     { "buffer",
1458       -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
1459       Jsvalify(TypedArray::prop_getBuffer), JS_StrictPropertyStub },
1460     {0,0,0,0,0}
1461 };
1462
1463 /*
1464  * TypedArray boilerplate
1465  */
1466
1467 #define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                  \
1468 template<> JSFunctionSpec _typedArray::jsfuncs[] = {                           \
1469     JS_FN("subarray", _typedArray::fun_subarray, 2, 0),                            \
1470     JS_FN("set", _typedArray::fun_set, 2, 0),                                  \
1471     JS_FS_END                                                                  \
1472 }
1473
1474 #define IMPL_TYPED_ARRAY_SLOW_CLASS(_typedArray)                               \
1475 {                                                                              \
1476     #_typedArray,                                                              \
1477     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray),     \
1478     PropertyStub,         /* addProperty */                                    \
1479     PropertyStub,         /* delProperty */                                    \
1480     PropertyStub,         /* getProperty */                                    \
1481     StrictPropertyStub,   /* setProperty */                                    \
1482     EnumerateStub,                                                             \
1483     ResolveStub,                                                               \
1484     ConvertStub,                                                               \
1485     FinalizeStub                                                               \
1486 }
1487
1488 #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray)                               \
1489 {                                                                              \
1490     #_typedArray,                                                              \
1491     Class::NON_NATIVE | JSCLASS_HAS_PRIVATE,                                   \
1492     PropertyStub,         /* addProperty */                                    \
1493     PropertyStub,         /* delProperty */                                    \
1494     PropertyStub,         /* getProperty */                                    \
1495     StrictPropertyStub,   /* setProperty */                                    \
1496     EnumerateStub,                                                             \
1497     ResolveStub,                                                               \
1498     ConvertStub,                                                               \
1499     _typedArray::class_finalize,                                               \
1500     NULL,           /* reserved0   */                                          \
1501     NULL,           /* checkAccess */                                          \
1502     NULL,           /* call        */                                          \
1503     NULL,           /* construct   */                                          \
1504     NULL,           /* xdrObject   */                                          \
1505     NULL,           /* hasInstance */                                          \
1506     NULL,           /* mark        */                                          \
1507     JS_NULL_CLASS_EXT,                                                         \
1508     {                                                                          \
1509         _typedArray::obj_lookupProperty,                                       \
1510         _typedArray::obj_defineProperty,                                       \
1511         _typedArray::obj_getProperty,                                          \
1512         _typedArray::obj_setProperty,                                          \
1513         _typedArray::obj_getAttributes,                                        \
1514         _typedArray::obj_setAttributes,                                        \
1515         _typedArray::obj_deleteProperty,                                       \
1516         _typedArray::obj_enumerate,                                            \
1517         _typedArray::obj_typeOf,                                               \
1518         _typedArray::obj_trace,                                                \
1519         NULL,       /* thisObject      */                                      \
1520         NULL,       /* clear           */                                      \
1521     }                                                                          \
1522 }
1523
1524 #define INIT_TYPED_ARRAY_CLASS(_typedArray,_type)                              \
1525 do {                                                                           \
1526     proto = js_InitClass(cx, obj, NULL,                                        \
1527                          &TypedArray::slowClasses[TypedArray::_type],          \
1528                          _typedArray::class_constructor, 3,                    \
1529                          _typedArray::jsprops,                                 \
1530                          _typedArray::jsfuncs,                                 \
1531                          NULL, NULL);                                          \
1532     if (!proto)                                                                \
1533         return NULL;                                                           \
1534     JSObject *ctor = JS_GetConstructor(cx, proto);                             \
1535     if (!ctor ||                                                               \
1536         !JS_DefineProperty(cx, ctor, "BYTES_PER_ELEMENT",                      \
1537                            INT_TO_JSVAL(sizeof(_typedArray::ThisType)),        \
1538                            JS_PropertyStub, JS_StrictPropertyStub,             \
1539                            JSPROP_PERMANENT | JSPROP_READONLY) ||              \
1540         !JS_DefineProperty(cx, proto, "BYTES_PER_ELEMENT",                     \
1541                            INT_TO_JSVAL(sizeof(_typedArray::ThisType)),        \
1542                            JS_PropertyStub, JS_StrictPropertyStub,             \
1543                            JSPROP_PERMANENT | JSPROP_READONLY))                \
1544     {                                                                          \
1545         return NULL;                                                           \
1546     }                                                                          \
1547     proto->setPrivate(0);                                                      \
1548 } while (0)
1549
1550 IMPL_TYPED_ARRAY_STATICS(Int8Array);
1551 IMPL_TYPED_ARRAY_STATICS(Uint8Array);
1552 IMPL_TYPED_ARRAY_STATICS(Int16Array);
1553 IMPL_TYPED_ARRAY_STATICS(Uint16Array);
1554 IMPL_TYPED_ARRAY_STATICS(Int32Array);
1555 IMPL_TYPED_ARRAY_STATICS(Uint32Array);
1556 IMPL_TYPED_ARRAY_STATICS(Float32Array);
1557 IMPL_TYPED_ARRAY_STATICS(Float64Array);
1558 IMPL_TYPED_ARRAY_STATICS(Uint8ClampedArray);
1559
1560 Class TypedArray::fastClasses[TYPE_MAX] = {
1561     IMPL_TYPED_ARRAY_FAST_CLASS(Int8Array),
1562     IMPL_TYPED_ARRAY_FAST_CLASS(Uint8Array),
1563     IMPL_TYPED_ARRAY_FAST_CLASS(Int16Array),
1564     IMPL_TYPED_ARRAY_FAST_CLASS(Uint16Array),
1565     IMPL_TYPED_ARRAY_FAST_CLASS(Int32Array),
1566     IMPL_TYPED_ARRAY_FAST_CLASS(Uint32Array),
1567     IMPL_TYPED_ARRAY_FAST_CLASS(Float32Array),
1568     IMPL_TYPED_ARRAY_FAST_CLASS(Float64Array),
1569     IMPL_TYPED_ARRAY_FAST_CLASS(Uint8ClampedArray)
1570 };
1571
1572 Class TypedArray::slowClasses[TYPE_MAX] = {
1573     IMPL_TYPED_ARRAY_SLOW_CLASS(Int8Array),
1574     IMPL_TYPED_ARRAY_SLOW_CLASS(Uint8Array),
1575     IMPL_TYPED_ARRAY_SLOW_CLASS(Int16Array),
1576     IMPL_TYPED_ARRAY_SLOW_CLASS(Uint16Array),
1577     IMPL_TYPED_ARRAY_SLOW_CLASS(Int32Array),
1578     IMPL_TYPED_ARRAY_SLOW_CLASS(Uint32Array),
1579     IMPL_TYPED_ARRAY_SLOW_CLASS(Float32Array),
1580     IMPL_TYPED_ARRAY_SLOW_CLASS(Float64Array),
1581     IMPL_TYPED_ARRAY_SLOW_CLASS(Uint8ClampedArray)
1582 };
1583
1584 JS_FRIEND_API(JSObject *)
1585 js_InitTypedArrayClasses(JSContext *cx, JSObject *obj)
1586 {
1587     /* Idempotency required: we initialize several things, possibly lazily. */
1588     JSObject *stop;
1589     if (!js_GetClassObject(cx, obj, JSProto_ArrayBuffer, &stop))
1590         return NULL;
1591     if (stop)
1592         return stop;
1593
1594     JSObject *proto;
1595
1596     INIT_TYPED_ARRAY_CLASS(Int8Array,TYPE_INT8);
1597     INIT_TYPED_ARRAY_CLASS(Uint8Array,TYPE_UINT8);
1598     INIT_TYPED_ARRAY_CLASS(Int16Array,TYPE_INT16);
1599     INIT_TYPED_ARRAY_CLASS(Uint16Array,TYPE_UINT16);
1600     INIT_TYPED_ARRAY_CLASS(Int32Array,TYPE_INT32);
1601     INIT_TYPED_ARRAY_CLASS(Uint32Array,TYPE_UINT32);
1602     INIT_TYPED_ARRAY_CLASS(Float32Array,TYPE_FLOAT32);
1603     INIT_TYPED_ARRAY_CLASS(Float64Array,TYPE_FLOAT64);
1604     INIT_TYPED_ARRAY_CLASS(Uint8ClampedArray,TYPE_UINT8_CLAMPED);
1605
1606     proto = js_InitClass(cx, obj, NULL, &ArrayBuffer::jsclass,
1607                          ArrayBuffer::class_constructor, 1,
1608                          ArrayBuffer::jsprops, NULL, NULL, NULL);
1609     if (!proto)
1610         return NULL;
1611
1612     proto->setPrivate(NULL);
1613     return proto;
1614 }
1615
1616 JS_FRIEND_API(JSBool)
1617 js_IsArrayBuffer(JSObject *obj)
1618 {
1619     JS_ASSERT(obj);
1620     return obj->getClass() == &ArrayBuffer::jsclass;
1621 }
1622
1623 JS_FRIEND_API(JSBool)
1624 js_IsTypedArray(JSObject *obj)
1625 {
1626     JS_ASSERT(obj);
1627     Class *clasp = obj->getClass();
1628     return clasp >= &TypedArray::fastClasses[0] &&
1629            clasp <  &TypedArray::fastClasses[TypedArray::TYPE_MAX];
1630 }
1631
1632 JS_FRIEND_API(JSObject *)
1633 js_CreateArrayBuffer(JSContext *cx, jsuint nbytes)
1634 {
1635     return ArrayBuffer::create(cx, nbytes);
1636 }
1637
1638 static inline JSObject *
1639 TypedArrayConstruct(JSContext *cx, jsint atype, uintN argc, Value *argv)
1640 {
1641     switch (atype) {
1642       case TypedArray::TYPE_INT8:
1643         return Int8Array::create(cx, argc, argv);
1644
1645       case TypedArray::TYPE_UINT8:
1646         return Uint8Array::create(cx, argc, argv);
1647
1648       case TypedArray::TYPE_INT16:
1649         return Int16Array::create(cx, argc, argv);
1650
1651       case TypedArray::TYPE_UINT16:
1652         return Uint16Array::create(cx, argc, argv);
1653
1654       case TypedArray::TYPE_INT32:
1655         return Int32Array::create(cx, argc, argv);
1656
1657       case TypedArray::TYPE_UINT32:
1658         return Uint32Array::create(cx, argc, argv);
1659
1660       case TypedArray::TYPE_FLOAT32:
1661         return Float32Array::create(cx, argc, argv);
1662
1663       case TypedArray::TYPE_FLOAT64:
1664         return Float64Array::create(cx, argc, argv);
1665
1666       case TypedArray::TYPE_UINT8_CLAMPED:
1667         return Uint8ClampedArray::create(cx, argc, argv);
1668
1669       default:
1670         JS_NOT_REACHED("shouldn't have gotten here");
1671         return false;
1672     }
1673 }
1674
1675 JS_FRIEND_API(JSObject *)
1676 js_CreateTypedArray(JSContext *cx, jsint atype, jsuint nelements)
1677 {
1678     JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
1679
1680     Value nelems = Int32Value(nelements);
1681     return TypedArrayConstruct(cx, atype, 1, &nelems);
1682 }
1683
1684 JS_FRIEND_API(JSObject *)
1685 js_CreateTypedArrayWithArray(JSContext *cx, jsint atype, JSObject *arrayArg)
1686 {
1687     JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
1688
1689     Value arrval = ObjectValue(*arrayArg);
1690     return TypedArrayConstruct(cx, atype, 1, &arrval);
1691 }
1692
1693 JS_FRIEND_API(JSObject *)
1694 js_CreateTypedArrayWithBuffer(JSContext *cx, jsint atype, JSObject *bufArg,
1695                               jsint byteoffset, jsint length)
1696 {
1697     JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
1698     JS_ASSERT(bufArg && ArrayBuffer::fromJSObject(bufArg));
1699     JS_ASSERT_IF(byteoffset < 0, length < 0);
1700
1701     Value vals[4];
1702
1703     int argc = 1;
1704     vals[0].setObject(*bufArg);
1705
1706     if (byteoffset >= 0) {
1707         vals[argc].setInt32(byteoffset);
1708         argc++;
1709     }
1710
1711     if (length >= 0) {
1712         vals[argc].setInt32(length);
1713         argc++;
1714     }
1715
1716     AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vals), vals);
1717     return TypedArrayConstruct(cx, atype, argc, &vals[0]);
1718 }
1719
1720 JS_FRIEND_API(JSBool)
1721 js_ReparentTypedArrayToScope(JSContext *cx, JSObject *obj, JSObject *scope)
1722 {
1723     JS_ASSERT(obj);
1724
1725     scope = JS_GetGlobalForObject(cx, scope);
1726     if (!scope)
1727         return JS_FALSE;
1728
1729     if (!js_IsTypedArray(obj))
1730         return JS_FALSE;
1731
1732     TypedArray *typedArray = TypedArray::fromJSObject(obj);
1733
1734     JSObject *buffer = typedArray->bufferJS;
1735     JS_ASSERT(js_IsArrayBuffer(buffer));
1736
1737     JSObject *proto;
1738     JSProtoKey key =
1739         JSCLASS_CACHED_PROTO_KEY(&TypedArray::slowClasses[typedArray->type]);
1740     if (!js_GetClassPrototype(cx, scope, key, &proto))
1741         return JS_FALSE;
1742
1743     obj->setProto(proto);
1744     obj->setParent(scope);
1745
1746     key = JSCLASS_CACHED_PROTO_KEY(&ArrayBuffer::jsclass);
1747     if (!js_GetClassPrototype(cx, scope, key, &proto))
1748         return JS_FALSE;
1749
1750     buffer->setProto(proto);
1751     buffer->setParent(scope);
1752
1753     return JS_TRUE;
1754 }