# HG changeset patch # User Jason Orendorff # Date 1458587139 18000 # Mon Mar 21 14:05:39 2016 -0500 # Node ID 62394f30f8490fe709e2d8a2500c59a5d9c95313 # Parent bfada2f14a324392e962d314848f3482570de642 Bug 1255128 - Standard argument coercion in `new ArrayBuffer(length)`. r=nbp. MozReview-Commit-ID: 8cFpoe4je9O diff --git a/js/src/jsnum.h b/js/src/jsnum.h --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -261,16 +261,29 @@ ToInteger(JSContext* cx, HandleValue v, extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, Value v, double* dp); if (!ToNumberSlow(cx, v, dp)) return false; } *dp = JS::ToInteger(*dp); return true; } +/* ECMA-262 draft (2016 Mar 19) 7.1.15 ToLength ( argument ) */ +inline double +ToLength(double argument) +{ + const double MAX_SAFE_INTEGER = 9007199254740991; + double len = JS::ToInteger(argument); + if (len <= 0) + return 0; + if (len > MAX_SAFE_INTEGER) + return MAX_SAFE_INTEGER; + return len; +} + /* ES6 7.1.15 ToLength, but clamped to the [0,2^32-2] range. If the * return value is false then *overflow will be true iff the value was * not clampable to uint32_t range. * * For JSContext and ExclusiveContext. */ template bool ToLengthClamped(T* cx, HandleValue v, uint32_t* out, bool* overflow); diff --git a/js/src/tests/ecma_6/ArrayBuffer/constructorTypeCheck.js b/js/src/tests/ecma_6/ArrayBuffer/constructorTypeCheck.js new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/ArrayBuffer/constructorTypeCheck.js @@ -0,0 +1,20 @@ +let badLengths = [ + -1, + 1.0001342, + 0x100000000, + 0x200000000, + 0x400000000, + Infinity, + NaN +]; + +for (let n of badLengths) + assertThrowsInstanceOf(() => new ArrayBuffer(n), RangeError); + +assertEq(new ArrayBuffer({valueOf: () => 123}).byteLength, 123); +assertEq(new ArrayBuffer(true).byteLength, 1); +assertEq(new ArrayBuffer([123]).byteLength, 123); +assertEq(new ArrayBuffer(["0x10"]).byteLength, 16); + +if (typeof reportCompare === 'function') + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/ecma_6/DataView/detach-after-construction.js b/js/src/tests/ecma_6/DataView/detach-after-construction.js --- a/js/src/tests/ecma_6/DataView/detach-after-construction.js +++ b/js/src/tests/ecma_6/DataView/detach-after-construction.js @@ -1,12 +1,12 @@ // |reftest| skip-if(!xulRuntime.shell) -- needs detachArrayBuffer for (var detachArg of ['change-data', 'same-data']) { - var buf = new ArrayBuffer([1,2]); + var buf = new ArrayBuffer(2); var bufView = new DataView(buf); detachArrayBuffer(buf, detachArg); assertThrowsInstanceOf(() => bufView.getInt8(0), TypeError); } if (typeof reportCompare === 'function') diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -197,47 +197,49 @@ bool ArrayBufferObject::fun_isView(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); args.rval().setBoolean(args.get(0).isObject() && JS_IsArrayBufferViewObject(&args.get(0).toObject())); return true; } -/* - * new ArrayBuffer(byteLength) - */ +// new ArrayBuffer(byteLength) - ECMA-262 draft (2016 Mar 19) 24.1.2.1 bool ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); + // Step 1. if (!ThrowIfNotConstructing(cx, args, "ArrayBuffer")) return false; - int32_t nbytes = 0; - if (argc > 0 && !ToInt32(cx, args[0], &nbytes)) - return false; + // Step 2. ES6 specifies that `new ArrayBuffer()` without arguments should + // throw, but it's a bug. + double length = 0; + if (args.hasDefined(0)) { + if (!ToNumber(cx, args[0], &length)) + return false; + } - 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. - */ + // Steps 3-4. Also refuse to allocate buffers 1GiB or larger. + double byteLength = ToLength(length); + const double SIZE_LIMIT = 1024.0 * 1024 * 1024; + if (length != byteLength || byteLength >= SIZE_LIMIT) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); return false; } + // Step 5. RootedObject proto(cx); RootedObject newTarget(cx, &args.newTarget().toObject()); if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) return false; - JSObject* bufobj = create(cx, uint32_t(nbytes), proto); + JSObject* bufobj = create(cx, uint32_t(byteLength), proto); if (!bufobj) return false; args.rval().setObject(*bufobj); return true; } static ArrayBufferObject::BufferContents AllocateArrayBufferContents(JSContext* cx, uint32_t nbytes) diff --git a/testing/web-platform/tests/typedarrays/ArrayBuffer_constructor.html b/testing/web-platform/tests/typedarrays/ArrayBuffer_constructor.html --- a/testing/web-platform/tests/typedarrays/ArrayBuffer_constructor.html +++ b/testing/web-platform/tests/typedarrays/ArrayBuffer_constructor.html @@ -3,25 +3,40 @@ Typed Arrays Test: ArrayBuffer constructor