Bug 636078 - Fix some typed array bugs. r=vlad, r=jorendorff
authorJeff Walden <jwalden@mit.edu>
Thu, 24 Feb 2011 01:23:57 -0800
changeset 63149 8323a963fd6c
parent 63148 d2f6db22db10
child 63150 41218b2d043a
push id19043
push userrsayre@mozilla.com
push date2011-02-27 15:29 +0000
treeherdermozilla-central@04e69fadcbcd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvlad, jorendorff
bugs636078
milestone2.0b12pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 636078 - Fix some typed array bugs. r=vlad, r=jorendorff
js/src/jstypedarray.cpp
js/src/jstypedarray.h
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -128,72 +128,66 @@ ArrayBuffer::class_finalize(JSContext *c
 }
 
 /*
  * new ArrayBuffer(byteLength)
  */
 JSBool
 ArrayBuffer::class_constructor(JSContext *cx, uintN argc, Value *vp)
 {
-    return create(cx, argc, JS_ARGV(cx, vp), vp);
+    int32 nbytes = 0;
+    if (argc > 0 && !ValueToECMAInt32(cx, vp[2], &nbytes))
+        return false;
+
+    JSObject *bufobj = create(cx, nbytes);
+    if (!bufobj)
+        return false;
+    vp->setObject(*bufobj);
+    return true;
 }
 
-bool
-ArrayBuffer::create(JSContext *cx, uintN argc, Value *argv, Value *rval)
+JSObject *
+ArrayBuffer::create(JSContext *cx, int32 nbytes)
 {
-    /* N.B. there may not be an argv[-2]/argv[-1]. */
-
     JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBuffer::jsclass);
     if (!obj)
-        return false;
-
-    int32_t nbytes = 0;
-    if (argc > 0) {
-        if (!ValueToECMAInt32(cx, argv[0], &nbytes))
-            return false;
-    }
+        return NULL;
 
     if (nbytes < 0) {
         /*
          * We're just not going to support arrays that are bigger than what will fit
          * as an integer value; if someone actually ever complains (validly), then we
          * can fix.
          */
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_BAD_ARRAY_LENGTH);
-        return false;
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
+        return NULL;
     }
 
     ArrayBuffer *abuf = cx->create<ArrayBuffer>();
-    if (!abuf) {
-        JS_ReportOutOfMemory(cx);
-        return false;
-    }
+    if (!abuf)
+        return NULL;
 
     if (!abuf->allocateStorage(cx, nbytes)) {
         cx->destroy<ArrayBuffer>(abuf);
-        return false;
+        return NULL;
     }
 
     obj->setPrivate(abuf);
-    rval->setObject(*obj);
-    return true;
+    return obj;
 }
 
 bool
 ArrayBuffer::allocateStorage(JSContext *cx, uint32 nbytes)
 {
     JS_ASSERT(data == 0);
 
     if (nbytes) {
         data = cx->calloc(nbytes);
-        if (!data) {
-            JS_ReportOutOfMemory(cx);
+        if (!data)
             return false;
-        }
     }
 
     byteLength = nbytes;
     return true;
 }
 
 void
 ArrayBuffer::freeStorage(JSContext *cx)
@@ -721,100 +715,121 @@ class TypedArrayTemplate
     }
 
     static JSType
     obj_typeOf(JSContext *cx, JSObject *obj)
     {
         return JSTYPE_OBJECT;
     }
 
+    static JSObject *
+    createTypedArray(JSContext *cx, JSObject *bufobj, uint32 byteOffset, uint32 len)
+    {
+        JSObject *obj = NewBuiltinClassInstance(cx, slowClass());
+        if (!obj)
+            return NULL;
+
+        ThisTypeArray *tarray = cx->create<ThisTypeArray>(bufobj, byteOffset, len);
+        if (!tarray)
+            return NULL;
+
+        JS_ASSERT(obj->getClass() == slowClass());
+        obj->setSharedNonNativeMap();
+        obj->clasp = fastClass();
+        obj->setPrivate(tarray);
+
+        // FIXME Bug 599008: make it ok to call preventExtensions here.
+        obj->flags |= JSObject::NOT_EXTENSIBLE;
+
+        return obj;
+    }
+
     /*
      * new [Type]Array(length)
      * new [Type]Array(otherTypedArray)
      * new [Type]Array(JSArray)
      * new [Type]Array(ArrayBuffer, [optional] byteOffset, [optional] length)
      */
     static JSBool
     class_constructor(JSContext *cx, uintN argc, Value *vp)
     {
         /* N.B. this is a constructor for slowClass, not fastClass! */
-        return create(cx, argc, JS_ARGV(cx, vp), vp);
+        JSObject *obj = create(cx, argc, JS_ARGV(cx, vp));
+        if (!obj)
+            return false;
+        vp->setObject(*obj);
+        return true;
     }
 
-    static JSBool
-    create(JSContext *cx, uintN argc, Value *argv, Value *rval)
+    static JSObject *
+    create(JSContext *cx, uintN argc, Value *argv)
     {
         /* N.B. there may not be an argv[-2]/argv[-1]. */
 
-        JSObject *obj = NewBuiltinClassInstance(cx, slowClass());
-        if (!obj)
-            return false;
-
-        ThisTypeArray *tarray = 0;
+        /* () or (number) */
+        jsuint len = 0;
+        if (argc == 0 || ValueIsLength(cx, argv[0], &len)) {
+            JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
+            if (!bufobj)
+                return NULL;
 
-        // figure out the type of the first argument;
-        // no args is treated like an int arg of 0.
-        jsuint len = 0;
-        bool hasLen = true;
-        if (argc > 0)
-            hasLen = ValueIsLength(cx, argv[0], &len);
+            return createTypedArray(cx, bufobj, 0, len);
+        }
+
+        /* (not an object) */
+        if (!argv[0].isObject()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                                 JSMSG_TYPED_ARRAY_BAD_ARGS);
+            return NULL;
+        }
+
+        JSObject *dataObj = &argv[0].toObject();
 
-        if (hasLen) {
-            tarray = cx->create<ThisTypeArray>();
-            if (!tarray) {
-                JS_ReportOutOfMemory(cx);
-                return false;
-            }
+        /* (typedArray) */
+        if (js_IsTypedArray(dataObj)) {
+            TypedArray *otherTypedArray = TypedArray::fromJSObject(dataObj);
+            JS_ASSERT(otherTypedArray);
+
+            uint32 len = otherTypedArray->length;
+            JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
+            if (!bufobj)
+                return NULL;
 
-            if (!tarray->init(cx, len)) {
-                cx->destroy<ThisTypeArray>(tarray);
-                return false;
-            }
-        } else if (argc > 0 && argv[0].isObject()) {
-            int32_t byteOffset = -1;
-            int32_t length = -1;
+            JSObject *obj = createTypedArray(cx, bufobj, 0, len);
+            if (!obj || !copyFrom(cx, obj, otherTypedArray, 0))
+                return NULL;
+            return obj;
+        }
 
-            if (argc > 1) {
-                if (!ValueToInt32(cx, argv[1], &byteOffset))
-                    return false;
-                if (byteOffset < 0) {
-                    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                                         JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1");
-                    return false;
-                }
+        /* (obj, byteOffset, length). */
+        int32_t byteOffset = -1;
+        int32_t length = -1;
+
+        if (argc > 1) {
+            if (!ValueToInt32(cx, argv[1], &byteOffset))
+                return NULL;
+            if (byteOffset < 0) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                                     JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1");
+                return NULL;
             }
 
             if (argc > 2) {
                 if (!ValueToInt32(cx, argv[2], &length))
-                    return false;
+                    return NULL;
                 if (length < 0) {
                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                          JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2");
-                    return false;
+                    return NULL;
                 }
             }
-
-            tarray = cx->create<ThisTypeArray>();
-            if (!tarray) {
-                JS_ReportOutOfMemory(cx);
-                return false;
-            }
-
-            if (!tarray->init(cx, &argv[0].toObject(), byteOffset, length)) {
-                cx->destroy<ThisTypeArray>(tarray);
-                return false;
-            }
-        } else {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                                 JSMSG_TYPED_ARRAY_BAD_ARGS);
-            return false;
         }
 
-        rval->setObject(*obj);
-        return makeFastWithPrivate(cx, obj, tarray);
+        /* (obj, byteOffset, length) */
+        return createTypedArrayWithOffsetLength(cx, dataObj, byteOffset, length);
     }
 
     static void
     class_finalize(JSContext *cx, JSObject *obj)
     {
         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
         if (tarray)
             cx->destroy<ThisTypeArray>(tarray);
@@ -870,35 +885,21 @@ class TypedArrayTemplate
                     end = length;
                 }
             }
         }
 
         if (begin > end)
             begin = end;
 
-        ThisTypeArray *ntarray = tarray->subarray(cx, begin, end);
-        if (!ntarray) {
-            // this should rarely ever happen
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                                 JSMSG_TYPED_ARRAY_BAD_ARGS);
+        JSObject *nobj = createSubarray(cx, tarray, begin, end);
+        if (!nobj)
             return false;
-        }
-
-        // note the usage of NewObject here -- we don't want the
-        // constructor to be called!
-        JS_ASSERT(slowClass() != &js_FunctionClass);
-        JSObject *nobj = NewNonFunction<WithProto::Class>(cx, slowClass(), NULL, NULL);
-        if (!nobj) {
-            cx->destroy<ThisTypeArray>(ntarray);
-            return false;
-        }
-
         vp->setObject(*nobj);
-        return makeFastWithPrivate(cx, nobj, ntarray);
+        return true;
     }
 
     /* set(array[, offset]) */
     static JSBool
     fun_set(JSContext *cx, uintN argc, Value *vp)
     {
         JSObject *obj = ToObject(cx, &vp[1]);
         if (!obj)
@@ -915,31 +916,33 @@ class TypedArrayTemplate
             return false;
         }
 
         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
         if (!tarray)
             return true;
 
         // these are the default values
-        int32_t offset = 0;
+        int32_t off = 0;
 
         Value *argv = JS_ARGV(cx, vp);
         if (argc > 1) {
-            if (!ValueToInt32(cx, argv[1], &offset))
+            if (!ValueToInt32(cx, argv[1], &off))
                 return false;
 
-            if (offset < 0 || uint32_t(offset) > tarray->length) {
+            if (off < 0 || uint32_t(off) > tarray->length) {
                 // the given offset is bogus
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
                 return false;
             }
         }
 
+        uint32 offset(off);
+
         // first arg must be either a typed array or a JS array
         if (argc == 0 || !argv[0].isObject()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
 
         JSObject *arg0 = argv[0].toObjectOrNull();
@@ -948,174 +951,168 @@ class TypedArrayTemplate
             if (!src ||
                 src->length > tarray->length - offset)
             {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
                 return false;
             }
 
-            if (!tarray->copyFrom(cx, src, offset))
+            if (!copyFrom(cx, obj, src, offset))
                 return false;
         } else {
             jsuint len;
             if (!js_GetLengthProperty(cx, arg0, &len))
                 return false;
 
             // avoid overflow; we know that offset <= length
             if (len > tarray->length - offset) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
                 return false;
             }
 
-            if (!tarray->copyFrom(cx, arg0, len, offset))
+            if (!copyFrom(cx, obj, arg0, len, offset))
                 return false;
         }
 
         vp->setUndefined();
         return true;
     }
 
     static ThisTypeArray *
     fromJSObject(JSObject *obj)
     {
         JS_ASSERT(obj->getClass() == fastClass());
         return reinterpret_cast<ThisTypeArray*>(obj->getPrivate());
     }
 
-    // helper used by both the constructor and Subarray()
-    static bool
-    makeFastWithPrivate(JSContext *cx, JSObject *obj, ThisTypeArray *tarray)
+  public:
+    TypedArrayTemplate(JSObject *bufobj, uint32 byteOffset, uint32 len)
     {
-        JS_ASSERT(obj->getClass() == slowClass());
-        obj->setSharedNonNativeMap();
-        obj->clasp = fastClass();
-        obj->setPrivate(tarray);
-        
-        // FIXME bug 599008. make it ok to call preventExtensions here.
-        // Keeping the boolean signature of this method for now.
-        obj->flags |= JSObject::NOT_EXTENSIBLE;
-        return true;
+        JS_ASSERT(bufobj->getClass() == &ArrayBuffer::jsclass);
+
+        type = ArrayTypeID();
+        bufferJS = bufobj;
+        buffer = ArrayBuffer::fromJSObject(bufobj);
+
+        this->byteOffset = byteOffset;
+
+        JS_ASSERT(byteOffset <= buffer->byteLength);
+        this->data = buffer->offsetData(byteOffset);
+        JS_ASSERT(buffer->data <= this->data);
+        JS_ASSERT(this->data <= buffer->offsetData(buffer->byteLength));
+
+        this->byteLength = len * sizeof(NativeType);
+        JS_ASSERT(buffer->byteLength - byteOffset >= this->byteLength);
+
+        this->length = len;
     }
 
-  public:
-    TypedArrayTemplate() { }
-
-    bool
-    init(JSContext *cx, uint32 len)
-    {
-        type = ArrayTypeID();
-        return createBufferWithSizeAndCount(cx, sizeof(NativeType), len);
-    }
-
-    bool
-    init(JSContext *cx, JSObject *other, int32 byteOffsetInt = -1, int32 lengthInt = -1)
+    static JSObject *
+    createTypedArrayWithOffsetLength(JSContext *cx, JSObject *other,
+                                     int32 byteOffsetInt, int32 lengthInt)
     {
-        type = ArrayTypeID();
-        ArrayBuffer *abuf;
+        JS_ASSERT(!js_IsTypedArray(other));
 
-        if (js_IsTypedArray(other)) {
-            TypedArray *tarray = TypedArray::fromJSObject(other);
-            JS_ASSERT(tarray);
-
-            if (!createBufferWithSizeAndCount(cx, sizeof(NativeType), tarray->length))
-                return false;
-            if (!copyFrom(cx, tarray))
-                return false;
-        } else if (other->getClass() == &ArrayBuffer::jsclass &&
+        /* Handle creation from an ArrayBuffer not ArrayBuffer.prototype. */
+        ArrayBuffer *abuf;
+        if (other->getClass() == &ArrayBuffer::jsclass &&
                    ((abuf = ArrayBuffer::fromJSObject(other)) != NULL)) {
             uint32 boffset = (byteOffsetInt < 0) ? 0 : uint32(byteOffsetInt);
 
             if (boffset > abuf->byteLength || boffset % sizeof(NativeType) != 0) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
-                return false; // invalid byteOffset
+                return NULL; // invalid byteOffset
             }
 
             uint32 len;
             if (lengthInt < 0) {
                 len = (abuf->byteLength - boffset) / sizeof(NativeType);
                 if (len * sizeof(NativeType) != (abuf->byteLength - boffset)) {
                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                          JSMSG_TYPED_ARRAY_BAD_ARGS);
-                    return false; // given byte array doesn't map exactly to sizeof(NativeType)*N
+                    return NULL; // given byte array doesn't map exactly to sizeof(NativeType)*N
                 }
             } else {
                 len = (uint32) lengthInt;
             }
 
             // Go slowly and check for overflow.
             uint32 arrayByteLength = len*sizeof(NativeType);
             if (uint32(len) >= INT32_MAX / sizeof(NativeType) ||
                 uint32(boffset) >= INT32_MAX - arrayByteLength)
             {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
-                return false; // overflow occurred along the way when calculating boffset+len*sizeof(NativeType)
+                return NULL; // overflow occurred along the way when calculating boffset+len*sizeof(NativeType)
             }
 
             if (arrayByteLength + boffset > abuf->byteLength) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
-                return false; // boffset+len is too big for the arraybuffer
+                return NULL; // boffset+len is too big for the arraybuffer
             }
 
-            buffer = abuf;
-            bufferJS = other;
-            byteOffset = boffset;
-            byteLength = arrayByteLength;
-            length = len;
-            data = abuf->offsetData(boffset);
-        } else {
-            jsuint len;
-            if (!js_GetLengthProperty(cx, other, &len))
-                return false;
-            if (!createBufferWithSizeAndCount(cx, sizeof(NativeType), len))
-                return false;
-            if (!copyFrom(cx, other, len))
-                return false;
+            return createTypedArray(cx, other, boffset, len);
         }
 
-        return true;
+        /*
+         * Otherwise create a new typed array and copy len properties from the
+         * object.
+         */
+        jsuint len;
+        if (!js_GetLengthProperty(cx, other, &len))
+            return NULL;
+
+        JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
+        if (!bufobj)
+            return NULL;
+
+        JSObject *obj = createTypedArray(cx, bufobj, 0, len);
+        if (!obj || !copyFrom(cx, obj, other, len))
+            return NULL;
+        return obj;
     }
 
     const NativeType
     getIndex(uint32 index) const
     {
         return *(static_cast<const NativeType*>(data) + index);
     }
 
     void
     setIndex(uint32 index, NativeType val)
     {
         *(static_cast<NativeType*>(data) + index) = val;
     }
 
     inline void copyIndexToValue(JSContext *cx, uint32 index, Value *vp);
 
-    ThisTypeArray *
-    subarray(JSContext *cx, uint32 begin, uint32 end)
+    static JSObject *
+    createSubarray(JSContext *cx, ThisTypeArray *tarray, uint32 begin, uint32 end)
     {
-        if (begin > length || end > length)
-            return NULL;
+        JS_ASSERT(tarray);
 
-        ThisTypeArray *tarray = cx->create<ThisTypeArray>();
-        if (!tarray)
-            return NULL;
+        JS_ASSERT(0 <= begin);
+        JS_ASSERT(begin <= tarray->length);
+        JS_ASSERT(0 <= end);
+        JS_ASSERT(end <= tarray->length);
 
-        tarray->buffer = buffer;
-        tarray->bufferJS = bufferJS;
-        tarray->byteOffset = byteOffset + begin * sizeof(NativeType);
-        tarray->byteLength = (end - begin) * sizeof(NativeType);
-        tarray->length = end - begin;
-        tarray->type = type;
-        tarray->data = buffer->offsetData(tarray->byteOffset);
+        JSObject *bufobj = tarray->bufferJS;
+        JS_ASSERT(bufobj);
 
-        return tarray;
+        JS_ASSERT(begin <= end);
+        uint32 length = end - begin;
+
+        JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
+        uint32 byteOffset = begin * sizeof(NativeType);
+
+        return createTypedArray(cx, bufobj, byteOffset, length);
     }
 
   protected:
     static NativeType
     nativeFromValue(JSContext *cx, const Value &v)
     {
         if (v.isInt32())
             return NativeType(v.toInt32());
@@ -1134,22 +1131,26 @@ class TypedArrayTemplate
         }
 
         if (ArrayTypeIsFloatingPoint())
             return NativeType(js_NaN);
 
         return NativeType(int32(0));
     }
     
-    bool
-    copyFrom(JSContext *cx, JSObject *ar, jsuint len, jsuint offset = 0)
+    static bool
+    copyFrom(JSContext *cx, JSObject *thisTypedArrayObj,
+             JSObject *ar, jsuint len, jsuint offset = 0)
     {
-        JS_ASSERT(offset <= length);
-        JS_ASSERT(len <= length - offset);
-        NativeType *dest = static_cast<NativeType*>(data) + offset;
+        ThisTypeArray *thisTypedArray = fromJSObject(thisTypedArrayObj);
+        JS_ASSERT(thisTypedArray);
+
+        JS_ASSERT(offset <= thisTypedArray->length);
+        JS_ASSERT(len <= thisTypedArray->length - offset);
+        NativeType *dest = static_cast<NativeType*>(thisTypedArray->data) + offset;
 
         if (ar->isDenseArray() && ar->getDenseArrayCapacity() >= len) {
             JS_ASSERT(ar->getArrayLength() == len);
 
             Value *src = ar->getDenseArrayElements();
 
             for (uintN i = 0; i < len; ++i)
                 *dest++ = nativeFromValue(cx, *src++);
@@ -1162,27 +1163,30 @@ class TypedArrayTemplate
                     return false;
                 *dest++ = nativeFromValue(cx, v);
             }
         }
 
         return true;
     }
 
-    bool
-    copyFrom(JSContext *cx, TypedArray *tarray, jsuint offset = 0)
+    static bool
+    copyFrom(JSContext *cx, JSObject *thisTypedArrayObj, TypedArray *tarray, jsuint offset)
     {
-        JS_ASSERT(offset <= length);
-        JS_ASSERT(tarray->length <= length - offset);
-        if (tarray->buffer == buffer)
-            return copyFromWithOverlap(cx, tarray, offset);
+        ThisTypeArray *thisTypedArray = fromJSObject(thisTypedArrayObj);
+        JS_ASSERT(thisTypedArray);
 
-        NativeType *dest = static_cast<NativeType*>(data) + offset;
+        JS_ASSERT(offset <= thisTypedArray->length);
+        JS_ASSERT(tarray->length <= thisTypedArray->length - offset);
+        if (tarray->buffer == thisTypedArray->buffer)
+            return thisTypedArray->copyFromWithOverlap(cx, tarray, offset);
 
-        if (tarray->type == type) {
+        NativeType *dest = static_cast<NativeType*>(thisTypedArray->data) + offset;
+
+        if (tarray->type == thisTypedArray->type) {
             memcpy(dest, tarray->data, tarray->byteLength);
             return true;
         }
 
         uintN srclen = tarray->length;
         switch (tarray->type) {
           case TypedArray::TYPE_INT8: {
             int8 *src = static_cast<int8*>(tarray->data);
@@ -1237,34 +1241,32 @@ class TypedArrayTemplate
             JS_NOT_REACHED("copyFrom with a TypedArray of unknown type");
             break;
         }
 
         return true;
     }
 
     bool
-    copyFromWithOverlap(JSContext *cx, TypedArray *tarray, jsuint offset = 0)
+    copyFromWithOverlap(JSContext *cx, TypedArray *tarray, jsuint offset)
     {
         JS_ASSERT(offset <= length);
 
         NativeType *dest = static_cast<NativeType*>(data) + offset;
 
         if (tarray->type == type) {
             memmove(dest, tarray->data, tarray->byteLength);
             return true;
         }
 
         // We have to make a copy of the source array here, since
         // there's overlap, and we have to convert types.
-        void *srcbuf = js_malloc(tarray->byteLength);
-        if (!srcbuf) {
-            js_ReportOutOfMemory(cx);
+        void *srcbuf = cx->malloc(tarray->byteLength);
+        if (!srcbuf)
             return false;
-        }
         memcpy(srcbuf, tarray->data, tarray->byteLength);
 
         switch (tarray->type) {
           case TypedArray::TYPE_INT8: {
             int8 *src = (int8*) srcbuf;
             for (uintN i = 0; i < tarray->length; ++i)
                 *dest++ = NativeType(*src++);
             break;
@@ -1316,52 +1318,28 @@ class TypedArrayTemplate
             JS_NOT_REACHED("copyFromWithOverlap with a TypedArray of unknown type");
             break;
         }
 
         js_free(srcbuf);
         return true;
     }
 
-    bool
-    createBufferWithSizeAndCount(JSContext *cx, uint32 size, uint32 count)
+    static JSObject *
+    createBufferWithSizeAndCount(JSContext *cx, uint32 count)
     {
-        JS_ASSERT(size != 0);
-
+        size_t size = sizeof(NativeType);
         if (size != 0 && count >= INT32_MAX / size) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_NEED_DIET, "size and count");
             return false;
         }
 
         int32 bytelen = size * count;
-        if (!createBufferWithByteLength(cx, bytelen))
-            return false;
-
-        length = count;
-        return true;
-    }
-
-    bool
-    createBufferWithByteLength(JSContext *cx, int32 bytes)
-    {
-        Value arg = Int32Value(bytes), rval;
-        if (!ArrayBuffer::create(cx, 1, &arg, &rval))
-            return false;
-
-        JSObject *obj = &rval.toObject();
-
-        bufferJS = obj;
-        buffer = ArrayBuffer::fromJSObject(obj);
-
-        byteOffset = 0;
-        byteLength = bytes;
-        data = buffer->data;
-
-        return true;
+        return ArrayBuffer::create(cx, bytelen);
     }
 };
 
 // this default implementation is only valid for integer types
 // less than 32-bits in size.
 template<typename NativeType>
 void
 TypedArrayTemplate<NativeType>::copyIndexToValue(JSContext *cx, uint32 index, Value *vp)
@@ -1645,120 +1623,99 @@ js_IsTypedArray(JSObject *obj)
     Class *clasp = obj->getClass();
     return clasp >= &TypedArray::fastClasses[0] &&
            clasp <  &TypedArray::fastClasses[TypedArray::TYPE_MAX];
 }
 
 JS_FRIEND_API(JSObject *)
 js_CreateArrayBuffer(JSContext *cx, jsuint nbytes)
 {
-    Value arg = NumberValue(nbytes), rval;
-    if (!ArrayBuffer::create(cx, 1, &arg, &rval))
-        return NULL;
-    return &rval.toObject();
+    return ArrayBuffer::create(cx, nbytes);
 }
 
-static inline JSBool
-TypedArrayConstruct(JSContext *cx, jsint atype, uintN argc, Value *argv, Value *rv)
+static inline JSObject *
+TypedArrayConstruct(JSContext *cx, jsint atype, uintN argc, Value *argv)
 {
     switch (atype) {
       case TypedArray::TYPE_INT8:
-        return Int8Array::create(cx, argc, argv, rv);
+        return Int8Array::create(cx, argc, argv);
 
       case TypedArray::TYPE_UINT8:
-        return Uint8Array::create(cx, argc, argv, rv);
+        return Uint8Array::create(cx, argc, argv);
 
       case TypedArray::TYPE_INT16:
-        return Int16Array::create(cx, argc, argv, rv);
+        return Int16Array::create(cx, argc, argv);
 
       case TypedArray::TYPE_UINT16:
-        return Uint16Array::create(cx, argc, argv, rv);
+        return Uint16Array::create(cx, argc, argv);
 
       case TypedArray::TYPE_INT32:
-        return Int32Array::create(cx, argc, argv, rv);
+        return Int32Array::create(cx, argc, argv);
 
       case TypedArray::TYPE_UINT32:
-        return Uint32Array::create(cx, argc, argv, rv);
+        return Uint32Array::create(cx, argc, argv);
 
       case TypedArray::TYPE_FLOAT32:
-        return Float32Array::create(cx, argc, argv, rv);
+        return Float32Array::create(cx, argc, argv);
 
       case TypedArray::TYPE_FLOAT64:
-        return Float64Array::create(cx, argc, argv, rv);
+        return Float64Array::create(cx, argc, argv);
 
       case TypedArray::TYPE_UINT8_CLAMPED:
-        return Uint8ClampedArray::create(cx, argc, argv, rv);
+        return Uint8ClampedArray::create(cx, argc, argv);
 
       default:
         JS_NOT_REACHED("shouldn't have gotten here");
         return false;
     }
 }
 
 JS_FRIEND_API(JSObject *)
 js_CreateTypedArray(JSContext *cx, jsint atype, jsuint nelements)
 {
     JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
 
-    Value vals[2];
-    vals[0].setInt32(nelements);
-    vals[1].setUndefined();
-
-    AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vals), vals);
-    if (!TypedArrayConstruct(cx, atype, 1, &vals[0], &vals[1]))
-        return NULL;
-
-    return &vals[1].toObject();
+    Value nelems = Int32Value(nelements);
+    return TypedArrayConstruct(cx, atype, 1, &nelems);
 }
 
 JS_FRIEND_API(JSObject *)
 js_CreateTypedArrayWithArray(JSContext *cx, jsint atype, JSObject *arrayArg)
 {
     JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
 
-    Value vals[2];
-    vals[0].setObject(*arrayArg);
-    vals[1].setUndefined();
-
-    AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vals), vals);
-    if (!TypedArrayConstruct(cx, atype, 1, &vals[0], &vals[1]))
-        return NULL;
-
-    return &vals[1].toObject();
+    Value arrval = ObjectValue(*arrayArg);
+    return TypedArrayConstruct(cx, atype, 1, &arrval);
 }
 
 JS_FRIEND_API(JSObject *)
 js_CreateTypedArrayWithBuffer(JSContext *cx, jsint atype, JSObject *bufArg,
                               jsint byteoffset, jsint length)
 {
     JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
     JS_ASSERT(bufArg && ArrayBuffer::fromJSObject(bufArg));
     JS_ASSERT_IF(byteoffset < 0, length < 0);
 
     Value vals[4];
 
     int argc = 1;
     vals[0].setObject(*bufArg);
-    vals[3].setUndefined();
 
     if (byteoffset >= 0) {
         vals[argc].setInt32(byteoffset);
         argc++;
     }
 
     if (length >= 0) {
         vals[argc].setInt32(length);
         argc++;
     }
 
     AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vals), vals);
-    if (!TypedArrayConstruct(cx, atype, argc, &vals[0], &vals[3]))
-        return NULL;
-
-    return &vals[3].toObject();
+    return TypedArrayConstruct(cx, atype, argc, &vals[0]);
 }
 
 JS_FRIEND_API(JSBool)
 js_ReparentTypedArrayToScope(JSContext *cx, JSObject *obj, JSObject *scope)
 {
     JS_ASSERT(obj);
 
     scope = JS_GetGlobalForObject(cx, scope);
--- a/js/src/jstypedarray.h
+++ b/js/src/jstypedarray.h
@@ -59,17 +59,17 @@ struct JS_FRIEND_API(ArrayBuffer) {
     static Class jsclass;
     static JSPropertySpec jsprops[];
 
     static JSBool prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp);
     static void class_finalize(JSContext *cx, JSObject *obj);
 
     static JSBool class_constructor(JSContext *cx, uintN argc, Value *vp);
 
-    static bool create(JSContext *cx, uintN argc, Value *argv, Value *rval);
+    static JSObject *create(JSContext *cx, int32 nbytes);
 
     static ArrayBuffer *fromJSObject(JSObject *obj);
 
     ArrayBuffer()
         : data(0), byteLength()
     {
     }