Bug 1263803 - Change AllocateArrayBuffer to receive byteLength instead of nelements. r=lth
authorTooru Fujisawa <arai_a@mac.com>
Sat, 16 Apr 2016 01:53:03 +0900
changeset 331276 a338aa9ac43ff12190109267d2210d230db9286e
parent 331275 2f5f51545d0e466479ec6fdf8b89af72685dfc6d
child 331277 6a609d255091c25c67b2b6bce103fc5a2b3ab6a9
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1263803
milestone48.0a1
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 1263803 - Change AllocateArrayBuffer to receive byteLength instead of nelements. r=lth
js/src/tests/ecma_6/ArrayBuffer/indivisible-byteLength.js
js/src/vm/TypedArrayObject.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/ArrayBuffer/indivisible-byteLength.js
@@ -0,0 +1,40 @@
+var BUGNUMBER = 1263803;
+var summary = 'CloneArrayBuffer should allocate ArrayBuffer even with individual byteLength';
+
+print(BUGNUMBER + ": " + summary);
+
+var abuf1 = new ArrayBuffer(38);
+assertEq(abuf1.byteLength, 38);
+
+var a1 = new Float64Array(abuf1, 24, 0);
+assertEq(a1.buffer.byteLength, 38);
+assertEq(a1.byteLength, 0);
+assertEq(a1.byteOffset, 24);
+
+var a2 = new Float64Array(a1);
+// NOTE: There is a behavior difference on ArrayBuffer's byteLength, between
+//       lazily allocated case and eagerly allocated case (bug 1264941).
+//       This one is lazily allocated case, and it equals to a2.byteLength.
+assertEq(a2.buffer.byteLength, 0);
+assertEq(a2.byteLength, 0);
+assertEq(a2.byteOffset, 0);
+
+class MyArrayBuffer extends ArrayBuffer {}
+
+var abuf2 = new MyArrayBuffer(38);
+assertEq(abuf2.byteLength, 38);
+
+var a3 = new Float64Array(abuf2, 24, 0);
+assertEq(a3.buffer.byteLength, 38);
+assertEq(a3.byteLength, 0);
+assertEq(a3.byteOffset, 24);
+
+var a4 = new Float64Array(a3);
+// NOTE: This one is eagerly allocated case, and it differs from a4.byteLength.
+//       Either one should be fixed once bug 1264941 is fixed.
+assertEq(a4.buffer.byteLength, 14);
+assertEq(a4.byteLength, 0);
+assertEq(a4.byteOffset, 0);
+
+if (typeof reportCompare === 'function')
+  reportCompare(true, true);
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -631,58 +631,57 @@ class TypedArrayObjectTemplate : public 
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return nullptr; // byteOffset + len is too big for the arraybuffer
         }
 
         return makeInstance(cx, buffer, byteOffset, len, proto);
     }
 
     static bool
-    maybeCreateArrayBuffer(JSContext* cx, uint32_t nelements, HandleObject nonDefaultProto,
+    maybeCreateArrayBuffer(JSContext* cx, uint32_t byteLength, HandleObject nonDefaultProto,
                            MutableHandle<ArrayBufferObject*> buffer)
     {
+        MOZ_ASSERT(byteLength < INT32_MAX);
         static_assert(INLINE_BUFFER_LIMIT % sizeof(NativeType) == 0,
                       "ArrayBuffer inline storage shouldn't waste any space");
 
-        if (!nonDefaultProto && nelements <= INLINE_BUFFER_LIMIT / sizeof(NativeType)) {
+        if (!nonDefaultProto && byteLength <= INLINE_BUFFER_LIMIT) {
             // The array's data can be inline, and the buffer created lazily.
             return true;
         }
 
-        if (nelements >= INT32_MAX / sizeof(NativeType)) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
-                                 JSMSG_NEED_DIET, "size and count");
-            return false;
-        }
-
-        ArrayBufferObject* buf = ArrayBufferObject::create(cx, nelements * sizeof(NativeType),
-                                                           nonDefaultProto);
+        ArrayBufferObject* buf = ArrayBufferObject::create(cx, byteLength, nonDefaultProto);
         if (!buf)
             return false;
 
         buffer.set(buf);
         return true;
     }
 
     static JSObject*
     fromLength(JSContext* cx, uint32_t nelements, HandleObject newTarget = nullptr)
     {
         RootedObject proto(cx);
         if (!GetPrototypeForInstance(cx, newTarget, &proto))
             return nullptr;
 
+        if (nelements >= INT32_MAX / BYTES_PER_ELEMENT) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                                 JSMSG_NEED_DIET, "size and count");
+            return nullptr;
+        }
         Rooted<ArrayBufferObject*> buffer(cx);
-        if (!maybeCreateArrayBuffer(cx, nelements, nullptr, &buffer))
+        if (!maybeCreateArrayBuffer(cx, nelements * BYTES_PER_ELEMENT, nullptr, &buffer))
             return nullptr;
 
         return makeInstance(cx, buffer, 0, nelements, proto);
     }
 
     static bool
-    AllocateArrayBuffer(JSContext* cx, HandleValue ctor, uint32_t elementLength,
+    AllocateArrayBuffer(JSContext* cx, HandleValue ctor, uint32_t byteLength,
                         MutableHandle<ArrayBufferObject*> buffer);
 
     static bool
     CloneArrayBufferNoCopy(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> srcBuffer,
                            bool isWrapped, uint32_t srcByteOffset,
                            MutableHandle<ArrayBufferObject*> buffer);
 
     static JSObject*
@@ -729,34 +728,40 @@ struct TypedArrayObject::OfType
 {
     typedef TypedArrayObjectTemplate<T> Type;
 };
 
 // ES 2016 draft Mar 25, 2016 24.1.1.1.
 template<typename T>
 /* static */ bool
 TypedArrayObjectTemplate<T>::AllocateArrayBuffer(JSContext* cx, HandleValue ctor,
-                                                 uint32_t elementLength,
+                                                 uint32_t byteLength,
                                                  MutableHandle<ArrayBufferObject*> buffer)
 {
     // ES 2016 draft Mar 25, 2016 24.1.1.1 step 1 (partially).
     // ES 2016 draft Mar 25, 2016 9.1.14 steps 1-2.
     MOZ_ASSERT(ctor.isObject());
     RootedObject proto(cx);
     RootedObject ctorObj(cx, &ctor.toObject());
     if (!GetPrototypeFromConstructor(cx, ctorObj, &proto))
         return false;
     JSObject* arrayBufferProto = GlobalObject::getOrCreateArrayBufferPrototype(cx, cx->global());
     if (!arrayBufferProto)
         return false;
     if (proto == arrayBufferProto)
         proto = nullptr;
 
+    if (byteLength >= INT32_MAX) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                             JSMSG_NEED_DIET, "size and count");
+        return false;
+    }
+
     // ES 2016 draft Mar 25, 2016 24.1.1.1 steps 1 (remaining part), 2-6.
-    if (!maybeCreateArrayBuffer(cx, elementLength, proto, buffer))
+    if (!maybeCreateArrayBuffer(cx, byteLength, proto, buffer))
         return false;
 
     return true;
 }
 
 static bool
 IsArrayBufferConstructor(const Value& v)
 {
@@ -842,18 +847,17 @@ TypedArrayObjectTemplate<T>::CloneArrayB
     MOZ_ASSERT(srcByteOffset <= srcLength);
 
     // Step 6.
     uint32_t cloneLength = srcLength - srcByteOffset;
 
     // Step 7 (skipped).
 
     // Steps 8.
-    MOZ_ASSERT(cloneLength % BYTES_PER_ELEMENT == 0);
-    if (!AllocateArrayBuffer(cx, cloneCtor, cloneLength / BYTES_PER_ELEMENT, buffer))
+    if (!AllocateArrayBuffer(cx, cloneCtor, cloneLength, buffer))
         return false;
 
     // Step 9.
     if (srcBuffer->isDetached()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
         return false;
     }
 
@@ -933,33 +937,33 @@ TypedArrayObjectTemplate<T>::fromTypedAr
     // Steps 11-12.
     Scalar::Type srcType = srcArray->type();
 
     // Step 13 (skipped).
 
     // Step 14.
     uint32_t srcByteOffset = srcArray->byteOffset();
 
-    // Steps 15-16 (skipped).
-    // Our AllocateArrayBuffer receives elementLength instead of byteLength.
+    // Steps 15-16.
+    uint32_t byteLength = BYTES_PER_ELEMENT * elementLength;
 
     // Steps 8-9, 17.
     Rooted<ArrayBufferObject*> buffer(cx);
     if (ArrayTypeID() == srcType) {
         // Step 17.a.
         if (!CloneArrayBufferNoCopy(cx, srcData, isWrapped, srcByteOffset, &buffer))
             return nullptr;
     } else {
         // Step 18.a.
         RootedValue bufferCtor(cx);
         if (!GetSpeciesConstructor(cx, srcData, isWrapped, &bufferCtor))
             return nullptr;
 
         // Step 18.b.
-        if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, &buffer))
+        if (!AllocateArrayBuffer(cx, bufferCtor, byteLength, &buffer))
             return nullptr;
 
         // Step 18.c.
         if (srcArray->hasDetachedBuffer()) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
             return nullptr;
         }
     }
@@ -986,17 +990,22 @@ TypedArrayObjectTemplate<T>::fromObject(
 {
     RootedObject proto(cx);
     Rooted<ArrayBufferObject*> buffer(cx);
     uint32_t len;
     if (!GetLengthProperty(cx, other, &len))
         return nullptr;
     if (!GetPrototypeForInstance(cx, newTarget, &proto))
         return nullptr;
-    if (!maybeCreateArrayBuffer(cx, len, nullptr, &buffer))
+    if (len >= INT32_MAX / BYTES_PER_ELEMENT) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                             JSMSG_NEED_DIET, "size and count");
+        return nullptr;
+    }
+    if (!maybeCreateArrayBuffer(cx, len * BYTES_PER_ELEMENT, nullptr, &buffer))
         return nullptr;
 
     Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, len, proto));
     if (!obj)
         return nullptr;
 
     if (!TypedArrayMethods<TypedArrayObject>::setFromNonTypedArray(cx, obj, other, len))
         return nullptr;