Bug 551763: Fix deletion of arguments ident. (r=Waldo)
[mozilla-central.git] / js / src / jstypedarray.cpp
blobd9cf66e2bbcc39f0200a87ba665bdd079e02078f
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
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/
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.
16 * The Original Code is Mozilla WebGL impl
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.
23 * Contributor(s):
24 * Vladimir Vukicevic <vladimir@pobox.com>
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.
38 * ***** END LICENSE BLOCK ***** */
40 #define __STDC_LIMIT_MACROS
42 #include <string.h>
44 #include "jstypes.h"
45 #include "jsstdint.h"
46 #include "jsutil.h"
47 #include "jshash.h"
48 #include "jsprf.h"
49 #include "jsapi.h"
50 #include "jsarray.h"
51 #include "jsatom.h"
52 #include "jsbool.h"
53 #include "jsbuiltins.h"
54 #include "jscntxt.h"
55 #include "jsversion.h"
56 #include "jsgc.h"
57 #include "jsinterp.h"
58 #include "jslock.h"
59 #include "jsnum.h"
60 #include "jsobj.h"
61 #include "jsstaticcheck.h"
62 #include "jsbit.h"
63 #include "jsvector.h"
64 #include "jstypedarray.h"
66 #include "jsobjinlines.h"
68 using namespace js;
71 * ArrayBuffer
73 * This class holds the underlying raw buffer that the TypedArray classes
74 * access. It can be created explicitly and passed to a TypedArray, or
75 * can be created implicitly by constructing a TypedArray with a size.
77 ArrayBuffer *
78 ArrayBuffer::fromJSObject(JSObject *obj)
80 JS_ASSERT(obj->getClass() == &ArrayBuffer::jsclass);
81 return reinterpret_cast<ArrayBuffer*>(obj->getPrivate());
84 JSBool
85 ArrayBuffer::prop_getByteLength(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
87 ArrayBuffer *abuf = ArrayBuffer::fromJSObject(obj);
88 if (abuf)
89 *vp = INT_TO_JSVAL(jsint(abuf->byteLength));
90 return true;
93 void
94 ArrayBuffer::class_finalize(JSContext *cx, JSObject *obj)
96 ArrayBuffer *abuf = ArrayBuffer::fromJSObject(obj);
97 if (abuf)
98 abuf->freeStorage(cx);
99 delete abuf;
103 * new ArrayBuffer(byteLength)
105 JSBool
106 ArrayBuffer::class_constructor(JSContext *cx, JSObject *obj,
107 uintN argc, jsval *argv, jsval *rval)
109 if (!JS_IsConstructing(cx)) {
110 obj = NewObject(cx, &ArrayBuffer::jsclass, NULL, NULL);
111 if (!obj)
112 return false;
113 *rval = OBJECT_TO_JSVAL(obj);
116 return create(cx, obj, argc, argv, rval);
119 bool
120 ArrayBuffer::create(JSContext *cx, JSObject *obj,
121 uintN argc, jsval *argv, jsval *rval)
123 if (!obj) {
124 obj = NewObject(cx, &ArrayBuffer::jsclass, NULL, NULL);
125 if (!obj)
126 return false;
127 *rval = OBJECT_TO_JSVAL(obj);
130 if (argc == 0) {
131 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
132 JSMSG_TYPED_ARRAY_BAD_ARGS);
133 return false;
136 int32_t nbytes;
137 if (!ValueToECMAInt32(cx, argv[0], &nbytes))
138 return false;
139 if (nbytes < 0 || !INT_FITS_IN_JSVAL(nbytes)) {
141 * We're just not going to support arrays that are bigger than what will fit
142 * as an integer jsval; if someone actually ever complains (validly), then we
143 * can fix.
145 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
146 JSMSG_BAD_ARRAY_LENGTH);
147 return false;
150 ArrayBuffer *abuf = new ArrayBuffer();
151 if (!abuf) {
152 JS_ReportOutOfMemory(cx);
153 return false;
156 if (!abuf->allocateStorage(cx, nbytes)) {
157 delete abuf;
158 return false;
161 obj->setPrivate(abuf);
162 return true;
165 bool
166 ArrayBuffer::allocateStorage(JSContext *cx, uint32 nbytes)
168 JS_ASSERT(data == 0);
170 if (nbytes) {
171 data = cx->calloc(nbytes);
172 if (!data) {
173 JS_ReportOutOfMemory(cx);
174 return false;
178 byteLength = nbytes;
179 return true;
182 void
183 ArrayBuffer::freeStorage(JSContext *cx)
185 if (data) {
186 cx->free(data);
187 #ifdef DEBUG
188 // the destructor asserts that data is 0 in debug builds
189 data = NULL;
190 #endif
194 ArrayBuffer::~ArrayBuffer()
196 JS_ASSERT(data == NULL);
200 * TypedArray
202 * The non-templated base class for the specific typed implementations.
203 * This class holds all the member variables that are used by
204 * the subclasses.
207 TypedArray *
208 TypedArray::fromJSObject(JSObject *obj)
210 return reinterpret_cast<TypedArray*>(obj->getPrivate());
213 inline bool
214 TypedArray::isArrayIndex(JSContext *cx, jsid id, jsuint *ip)
216 jsuint index;
217 if (js_IdIsIndex(id, &index) && index < length) {
218 if (ip)
219 *ip = index;
220 return true;
223 return false;
226 JSBool
227 TypedArray::prop_getBuffer(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
229 TypedArray *tarray = fromJSObject(obj);
230 if (tarray)
231 *vp = OBJECT_TO_JSVAL(tarray->bufferJS);
232 return true;
235 JSBool
236 TypedArray::prop_getByteOffset(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
238 TypedArray *tarray = fromJSObject(obj);
239 if (tarray)
240 *vp = INT_TO_JSVAL(tarray->byteOffset);
241 return true;
244 JSBool
245 TypedArray::prop_getByteLength(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
247 TypedArray *tarray = fromJSObject(obj);
248 if (tarray)
249 *vp = INT_TO_JSVAL(tarray->byteLength);
250 return true;
253 JSBool
254 TypedArray::prop_getLength(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
256 TypedArray *tarray = fromJSObject(obj);
257 if (tarray)
258 *vp = INT_TO_JSVAL(tarray->length);
259 return true;
262 JSBool
263 TypedArray::obj_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
264 JSObject **objp, JSProperty **propp)
266 TypedArray *tarray = fromJSObject(obj);
267 JS_ASSERT(tarray);
269 if (tarray->isArrayIndex(cx, id)) {
270 *propp = (JSProperty *) id;
271 *objp = obj;
272 return true;
275 JSObject *proto = obj->getProto();
276 if (!proto) {
277 *objp = NULL;
278 *propp = NULL;
279 return true;
282 return proto->lookupProperty(cx, id, objp, propp);
285 void
286 TypedArray::obj_dropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
288 #ifdef DEBUG
289 TypedArray *tarray = fromJSObject(obj);
290 JS_ASSERT_IF(tarray, tarray->isArrayIndex(cx, (jsid) prop));
291 #endif
294 void
295 TypedArray::obj_trace(JSTracer *trc, JSObject *obj)
297 TypedArray *tarray = fromJSObject(obj);
298 JS_ASSERT(tarray);
300 obj->traceProtoAndParent(trc);
302 JS_CALL_OBJECT_TRACER(trc, tarray->bufferJS, "typedarray.buffer");
305 JSBool
306 TypedArray::obj_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
307 uintN *attrsp)
309 *attrsp = (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
310 ? JSPROP_PERMANENT | JSPROP_READONLY
311 : JSPROP_PERMANENT | JSPROP_ENUMERATE;
312 return true;
315 JSBool
316 TypedArray::obj_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
317 uintN *attrsp)
319 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
320 JSMSG_CANT_SET_ARRAY_ATTRS);
321 return false;
324 /* Helper clamped uint8 type */
326 int32 JS_FASTCALL
327 js_TypedArray_uint8_clamp_double(const double x)
329 // Not < so that NaN coerces to 0
330 if (!(x >= 0))
331 return 0;
333 if (x > 255)
334 return 255;
336 jsdouble toTruncate = x + 0.5;
337 JSUint8 y = JSUint8(toTruncate);
340 * now val is rounded to nearest, ties rounded up. We want
341 * rounded to nearest ties to even, so check whether we had a
342 * tie.
344 if (y == toTruncate) {
346 * It was a tie (since adding 0.5 gave us the exact integer
347 * we want). Since we rounded up, we either already have an
348 * even number or we have an odd number but the number we
349 * want is one less. So just unconditionally masking out the
350 * ones bit should do the trick to get us the value we
351 * want.
353 return (y & ~1);
356 return y;
359 JS_DEFINE_CALLINFO_1(extern, INT32, js_TypedArray_uint8_clamp_double, DOUBLE, 1, nanojit::ACC_NONE)
362 struct uint8_clamped {
363 uint8 val;
365 uint8_clamped() { }
366 uint8_clamped(const uint8_clamped& other) : val(other.val) { }
368 // invoke our assignment helpers for constructor conversion
369 uint8_clamped(uint8 x) { *this = x; }
370 uint8_clamped(uint16 x) { *this = x; }
371 uint8_clamped(uint32 x) { *this = x; }
372 uint8_clamped(int8 x) { *this = x; }
373 uint8_clamped(int16 x) { *this = x; }
374 uint8_clamped(int32 x) { *this = x; }
375 uint8_clamped(jsdouble x) { *this = x; }
377 inline uint8_clamped& operator= (const uint8_clamped& x) {
378 val = x.val;
379 return *this;
382 inline uint8_clamped& operator= (uint8 x) {
383 val = x;
384 return *this;
387 inline uint8_clamped& operator= (uint16 x) {
388 val = (x > 255) ? 255 : uint8(x);
389 return *this;
392 inline uint8_clamped& operator= (uint32 x) {
393 val = (x > 255) ? 255 : uint8(x);
394 return *this;
397 inline uint8_clamped& operator= (int8 x) {
398 val = (x >= 0) ? uint8(x) : 0;
399 return *this;
402 inline uint8_clamped& operator= (int16 x) {
403 val = (x >= 0)
404 ? ((x < 255)
405 ? uint8(x)
406 : 255)
407 : 0;
408 return *this;
411 inline uint8_clamped& operator= (int32 x) {
412 val = (x >= 0)
413 ? ((x < 255)
414 ? uint8(x)
415 : 255)
416 : 0;
417 return *this;
420 inline uint8_clamped& operator= (const jsdouble x) {
421 val = uint8(js_TypedArray_uint8_clamp_double(x));
422 return *this;
425 inline operator uint8() const {
426 return val;
430 /* Make sure the compiler isn't doing some funky stuff */
431 JS_STATIC_ASSERT(sizeof(uint8_clamped) == 1);
433 template<typename NativeType> static inline const int TypeIDOfType();
434 template<> inline const int TypeIDOfType<int8>() { return TypedArray::TYPE_INT8; }
435 template<> inline const int TypeIDOfType<uint8>() { return TypedArray::TYPE_UINT8; }
436 template<> inline const int TypeIDOfType<int16>() { return TypedArray::TYPE_INT16; }
437 template<> inline const int TypeIDOfType<uint16>() { return TypedArray::TYPE_UINT16; }
438 template<> inline const int TypeIDOfType<int32>() { return TypedArray::TYPE_INT32; }
439 template<> inline const int TypeIDOfType<uint32>() { return TypedArray::TYPE_UINT32; }
440 template<> inline const int TypeIDOfType<float>() { return TypedArray::TYPE_FLOAT32; }
441 template<> inline const int TypeIDOfType<double>() { return TypedArray::TYPE_FLOAT64; }
442 template<> inline const int TypeIDOfType<uint8_clamped>() { return TypedArray::TYPE_UINT8_CLAMPED; }
444 template<typename NativeType> static inline const bool TypeIsUnsigned() { return false; }
445 template<> inline const bool TypeIsUnsigned<uint8>() { return true; }
446 template<> inline const bool TypeIsUnsigned<uint16>() { return true; }
447 template<> inline const bool TypeIsUnsigned<uint32>() { return true; }
449 template<typename NativeType> static inline const bool TypeIsFloatingPoint() { return false; }
450 template<> inline const bool TypeIsFloatingPoint<float>() { return true; }
451 template<> inline const bool TypeIsFloatingPoint<double>() { return true; }
453 template<typename NativeType> class TypedArrayTemplate;
455 typedef TypedArrayTemplate<int8> Int8Array;
456 typedef TypedArrayTemplate<uint8> Uint8Array;
457 typedef TypedArrayTemplate<int16> Int16Array;
458 typedef TypedArrayTemplate<uint16> Uint16Array;
459 typedef TypedArrayTemplate<int32> Int32Array;
460 typedef TypedArrayTemplate<uint32> Uint32Array;
461 typedef TypedArrayTemplate<float> Float32Array;
462 typedef TypedArrayTemplate<double> Float64Array;
463 typedef TypedArrayTemplate<uint8_clamped> Uint8ClampedArray;
465 template<typename NativeType>
466 class TypedArrayTemplate
467 : public TypedArray
469 public:
470 typedef TypedArrayTemplate<NativeType> ThisTypeArray;
471 static const int ArrayTypeID() { return TypeIDOfType<NativeType>(); }
472 static const bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
473 static const bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
475 static JSObjectOps fastObjectOps;
476 static JSObjectMap fastObjectMap;
478 static JSFunctionSpec jsfuncs[];
480 static inline JSClass *slowClass()
482 return &TypedArray::slowClasses[ArrayTypeID()];
485 static inline JSClass *fastClass()
487 return &TypedArray::fastClasses[ArrayTypeID()];
490 static JSObjectOps *getObjectOps(JSContext *cx, JSClass *clasp)
492 return &fastObjectOps;
495 static JSBool
496 obj_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
498 ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
499 JS_ASSERT(tarray);
501 if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
502 *vp = INT_TO_JSVAL(tarray->length);
503 return true;
506 jsuint index;
507 if (tarray->isArrayIndex(cx, id, &index)) {
508 // this inline function is specialized for each type
509 tarray->copyIndexToValue(cx, index, vp);
510 } else {
511 JSObject *obj2;
512 JSProperty *prop;
513 JSScopeProperty *sprop;
515 JSObject *proto = obj->getProto();
516 if (!proto) {
517 *vp = JSVAL_VOID;
518 return true;
521 *vp = JSVAL_VOID;
522 if (js_LookupPropertyWithFlags(cx, proto, id, cx->resolveFlags, &obj2, &prop) < 0)
523 return false;
525 if (prop) {
526 if (obj2->isNative()) {
527 sprop = (JSScopeProperty *) prop;
528 if (!js_NativeGet(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, vp))
529 return false;
531 obj2->dropProperty(cx, prop);
535 return true;
538 static JSBool
539 obj_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
541 ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
542 JS_ASSERT(tarray);
544 if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
545 *vp = INT_TO_JSVAL(tarray->length);
546 return true;
549 jsuint index;
550 // We can't just chain to js_SetProperty, because we're not a normal object.
551 if (!tarray->isArrayIndex(cx, id, &index)) {
552 #if 0
553 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
554 JSMSG_TYPED_ARRAY_BAD_INDEX);
555 return false;
556 #endif
557 // Silent ignore is better than an exception here, because
558 // at some point we may want to support other properties on
559 // these objects. This is especially true when these arrays
560 // are used to implement HTML Canvas 2D's PixelArray objects,
561 // which used to be plain old arrays.
562 *vp = JSVAL_VOID;
563 return true;
566 if (JSVAL_IS_INT(*vp)) {
567 tarray->setIndex(index, NativeType(JSVAL_TO_INT(*vp)));
568 return true;
571 jsdouble d;
573 if (JSVAL_IS_DOUBLE(*vp)) {
574 d = *JSVAL_TO_DOUBLE(*vp);
575 } else if (JSVAL_IS_NULL(*vp)) {
576 d = 0.0f;
577 } else if (JSVAL_IS_PRIMITIVE(*vp)) {
578 JS_ASSERT(JSVAL_IS_STRING(*vp) || JSVAL_IS_SPECIAL(*vp));
579 if (JSVAL_IS_STRING(*vp)) {
580 // note that ValueToNumber will always succeed with a string arg
581 ValueToNumber(cx, *vp, &d);
582 } else if (*vp == JSVAL_VOID) {
583 d = js_NaN;
584 } else {
585 d = (double) JSVAL_TO_BOOLEAN(*vp);
587 } else {
588 // non-primitive assignments become NaN or 0 (for float/int arrays)
589 d = js_NaN;
592 // If the array is an integer array, we only handle up to
593 // 32-bit ints from this point on. if we want to handle
594 // 64-bit ints, we'll need some changes.
596 // Assign based on characteristics of the destination type
597 if (ArrayTypeIsFloatingPoint()) {
598 tarray->setIndex(index, NativeType(d));
599 } else if (ArrayTypeIsUnsigned()) {
600 JS_ASSERT(sizeof(NativeType) <= 4);
601 uint32 n = js_DoubleToECMAUint32(d);
602 tarray->setIndex(index, NativeType(n));
603 } else if (ArrayTypeID() == TypedArray::TYPE_UINT8_CLAMPED) {
604 // The uint8_clamped type has a special rounding converter
605 // for doubles.
606 tarray->setIndex(index, NativeType(d));
607 } else {
608 JS_ASSERT(sizeof(NativeType) <= 4);
609 int32 n = js_DoubleToECMAInt32(d);
610 tarray->setIndex(index, NativeType(n));
613 return true;
616 static JSBool
617 obj_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
618 JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
620 if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
621 return true;
623 return obj_setProperty(cx, obj, id, &value);
626 static JSBool
627 obj_deleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
629 if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
630 *rval = JSVAL_FALSE;
631 return true;
634 TypedArray *tarray = TypedArray::fromJSObject(obj);
635 JS_ASSERT(tarray);
637 if (tarray->isArrayIndex(cx, id)) {
638 *rval = JSVAL_FALSE;
639 return true;
642 *rval = JSVAL_TRUE;
643 return true;
646 static JSBool
647 obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
648 jsval *statep, jsid *idp)
650 ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
651 JS_ASSERT(tarray);
653 jsint curVal;
654 switch (enum_op) {
655 case JSENUMERATE_INIT:
656 *statep = JSVAL_ZERO;
657 if (idp)
658 *idp = INT_TO_JSID(tarray->length);
659 break;
661 case JSENUMERATE_NEXT:
662 curVal = JSVAL_TO_INT(*statep);
663 *idp = INT_TO_JSID(curVal);
664 *statep = (curVal == int32(tarray->length))
665 ? JSVAL_NULL
666 : INT_TO_JSVAL(curVal+1);
667 break;
669 case JSENUMERATE_DESTROY:
670 *statep = JSVAL_NULL;
671 break;
674 return true;
677 static JSType
678 obj_typeOf(JSContext *cx, JSObject *obj)
680 return JSTYPE_OBJECT;
684 * new [Type]Array(length)
685 * new [Type]Array(otherTypedArray)
686 * new [Type]Array(JSArray)
687 * new [Type]Array(ArrayBuffer, [optional] byteOffset, [optional] length)
689 static JSBool
690 class_constructor(JSContext *cx, JSObject *obj,
691 uintN argc, jsval *argv, jsval *rval)
694 // Note: this is a constructor for slowClass, not fastClass!
697 if (!JS_IsConstructing(cx)) {
698 obj = NewObject(cx, slowClass(), NULL, NULL);
699 if (!obj)
700 return false;
701 *rval = OBJECT_TO_JSVAL(obj);
704 return create(cx, obj, argc, argv, rval);
707 static bool
708 create(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
710 if (!obj) {
711 obj = NewObject(cx, slowClass(), NULL, NULL);
712 if (!obj)
713 return false;
714 *rval = OBJECT_TO_JSVAL(obj);
717 ThisTypeArray *tarray = 0;
719 // must have at least one arg
720 if (argc == 0) {
721 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
722 JSMSG_TYPED_ARRAY_BAD_ARGS);
723 return false;
726 // figure out the type of the first argument
727 if (JSVAL_IS_INT(argv[0])) {
728 int32 len = JSVAL_TO_INT(argv[0]);
729 if (len < 0) {
730 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
731 JSMSG_BAD_ARRAY_LENGTH);
733 return false;
736 tarray = new ThisTypeArray();
737 if (!tarray) {
738 JS_ReportOutOfMemory(cx);
739 return false;
742 if (!tarray->init(cx, len)) {
743 delete tarray;
744 return false;
746 } else if (JSVAL_IS_OBJECT(argv[0])) {
747 int32_t byteOffset = -1;
748 int32_t length = -1;
750 if (argc > 1) {
751 if (!ValueToInt32(cx, argv[1], &byteOffset))
752 return false;
753 if (byteOffset < 0) {
754 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
755 JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1");
756 return false;
760 if (argc > 2) {
761 if (!ValueToInt32(cx, argv[2], &length))
762 return false;
763 if (length < 0) {
764 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
765 JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2");
766 return false;
770 tarray = new ThisTypeArray();
771 if (!tarray) {
772 JS_ReportOutOfMemory(cx);
773 return false;
776 if (!tarray->init(cx, JSVAL_TO_OBJECT(argv[0]), byteOffset, length)) {
777 delete tarray;
778 return false;
780 } else {
781 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
782 JSMSG_TYPED_ARRAY_BAD_ARGS);
783 return false;
786 makeFastWithPrivate(cx, obj, tarray);
787 return true;
790 static void
791 class_finalize(JSContext *cx, JSObject *obj)
793 ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
794 delete tarray;
797 /* slice(start[, end]) */
798 static JSBool
799 fun_slice(JSContext *cx, uintN argc, jsval *vp)
801 jsval *argv;
802 JSObject *obj;
804 argv = JS_ARGV(cx, vp);
805 obj = JS_THIS_OBJECT(cx, vp);
807 ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
808 if (!tarray)
809 return true;
811 // these are the default values
812 int32_t begin = 0, end = tarray->length;
813 int32_t length = int32(tarray->length);
815 if (argc > 0) {
816 if (!ValueToInt32(cx, argv[0], &begin))
817 return false;
818 if (begin < 0) {
819 begin += length;
820 if (begin < 0)
821 begin = 0;
822 } else if (begin > length) {
823 begin = length;
826 if (argc > 1) {
827 if (!ValueToInt32(cx, argv[1], &end))
828 return false;
829 if (end < 0) {
830 end += length;
831 if (end < 0)
832 end = 0;
833 } else if (end > length) {
834 end = length;
839 if (begin > end)
840 begin = end;
842 ThisTypeArray *ntarray = tarray->slice(begin, end);
843 if (!ntarray) {
844 // this should rarely ever happen
845 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
846 JSMSG_TYPED_ARRAY_BAD_ARGS);
847 return false;
850 // note the usage of JS_NewObject here -- we don't want the
851 // constructor to be called!
852 JSObject *nobj = JS_NewObject(cx, slowClass(), NULL, NULL);
853 if (!nobj) {
854 delete ntarray;
855 return false;
858 makeFastWithPrivate(cx, nobj, ntarray);
860 *vp = OBJECT_TO_JSVAL(nobj);
861 return true;
864 static ThisTypeArray *
865 fromJSObject(JSObject *obj)
867 JS_ASSERT(obj->getClass() == fastClass());
868 return reinterpret_cast<ThisTypeArray*>(obj->getPrivate());
871 // helper used by both the constructor and Slice()
872 static void
873 makeFastWithPrivate(JSContext *cx, JSObject *obj, ThisTypeArray *tarray)
875 JS_ASSERT(obj->getClass() == slowClass());
877 obj->setPrivate(tarray);
879 // now munge the classword and make this into a fast typed
880 // array class, since it's an instance
881 obj->classword ^= jsuword(slowClass());
882 obj->classword |= jsuword(fastClass());
884 obj->map = &fastObjectMap;
887 public:
888 TypedArrayTemplate() { }
890 bool
891 init(JSContext *cx, uint32 len)
893 type = ArrayTypeID();
894 return createBufferWithSizeAndCount(cx, sizeof(NativeType), len);
897 bool
898 init(JSContext *cx, JSObject *other, int32 byteOffsetInt = -1, int32 lengthInt = -1)
900 type = ArrayTypeID();
902 //printf ("Constructing with type %d other %p offset %d length %d\n", type, other, byteOffset, length);
904 if (JS_IsArrayObject(cx, other)) {
905 jsuint len;
906 if (!JS_GetArrayLength(cx, other, &len))
907 return false;
908 if (!createBufferWithSizeAndCount(cx, sizeof(NativeType), len))
909 return false;
910 if (!copyFrom(cx, other, len))
911 return false;
912 } else if (js_IsTypedArray(other)) {
913 TypedArray *tarray = TypedArray::fromJSObject(other);
915 //printf ("SizeAndCount: %d %d\n", sizeof(NativeType), tarray->length);
917 if (!createBufferWithSizeAndCount(cx, sizeof(NativeType), tarray->length))
918 return false;
919 if (!copyFrom(tarray))
920 return false;
921 } else if (other->getClass() == &ArrayBuffer::jsclass) {
922 ArrayBuffer *abuf = ArrayBuffer::fromJSObject(other);
924 //printf ("buffer: %d %d %d\n", abuf->byteLength, abuf->byteLength / sizeof(NativeType), len * sizeof(NativeType) == abuf->byteLength);
925 uint32 boffset = (byteOffsetInt < 0) ? 0 : uint32(byteOffsetInt);
927 if (boffset > abuf->byteLength || boffset % sizeof(NativeType) != 0) {
928 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
929 JSMSG_TYPED_ARRAY_BAD_ARGS);
930 return false; // invalid byteOffset
933 uint32 len;
934 if (lengthInt < 0) {
935 len = (abuf->byteLength - boffset) / sizeof(NativeType);
936 if (len * sizeof(NativeType) != (abuf->byteLength - boffset)) {
937 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
938 JSMSG_TYPED_ARRAY_BAD_ARGS);
939 return false; // given byte array doesn't map exactly to sizeof(NativeType)*N
941 } else {
942 len = (uint32) lengthInt;
945 // Go slowly and check for overflow.
946 uint32 arrayByteLength = len*sizeof(NativeType);
947 if (uint32(len) >= INT32_MAX / sizeof(NativeType) ||
948 uint32(boffset) >= INT32_MAX - arrayByteLength)
950 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
951 JSMSG_TYPED_ARRAY_BAD_ARGS);
952 return false; // overflow occurred along the way when calculating boffset+len*sizeof(NativeType)
955 if (arrayByteLength + boffset > abuf->byteLength) {
956 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
957 JSMSG_TYPED_ARRAY_BAD_ARGS);
958 return false; // boffset+len is too big for the arraybuffer
961 buffer = abuf;
962 bufferJS = other;
963 byteOffset = boffset;
964 byteLength = arrayByteLength;
965 length = len;
966 data = abuf->offsetData(boffset);
967 } else {
968 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
969 JSMSG_TYPED_ARRAY_BAD_ARGS);
970 return false;
973 return true;
976 const NativeType
977 getIndex(uint32 index) const
979 return *(static_cast<const NativeType*>(data) + index);
982 void
983 setIndex(uint32 index, NativeType val)
985 *(static_cast<NativeType*>(data) + index) = val;
988 inline void copyIndexToValue(JSContext *cx, uint32 index, jsval *vp);
990 ThisTypeArray *
991 slice(uint32 begin, uint32 end)
993 if (begin > length || end > length)
994 return NULL;
996 ThisTypeArray *tarray = new ThisTypeArray();
997 if (!tarray)
998 return NULL;
1000 tarray->buffer = buffer;
1001 tarray->bufferJS = bufferJS;
1002 tarray->byteOffset = byteOffset + begin * sizeof(NativeType);
1003 tarray->byteLength = (end - begin) * sizeof(NativeType);
1004 tarray->length = end - begin;
1005 tarray->type = type;
1006 tarray->data = buffer->offsetData(tarray->byteOffset);
1008 return tarray;
1011 protected:
1012 static NativeType
1013 nativeFromValue(JSContext *cx, jsval v)
1015 if (JSVAL_IS_INT(v))
1016 return NativeType(JSVAL_TO_INT(v));
1018 if (JSVAL_IS_DOUBLE(v))
1019 return NativeType(*JSVAL_TO_DOUBLE(v));
1021 if (JSVAL_IS_PRIMITIVE(v) && v != JSVAL_HOLE) {
1022 jsdouble dval;
1023 ValueToNumber(cx, v, &dval);
1024 return NativeType(dval);
1027 if (ArrayTypeIsFloatingPoint())
1028 return NativeType(js_NaN);
1030 return NativeType(int32(0));
1033 bool
1034 copyFrom(JSContext *cx, JSObject *ar, jsuint len)
1036 NativeType *dest = static_cast<NativeType*>(data);
1038 if (ar->isDenseArray() && ar->getDenseArrayCapacity() >= len) {
1039 JS_ASSERT(ar->getArrayLength() == len);
1041 jsval *src = ar->getDenseArrayElements();
1043 for (uintN i = 0; i < len; ++i) {
1044 jsval v = *src++;
1045 *dest++ = nativeFromValue(cx, v);
1047 } else {
1048 // slow path
1049 jsval v;
1051 for (uintN i = 0; i < len; ++i) {
1052 if (!ar->getProperty(cx, INT_TO_JSID(i), &v))
1053 return false;
1054 *dest++ = nativeFromValue(cx, v);
1058 return true;
1061 bool
1062 copyFrom(TypedArray *tarray)
1064 NativeType *dest = static_cast<NativeType*>(data);
1066 if (tarray->type == type) {
1067 memcpy(dest, tarray->data, tarray->byteLength);
1068 return true;
1071 switch (tarray->type) {
1072 case TypedArray::TYPE_INT8: {
1073 int8 *src = static_cast<int8*>(tarray->data);
1074 for (uintN i = 0; i < length; ++i)
1075 *dest++ = NativeType(*src++);
1076 break;
1078 case TypedArray::TYPE_UINT8:
1079 case TypedArray::TYPE_UINT8_CLAMPED: {
1080 uint8 *src = static_cast<uint8*>(tarray->data);
1081 for (uintN i = 0; i < length; ++i)
1082 *dest++ = NativeType(*src++);
1083 break;
1085 case TypedArray::TYPE_INT16: {
1086 int16 *src = static_cast<int16*>(tarray->data);
1087 for (uintN i = 0; i < length; ++i)
1088 *dest++ = NativeType(*src++);
1089 break;
1091 case TypedArray::TYPE_UINT16: {
1092 uint16 *src = static_cast<uint16*>(tarray->data);
1093 for (uintN i = 0; i < length; ++i)
1094 *dest++ = NativeType(*src++);
1095 break;
1097 case TypedArray::TYPE_INT32: {
1098 int32 *src = static_cast<int32*>(tarray->data);
1099 for (uintN i = 0; i < length; ++i)
1100 *dest++ = NativeType(*src++);
1101 break;
1103 case TypedArray::TYPE_UINT32: {
1104 uint32 *src = static_cast<uint32*>(tarray->data);
1105 for (uintN i = 0; i < length; ++i)
1106 *dest++ = NativeType(*src++);
1107 break;
1109 case TypedArray::TYPE_FLOAT32: {
1110 float *src = static_cast<float*>(tarray->data);
1111 for (uintN i = 0; i < length; ++i)
1112 *dest++ = NativeType(*src++);
1113 break;
1115 case TypedArray::TYPE_FLOAT64: {
1116 double *src = static_cast<double*>(tarray->data);
1117 for (uintN i = 0; i < length; ++i)
1118 *dest++ = NativeType(*src++);
1119 break;
1121 default:
1122 JS_NOT_REACHED("copyFrom with a TypedArray of unknown type");
1123 break;
1126 return true;
1129 bool
1130 createBufferWithSizeAndCount(JSContext *cx, uint32 size, uint32 count)
1132 JS_ASSERT(size != 0);
1134 if (size != 0 && count >= INT32_MAX / size) {
1135 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1136 JSMSG_NEED_DIET, "size and count");
1137 return false;
1140 int32 bytelen = size * count;
1141 if (!createBufferWithByteLength(cx, bytelen))
1142 return false;
1144 length = count;
1145 return true;
1148 bool
1149 createBufferWithByteLength(JSContext *cx, int32 bytes)
1151 if (!INT_FITS_IN_JSVAL(bytes)) {
1152 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1153 JSMSG_NEED_DIET, "byte length");
1154 return false;
1157 jsval argv = INT_TO_JSVAL(bytes);
1158 JSObject *obj = JS_ConstructObjectWithArguments(cx, &ArrayBuffer::jsclass, NULL, NULL,
1159 1, &argv);
1160 if (!obj)
1161 return false;
1163 bufferJS = obj;
1164 buffer = ArrayBuffer::fromJSObject(obj);
1166 byteOffset = 0;
1167 byteLength = bytes;
1168 data = buffer->data;
1170 return true;
1174 // this default implementation is only valid for integer types
1175 // less than 32-bits in size.
1176 template<typename NativeType>
1177 void
1178 TypedArrayTemplate<NativeType>::copyIndexToValue(JSContext *cx, uint32 index, jsval *vp)
1180 JS_STATIC_ASSERT(sizeof(NativeType) < 4);
1182 *vp = INT_TO_JSVAL(getIndex(index));
1185 // and we need to specialize for 32-bit integers and floats
1186 template<>
1187 void
1188 TypedArrayTemplate<int32>::copyIndexToValue(JSContext *cx, uint32 index, jsval *vp)
1190 int32 val = getIndex(index);
1191 if (INT_FITS_IN_JSVAL(val)) {
1192 *vp = INT_TO_JSVAL(val);
1193 } else {
1194 jsdouble *dp = js_NewWeaklyRootedDouble(cx, jsdouble(val));
1195 *vp = dp ? DOUBLE_TO_JSVAL(dp) : JSVAL_VOID;
1199 template<>
1200 void
1201 TypedArrayTemplate<uint32>::copyIndexToValue(JSContext *cx, uint32 index, jsval *vp)
1203 uint32 val = getIndex(index);
1204 if (val < uint32(JSVAL_INT_MAX)) {
1205 *vp = INT_TO_JSVAL(int32(val));
1206 } else {
1207 jsdouble *dp = js_NewWeaklyRootedDouble(cx, jsdouble(val));
1208 *vp = dp ? DOUBLE_TO_JSVAL(dp) : JSVAL_VOID;
1212 template<>
1213 void
1214 TypedArrayTemplate<float>::copyIndexToValue(JSContext *cx, uint32 index, jsval *vp)
1216 float val = getIndex(index);
1217 if (!js_NewWeaklyRootedNumber(cx, jsdouble(val), vp))
1218 *vp = JSVAL_VOID;
1221 template<>
1222 void
1223 TypedArrayTemplate<double>::copyIndexToValue(JSContext *cx, uint32 index, jsval *vp)
1225 double val = getIndex(index);
1226 if (!js_NewWeaklyRootedNumber(cx, jsdouble(val), vp))
1227 *vp = JSVAL_VOID;
1230 /***
1231 *** JS impl
1232 ***/
1235 * ArrayBuffer (base)
1238 JSClass ArrayBuffer::jsclass = {
1239 "ArrayBuffer",
1240 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
1241 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
1242 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ArrayBuffer::class_finalize,
1243 JSCLASS_NO_OPTIONAL_MEMBERS
1246 JSPropertySpec ArrayBuffer::jsprops[] = {
1247 { "byteLength",
1248 -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
1249 ArrayBuffer::prop_getByteLength, ArrayBuffer::prop_getByteLength },
1250 {0,0,0,0,0}
1254 * shared TypedArray
1257 JSPropertySpec TypedArray::jsprops[] = {
1258 { js_length_str,
1259 -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
1260 TypedArray::prop_getLength, TypedArray::prop_getLength },
1261 { "byteLength",
1262 -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
1263 TypedArray::prop_getByteLength, TypedArray::prop_getByteLength },
1264 { "byteOffset",
1265 -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
1266 TypedArray::prop_getByteOffset, TypedArray::prop_getByteOffset },
1267 { "buffer",
1268 -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
1269 TypedArray::prop_getBuffer, TypedArray::prop_getBuffer },
1270 {0,0,0,0,0}
1275 * TypedArray boilerplate
1278 #define IMPL_TYPED_ARRAY_STATICS(_typedArray) \
1279 template<> JSObjectMap _typedArray::fastObjectMap(&_typedArray::fastObjectOps, \
1280 JSObjectMap::SHAPELESS); \
1281 template<> JSObjectOps _typedArray::fastObjectOps = { \
1282 &_typedArray::fastObjectMap, \
1283 _typedArray::obj_lookupProperty, \
1284 _typedArray::obj_defineProperty, \
1285 _typedArray::obj_getProperty, \
1286 _typedArray::obj_setProperty, \
1287 _typedArray::obj_getAttributes, \
1288 _typedArray::obj_setAttributes, \
1289 _typedArray::obj_deleteProperty, \
1290 js_DefaultValue, \
1291 _typedArray::obj_enumerate, \
1292 js_CheckAccess, \
1293 _typedArray::obj_typeOf, \
1294 _typedArray::obj_trace, \
1295 NULL, \
1296 _typedArray::obj_dropProperty, \
1297 NULL, NULL, NULL, \
1298 NULL \
1299 }; \
1300 template<> JSFunctionSpec _typedArray::jsfuncs[] = { \
1301 JS_FN("slice", _typedArray::fun_slice, 2, 0), \
1302 JS_FS_END \
1305 #define IMPL_TYPED_ARRAY_SLOW_CLASS(_typedArray) \
1307 #_typedArray, \
1308 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \
1309 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, \
1310 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, \
1311 JSCLASS_NO_OPTIONAL_MEMBERS \
1314 #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray) \
1316 #_typedArray, \
1317 JSCLASS_HAS_PRIVATE, \
1318 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, \
1319 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, \
1320 _typedArray::class_finalize, \
1321 _typedArray::getObjectOps, NULL, NULL, NULL, \
1322 NULL, NULL, NULL, NULL \
1325 #define INIT_TYPED_ARRAY_CLASS(_typedArray,_type) \
1326 do { \
1327 proto = js_InitClass(cx, obj, NULL, \
1328 &TypedArray::slowClasses[TypedArray::_type], \
1329 _typedArray::class_constructor, 3, \
1330 _typedArray::jsprops, \
1331 _typedArray::jsfuncs, \
1332 NULL, NULL); \
1333 if (!proto) \
1334 return NULL; \
1335 proto->setPrivate(0); \
1336 } while (0)
1338 IMPL_TYPED_ARRAY_STATICS(Int8Array);
1339 IMPL_TYPED_ARRAY_STATICS(Uint8Array);
1340 IMPL_TYPED_ARRAY_STATICS(Int16Array);
1341 IMPL_TYPED_ARRAY_STATICS(Uint16Array);
1342 IMPL_TYPED_ARRAY_STATICS(Int32Array);
1343 IMPL_TYPED_ARRAY_STATICS(Uint32Array);
1344 IMPL_TYPED_ARRAY_STATICS(Float32Array);
1345 IMPL_TYPED_ARRAY_STATICS(Float64Array);
1346 IMPL_TYPED_ARRAY_STATICS(Uint8ClampedArray);
1348 JSClass TypedArray::fastClasses[TYPE_MAX] = {
1349 IMPL_TYPED_ARRAY_FAST_CLASS(Int8Array),
1350 IMPL_TYPED_ARRAY_FAST_CLASS(Uint8Array),
1351 IMPL_TYPED_ARRAY_FAST_CLASS(Int16Array),
1352 IMPL_TYPED_ARRAY_FAST_CLASS(Uint16Array),
1353 IMPL_TYPED_ARRAY_FAST_CLASS(Int32Array),
1354 IMPL_TYPED_ARRAY_FAST_CLASS(Uint32Array),
1355 IMPL_TYPED_ARRAY_FAST_CLASS(Float32Array),
1356 IMPL_TYPED_ARRAY_FAST_CLASS(Float64Array),
1357 IMPL_TYPED_ARRAY_FAST_CLASS(Uint8ClampedArray)
1360 JSClass TypedArray::slowClasses[TYPE_MAX] = {
1361 IMPL_TYPED_ARRAY_SLOW_CLASS(Int8Array),
1362 IMPL_TYPED_ARRAY_SLOW_CLASS(Uint8Array),
1363 IMPL_TYPED_ARRAY_SLOW_CLASS(Int16Array),
1364 IMPL_TYPED_ARRAY_SLOW_CLASS(Uint16Array),
1365 IMPL_TYPED_ARRAY_SLOW_CLASS(Int32Array),
1366 IMPL_TYPED_ARRAY_SLOW_CLASS(Uint32Array),
1367 IMPL_TYPED_ARRAY_SLOW_CLASS(Float32Array),
1368 IMPL_TYPED_ARRAY_SLOW_CLASS(Float64Array),
1369 IMPL_TYPED_ARRAY_SLOW_CLASS(Uint8ClampedArray)
1372 JS_FRIEND_API(JSObject *)
1373 js_InitTypedArrayClasses(JSContext *cx, JSObject *obj)
1375 /* Idempotency required: we initialize several things, possibly lazily. */
1376 JSObject *stop;
1377 if (!js_GetClassObject(cx, obj, JSProto_ArrayBuffer, &stop))
1378 return NULL;
1379 if (stop)
1380 return stop;
1382 JSObject *proto;
1384 INIT_TYPED_ARRAY_CLASS(Int8Array,TYPE_INT8);
1385 INIT_TYPED_ARRAY_CLASS(Uint8Array,TYPE_UINT8);
1386 INIT_TYPED_ARRAY_CLASS(Int16Array,TYPE_INT16);
1387 INIT_TYPED_ARRAY_CLASS(Uint16Array,TYPE_UINT16);
1388 INIT_TYPED_ARRAY_CLASS(Int32Array,TYPE_INT32);
1389 INIT_TYPED_ARRAY_CLASS(Uint32Array,TYPE_UINT32);
1390 INIT_TYPED_ARRAY_CLASS(Float32Array,TYPE_FLOAT32);
1391 INIT_TYPED_ARRAY_CLASS(Float64Array,TYPE_FLOAT64);
1392 INIT_TYPED_ARRAY_CLASS(Uint8ClampedArray,TYPE_UINT8_CLAMPED);
1394 proto = js_InitClass(cx, obj, NULL, &ArrayBuffer::jsclass,
1395 ArrayBuffer::class_constructor, 1,
1396 ArrayBuffer::jsprops, NULL, NULL, NULL);
1397 if (!proto)
1398 return NULL;
1400 proto->setPrivate(NULL);
1401 return proto;
1404 JS_FRIEND_API(JSBool)
1405 js_IsArrayBuffer(JSObject *obj)
1407 return obj && obj->getClass() == &ArrayBuffer::jsclass;
1410 JS_FRIEND_API(JSBool)
1411 js_IsTypedArray(JSObject *obj)
1413 return obj &&
1414 obj->getClass() >= &TypedArray::fastClasses[0] &&
1415 obj->getClass() < &TypedArray::fastClasses[TypedArray::TYPE_MAX];
1418 JS_FRIEND_API(JSObject *)
1419 js_CreateArrayBuffer(JSContext *cx, jsuint nbytes)
1421 AutoValueRooter tvr(cx);
1422 if (!js_NewNumberInRootedValue(cx, jsdouble(nbytes), tvr.addr()))
1423 return NULL;
1425 AutoValueRooter rval(cx);
1426 if (!ArrayBuffer::create(cx, NULL, 1, tvr.addr(), rval.addr()))
1427 return NULL;
1429 return JSVAL_TO_OBJECT(rval.value());
1432 static inline JSBool
1433 TypedArrayConstruct(JSContext *cx, jsint atype, uintN argc, jsval *argv, jsval *rv)
1435 switch (atype) {
1436 case TypedArray::TYPE_INT8:
1437 return !!Int8Array::create(cx, NULL, argc, argv, rv);
1439 case TypedArray::TYPE_UINT8:
1440 return !!Uint8Array::create(cx, NULL, argc, argv, rv);
1442 case TypedArray::TYPE_INT16:
1443 return !!Int16Array::create(cx, NULL, argc, argv, rv);
1445 case TypedArray::TYPE_UINT16:
1446 return !!Uint16Array::create(cx, NULL, argc, argv, rv);
1448 case TypedArray::TYPE_INT32:
1449 return !!Int32Array::create(cx, NULL, argc, argv, rv);
1451 case TypedArray::TYPE_UINT32:
1452 return !!Uint32Array::create(cx, NULL, argc, argv, rv);
1454 case TypedArray::TYPE_FLOAT32:
1455 return !!Float32Array::create(cx, NULL, argc, argv, rv);
1457 case TypedArray::TYPE_FLOAT64:
1458 return !!Float64Array::create(cx, NULL, argc, argv, rv);
1460 case TypedArray::TYPE_UINT8_CLAMPED:
1461 return !!Uint8ClampedArray::create(cx, NULL, argc, argv, rv);
1463 default:
1464 JS_NOT_REACHED("shouldn't have gotten here");
1465 return false;
1469 JS_FRIEND_API(JSObject *)
1470 js_CreateTypedArray(JSContext *cx, jsint atype, jsuint nelements)
1472 JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
1474 jsval vals[2] = { JSVAL_NULL, JSVAL_NULL };
1475 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vals), vals);
1477 if (!js_NewNumberInRootedValue(cx, jsdouble(nelements), &vals[0]))
1478 return NULL;
1480 if (!TypedArrayConstruct(cx, atype, 1, &vals[0], &vals[1]))
1481 return NULL;
1483 return JSVAL_TO_OBJECT(vals[1]);
1486 JS_FRIEND_API(JSObject *)
1487 js_CreateTypedArrayWithArray(JSContext *cx, jsint atype, JSObject *arrayArg)
1489 JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
1491 jsval vals[2] = { JSVAL_NULL, JSVAL_NULL };
1492 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vals), vals);
1494 vals[0] = OBJECT_TO_JSVAL(arrayArg);
1496 if (!TypedArrayConstruct(cx, atype, 1, &vals[0], &vals[1]))
1497 return NULL;
1499 return JSVAL_TO_OBJECT(vals[1]);
1502 JS_FRIEND_API(JSObject *)
1503 js_CreateTypedArrayWithBuffer(JSContext *cx, jsint atype, JSObject *bufArg,
1504 jsint byteoffset, jsint length)
1506 JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
1507 JS_ASSERT(bufArg && ArrayBuffer::fromJSObject(bufArg));
1508 JS_ASSERT_IF(byteoffset < 0, length < 0);
1510 jsval vals[4] = { JSVAL_NULL, JSVAL_NULL, JSVAL_NULL, JSVAL_NULL };
1511 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vals), vals);
1513 int argc = 1;
1514 vals[0] = OBJECT_TO_JSVAL(bufArg);
1516 if (byteoffset >= 0) {
1517 if (!js_NewNumberInRootedValue(cx, jsdouble(byteoffset), &vals[argc]))
1518 return NULL;
1520 argc++;
1523 if (length >= 0) {
1524 if (!js_NewNumberInRootedValue(cx, jsdouble(length), &vals[argc]))
1525 return NULL;
1527 argc++;
1530 if (!TypedArrayConstruct(cx, atype, argc, &vals[0], &vals[3]))
1531 return NULL;
1533 return JSVAL_TO_OBJECT(vals[3]);
1536 JS_FRIEND_API(JSBool)
1537 js_ReparentTypedArrayToScope(JSContext *cx, JSObject *obj, JSObject *scope)
1539 scope = JS_GetGlobalForObject(cx, scope);
1540 if (!scope)
1541 return JS_FALSE;
1543 if (!js_IsTypedArray(obj))
1544 return JS_FALSE;
1546 TypedArray *typedArray = TypedArray::fromJSObject(obj);
1548 JSObject *buffer = typedArray->bufferJS;
1549 JS_ASSERT(js_IsArrayBuffer(buffer));
1551 JSObject *proto;
1552 JSProtoKey key =
1553 JSCLASS_CACHED_PROTO_KEY(&TypedArray::slowClasses[typedArray->type]);
1554 if (!js_GetClassPrototype(cx, scope, key, &proto))
1555 return JS_FALSE;
1557 obj->setProto(proto);
1558 obj->setParent(scope);
1560 key = JSCLASS_CACHED_PROTO_KEY(&ArrayBuffer::jsclass);
1561 if (!js_GetClassPrototype(cx, scope, key, &proto))
1562 return JS_FALSE;
1564 buffer->setProto(proto);
1565 buffer->setParent(scope);
1567 return JS_TRUE;