diff options
Diffstat (limited to 'js/src/tests/non262/TypedArray')
118 files changed, 7545 insertions, 0 deletions
diff --git a/js/src/tests/non262/TypedArray/Tconstructor-fromTypedArray-byteLength.js b/js/src/tests/non262/TypedArray/Tconstructor-fromTypedArray-byteLength.js new file mode 100644 index 0000000000..6aadca5c81 --- /dev/null +++ b/js/src/tests/non262/TypedArray/Tconstructor-fromTypedArray-byteLength.js @@ -0,0 +1,17 @@ +var g = newGlobal(); + +var arr = [1, 2, 3]; +for (var constructor of anyTypedArrayConstructors) { + var tarr = new constructor(arr); + for (var constructor2 of anyTypedArrayConstructors) { + var copied = new constructor2(tarr); + assertEq(copied.buffer.byteLength, arr.length * constructor2.BYTES_PER_ELEMENT); + + g.tarr = tarr; + copied = g.eval(`new ${constructor2.name}(tarr);`); + assertEq(copied.buffer.byteLength, arr.length * constructor2.BYTES_PER_ELEMENT); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/at.js b/js/src/tests/non262/TypedArray/at.js new file mode 100644 index 0000000000..bab5dc8ff2 --- /dev/null +++ b/js/src/tests/non262/TypedArray/at.js @@ -0,0 +1,47 @@ +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.at.length, 1); + + assertEq(new constructor([0]).at(0), 0); + assertEq(new constructor([0]).at(-1), 0); + + assertEq(new constructor([]).at(0), undefined); + assertEq(new constructor([]).at(-1), undefined); + assertEq(new constructor([]).at(1), undefined); + + assertEq(new constructor([0, 1]).at(0), 0); + assertEq(new constructor([0, 1]).at(1), 1); + assertEq(new constructor([0, 1]).at(-2), 0); + assertEq(new constructor([0, 1]).at(-1), 1); + + assertEq(new constructor([0, 1]).at(2), undefined); + assertEq(new constructor([0, 1]).at(-3), undefined); + assertEq(new constructor([0, 1]).at(-4), undefined); + assertEq(new constructor([0, 1]).at(Infinity), undefined); + assertEq(new constructor([0, 1]).at(-Infinity), undefined); + assertEq(new constructor([0, 1]).at(NaN), 0); // ToInteger(NaN) = 0 + + // Called from other globals. + if (typeof newGlobal === "function") { + var at = newGlobal()[constructor.name].prototype.at; + assertEq(at.call(new constructor([1, 2, 3]), 2), 3); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.at.call(invalidReceiver); + }, TypeError, "Assert that 'at' fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).at(1), 2); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/browser.js b/js/src/tests/non262/TypedArray/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/TypedArray/browser.js diff --git a/js/src/tests/non262/TypedArray/bug1526838.js b/js/src/tests/non262/TypedArray/bug1526838.js new file mode 100644 index 0000000000..1d652f7783 --- /dev/null +++ b/js/src/tests/non262/TypedArray/bug1526838.js @@ -0,0 +1,8 @@ +const testArray = [1n]; +for (const constructor of anyTypedArrayConstructors) { + assertThrowsInstanceOf(() => new constructor(testArray), TypeError); + assertThrowsInstanceOf(() => new constructor(testArray.values()), TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-ArrayBuffer-species-wrap.js b/js/src/tests/non262/TypedArray/constructor-ArrayBuffer-species-wrap.js new file mode 100644 index 0000000000..d454804f94 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-ArrayBuffer-species-wrap.js @@ -0,0 +1,55 @@ +// |reftest| skip-if(!xulRuntime.shell) + +let g = newGlobal(); + +// Both TypedArray and ArrayBuffer from different global. +for (let ctor of typedArrayConstructors) { + let a = g.eval(`new ${ctor.name}([1, 2, 3, 4, 5]);`); + for (let ctor2 of typedArrayConstructors) { + let b = new ctor2(a); + assertEq(Object.getPrototypeOf(b).constructor, ctor2); + assertEq(Object.getPrototypeOf(b.buffer).constructor, ArrayBuffer); + } +} + +// Only ArrayBuffer from different global. +let origSpecies = Object.getOwnPropertyDescriptor(ArrayBuffer, Symbol.species); +let modSpecies = { + get() { + throw new Error("unexpected @@species access"); + } +}; +for (let ctor of typedArrayConstructors) { + let a = new ctor([1, 2, 3, 4, 5]); + for (let ctor2 of typedArrayConstructors) { + Object.defineProperty(ArrayBuffer, Symbol.species, modSpecies); + let b = new ctor2(a); + Object.defineProperty(ArrayBuffer, Symbol.species, origSpecies); + assertEq(Object.getPrototypeOf(b).constructor, ctor2); + assertEq(Object.getPrototypeOf(b.buffer).constructor, ArrayBuffer); + } +} + +// Only TypedArray from different global. +g.otherArrayBuffer = ArrayBuffer; +g.eval(` +var origSpecies = Object.getOwnPropertyDescriptor(ArrayBuffer, Symbol.species); +var modSpecies = { + get() { + throw new Error("unexpected @@species access"); + } +}; +`); +for (let ctor of typedArrayConstructors) { + let a = g.eval(`new ${ctor.name}([1, 2, 3, 4, 5]);`); + for (let ctor2 of typedArrayConstructors) { + g.eval(`Object.defineProperty(ArrayBuffer, Symbol.species, modSpecies);`); + let b = new ctor2(a); + g.eval(`Object.defineProperty(ArrayBuffer, Symbol.species, origSpecies);`); + assertEq(Object.getPrototypeOf(b).constructor, ctor2); + assertEq(Object.getPrototypeOf(b.buffer).constructor, ArrayBuffer); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-ArrayBuffer-species.js b/js/src/tests/non262/TypedArray/constructor-ArrayBuffer-species.js new file mode 100644 index 0000000000..aa7830df67 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-ArrayBuffer-species.js @@ -0,0 +1,19 @@ +for (let ctor of typedArrayConstructors) { + let arr = new ctor([1, 2, 3, 4, 5, 6, 7, 8]); + + arr.buffer.constructor = { + get [Symbol.species]() { + throw new Error("unexpected @@species access"); + } + }; + + for (let ctor2 of typedArrayConstructors) { + let arr2 = new ctor2(arr); + + assertEq(Object.getPrototypeOf(arr2.buffer), ArrayBuffer.prototype); + assertEq(arr2.buffer.constructor, ArrayBuffer); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-buffer-sequence.js b/js/src/tests/non262/TypedArray/constructor-buffer-sequence.js new file mode 100644 index 0000000000..d69478344d --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-buffer-sequence.js @@ -0,0 +1,230 @@ +// 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] ) + +// Ensure the various error conditions are tested in the correct order. + +const otherGlobal = newGlobal(); + +function* createBuffers(lengths = [0, 8]) { + for (let length of lengths) { + let buffer = new ArrayBuffer(length); + yield {buffer, detach: () => detachArrayBuffer(buffer)}; + } + + for (let length of lengths) { + let buffer = new otherGlobal.ArrayBuffer(length); + yield {buffer, detach: () => otherGlobal.detachArrayBuffer(buffer)}; + } +} + +const poisonedValue = new Proxy({}, new Proxy({}, { + get() { + // Throws an exception when any proxy trap is invoked. + throw new Error("Poisoned Value"); + } +})); + +class ExpectedError extends Error { } + +function ConstructorWithThrowingPrototype(detach) { + return Object.defineProperty(function(){}.bind(null), "prototype", { + get() { + if (detach) + detach(); + throw new ExpectedError(); + } + }); +} + +function ValueThrowing(detach) { + return { + valueOf() { + if (detach) + detach(); + throw new ExpectedError(); + } + }; +} + +function ValueReturning(value, detach) { + return { + valueOf() { + if (detach) + detach(); + return value; + } + }; +} + +// Ensure step 4 |AllocateTypedArray| is executed before step 6 |ToIndex(byteOffset)|. +for (let {buffer} of createBuffers()) { + let constructor = ConstructorWithThrowingPrototype(); + + assertThrowsInstanceOf(() => + Reflect.construct(Int32Array, [buffer, poisonedValue, 0], constructor), ExpectedError); +} + +// Ensure step 4 |AllocateTypedArray| is executed before step 9 |IsDetachedBuffer(buffer)|. +for (let {buffer, detach} of createBuffers()) { + let constructor = ConstructorWithThrowingPrototype(); + + detach(); + assertThrowsInstanceOf(() => + Reflect.construct(Int32Array, [buffer, 0, 0], constructor), ExpectedError); +} + +// Ensure step 4 |AllocateTypedArray| is executed before step 9 |IsDetachedBuffer(buffer)|. +// - Variant: Detach buffer dynamically. +for (let {buffer, detach} of createBuffers()) { + let constructor = ConstructorWithThrowingPrototype(detach); + + assertThrowsInstanceOf(() => + Reflect.construct(Int32Array, [buffer, 0, 0], constructor), ExpectedError); +} + +// Ensure step 4 |AllocateTypedArray| is executed before step 8.a |ToIndex(length)|. +for (let {buffer} of createBuffers()) { + let constructor = ConstructorWithThrowingPrototype(); + + assertThrowsInstanceOf(() => + Reflect.construct(Int32Array, [buffer, 0, poisonedValue], constructor), ExpectedError); +} + +// Ensure step 6 |ToIndex(byteOffset)| is executed before step 9 |IsDetachedBuffer(buffer)|. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = ValueThrowing(); + + detach(); + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), ExpectedError); +} + +// Ensure step 6 |ToIndex(byteOffset)| is executed before step 9 |IsDetachedBuffer(buffer)|. +// - Variant: Detach buffer dynamically. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = ValueThrowing(detach); + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), ExpectedError); +} + +// Ensure step 6 |ToIndex(byteOffset)| is executed before step 8.a |ToIndex(length)|. +for (let {buffer} of createBuffers()) { + let byteOffset = ValueThrowing(); + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, poisonedValue), ExpectedError); +} + +// Ensure step 7 |offset modulo elementSize ≠0| is executed before step 9 |IsDetachedBuffer(buffer)|. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = 1; + + detach(); + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), RangeError); +} + +// Ensure step 7 |offset modulo elementSize ≠0| is executed before step 9 |IsDetachedBuffer(buffer)|. +// - Variant: Detach buffer dynamically. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = ValueReturning(1, detach); + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), RangeError); +} + +// Ensure step 7 |offset modulo elementSize ≠0| is executed before step 8.a |ToIndex(length)|. +for (let {buffer} of createBuffers()) { + assertThrowsInstanceOf(() => new Int32Array(buffer, 1, poisonedValue), RangeError); +} + +// Ensure step 8.a |ToIndex(length)| is executed before step 9 |IsDetachedBuffer(buffer)|. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = 0; + let length = ValueThrowing(); + + detach(); + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), ExpectedError); +} + +// Ensure step 8.a |ToIndex(length)| is executed before step 9 |IsDetachedBuffer(buffer)|. +// - Variant: Detach buffer dynamically (1). +for (let {buffer, detach} of createBuffers()) { + let byteOffset = ValueReturning(0, detach); + let length = ValueThrowing(); + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), ExpectedError); +} + +// Ensure step 8.a |ToIndex(length)| is executed before step 9 |IsDetachedBuffer(buffer)|. +// - Variant: Detach buffer dynamically (2). +for (let {buffer, detach} of createBuffers()) { + let byteOffset = 0; + let length = ValueThrowing(detach); + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), ExpectedError); +} + +// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.a |bufferByteLength modulo elementSize ≠0|. +for (let {buffer, detach} of createBuffers([1, 9])) { + let byteOffset = 0; + + detach(); + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError); +} + +// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.a |bufferByteLength modulo elementSize ≠0|. +// - Variant: Detach buffer dynamically. +for (let {buffer, detach} of createBuffers([1, 9])) { + let byteOffset = ValueReturning(0, detach); + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError); +} + +// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.c |newByteLength < 0|. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = 64; + + detach(); + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError); +} + +// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.c |newByteLength < 0|. +// - Variant: Detach buffer dynamically. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = ValueReturning(64, detach); + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError); +} + +// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 12.b |offset+newByteLength > bufferByteLength|. +// - Case A: The given byteOffset is too large. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = 64; + let length = ValueReturning(0, detach); + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError); +} + +// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 12.b |offset+newByteLength > bufferByteLength|. +// - Case B: The given length is too large. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = 0; + let length = ValueReturning(64, detach); + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError); +} + +// Ensure we handle the case when ToIndex(byteOffset) detaches the array buffer. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = ValueReturning(0, detach); + let length = 0; + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError); +} + +// Ensure we handle the case when ToIndex(length) detaches the array buffer. +for (let {buffer, detach} of createBuffers()) { + let byteOffset = 0; + let length = ValueReturning(0, detach); + + assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-byteoffsets-bounds.js b/js/src/tests/non262/TypedArray/constructor-byteoffsets-bounds.js new file mode 100644 index 0000000000..ce213038d4 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-byteoffsets-bounds.js @@ -0,0 +1,34 @@ +// 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] ) + +// Test bound checks around for |byteOffset| and |length| arguments. + +const ab = new ArrayBuffer(0); + +for (let TA of typedArrayConstructors) { + // Test bound checks around INT32_MAX for |byteOffset| argument. + assertThrowsInstanceOf(() => new TA(ab, 2**31 - TA.BYTES_PER_ELEMENT), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 2**31 - 1), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 2**31), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 2**31 + 1), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 2**31 + TA.BYTES_PER_ELEMENT), RangeError); + + // Test bound checks around UINT32_MAX for |byteOffset| argument. + assertThrowsInstanceOf(() => new TA(ab, 2**32 - TA.BYTES_PER_ELEMENT), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 2**32 - 1), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 2**32), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 2**32 + 1), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 2**32 + TA.BYTES_PER_ELEMENT), RangeError); + + // Test bound checks around INT32_MAX for |length| argument. + assertThrowsInstanceOf(() => new TA(ab, 0, 2**31 - 1), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 0, 2**31), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 0, 2**31 + 1), RangeError); + + // Test bound checks around UINT32_MAX for |length| argument. + assertThrowsInstanceOf(() => new TA(ab, 0, 2**32 - 1), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 0, 2**32), RangeError); + assertThrowsInstanceOf(() => new TA(ab, 0, 2**32 + 1), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-iterable-generator.js b/js/src/tests/non262/TypedArray/constructor-iterable-generator.js new file mode 100644 index 0000000000..233eeb9d92 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-iterable-generator.js @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Construct typed array from generator. +for (let constructor of anyTypedArrayConstructors) { + for (let array of [[], [1], [2, 3], [4, 5, 6], Array(1024).fill(0).map((v, i) => i % 128)]) { + let typedArray = new constructor(function*(){ yield* array; }()); + + assertEq(typedArray.length, array.length); + assertEqArray(typedArray, array); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-iterable-modified-array-iterator-next.js b/js/src/tests/non262/TypedArray/constructor-iterable-modified-array-iterator-next.js new file mode 100644 index 0000000000..76eaaae46d --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-iterable-modified-array-iterator-next.js @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Construct typed array from array with modified array iterator next method. +const ArrayIteratorPrototype = Object.getPrototypeOf([][Symbol.iterator]()); +const origNext = ArrayIteratorPrototype.next; +const modifiedNext = function() { + let {value, done} = origNext.call(this); + return {value: value * 5, done}; +}; +for (let constructor of anyTypedArrayConstructors) { + for (let array of [[], [1], [2, 3], [4, 5, 6], Array(1024).fill(0).map((v, i) => i % 24)]) { + ArrayIteratorPrototype.next = modifiedNext; + let typedArray; + try { + typedArray = new constructor(array); + } finally { + ArrayIteratorPrototype.next = origNext; + } + + assertEq(typedArray.length, array.length); + assertEqArray(typedArray, array.map(v => v * 5)); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-iterable-modified-array-iterator.js b/js/src/tests/non262/TypedArray/constructor-iterable-modified-array-iterator.js new file mode 100644 index 0000000000..7c3192a6a3 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-iterable-modified-array-iterator.js @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Construct typed array from array with modified array iterator. +const origIterator = Array.prototype[Symbol.iterator]; +const modifiedIterator = function*() { + for (let v of origIterator.call(this)) { + yield v * 5; + } +}; +for (let constructor of anyTypedArrayConstructors) { + for (let array of [[], [1], [2, 3], [4, 5, 6], Array(1024).fill(0).map((v, i) => i % 24)]) { + Array.prototype[Symbol.iterator] = modifiedIterator; + let typedArray; + try { + typedArray = new constructor(array) + } finally { + Array.prototype[Symbol.iterator] = origIterator; + } + + assertEq(typedArray.length, array.length); + assertEqArray(typedArray, array.map(v => v * 5)); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-iterable-nonpacked-array.js b/js/src/tests/non262/TypedArray/constructor-iterable-nonpacked-array.js new file mode 100644 index 0000000000..b9586a57e5 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-iterable-nonpacked-array.js @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Construct typed array from iterable non-packed array. +for (let constructor of anyTypedArrayConstructors) { + for (let array of [[,], [,,], Array(1024)]) { + let typedArray = new constructor(array); + + assertEq(typedArray.length, array.length); + let expectedArray = Array(array.length).fill(isFloatConstructor(constructor) ? NaN : 0); + assertEqArray(typedArray, expectedArray); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-iterable-not-callable.js b/js/src/tests/non262/TypedArray/constructor-iterable-not-callable.js new file mode 100644 index 0000000000..824760900a --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-iterable-not-callable.js @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Construct typed array from object with non-callable [Symbol.iterator] property. +for (let constructor of anyTypedArrayConstructors) { + for (let iterator of [true, 0, Math.PI, "", "10", Symbol.iterator, {}, []]) { + assertThrowsInstanceOf(() => new constructor({ + [Symbol.iterator]: iterator + }), TypeError); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-iterable-packed-array-side-effect.js b/js/src/tests/non262/TypedArray/constructor-iterable-packed-array-side-effect.js new file mode 100644 index 0000000000..3c00af0de2 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-iterable-packed-array-side-effect.js @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Construct typed array from iterable packed array, array contains object with modifying +// valueOf() method. +for (let constructor of anyTypedArrayConstructors) { + let array = [ + 0, 1, {valueOf() { array[3] = 30; return 2; }}, 3, 4 + ]; + let typedArray = new constructor(array); + assertEq(typedArray.length, array.length); + assertEqArray(typedArray, [0, 1, 2, 3, 4]); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-iterable-packed-array.js b/js/src/tests/non262/TypedArray/constructor-iterable-packed-array.js new file mode 100644 index 0000000000..495c2da496 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-iterable-packed-array.js @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Construct typed array from iterable packed array. +for (let constructor of anyTypedArrayConstructors) { + for (let array of [[], [1], [2, 3], [4, 5, 6], Array(1024).fill(0).map((v, i) => i % 128)]) { + let typedArray = new constructor(array); + + assertEq(typedArray.length, array.length); + assertEqArray(typedArray, array); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-iterable-undefined-or-null.js b/js/src/tests/non262/TypedArray/constructor-iterable-undefined-or-null.js new file mode 100644 index 0000000000..48e5aaa648 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-iterable-undefined-or-null.js @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Construct typed array from object with undefined or null [Symbol.iterator] property. +for (let constructor of anyTypedArrayConstructors) { + for (let iterator of [undefined, null]) { + let arrayLike = { + [Symbol.iterator]: iterator, + length: 2, + 0: 10, + 1: 20, + }; + let typedArray = new constructor(arrayLike); + + assertEq(typedArray.length, arrayLike.length); + assertEqArray(typedArray, arrayLike); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-iterator-primitive.js b/js/src/tests/non262/TypedArray/constructor-iterator-primitive.js new file mode 100644 index 0000000000..5a14d7d5a7 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-iterator-primitive.js @@ -0,0 +1,29 @@ +var BUGNUMBER = 1021835; +var summary = "Returning non-object from @@iterator should throw"; + +print(BUGNUMBER + ": " + summary); + +let primitives = [ + 1, + true, + undefined, + null, + "foo", + Symbol.iterator +]; + +for (let ctor of typedArrayConstructors) { + for (let primitive of primitives) { + let arg = { + [Symbol.iterator]() { + return primitive; + } + }; + assertThrowsInstanceOf(() => { + new ctor(arg); + }, TypeError); + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/constructor-length-too-large.js b/js/src/tests/non262/TypedArray/constructor-length-too-large.js new file mode 100644 index 0000000000..7a7a785041 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-length-too-large.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Test that all TypedArray constructor variants throw a RangeError when +// attempting to create a too large array. + +// The maximum typed array length is limited to `(INT32_MAX/BYTES_PER_ELEMENT)` +// on all 32-bit systems; on 64-bit systems the limit is 8GB presently. + +const INT32_MAX = 2**31 - 1; +const EIGHTGB = 8 * 1024 * 1024 * 1024; + +function tooLarge(elementSize) { + if (largeArrayBufferSupported()) { + return (EIGHTGB / elementSize) + 1; + } + return (INT32_MAX + 1) / elementSize; +} + +// 22.2.4.2 TypedArray ( length ) +for (let TA of typedArrayConstructors) { + assertThrowsInstanceOf(() => new TA(tooLarge(1)), RangeError); + assertThrowsInstanceOf(() => new TA(tooLarge(TA.BYTES_PER_ELEMENT)), RangeError); +} + +// Test disabled because allocating a 2**30 Int8Array easily leads to OOMs. +// +// 22.2.4.3 TypedArray ( typedArray ) +// const largeInt8Array = new Int8Array(2**30); +// for (let TA of typedArrayConstructors.filter(c => c.BYTES_PER_ELEMENT > 1)) { +// assertThrowsInstanceOf(() => new TA(largeInt8Array), RangeError); +// } + +// 22.2.4.4 TypedArray ( object ) +for (let TA of typedArrayConstructors) { + assertThrowsInstanceOf(() => new TA({length: tooLarge(1)}), RangeError); + assertThrowsInstanceOf(() => new TA({length: tooLarge(TA.BYTES_PER_ELEMENT)}), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-non-detached.js b/js/src/tests/non262/TypedArray/constructor-non-detached.js new file mode 100644 index 0000000000..9ec9329be8 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-non-detached.js @@ -0,0 +1,13 @@ +for (var constructor of typedArrayConstructors) { + var buf = new constructor(); + detachArrayBuffer(buf.buffer); + assertThrowsInstanceOf(() => new constructor(buf), TypeError); + + var buffer = new ArrayBuffer(); + detachArrayBuffer(buffer); + assertThrowsInstanceOf(() => new constructor(buffer), TypeError); +} + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-not-callable.js b/js/src/tests/non262/TypedArray/constructor-not-callable.js new file mode 100644 index 0000000000..a3a2f8d1d5 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-not-callable.js @@ -0,0 +1,10 @@ +for (var constructor of anyTypedArrayConstructors) { + assertThrowsInstanceOf(() => constructor(), TypeError); + assertThrowsInstanceOf(() => constructor(1), TypeError); + assertThrowsInstanceOf(() => constructor.call(null), TypeError); + assertThrowsInstanceOf(() => constructor.apply(null, []), TypeError); + assertThrowsInstanceOf(() => Reflect.apply(constructor, null, []), TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor-typedarray-species-other-global.js b/js/src/tests/non262/TypedArray/constructor-typedarray-species-other-global.js new file mode 100644 index 0000000000..872f46408a --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-typedarray-species-other-global.js @@ -0,0 +1,33 @@ +// 22.2.4.3 TypedArray ( typedArray ) + +// Test [[Prototype]] of newly created typed array and its array buffer, and +// ensure they are both created in the correct global. + +const thisGlobal = this; +const otherGlobal = newGlobal(); + +const typedArrays = [otherGlobal.eval("new Int32Array(0)")]; + +if (this.SharedArrayBuffer) { + typedArrays.push(otherGlobal.eval("new Int32Array(new SharedArrayBuffer(0))")); +} + +for (let typedArray of typedArrays) { + // Ensure the "constructor" property isn't accessed. + Object.defineProperty(typedArray.buffer, "constructor", { + get() { + throw new Error("constructor property accessed"); + } + }); + + for (let ctor of typedArrayConstructors) { + let newTypedArray = new ctor(typedArray); + + assertEq(Object.getPrototypeOf(newTypedArray), ctor.prototype); + assertEq(Object.getPrototypeOf(newTypedArray.buffer), ArrayBuffer.prototype); + assertEq(newTypedArray.buffer.constructor, ArrayBuffer); + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/constructor-undefined-args.js b/js/src/tests/non262/TypedArray/constructor-undefined-args.js new file mode 100644 index 0000000000..91a557a2c1 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor-undefined-args.js @@ -0,0 +1,14 @@ +// Bug 1040402 + +var ab = new ArrayBuffer(16); + +assertEq(new Int32Array(ab).length, 4); +assertEq(new Int32Array(ab, undefined).length, 4); +assertEq(new Int32Array(ab, undefined, undefined).length, 4); +assertEq(new Int32Array(ab, 0).length, 4); +assertEq(new Int32Array(ab, 0, undefined).length, 4); +assertEq(new Int32Array(ab, 4).length, 3); +assertEq(new Int32Array(ab, 4, undefined).length, 3); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/constructor_bad-args.js b/js/src/tests/non262/TypedArray/constructor_bad-args.js new file mode 100644 index 0000000000..00bbf884c6 --- /dev/null +++ b/js/src/tests/non262/TypedArray/constructor_bad-args.js @@ -0,0 +1,13 @@ +// Bug 1227207 + +var AB = new ArrayBuffer(12); // Length divides 4 +var BC = new ArrayBuffer(14); // Length does not divide 4 + +assertThrowsInstanceOf(() => new Int32Array(AB, -1), RangeError); // 22.2.4.5 #8 +assertThrowsInstanceOf(() => new Int32Array(AB, 2), RangeError); // 22.2.4.5 #10 +assertThrowsInstanceOf(() => new Int32Array(BC), RangeError); // 22.2.4.5 #13.a +assertThrowsInstanceOf(() => new Int32Array(AB, 16), RangeError); // 22.2.4.5 #13.c +assertThrowsInstanceOf(() => new Int32Array(AB, 0, 4), RangeError); // 22.2.4.5 #14.c + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/detached-array-buffer-checks.js b/js/src/tests/non262/TypedArray/detached-array-buffer-checks.js new file mode 100644 index 0000000000..55256fb29d --- /dev/null +++ b/js/src/tests/non262/TypedArray/detached-array-buffer-checks.js @@ -0,0 +1,107 @@ +// Nearly every %TypedArray%.prototype method should throw a TypeError when called +// atop a detached array buffer. Here we check verify that this holds true for +// all relevant functions. +let buffer = new ArrayBuffer(32); +let array = new Int32Array(buffer); +detachArrayBuffer(buffer); + +// A nice poisoned callable to ensure that we fail on a detached buffer check +// before a method attempts to do anything with its arguments. +var POISON = (function() { + var internalTarget = {}; + var throwForAllTraps = + new Proxy(internalTarget, { get(target, prop, receiver) { + assertEq(target, internalTarget); + assertEq(receiver, throwForAllTraps); + throw "FAIL: " + prop + " trap invoked"; + }}); + return new Proxy(throwForAllTraps, throwForAllTraps); +}); + + +assertThrowsInstanceOf(() => { + array.copyWithin(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.entries(); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.fill(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.filter(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.find(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.findIndex(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.forEach(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.indexOf(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.includes(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.join(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.keys(); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.lastIndexOf(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.map(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.reduce(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.reduceRight(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.reverse(); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.slice(POISON, POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.some(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.values(); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.every(POISON); +}, TypeError); + +assertThrowsInstanceOf(() => { + array.sort(POISON); +}, TypeError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/element-setting-converts-using-ToNumber.js b/js/src/tests/non262/TypedArray/element-setting-converts-using-ToNumber.js new file mode 100644 index 0000000000..ae465f0147 --- /dev/null +++ b/js/src/tests/non262/TypedArray/element-setting-converts-using-ToNumber.js @@ -0,0 +1,91 @@ +// |reftest| slow +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = 'element-setting-converts-using-ToNumber.js'; +//----------------------------------------------------------------------------- +var BUGNUMBER = 985733; +var summary = + "Typed array element-setting should convert to target type using ToNumber " + "followed by an element-type-specific truncation function"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +anyTypedArrayConstructors.forEach(function(TypedArray) { + var ta = new TypedArray(1); + assertEq(ta[0], 0); + + var count = 0; + function setToObject() + { + for (var i = 0; i < 1e4; i++) + { + assertEq(count, i); + ta[0] = { valueOf: function() { count++; return 17; } }; + } + } + setToObject(); + assertEq(count, 1e4); + assertEq(ta[0], 17); + + function setToString() + { + for (var i = 0; i < 2e4; i++) + ta[0] = "17.0000000000000000000000000000000000000000000000000000001"; + } + setToString(); + assertEq(ta[0], 17); + + count = 0; + var arrayConstructed = + new TypedArray([{ valueOf: function() { count++; return 17; } }, + "17.0000000000000000000000000000000000000000000000000001"]); + assertEq(count, 1); + assertEq(arrayConstructed[0], 17); + assertEq(arrayConstructed[1], 17); + + count = 0; + var arraySet = new TypedArray(5); + arraySet.set({ 0: 17, + 1: "17.000000000000000000000000000000000000000000000000000", + get 2() { + return { valueOf: undefined, + toString: function() { count++; return 42; } }; + }, + get 3() { return true; }, + set 3(v) { throw "FAIL"; }, + 4: { valueOf: function() { count++; return 127; } }, + length: 5 }); + assertEq(count, 2); + assertEq(arraySet[0], 17); + assertEq(arraySet[1], 17); + assertEq(arraySet[2], 42); + assertEq(arraySet[3], 1); + assertEq(arraySet[4], 127); + + var bigLen = 1e4; + var big = new TypedArray(bigLen); + function initBig() + { + for (var i = 0; i < bigLen; i++) + big[i] = (i % 2) ? 3 : { valueOf: function() { return 3; } }; + } + initBig(); + for (var i = 0; i < bigLen; i++) + { + assertEq(big[i], 3, + "(" + Object.prototype.toString.call(big) + ")"); + } +}); + +/******************************************************************************/ + +reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/TypedArray/entries.js b/js/src/tests/non262/TypedArray/entries.js new file mode 100644 index 0000000000..c55a959e3a --- /dev/null +++ b/js/src/tests/non262/TypedArray/entries.js @@ -0,0 +1,38 @@ +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.entries.length, 0); + assertEq(constructor.prototype.entries.name, "entries"); + + assertDeepEq([...new constructor(0).entries()], []); + assertDeepEq([...new constructor(1).entries()], [[0, 0]]); + assertDeepEq([...new constructor(2).entries()], [[0, 0], [1, 0]]); + assertDeepEq([...new constructor([15]).entries()], [[0, 15]]); + + var arr = new constructor([1, 2, 3]); + var iterator = arr.entries(); + assertDeepEq(iterator.next(), {value: [0, 1], done: false}); + assertDeepEq(iterator.next(), {value: [1, 2], done: false}); + assertDeepEq(iterator.next(), {value: [2, 3], done: false}); + assertDeepEq(iterator.next(), {value: undefined, done: true}); + + // Called from other globals. + if (typeof newGlobal === "function") { + var otherGlobal = newGlobal(); + var entries = otherGlobal[constructor.name].prototype.entries; + assertDeepEq([...entries.call(new constructor(2))], + [new otherGlobal.Array(0, 0), new otherGlobal.Array(1, 0)]); + arr = new (newGlobal()[constructor.name])(2); + assertEq([...constructor.prototype.entries.call(arr)].toString(), "0,0,1,0"); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.entries.call(invalidReceiver); + }, TypeError, "Assert that entries fails if this value is not a TypedArray"); + }); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/every-and-some.js b/js/src/tests/non262/TypedArray/every-and-some.js new file mode 100644 index 0000000000..940ac736f9 --- /dev/null +++ b/js/src/tests/non262/TypedArray/every-and-some.js @@ -0,0 +1,249 @@ +// Tests for TypedArray#every. +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.every.length, 1); + + // Basic tests. + assertEq(new constructor([1, 3, 5]).every(v => v % 2), true); + assertEq(new constructor([1, 3, 5]).every(v => v > 2), false); + assertEq(new constructor(10).every(v => v === 0), true); + assertEq(new constructor().every(v => v > 1), true); + + var arr = new constructor([1, 2, 3, 4, 5]); + var sum = 0; + var count = 0; + assertEq(arr.every((v, k, o) => { + count++; + sum += v; + assertEq(k, v - 1); + assertEq(o, arr); + return v < 3; + }), false); + assertEq(sum, 6); + assertEq(count, 3); + + // Tests for `thisArg` argument. + function assertThisArg(thisArg, thisValue) { + // In sloppy mode, `this` could be global object or a wrapper of `thisArg`. + assertEq(arr.every(function() { + assertDeepEq(this, thisValue); + return true; + }, thisArg), true); + + // In strict mode, `this` strictly equals `thisArg`. + assertEq(arr.every(function() { + "use strict"; + assertDeepEq(this, thisArg); + return true; + }, thisArg), true); + + // Passing `thisArg` has no effect if callback is an arrow function. + var self = this; + assertEq(arr.every(() => { + assertEq(this, self); + return true; + }, thisArg), true); + } + assertThisArg([1, 2, 3], [1, 2, 3]); + assertThisArg(Object, Object); + assertThisArg(1, Object(1)); + assertThisArg("1", Object("1")); + assertThisArg(false, Object(false)); + assertThisArg(undefined, this); + assertThisArg(null, this); + + // Throw an exception in the callback. + var sum = 0; + var count = 0; + var thrown = false; + try { + arr.every((v, k, o) => { + count++; + sum += v; + assertEq(k, v - 1); + assertEq(o, arr); + if (v === 3) { + throw "every"; + } + return true + }) + } catch(e) { + assertEq(e, "every"); + thrown = true; + } + assertEq(thrown, true); + assertEq(sum, 6); + assertEq(count, 3); + + // There is no callback or callback is not a function. + assertThrowsInstanceOf(() => { + arr.every(); + }, TypeError); + var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidCallbacks.forEach(callback => { + assertThrowsInstanceOf(() => { + arr.every(callback); + }, TypeError); + }) + + // Callback is a generator. + arr.every(function*(){ + throw "This line will not be executed"; + }); + + // Called from other globals. + if (typeof newGlobal === "function") { + var every = newGlobal()[constructor.name].prototype.every; + var sum = 0; + assertEq(every.call(new constructor([1, 2, 3]), v => sum += v), true); + assertEq(sum, 6); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.every.call(invalidReceiver, () => true); + }, TypeError, "Assert that every fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).every(() => true), true); +} + +for (let constructor of anyTypedArrayConstructors.filter(isFloatConstructor)) { + assertEq(new constructor([undefined, , NaN]).every(v => Object.is(v, NaN)), true); +} + +// Tests for TypedArray#some. +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.some.length, 1); + + // Basic tests. + assertEq(new constructor([1, 2, 3]).some(v => v % 2), true); + assertEq(new constructor([0, 2, 4]).some(v => v % 2), false); + assertEq(new constructor([1, 3, 5]).some(v => v > 2), true); + assertEq(new constructor([1, 3, 5]).some(v => v < 0), false); + assertEq(new constructor(10).some(v => v !== 0), false); + assertEq(new constructor().some(v => v > 1), false); + + var arr = new constructor([1, 2, 3, 4, 5]); + var sum = 0; + var count = 0; + assertEq(arr.some((v, k, o) => { + count++; + sum += v; + assertEq(k, v - 1); + assertEq(o, arr); + return v > 2; + }), true); + assertEq(sum, 6); + assertEq(count, 3); + + // Tests for `thisArg` argument. + function assertThisArg(thisArg, thisValue) { + // In sloppy mode, `this` could be global object or a wrapper of `thisArg`. + assertEq(arr.some(function() { + assertDeepEq(this, thisValue); + return false; + }, thisArg), false); + + // In strict mode, `this` strictly equals `thisArg`. + assertEq(arr.some(function() { + "use strict"; + assertDeepEq(this, thisArg); + return false; + }, thisArg), false); + + // Passing `thisArg` has no effect if callback is an arrow function. + var self = this; + assertEq(arr.some(() => { + assertEq(this, self); + return false; + }, thisArg), false); + } + assertThisArg([1, 2, 3], [1, 2, 3]); + assertThisArg(Object, Object); + assertThisArg(1, Object(1)); + assertThisArg("1", Object("1")); + assertThisArg(false, Object(false)); + assertThisArg(undefined, this); + assertThisArg(null, this); + + // Throw an exception in the callback. + var sum = 0; + var count = 0; + var thrown = false; + try { + arr.some((v, k, o) => { + count++; + sum += v; + assertEq(k, v - 1); + assertEq(o, arr); + if (v === 3) { + throw "some"; + } + return false + }) + } catch(e) { + assertEq(e, "some"); + thrown = true; + } + assertEq(thrown, true); + assertEq(sum, 6); + assertEq(count, 3); + + // There is no callback or callback is not a function. + assertThrowsInstanceOf(() => { + arr.some(); + }, TypeError); + var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidCallbacks.forEach(callback => { + assertThrowsInstanceOf(() => { + arr.some(callback); + }, TypeError); + }) + + // Callback is a generator. + arr.some(function*(){ + throw "This line will not be executed"; + }); + + // Called from other globals. + if (typeof newGlobal === "function") { + var some = newGlobal()[constructor.name].prototype.some; + var sum = 0; + assertEq(some.call(new constructor([1, 2, 3]), v => { + sum += v; + return false; + }), false); + assertEq(sum, 6); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.some.call(invalidReceiver, () => true); + }, TypeError, "Assert that some fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).some(() => false), false); +} + +for (let constructor of anyTypedArrayConstructors.filter(isFloatConstructor)) { + assertEq(new constructor([undefined, , NaN]).some(v => v === v), false); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/fill-detached.js b/js/src/tests/non262/TypedArray/fill-detached.js new file mode 100644 index 0000000000..770067a81f --- /dev/null +++ b/js/src/tests/non262/TypedArray/fill-detached.js @@ -0,0 +1,36 @@ +// Ensure %TypedArray%.prototype.fill checks for detached buffers. + +function DetachArrayBufferValue(buffer, value) { + return { + valueOf() { + detachArrayBuffer(buffer); + return value; + } + }; +} + +function DetachTypedArrayValue(ta, value) { + return { + valueOf() { + detachArrayBuffer(ta.buffer); + return value; + } + }; +} + +// Test when ArrayBuffer is already reified. +for (let length of [0, 1, 10, 4096]) { + let ta = new Int32Array(length); + let value = DetachArrayBufferValue(ta.buffer, 123); + assertThrowsInstanceOf(() => ta.fill(value), TypeError); +} + +// Test when ArrayBuffer is reified during the fill() call. +for (let length of [0, 1, 10, 4096]) { + let ta = new Int32Array(length); + let value = DetachTypedArrayValue(ta, 123); + assertThrowsInstanceOf(() => ta.fill(value), TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/fill.js b/js/src/tests/non262/TypedArray/fill.js new file mode 100644 index 0000000000..4349da06a9 --- /dev/null +++ b/js/src/tests/non262/TypedArray/fill.js @@ -0,0 +1,65 @@ +for (var constructor of anyTypedArrayConstructors) { + assertDeepEq(constructor.prototype.fill.length, 1); + + assertDeepEq(new constructor([]).fill(1), new constructor([])); + assertDeepEq(new constructor([1,1,1]).fill(2), new constructor([2,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, 1), new constructor([1,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, 1, 2), new constructor([1,2,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, -2), new constructor([1,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, -2, -1), new constructor([1,2,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, undefined), new constructor([2,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, undefined, undefined), new constructor([2,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, 1, undefined), new constructor([1,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, undefined, 1), new constructor([2,1,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, 2, 1), new constructor([1,1,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, -1, 1), new constructor([1,1,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, -2, 1), new constructor([1,1,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, 1, -2), new constructor([1,1,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, 0.1), new constructor([2,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, 0.9), new constructor([2,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, 1.1), new constructor([1,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, 0.1, 0.9), new constructor([1,1,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, 0.1, 1.9), new constructor([2,1,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, 0.1, 1.9), new constructor([2,1,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, -0), new constructor([2,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, 0, -0), new constructor([1,1,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, NaN), new constructor([2,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, 0, NaN), new constructor([1,1,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, false), new constructor([2,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, true), new constructor([1,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, "0"), new constructor([2,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, "1"), new constructor([1,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, "-2"), new constructor([1,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, "-2", "-1"), new constructor([1,2,1])); + assertDeepEq(new constructor([1,1,1]).fill(2, {valueOf: ()=>1}), new constructor([1,2,2])); + assertDeepEq(new constructor([1,1,1]).fill(2, 0, {valueOf: ()=>1}), new constructor([2,1,1])); + + // Called from other globals. + if (typeof newGlobal === "function") { + var fill = newGlobal()[constructor.name].prototype.fill; + assertDeepEq(fill.call(new constructor([3, 2, 1]), 2), new constructor([2, 2, 2])); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.fill.call(invalidReceiver, 1); + }, TypeError); + }); + + // Test that the length getter is never called. + Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).fill(1); +} + +for (let constructor of anyTypedArrayConstructors.filter(isFloatConstructor)) { + assertDeepEq(new constructor([0, 0]).fill(NaN), new constructor([NaN, NaN])); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/filter-species.js b/js/src/tests/non262/TypedArray/filter-species.js new file mode 100644 index 0000000000..2de4e254e8 --- /dev/null +++ b/js/src/tests/non262/TypedArray/filter-species.js @@ -0,0 +1,56 @@ +function test(constructor, constructor2, from=[1, 2, 3, 4, 5], to=[2, 4]) { + var modifiedConstructor = new constructor(from); + modifiedConstructor.constructor = constructor2; + assertDeepEq(modifiedConstructor.filter(x => x % 2 == 0), new constructor2(to)); + var modifiedSpecies = new constructor(from); + modifiedSpecies.constructor = { [Symbol.species]: constructor2 }; + assertDeepEq(modifiedSpecies.filter(x => x % 2 == 0), new constructor2(to)); +} + +// same size, same sign + +test(Int8Array, Uint8Array); +test(Int8Array, Uint8ClampedArray); + +test(Uint8Array, Int8Array); +test(Uint8Array, Uint8ClampedArray); + +test(Uint8ClampedArray, Int8Array); +test(Uint8ClampedArray, Uint8Array); + +test(Int16Array, Uint16Array); +test(Uint16Array, Int16Array); + +test(Int32Array, Uint32Array); +test(Uint32Array, Int32Array); + +// same size, different sign + +test(Int8Array, Uint8Array, [-1, -2, -3, -4, -5], [0xFE, 0xFC]); +test(Int8Array, Uint8ClampedArray, [-1, -2, -3, -4, -5], [0, 0]); + +test(Uint8Array, Int8Array, [0xFF, 0xFE, 0xFD, 0xFC, 0xFB], [-2, -4]); +test(Uint8ClampedArray, Int8Array, [0xFF, 0xFE, 0xFD, 0xFC, 0xFB], [-2, -4]); + +test(Int16Array, Uint16Array, [-1, -2, -3, -4, -5], [0xFFFE, 0xFFFC]); +test(Uint16Array, Int16Array, [0xFFFF, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFB], [-2, -4]); + +test(Int32Array, Uint32Array, [-1, -2, -3, -4, -5], [0xFFFFFFFE, 0xFFFFFFFC]); +test(Uint32Array, Int32Array, [0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFC, 0xFFFFFFFB], [-2, -4]); + +// different size + +test(Uint8Array, Uint16Array); +test(Uint16Array, Uint8Array); + +test(Uint8Array, Uint32Array); +test(Uint32Array, Uint8Array); + +test(Uint16Array, Uint32Array); +test(Uint32Array, Uint16Array); + +test(Float32Array, Float64Array); +test(Float64Array, Float32Array); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/filter-validation.js b/js/src/tests/non262/TypedArray/filter-validation.js new file mode 100644 index 0000000000..450b4fa2cf --- /dev/null +++ b/js/src/tests/non262/TypedArray/filter-validation.js @@ -0,0 +1,185 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.prototype.filter. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayLengths = [0, 1, 1024]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.prototype.filter, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.prototype.filter, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].prototype.filter, + error: otherGlobal.TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => true), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => false), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + + // Passes when the length is zero. + if (length === 0) { + let result = method.call(ta, () => true); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(ta, () => true), error); + } + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, () => false); + assertEq(result.length, 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, () => true); + assertEq(result.length, length + extraLength); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, () => false); + assertEq(result.length, extraLength); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => true), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => false), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/find-and-findIndex.js b/js/src/tests/non262/TypedArray/find-and-findIndex.js new file mode 100644 index 0000000000..125ababd0b --- /dev/null +++ b/js/src/tests/non262/TypedArray/find-and-findIndex.js @@ -0,0 +1,52 @@ +/* + * Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ + */ + +var BUGNUMBER = 1078975; +var summary = "Implement %TypedArray%.prototype.{find, findIndex}"; +print(BUGNUMBER + ": " + summary); + +const methods = ["find", "findIndex"]; + +anyTypedArrayConstructors.forEach(constructor => { + methods.forEach(method => { + var arr = new constructor([0, 1, 2, 3, 4, 5]); + // test that this.length is never called + Object.defineProperty(arr, "length", { + get() { + throw new Error("length accessor called"); + } + }); + assertEq(arr[method].length, 1); + assertEq(arr[method](v => v === 3), 3); + assertEq(arr[method](v => v === 6), method === "find" ? undefined : -1); + + var thisValues = [undefined, null, true, 1, "foo", [], {}]; + if (typeof Symbol == "function") + thisValues.push(Symbol()); + + thisValues.forEach(thisArg => + assertThrowsInstanceOf(() => arr[method].call(thisArg, () => true), TypeError) + ); + + assertThrowsInstanceOf(() => arr[method](), TypeError); + assertThrowsInstanceOf(() => arr[method](1), TypeError); + }); +}); + +anyTypedArrayConstructors.filter(isFloatConstructor).forEach(constructor => { + var arr = new constructor([-0, 0, 1, 5, NaN, 6]); + assertEq(arr.find(v => Number.isNaN(v)), NaN); + assertEq(arr.findIndex(v => Number.isNaN(v)), 4); + + assertEq(arr.find(v => Object.is(v, 0)), 0); + assertEq(arr.findIndex(v => Object.is(v, 0)), 1); + + assertEq(arr.find(v => Object.is(v, -0)), -0); + assertEq(arr.findIndex(v => Object.is(v, -0)), 0); +}) + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/findLast-and-findLastIndex.js b/js/src/tests/non262/TypedArray/findLast-and-findLastIndex.js new file mode 100644 index 0000000000..ed14b4a002 --- /dev/null +++ b/js/src/tests/non262/TypedArray/findLast-and-findLastIndex.js @@ -0,0 +1,52 @@ +/* + * Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ + */ + +var BUGNUMBER = 1704385; +var summary = "Implement %TypedArray%.prototype.{findLast, findLastIndex}"; +print(BUGNUMBER + ": " + summary); + +const methods = ["findLast", "findLastIndex"]; + +anyTypedArrayConstructors.forEach(constructor => { + methods.forEach(method => { + var arr = new constructor([0, 1, 2, 3, 4, 5]); + // test that this.length is never called + Object.defineProperty(arr, "length", { + get() { + throw new Error("length accessor called"); + } + }); + assertEq(arr[method].length, 1); + assertEq(arr[method](v => v === 3), 3); + assertEq(arr[method](v => v === 6), method === "findLast" ? undefined : -1); + + var thisValues = [undefined, null, true, 1, "foo", [], {}]; + if (typeof Symbol == "function") + thisValues.push(Symbol()); + + thisValues.forEach(thisArg => + assertThrowsInstanceOf(() => arr[method].call(thisArg, () => true), TypeError) + ); + + assertThrowsInstanceOf(() => arr[method](), TypeError); + assertThrowsInstanceOf(() => arr[method](1), TypeError); + }); +}); + +anyTypedArrayConstructors.filter(isFloatConstructor).forEach(constructor => { + var arr = new constructor([-0, 0, 1, 5, NaN, 6]); + assertEq(arr.findLast(v => Number.isNaN(v)), NaN); + assertEq(arr.findLastIndex(v => Number.isNaN(v)), 4); + + assertEq(arr.findLast(v => Object.is(v, 0)), 0); + assertEq(arr.findLastIndex(v => Object.is(v, 0)), 1); + + assertEq(arr.findLast(v => Object.is(v, -0)), -0); + assertEq(arr.findLastIndex(v => Object.is(v, -0)), 0); +}) + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/forEach.js b/js/src/tests/non262/TypedArray/forEach.js new file mode 100644 index 0000000000..9daa690e2e --- /dev/null +++ b/js/src/tests/non262/TypedArray/forEach.js @@ -0,0 +1,93 @@ +// Tests for TypedArray#forEach +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.forEach.length, 1); + + var arr = new constructor([1, 2, 3, 4, 5]); + // Tests for `thisArg` argument. + function assertThisArg(thisArg, thisValue) { + // In sloppy mode, `this` could be global object or a wrapper of `thisArg`. + arr.forEach(function() { + assertDeepEq(this, thisValue); + return false; + }, thisArg); + + // In strict mode, `this` strictly equals `thisArg`. + arr.forEach(function() { + "use strict"; + assertDeepEq(this, thisArg); + return false; + }, thisArg); + + // Passing `thisArg` has no effect if callback is an arrow function. + var self = this; + arr.forEach(() => { + assertEq(this, self); + return false; + }, thisArg); + } + assertThisArg([1, 2, 3], [1, 2, 3]); + assertThisArg(Object, Object); + assertThisArg(1, Object(1)); + assertThisArg("1", Object("1")); + assertThisArg(false, Object(false)); + assertThisArg(undefined, this); + assertThisArg(null, this); + + // Throw an exception in the callback. + var sum = 0; + var count = 0; + var thrown = false; + try { + assertEq(arr.forEach((v) => { + count++; + sum += v; + if (v === 3) { + throw "forEach"; + } + }), undefined) + } catch(e) { + assertEq(e, "forEach"); + thrown = true; + } + assertEq(thrown, true); + assertEq(sum, 6); + assertEq(count, 3); + + // There is no callback or callback is not a function. + assertThrowsInstanceOf(() => { + arr.forEach(); + }, TypeError); + var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidCallbacks.forEach(callback => { + assertThrowsInstanceOf(() => { + arr.forEach(callback); + }, TypeError); + }) + + // Callback is a generator. + arr.forEach(function*(){ + throw "This line will not be executed"; + }); + + // Called from other globals. + if (typeof newGlobal === "function") { + var forEach = newGlobal()[constructor.name].prototype.forEach; + var sum = 0; + forEach.call(new constructor([1, 2, 3]), v => { + sum += v; + }); + assertEq(sum, 6); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.forEach.call(invalidReceiver, () => true); + }, TypeError, "Assert that some fails if this value is not a TypedArray"); + }); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/from-iterable-validation.js b/js/src/tests/non262/TypedArray/from-iterable-validation.js new file mode 100644 index 0000000000..50d46bb81f --- /dev/null +++ b/js/src/tests/non262/TypedArray/from-iterable-validation.js @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.from. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayArgs = [[], [123], [123, ...Array(1023).fill(0)]]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.from, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.from, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].from, + error: otherGlobal.TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, args), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + // Passes when the length is zero. + if (args.length === 0) { + let result = method.call(species, args); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(species, args), error); + } + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + let result = method.call(species, args); + assertEq(result.length, args.length + extraLength); + assertEq(result[0], (args.length === 0 ? 0 : 123)); + assertEq(result[args.length + extraLength - 1], 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, args), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/from-non-iterable-validation.js b/js/src/tests/non262/TypedArray/from-non-iterable-validation.js new file mode 100644 index 0000000000..3276426b28 --- /dev/null +++ b/js/src/tests/non262/TypedArray/from-non-iterable-validation.js @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.from. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayArgs = [{length: 0}, {length: 1, 0: 123}, {length: 1024, 0: 123}]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.from, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.from, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].from, + error: otherGlobal.TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, args), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + // Passes when the length is zero. + if (args.length === 0) { + let result = method.call(species, args); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(species, args), error); + } + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + let result = method.call(species, args); + assertEq(result.length, args.length + extraLength); + assertEq(result[0], (args.length === 0 ? 0 : 123)); + assertEq(result[args.length + extraLength - 1], 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, args), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/from_basics.js b/js/src/tests/non262/TypedArray/from_basics.js new file mode 100644 index 0000000000..9f53feb72d --- /dev/null +++ b/js/src/tests/non262/TypedArray/from_basics.js @@ -0,0 +1,42 @@ +for (var constructor of anyTypedArrayConstructors) { + // 'from' method is identical for all typed array constructors. + assertEq(anyTypedArrayConstructors[0].from === constructor.from, true); + + // %TypedArray%.from copies arrays. + var src = new constructor([1, 2, 3]), copy = constructor.from(src); + assertEq(copy === src, false); + assertEq(copy instanceof constructor, true); + assertDeepEq(copy, src); + + // Non-element properties are not copied. + var a = new constructor([0, 1]); + a.name = "lisa"; + assertDeepEq(constructor.from(a), new constructor([0, 1])); + + // %TypedArray%.from can copy non-iterable objects, if they're array-like. + src = {0: 0, 1: 1, length: 2}; + copy = constructor.from(src); + assertEq(copy instanceof constructor, true); + assertDeepEq(copy, new constructor([0, 1])); + + // Properties past the .length are not copied. + src = {0: "0", 1: "1", 2: "two", 9: "nine", name: "lisa", length: 2}; + assertDeepEq(constructor.from(src), new constructor([0, 1])); + + // If an object has neither an @@iterator method nor .length, + // then it's treated as zero-length. + assertDeepEq(constructor.from({}), new constructor()); + + // Primitives will be coerced to primitive wrapper first. + assertDeepEq(constructor.from(1), new constructor()); + assertDeepEq(constructor.from("123"), new constructor([1, 2, 3])); + assertDeepEq(constructor.from(true), new constructor()); + assertDeepEq(constructor.from(Symbol()), new constructor()); + + // Source object property order doesn't matter. + src = {length: 2, 1: "1", 0: "0"}; + assertDeepEq(constructor.from(src), new constructor([0, 1])); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/from_constructor.js b/js/src/tests/non262/TypedArray/from_constructor.js new file mode 100644 index 0000000000..0b0773b85c --- /dev/null +++ b/js/src/tests/non262/TypedArray/from_constructor.js @@ -0,0 +1,36 @@ +for (var constructor of anyTypedArrayConstructors) { + // Note %TypedArray%.from(iterable) calls 'this' with an argument whose value is + // `[...iterable].length`, while Array.from(iterable) doesn't pass any argument. + constructor.from.call(function(len){ + assertEq(len, 3); + return new constructor(len); + }, Array(3)); + + // If the 'this' value passed to %TypedArray.from is not a constructor, + // then an exception is thrown, while Array.from will use Array as it's constructor. + var arr = [3, 4, 5]; + var nonconstructors = [ + {}, Math, Object.getPrototypeOf, undefined, 17, + () => ({}) // arrow functions are not constructors + ]; + for (var v of nonconstructors) { + assertThrowsInstanceOf(() => { + constructor.from.call(v, arr); + }, TypeError); + } + + // %TypedArray%.from does not get confused if global constructors for typed arrays + // are replaced with another constructor. + function NotArray(...rest) { + return new constructor(...rest); + } + var RealArray = constructor; + NotArray.from = constructor.from; + this[constructor.name] = NotArray; + assertEq(RealArray.from([1]) instanceof RealArray, true); + assertEq(NotArray.from([1]) instanceof RealArray, true); + this[constructor.name] = RealArray; +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/from_errors.js b/js/src/tests/non262/TypedArray/from_errors.js new file mode 100644 index 0000000000..281b1b5129 --- /dev/null +++ b/js/src/tests/non262/TypedArray/from_errors.js @@ -0,0 +1,76 @@ +for (var constructor of anyTypedArrayConstructors) { + // %TypedArray%.from throws if the argument is undefined or null. + assertThrowsInstanceOf(() => constructor.from(), TypeError); + assertThrowsInstanceOf(() => constructor.from(undefined), TypeError); + assertThrowsInstanceOf(() => constructor.from(null), TypeError); + + // Unlike Array.from, %TypedArray%.from doesn't get or set the length property. + function ObjectWithThrowingLengthGetterSetter(...rest) { + var ta = new constructor(...rest); + Object.defineProperty(ta, "length", { + configurable: true, + get() { throw new RangeError("getter!"); }, + set() { throw new RangeError("setter!"); } + }); + return ta; + } + ObjectWithThrowingLengthGetterSetter.from = constructor.from; + assertEq(ObjectWithThrowingLengthGetterSetter.from([123])[0], 123); + + // %TypedArray%.from throws if mapfn is neither callable nor undefined. + assertThrowsInstanceOf(() => constructor.from([3, 4, 5], {}), TypeError); + assertThrowsInstanceOf(() => constructor.from([3, 4, 5], "also not a function"), TypeError); + assertThrowsInstanceOf(() => constructor.from([3, 4, 5], null), TypeError); + + // Even if the function would not have been called. + assertThrowsInstanceOf(() => constructor.from([], JSON), TypeError); + + // If mapfn is not undefined and not callable, the error happens before anything else. + // Before calling the constructor, before touching the arrayLike. + var log = ""; + var obj; + function C(...rest) { + log += "C"; + obj = new constructor(...rest); + return obj; + } + var p = new Proxy({}, { + has: function () { log += "1"; }, + get: function () { log += "2"; }, + getOwnPropertyDescriptor: function () { log += "3"; } + }); + assertThrowsInstanceOf(() => constructor.from.call(C, p, {}), TypeError); + assertEq(log, ""); + + // If mapfn throws, the new object has already been created. + var arrayish = { + get length() { log += "l"; return 1; }, + get 0() { log += "0"; return "q"; } + }; + log = ""; + var exc = {surprise: "ponies"}; + assertThrowsValue(() => constructor.from.call(C, arrayish, () => { throw exc; }), exc); + assertEq(log, "lC0"); + assertEq(obj instanceof constructor, true); + + // It's a TypeError if the @@iterator property is a primitive (except null and undefined). + for (var primitive of ["foo", 17, Symbol(), true]) { + assertThrowsInstanceOf(() => constructor.from({[Symbol.iterator] : primitive}), TypeError); + } + assertDeepEq(constructor.from({[Symbol.iterator]: null}), new constructor()); + assertDeepEq(constructor.from({[Symbol.iterator]: undefined}), new constructor()); + + // It's a TypeError if the iterator's .next() method returns a primitive. + for (var primitive of [undefined, null, "foo", 17, Symbol(), true]) { + assertThrowsInstanceOf( + () => constructor.from({ + [Symbol.iterator]() { + return {next() { return primitive; }}; + } + }), + TypeError); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/from_iterable.js b/js/src/tests/non262/TypedArray/from_iterable.js new file mode 100644 index 0000000000..889c815898 --- /dev/null +++ b/js/src/tests/non262/TypedArray/from_iterable.js @@ -0,0 +1,49 @@ +for (var constructor of anyTypedArrayConstructors) { + // %TypedArray%.from works on arguments objects. + (function () { + assertDeepEq(constructor.from(arguments), new constructor(["0", "1", undefined])); + })("0", "1", undefined); + + // If an object has both .length and [@@iterator] properties, [@@iterator] is used. + var a = ['0', '1', '2', '3', '4']; + a[Symbol.iterator] = function* () { + for (var i = 5; i--; ) + yield this[i]; + }; + + var log = ''; + function f(x) { + log += x; + return x + x; + } + + var b = constructor.from(a, f); + assertDeepEq(b, new constructor(['44', '33', '22', '11', '00'])); + assertEq(log, '43210'); + + // In fact, if [@@iterator] is present, .length isn't queried at all. + var pa = new Proxy(a, { + has: function (target, id) { + if (id === "length") + throw new Error(".length should not be queried (has)"); + return id in target; + }, + get: function (target, id) { + if (id === "length") + throw new Error(".length should not be queried (get)"); + return target[id]; + }, + getOwnPropertyDescriptor: function (target, id) { + if (id === "length") + throw new Error(".length should not be queried (getOwnPropertyDescriptor)"); + return Object.getOwnPropertyDescriptor(target, id) + } + }); + log = ""; + b = constructor.from(pa, f); + assertDeepEq(b, new constructor(['44', '33', '22', '11', '00'])); + assertEq(log, '43210'); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/from_mapping.js b/js/src/tests/non262/TypedArray/from_mapping.js new file mode 100644 index 0000000000..1c24815418 --- /dev/null +++ b/js/src/tests/non262/TypedArray/from_mapping.js @@ -0,0 +1,45 @@ +for (var constructor of anyTypedArrayConstructors) { + // If the mapfn argument to %TypedArray%.from is undefined, don't map. + assertDeepEq(constructor.from([3, 4, 5], undefined), new constructor([3, 4, 5])); + assertDeepEq(constructor.from([4, 5, 6], undefined, Math), new constructor([4, 5, 6])); + + // mapfn is called with two arguments: value and index. + var log = []; + function f(...args) { + log.push(args); + return log.length; + } + assertDeepEq(constructor.from(['a', 'e', 'i', 'o', 'u'], f), new constructor([1, 2, 3, 4, 5])); + assertDeepEq(log, [['a', 0], ['e', 1], ['i', 2], ['o', 3], ['u', 4]]); + + // If the object to be copied is non-iterable, mapfn is still called with two + // arguments. + log = []; + assertDeepEq(constructor.from({0: "zero", 1: "one", length: 2}, f), new constructor([1, 2])); + assertDeepEq(log, [["zero", 0], ["one", 1]]); + + // If the object to be copied is iterable and the constructor is not Array, + // mapfn is still called with two arguments. + log = []; + function C(...rest) { + return new constructor(...rest); + } + C.from = constructor.from; + var c = new C(2); + c[0] = 1; + c[1] = 2; + assertDeepEq(C.from(["zero", "one"], f), c); + assertDeepEq(log, [["zero", 0], ["one", 1]]); + + // The mapfn is called even if the value to be mapped is undefined. + assertDeepEq(constructor.from([0, 1, , 3], String), new constructor(["0", "1", "undefined", "3"])); + var arraylike = {length: 4, "0": 0, "1": 1, "3": 3}; + assertDeepEq(constructor.from(arraylike, String), new constructor(["0", "1", "undefined", "3"])); +} + +// %TypedArray%.from(obj, map) is not exactly the same as %TypedArray%.from(obj).map(mapFn). +assertDeepEq(Int8Array.from([150], v => v / 2), new Int8Array([75])); +assertDeepEq(Int8Array.from([150]).map(v => v / 2), new Int8Array([-53])); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/from_realms.js b/js/src/tests/non262/TypedArray/from_realms.js new file mode 100644 index 0000000000..590589392f --- /dev/null +++ b/js/src/tests/non262/TypedArray/from_realms.js @@ -0,0 +1,32 @@ +for (var constructor of anyTypedArrayConstructors) { + if (typeof newGlobal !== 'function') + break; + + // G[constructor.name].from, where G is any global, produces an array whose prototype + // is G[constructor.name].prototype. + var g = newGlobal(); + var ga = g[constructor.name].from([1, 2, 3]); + assertEq(ga instanceof g[constructor.name], true); + + // %TypedArray%.from can be applied to a constructor from another realm. + var p = constructor.from.call(g[constructor.name], [1, 2, 3]); + assertEq(p instanceof g[constructor.name], true); + var q = g[constructor.name].from.call(constructor, [3, 4, 5]); + assertEq(q instanceof constructor, true); + + // The default 'this' value received by a non-strict mapping function is + // that function's global, not %TypedArray%.from's global or the caller's global. + var h = newGlobal(), result = undefined; + h.mainGlobal = this; + h.eval("function f() { mainGlobal.result = this; }"); + g[constructor.name].from.call(constructor, [5, 6, 7], h.f); + // (Give each global in the test a name, for better error messages. But use + // globalName, because window.name is complicated.) + this.globalName = "main"; + g.globalName = "g"; + h.globalName = "h"; + assertEq(result.globalName, "h"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/from_string.js b/js/src/tests/non262/TypedArray/from_string.js new file mode 100644 index 0000000000..09a2c36710 --- /dev/null +++ b/js/src/tests/non262/TypedArray/from_string.js @@ -0,0 +1,24 @@ +// %TypedArray%.from called on Array should also handle strings correctly. +var from = Int8Array.from.bind(Uint32Array); +var toCodePoint = s => s.codePointAt(0); + +// %TypedArray%.from on a string iterates over the string. +assertEqArray(from("test string", toCodePoint), + ['t', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g'].map(toCodePoint)); + +// %TypedArray%.from on a string handles surrogate pairs correctly. +var gclef = "\uD834\uDD1E"; // U+1D11E MUSICAL SYMBOL G CLEF +assertEqArray(from(gclef, toCodePoint), [gclef].map(toCodePoint)); +assertEqArray(from(gclef + " G", toCodePoint), [gclef, " ", "G"].map(toCodePoint)); + +// %TypedArray%.from on a string calls the @@iterator method. +String.prototype[Symbol.iterator] = function* () { yield 1; yield 2; }; +assertEqArray(from("anything"), [1, 2]); + +// If the iterator method is deleted, Strings are still arraylike. +delete String.prototype[Symbol.iterator]; +assertEqArray(from("works", toCodePoint), ['w', 'o', 'r', 'k', 's'].map(toCodePoint)); +assertEqArray(from(gclef, toCodePoint), ['\uD834', '\uDD1E'].map(toCodePoint)); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/from_surfaces.js b/js/src/tests/non262/TypedArray/from_surfaces.js new file mode 100644 index 0000000000..ef15536378 --- /dev/null +++ b/js/src/tests/non262/TypedArray/from_surfaces.js @@ -0,0 +1,12 @@ +for (var constructor of anyTypedArrayConstructors) { + // Check superficial features of %TypeArray%.from. + var desc = Object.getOwnPropertyDescriptor(constructor.__proto__, "from"); + assertEq(desc.configurable, true); + assertEq(desc.enumerable, false); + assertEq(desc.writable, true); + assertEq(constructor.from.length, 1); + assertThrowsInstanceOf(() => new constructor.from(), TypeError); // not a constructor +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/from_this.js b/js/src/tests/non262/TypedArray/from_this.js new file mode 100644 index 0000000000..95af053b8d --- /dev/null +++ b/js/src/tests/non262/TypedArray/from_this.js @@ -0,0 +1,59 @@ +for (var constructor of anyTypedArrayConstructors) { + // The third argument to %TypedArray%.from is passed as the 'this' value to the + // mapping function. + var hits = 0, obj = {}; + function f(x) { + assertEq(this, obj); + hits++; + } + constructor.from(["a", "b", "c"], f, obj); + assertEq(hits, 3); + + // Without an argument, undefined is passed... + hits = 0; + function gs(x) { + "use strict"; + assertEq(this, undefined); + hits++; + } + constructor.from("def", gs); + assertEq(hits, 3); + + // ...and if the mapping function is non-strict, that means the global is + // passed. + var global = this; + hits = 0; + function g(x) { + assertEq(this, global); + hits++; + } + constructor.from("ghi", g); + assertEq(hits, 3); + + // A primitive value can be passed. + for (var v of [0, "str", undefined]) { + hits = 0; + var mapfn = function h(x) { + "use strict"; + assertEq(this, v); + hits++; + }; + constructor.from("pq", mapfn, v); + assertEq(hits, 2); + } + + // ...and if the mapping function is non-strict, primitive values will + // be wrapped to objects. + for (var v of [0, "str", true]) { + hits = 0; + var mapfn = function h(x) { + assertDeepEq(this, Object(v)); + hits++; + }; + constructor.from("pq", mapfn, v); + assertEq(hits, 2); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/from_typedarray_fastpath_detached.js b/js/src/tests/non262/TypedArray/from_typedarray_fastpath_detached.js new file mode 100644 index 0000000000..d1f574cb10 --- /dev/null +++ b/js/src/tests/non262/TypedArray/from_typedarray_fastpath_detached.js @@ -0,0 +1,10 @@ +// Ensure the fast-path when TypedArray.from is called with a TypedArray still +// checks for detached buffers. + +var ta = new Int32Array(4); +detachArrayBuffer(ta.buffer); + +assertThrowsInstanceOf(() => Int32Array.from(ta), TypeError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/getter-name.js b/js/src/tests/non262/TypedArray/getter-name.js new file mode 100644 index 0000000000..47050e3c1b --- /dev/null +++ b/js/src/tests/non262/TypedArray/getter-name.js @@ -0,0 +1,16 @@ +var BUGNUMBER = 1180290; +var summary = 'TypedArray getters should have get prefix'; + +print(BUGNUMBER + ": " + summary); + +let TypedArray = Object.getPrototypeOf(Float32Array.prototype).constructor; + +assertEq(Object.getOwnPropertyDescriptor(TypedArray, Symbol.species).get.name, "get [Symbol.species]"); +assertEq(Object.getOwnPropertyDescriptor(TypedArray.prototype, "buffer").get.name, "get buffer"); +assertEq(Object.getOwnPropertyDescriptor(TypedArray.prototype, "byteLength").get.name, "get byteLength"); +assertEq(Object.getOwnPropertyDescriptor(TypedArray.prototype, "byteOffset").get.name, "get byteOffset"); +assertEq(Object.getOwnPropertyDescriptor(TypedArray.prototype, "length").get.name, "get length"); +assertEq(Object.getOwnPropertyDescriptor(TypedArray.prototype, Symbol.toStringTag).get.name, "get [Symbol.toStringTag]"); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/has-property-op.js b/js/src/tests/non262/TypedArray/has-property-op.js new file mode 100644 index 0000000000..7ae70ef36d --- /dev/null +++ b/js/src/tests/non262/TypedArray/has-property-op.js @@ -0,0 +1,22 @@ +for (var constructor of anyTypedArrayConstructors) { + var obj = new constructor(5); + + for (var i = 0; i < obj.length; i++) + assertEq(i in obj, true); + + for (var v of [20, 300, -1, 5, -10, Math.pow(2, 32) - 1, -Math.pow(2, 32)]) + assertEq(v in obj, false); + + // Don't inherit elements + obj.__proto__[50] = "hello"; + assertEq(obj.__proto__[50], "hello"); + assertEq(50 in obj, false); + + // Do inherit normal properties + obj.__proto__.a = "world"; + assertEq(obj.__proto__.a, "world"); + assertEq("a" in obj, true); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/includes.js b/js/src/tests/non262/TypedArray/includes.js new file mode 100644 index 0000000000..bcc50a2572 --- /dev/null +++ b/js/src/tests/non262/TypedArray/includes.js @@ -0,0 +1,40 @@ +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.includes.length, 1); + + assertEq(new constructor([1, 2, 3]).includes(1), true); + assertEq(new constructor([1, 2, 3]).includes(2), true); + assertEq(new constructor([1, 2, 3]).includes(3), true); + assertEq(new constructor([1, 2, 3]).includes(2, 1), true); + assertEq(new constructor([1, 2, 3]).includes(2, -2), true); + assertEq(new constructor([1, 2, 3]).includes(2, -100), true); + + assertEq(new constructor([1, 2, 3]).includes("2"), false); + assertEq(new constructor([1, 2, 3]).includes(2, 2), false); + assertEq(new constructor([1, 2, 3]).includes(2, -1), false); + assertEq(new constructor([1, 2, 3]).includes(2, 100), false); + + // Called from other globals. + if (typeof newGlobal === "function") { + var includes = newGlobal()[constructor.name].prototype.includes; + assertEq(includes.call(new constructor([1, 2, 3]), 2), true); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.includes.call(invalidReceiver); + }, TypeError, "Assert that reverse fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).includes(2), true); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/indexOf-and-lastIndexOf.js b/js/src/tests/non262/TypedArray/indexOf-and-lastIndexOf.js new file mode 100644 index 0000000000..c179f78502 --- /dev/null +++ b/js/src/tests/non262/TypedArray/indexOf-and-lastIndexOf.js @@ -0,0 +1,123 @@ +// Tests for TypedArray#indexOf. +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.indexOf.length, 1); + + // Works with one argument. + assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(0), -1); + assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(1), 0); + assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(5), 4); + assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(6), -1); + assertEq(new constructor([1, 2, 1, 2, 1]).indexOf(1), 0); + + if (isFloatConstructor(constructor)) { + assertEq(new constructor([NaN, 0, -0]).indexOf(NaN), -1); + assertEq(new constructor([NaN, 0, -0]).indexOf(0), 1); + assertEq(new constructor([NaN, 0, -0]).indexOf(-0), 1); + } else { + // [NaN, 0, -0] will be coerced to [0, 0, 0] + assertEq(new constructor([NaN, 0, -0]).indexOf(NaN), -1); + assertEq(new constructor([NaN, 0, -0]).indexOf(0), 0); + assertEq(new constructor([NaN, 0, -0]).indexOf(-0), 0); + } + + // Works with two arguments. + assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(1, 1), -1); + assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(1, -100), 0); + assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(3, 100), -1); + assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(5, -1), 4); + assertEq(new constructor([1, 2, 1, 2, 1]).indexOf(1, 2), 2); + assertEq(new constructor([1, 2, 1, 2, 1]).indexOf(1, -2), 4); + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.indexOf.call(invalidReceiver); + }, TypeError, "Assert that indexOf fails if this value is not a TypedArray"); + }); + + // test that this.length is never called + assertEq(Object.defineProperty(new constructor([0, 1, 2, 3, 5]), "length", { + get() { + throw new Error("length accessor called"); + } + }).indexOf(1), 1); +} + +for (let constructor of anyTypedArrayConstructors.filter(isFloatConstructor)) { + if (constructor.BYTES_PER_ELEMENT === 4) { + assertEq(new constructor([.1, .2, .3]).indexOf(.2), -1); + assertEq(new constructor([.1, .2, .3]).indexOf(Math.fround(.2)), 1); + } else { + assertEq(constructor.BYTES_PER_ELEMENT, 8); + assertEq(new constructor([.1, .2, .3]).indexOf(.2), 1); + } +} + +// Tests for TypedArray#lastIndexOf. +for (var constructor of anyTypedArrayConstructors) { + + assertEq(constructor.prototype.lastIndexOf.length, 1); + + // Works with one arguments. + assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(0), -1); + assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(1), 0); + assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(5), 4); + assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(6), -1); + assertEq(new constructor([1, 2, 1, 2, 1]).lastIndexOf(1), 4); + + if (isFloatConstructor(constructor)) { + assertEq(new constructor([NaN, 0, -0]).lastIndexOf(NaN), -1); + assertEq(new constructor([NaN, 0, -0]).lastIndexOf(0), 2); + assertEq(new constructor([NaN, 0, -0]).lastIndexOf(-0), 2); + } else { + // [NaN, 0, -0] will be coerced to [0, 0, 0]. + assertEq(new constructor([NaN, 0, -0]).lastIndexOf(NaN), -1); + assertEq(new constructor([NaN, 0, -0]).lastIndexOf(0), 2); + assertEq(new constructor([NaN, 0, -0]).lastIndexOf(-0), 2); + } + + // Works with two arguments. + assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(1, 1), 0); + assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(1, -100), -1); + assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(3, 100), 2); + assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(5, -1), 4); + assertEq(new constructor([1, 2, 1, 2, 1]).lastIndexOf(1, 2), 2); + assertEq(new constructor([1, 2, 1, 2, 1]).lastIndexOf(1, -2), 2); + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.lastIndexOf.call(invalidReceiver); + }, TypeError, "Assert that lastIndexOf fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + assertEq(Object.defineProperty(new constructor([0, 1, 2, 3, 5]), "length", { + get() { + throw new Error("length accessor called"); + } + }).lastIndexOf(1), 1); + + // Starts search at last index when fromIndex parameter is absent. + assertEq(new constructor([10, 20, 10]).lastIndexOf(10), 2); + + // Starts search at first index when fromIndex parameter is undefined. + assertEq(new constructor([10, 20, 10]).lastIndexOf(10, undefined), 0); +} + +for (let constructor of anyTypedArrayConstructors.filter(isFloatConstructor)) { + if (constructor.BYTES_PER_ELEMENT === 4) { + assertEq(new constructor([.1, .2, .3]).lastIndexOf(.2), -1); + assertEq(new constructor([.1, .2, .3]).lastIndexOf(Math.fround(.2)), 1); + } else { + assertEq(constructor.BYTES_PER_ELEMENT, 8); + assertEq(new constructor([.1, .2, .3]).lastIndexOf(.2), 1); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/indexOf-never-returns-negative-zero.js b/js/src/tests/non262/TypedArray/indexOf-never-returns-negative-zero.js new file mode 100644 index 0000000000..72f8342156 --- /dev/null +++ b/js/src/tests/non262/TypedArray/indexOf-never-returns-negative-zero.js @@ -0,0 +1,7 @@ +var ta = new Uint8Array(1); +ta[0] = 17; + +assertEq(ta.indexOf(17, -0), +0); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/iterator-next-with-detached.js b/js/src/tests/non262/TypedArray/iterator-next-with-detached.js new file mode 100644 index 0000000000..d6a31160a6 --- /dev/null +++ b/js/src/tests/non262/TypedArray/iterator-next-with-detached.js @@ -0,0 +1,73 @@ +function checkResult(actual, expected) +{ + assertEq(actual.value, expected.value); + assertEq(actual.done, expected.done); +} + +if (typeof detachArrayBuffer === "function" && typeof newGlobal === "function") +{ + var iteratorFunction = Uint8Array.prototype[Symbol.iterator]; + + + var thisGlobal = this; + var otherGlobal = newGlobal(); + + var thisNext = new Uint8Array()[Symbol.iterator]().next + + for (const constructor of typedArrayConstructors) + { + assertEq(new constructor()[Symbol.iterator]().next, thisNext); + + var globals = + [ + [thisGlobal, thisGlobal], + [thisGlobal, otherGlobal], + [otherGlobal, otherGlobal], + [otherGlobal, thisGlobal], + ]; + + for (const [arrayGlobal, bufferGlobal] of globals) + { + var arr, buffer, iterator; + + function arrayBufferIterator() + { + var byteLength = 2 * constructor.BYTES_PER_ELEMENT; + var buf = new bufferGlobal.ArrayBuffer(byteLength); + var tarray = new arrayGlobal[constructor.name](buf); + + tarray[0] = 1; + tarray[1] = 2; + + return [tarray, buf, Reflect.apply(iteratorFunction, tarray, [])]; + } + + [arr, buffer, iterator] = arrayBufferIterator(); + checkResult(thisNext.call(iterator), {value: 1, done: false}); + checkResult(thisNext.call(iterator), {value: 2, done: false}); + checkResult(thisNext.call(iterator), {value: undefined, done: true}); + + // Test an exhausted iterator. + bufferGlobal.detachArrayBuffer(buffer); + checkResult(thisNext.call(iterator), {value: undefined, done: true}); + + // Test an all-but-exhausted iterator. + [arr, buffer, iterator] = arrayBufferIterator(); + checkResult(thisNext.call(iterator), {value: 1, done: false}); + checkResult(thisNext.call(iterator), {value: 2, done: false}); + + bufferGlobal.detachArrayBuffer(buffer); + assertThrowsInstanceOf(() => thisNext.call(iterator), TypeError); + + // Test an unexhausted iterator. + [arr, buffer, iterator] = arrayBufferIterator(); + checkResult(thisNext.call(iterator), {value: 1, done: false}); + + bufferGlobal.detachArrayBuffer(buffer); + assertThrowsInstanceOf(() => thisNext.call(iterator), TypeError); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/iterator.js b/js/src/tests/non262/TypedArray/iterator.js new file mode 100644 index 0000000000..3f943aa70d --- /dev/null +++ b/js/src/tests/non262/TypedArray/iterator.js @@ -0,0 +1,42 @@ +// Ensure that we're using [[ArrayLength]] to determine the number of +// values to produce instead of the length property. + +function testIterationCount(iterator, expectedLength) { + for (let i = 0; i < expectedLength; i++) + assertEq(iterator.next().done, false); + assertEq(iterator.next().done, true); +} + +let i8Array = new Int8Array(4); +Object.defineProperty(i8Array, "length", {value: 0}); +let i8Iterator = i8Array[Symbol.iterator](); +testIterationCount(i8Iterator, 4); + +// Veryify that the length property isn't even touched +i8Array = new Int8Array(); +Object.defineProperty(i8Array, "length", { + get() { + throw TypeError; + } +}); +i8Iterator = i8Array[Symbol.iterator](); +testIterationCount(i8Iterator, 0); + +// Verify that it works for set as well +i8Array = new Uint8Array(1); +// Try setting a high length which would trigger failure +Object.defineProperty(i8Array, "length", {value: 15}); +// Works if the fake length is ignored +(new Uint8Array(4)).set(i8Array, 3); + +// Ensure that it works across globals +let g2 = newGlobal(); + +i8Array = new Int8Array(8); +Object.defineProperty(i8Array, "length", {value: 0}); +let iterator = i8Array[Symbol.iterator](); +iterator.next = g2.Array.prototype[Symbol.iterator]().next; +testIterationCount(iterator, 8); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/join.js b/js/src/tests/non262/TypedArray/join.js new file mode 100644 index 0000000000..0e797320fd --- /dev/null +++ b/js/src/tests/non262/TypedArray/join.js @@ -0,0 +1,48 @@ +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.join.length, 1); + + assertEq(new constructor([1, 2, 3]).join(), "1,2,3"); + assertEq(new constructor([1, 2, 3]).join(undefined), "1,2,3"); + assertEq(new constructor([1, 2, 3]).join(null), "1null2null3"); + assertEq(new constructor([1, 2, 3]).join(""), "123"); + assertEq(new constructor([1, 2, 3]).join("+"), "1+2+3"); + assertEq(new constructor([1, 2, 3]).join(.1), "10.120.13"); + assertEq(new constructor([1, 2, 3]).join({toString(){return "foo"}}), "1foo2foo3"); + assertEq(new constructor([1]).join("-"), "1"); + assertEq(new constructor().join(), ""); + assertEq(new constructor().join("*"), ""); + assertEq(new constructor(1).join(), "0"); + assertEq(new constructor(3).join(), "0,0,0"); + + assertThrowsInstanceOf(() => new constructor().join({toString(){throw new TypeError}}), TypeError); + assertThrowsInstanceOf(() => new constructor().join(Symbol()), TypeError); + + // Called from other globals. + if (typeof newGlobal === "function") { + var join = newGlobal()[constructor.name].prototype.join; + assertEq(join.call(new constructor([1, 2, 3]), "\t"), "1\t2\t3"); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.join.call(invalidReceiver); + }, TypeError, "Assert that join fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).join("\0"), "1\0002\0003"); +} + +for (let constructor of anyTypedArrayConstructors.filter(isFloatConstructor)) { + assertDeepEq(new constructor([null, , NaN]).join(), "0,NaN,NaN"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/keys.js b/js/src/tests/non262/TypedArray/keys.js new file mode 100644 index 0000000000..88477cd758 --- /dev/null +++ b/js/src/tests/non262/TypedArray/keys.js @@ -0,0 +1,36 @@ +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.keys.length, 0); + assertEq(constructor.prototype.keys.name, "keys"); + + assertDeepEq([...new constructor(0).keys()], []); + assertDeepEq([...new constructor(1).keys()], [0]); + assertDeepEq([...new constructor(2).keys()], [0, 1]); + assertDeepEq([...new constructor([15]).keys()], [0]); + + var arr = new constructor([1, 2, 3]); + var iterator = arr.keys(); + assertDeepEq(iterator.next(), {value: 0, done: false}); + assertDeepEq(iterator.next(), {value: 1, done: false}); + assertDeepEq(iterator.next(), {value: 2, done: false}); + assertDeepEq(iterator.next(), {value: undefined, done: true}); + + // Called from other globals. + if (typeof newGlobal === "function") { + var keys = newGlobal()[constructor.name].prototype.keys; + assertDeepEq([...keys.call(new constructor(2))], [0, 1]); + arr = new (newGlobal()[constructor.name])(2); + assertEq([...constructor.prototype.keys.call(arr)].toString(), "0,1"); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.keys.call(invalidReceiver); + }, TypeError, "Assert that keys fails if this value is not a TypedArray"); + }); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/large-arrays.js b/js/src/tests/non262/TypedArray/large-arrays.js new file mode 100644 index 0000000000..feb0e05ac6 --- /dev/null +++ b/js/src/tests/non262/TypedArray/large-arrays.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Test that we can access TypedArrays beyond the 4GB mark, if large buffers are +// supported. + +const gb = 1024 * 1024 * 1024; + +if (largeArrayBufferSupported()) { + for (let TA of typedArrayConstructors) { + let ta = new TA(6*gb / TA.BYTES_PER_ELEMENT); + + // Set element at the 5GB mark + ta[5*gb / TA.BYTES_PER_ELEMENT] = 37; + + // Check that it was set + assertEq(ta[5*gb / TA.BYTES_PER_ELEMENT], 37); + + // Check that we're not operating mod 4GB + assertEq(ta[1*gb / TA.BYTES_PER_ELEMENT], 0); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/lastIndexOf-never-returns-negative-zero.js b/js/src/tests/non262/TypedArray/lastIndexOf-never-returns-negative-zero.js new file mode 100644 index 0000000000..bffc05af73 --- /dev/null +++ b/js/src/tests/non262/TypedArray/lastIndexOf-never-returns-negative-zero.js @@ -0,0 +1,7 @@ +var ta = new Uint8Array(1); +ta[0] = 17; + +assertEq(ta.lastIndexOf(17, -0), +0); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/length.js b/js/src/tests/non262/TypedArray/length.js new file mode 100644 index 0000000000..7ed79b4954 --- /dev/null +++ b/js/src/tests/non262/TypedArray/length.js @@ -0,0 +1,14 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const TypedArray = Object.getPrototypeOf(Int32Array); + +assertEq(TypedArray.length, 0); + +assertDeepEq(Object.getOwnPropertyDescriptor(TypedArray, "length"), { + value: 0, writable: false, enumerable: false, configurable: true, +}); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/map-and-filter.js b/js/src/tests/non262/TypedArray/map-and-filter.js new file mode 100644 index 0000000000..0d86fc02b2 --- /dev/null +++ b/js/src/tests/non262/TypedArray/map-and-filter.js @@ -0,0 +1,274 @@ +// Tests for TypedArray#map. +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.map.length, 1); + + // Basic tests. + assertDeepEq(new constructor([1, 3, 5]).map(v => v * 2), new constructor([2,6,10])); + assertDeepEq(new constructor([-1, 13, 5]).map(v => v - 2), new constructor([-3, 11, 3])); + assertDeepEq(new constructor(10).map(v => v), new constructor(10)); + assertDeepEq(new constructor().map(v => v + 1), new constructor); + assertDeepEq(new constructor([1,2,3]).map(v => v), new constructor([1,2,3])); + + var arr = new constructor([1, 2, 3, 4, 5]); + var sum = 0; + var count = 0; + assertDeepEq(arr.map((v, k, o) => { + count++; + sum += v; + assertEq(k, v - 1); + assertEq(o, arr); + return v; + }), arr); + assertEq(sum, 15); + assertEq(count, 5); + + // Test that changing elements that have been visited does not affect the result. + var changeArr = new constructor([1,2,3,4,5]); + assertDeepEq(arr.map((v,k) => { + changeArr[k] = v + 1; + return v; + }), new constructor([1,2,3,4,5])); + + // Tests for `thisArg` argument. + function assertThisArg(thisArg, thisValue) { + // In sloppy mode, `this` could be global object or a wrapper of `thisArg`. + assertDeepEq(arr.map(function(v) { + assertDeepEq(this, thisValue); + return v; + }, thisArg), arr); + + // In strict mode, `this` strictly equals `thisArg`. + assertDeepEq(arr.map(function(v) { + "use strict"; + assertDeepEq(this, thisArg); + return v; + }, thisArg), arr); + + // Passing `thisArg` has no effect if callback is an arrow function. + var self = this; + assertDeepEq(arr.map((v) => { + assertEq(this, self); + return v; + }, thisArg), arr); + } + assertThisArg([1, 2, 3], [1, 2, 3]); + assertThisArg(Object, Object); + assertThisArg(1, Object(1)); + assertThisArg("1", Object("1")); + assertThisArg(false, Object(false)); + assertThisArg(undefined, this); + assertThisArg(null, this); + + // Throw an exception in the callback. + var sum = 0; + var count = 0; + var thrown = false; + try { + arr.map((v, k, o) => { + count++; + sum += v; + assertEq(k, v - 1); + assertEq(o, arr); + if (v === 3) { + throw "map"; + } + return v; + }) + } catch(e) { + assertEq(e, "map"); + thrown = true; + } + assertEq(thrown, true); + assertEq(sum, 6); + assertEq(count, 3); + + // There is no callback or callback is not a function. + assertThrowsInstanceOf(() => { + arr.map(); + }, TypeError); + var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidCallbacks.forEach(callback => { + assertThrowsInstanceOf(() => { + arr.map(callback); + }, TypeError); + }) + + // Callback is a generator. + arr.map(function*(){ + throw "This line will not be executed"; + }); + + // Called from other globals. + if (typeof newGlobal === "function") { + var map = newGlobal()[constructor.name].prototype.map; + var sum = 0; + assertDeepEq(map.call(new constructor([1, 2, 3]), v => sum += v), new constructor([1,3,6])); + assertEq(sum, 6); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.filter.call(invalidReceiver, () => true); + }, TypeError, "Assert that map fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + assertDeepEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).map((b) => b), new constructor([1,2,3])); +} + +// Test For TypedArray#filter. +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.filter.length, 1) + + // Basic tests. + assertDeepEq(new constructor([1,2,3]).filter(x => x == x), new constructor([1,2,3])); + assertDeepEq(new constructor([1,2,3,4]).filter(x => x % 2 == 0), new constructor([2,4])); + assertDeepEq(new constructor([1,2,3,4,5]).filter(x => x < 4), new constructor([1,2,3])); + assertDeepEq(new constructor().filter(x => x * 2 == 4), new constructor()); + + var arr = new constructor([1,2,3,4,5]); + var sum = 0; + var count = 0; + assertDeepEq(arr.filter((v, k, o) => { + count++; + sum += v; + assertEq(k, v - 1); + assertEq(o, arr); + return (v < 4); + }), new constructor([1,2,3])); + assertEq(sum, 15); + assertEq(count, 5); + + // Test that changing elements that have been visited does not affect the result. + var changeArr = new constructor([1,2,3,4,5]); + assertDeepEq(arr.filter((v,k) => { + changeArr[k] = v + 1; + return true; + }), new constructor([1,2,3,4,5])); + + // Tests for `thisArg` argument. + function assertThisArg(thisArg, thisValue) { + // In sloppy mode, `this` could be global object or a wrapper of `thisArg`. + assertDeepEq(arr.filter(function(v) { + assertDeepEq(this, thisValue); + return v; + }, thisArg), arr); + + // In strict mode, `this` strictly equals `thisArg`. + assertDeepEq(arr.filter(function(v) { + "use strict"; + assertDeepEq(this, thisArg); + return v; + }, thisArg), arr); + + // Passing `thisArg` has no effect if callback is an arrow function. + var self = this; + assertDeepEq(arr.filter((v) => { + assertEq(this, self); + return v; + }, thisArg), arr); + } + assertThisArg([1, 2, 3], [1, 2, 3]); + assertThisArg(Object, Object); + assertThisArg(1, Object(1)); + assertThisArg("1", Object("1")); + assertThisArg(false, Object(false)); + assertThisArg(undefined, this); + assertThisArg(null, this); + + // Throw an exception in the callback. + var sum = 0; + var count = 0; + var thrown = false; + try { + arr.filter((v, k, o) => { + count++; + sum += v; + assertEq(k, v - 1); + assertEq(o, arr); + if (v === 3) { + throw "filter"; + } + return v; + }) + } catch(e) { + assertEq(e, "filter"); + thrown = true; + } + assertEq(thrown, true); + assertEq(sum, 6); + assertEq(count, 3); + + // There is no callback or callback is not a function. + assertThrowsInstanceOf(() => { + arr.filter(); + }, TypeError); + var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidCallbacks.forEach(callback => { + assertThrowsInstanceOf(() => { + arr.filter(callback); + }, TypeError); + }) + + // Callback is a generator. + arr.filter(function*(){ + throw "This line will not be executed"; + }); + + // Called from other globals. + if (typeof newGlobal === "function") { + var filter = newGlobal()[constructor.name].prototype.filter; + var sum = 0; + assertDeepEq(filter.call(new constructor([1, 2, 3]), v => {sum += v; return true}), + new constructor([1,2,3])); + assertEq(sum, 6); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.filter.call(invalidReceiver, () => true); + }, TypeError, "Assert that filter fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + assertDeepEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).filter((b) => true), new constructor([1,2,3])); +} + +// Test that changing Array.prototype[Symbol.iterator] does not affect the +// behaviour of filter. See https://bugzilla.mozilla.org/show_bug.cgi?id=1121936#c18 +// for more details. + +var arr = new Uint16Array([1,2,3]); + +// save +var old = Array.prototype[Symbol.iterator]; + +Array.prototype[Symbol.iterator] = () => { throw new Error("unreachable"); }; +assertDeepEq(arr.filter(v => true), arr); + +// restore +Array.prototype[Symbol.iterator] = old; + +// Test that defining accessors on Array.prototype doesn't affect the behaviour +// of filter. See https://bugzilla.mozilla.org/show_bug.cgi?id=1121936#c18 +// for more details. +Object.defineProperty(Array.prototype, 0, {configurable: true, get: function() { return 1; }, set: function() { this.b = 1; }}); +assertDeepEq(new Uint16Array([1,2,3]).filter(v => true), new Uint16Array([1,2,3])); +delete Array.prototype[0]; + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/map-species.js b/js/src/tests/non262/TypedArray/map-species.js new file mode 100644 index 0000000000..8353d914bd --- /dev/null +++ b/js/src/tests/non262/TypedArray/map-species.js @@ -0,0 +1,56 @@ +function test(constructor, constructor2, from=[1, 2, 3, 4, 5], to=[2, 4, 6, 8, 10]) { + var modifiedConstructor = new constructor(from); + modifiedConstructor.constructor = constructor2; + assertDeepEq(modifiedConstructor.map(x => x * 2), new constructor2(to)); + var modifiedSpecies = new constructor(from); + modifiedSpecies.constructor = { [Symbol.species]: constructor2 }; + assertDeepEq(modifiedSpecies.map(x => x * 2), new constructor2(to)); +} + +// same size, same sign + +test(Int8Array, Uint8Array); +test(Int8Array, Uint8ClampedArray); + +test(Uint8Array, Int8Array); +test(Uint8Array, Uint8ClampedArray); + +test(Uint8ClampedArray, Int8Array); +test(Uint8ClampedArray, Uint8Array); + +test(Int16Array, Uint16Array); +test(Uint16Array, Int16Array); + +test(Int32Array, Uint32Array); +test(Uint32Array, Int32Array); + +// same size, different sign + +test(Int8Array, Uint8Array, [-1, -2, -3, -4, -5], [0xFE, 0xFC, 0xFA, 0xF8, 0xF6]); +test(Int8Array, Uint8ClampedArray, [-1, -2, -3, -4, -5], [0, 0, 0, 0, 0]); + +test(Uint8Array, Int8Array, [0xFF, 0xFE, 0xFD, 0xFC, 0xFB], [-2, -4, -6, -8, -10]); +test(Uint8ClampedArray, Int8Array, [0xFF, 0xFE, 0xFD, 0xFC, 0xFB], [-2, -4, -6, -8, -10]); + +test(Int16Array, Uint16Array, [-1, -2, -3, -4, -5], [0xFFFE, 0xFFFC, 0xFFFA, 0xFFF8, 0xFFF6]); +test(Uint16Array, Int16Array, [0xFFFF, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFB], [-2, -4, -6, -8, -10]); + +test(Int32Array, Uint32Array, [-1, -2, -3, -4, -5], [0xFFFFFFFE, 0xFFFFFFFC, 0xFFFFFFFA, 0xFFFFFFF8, 0xFFFFFFF6]); +test(Uint32Array, Int32Array, [0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFC, 0xFFFFFFFB], [-2, -4, -6, -8, -10]); + +// different size + +test(Uint8Array, Uint16Array); +test(Uint16Array, Uint8Array); + +test(Uint8Array, Uint32Array); +test(Uint32Array, Uint8Array); + +test(Uint16Array, Uint32Array); +test(Uint32Array, Uint16Array); + +test(Float32Array, Float64Array); +test(Float64Array, Float32Array); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/map-validation.js b/js/src/tests/non262/TypedArray/map-validation.js new file mode 100644 index 0000000000..1694ef50cb --- /dev/null +++ b/js/src/tests/non262/TypedArray/map-validation.js @@ -0,0 +1,149 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.prototype.map. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayLengths = [0, 1, 1024]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.prototype.map, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.prototype.map, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].prototype.map, + error: otherGlobal.TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => 123), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + + // Passes when the length is zero. + if (length === 0) { + let result = method.call(ta, () => 123); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(ta, () => 123), error); + } + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, () => 123); + assertEq(result.length, length + extraLength); + assertEq(result[0], (length === 0 ? 0 : 123)); + assertEq(result[length + extraLength - 1], 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => 123), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/object-defineproperty.js b/js/src/tests/non262/TypedArray/object-defineproperty.js new file mode 100644 index 0000000000..b19f76b845 --- /dev/null +++ b/js/src/tests/non262/TypedArray/object-defineproperty.js @@ -0,0 +1,68 @@ +var obj = new Int32Array(2); +obj[0] = 100; + +var throws = [ + // Disallow accessors + {get: undefined}, + {set: undefined}, + {get: undefined, set: undefined}, + {get: function() {}}, + {set: function() {}}, + {get: function() {}, set: function() {}}, + + {configurable: false}, + {enumerable: false}, + {writable: false}, + + {configurable: false, writable: true}, + {enumerable: false, configurable: false}, + + {configurable: false, value: 15} +]; + +for (var desc of throws) { + assertThrowsInstanceOf(function() { Object.defineProperty(obj, 0, desc); }, TypeError); + assertThrowsInstanceOf(function() { Object.defineProperties(obj, {0: desc}); }, TypeError); +} + +Object.defineProperty(obj, 0, {}); +Object.defineProperty(obj, 0, {configurable: true}); +Object.defineProperty(obj, 0, {enumerable: true}); +Object.defineProperty(obj, 0, {writable: true}); + +assertEq(obj[0], 100); + +Object.defineProperty(obj, 0, {configurable: true, value: 15}); +assertEq(obj[0], 15); +Object.defineProperty(obj, 0, {enumerable: true, value: 16}); +assertEq(obj[0], 16); +Object.defineProperty(obj, 0, {writable: true, value: 17}); +assertEq(obj[0], 17); +Object.defineProperty(obj, 0, {value: 18}); +assertEq(obj[0], 18); + +var desc = Object.getOwnPropertyDescriptor(obj, 0); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, true); +assertEq(desc.writable, true); +assertEq(desc.value, 18); +assertEq('get' in desc, false); +assertEq('set' in desc, false); + +Object.defineProperties(obj, {0: {value: 20}, 1: {value: 42}}); +assertEq(obj[0], 20); +assertEq(obj[1], 42); + +anyTypedArrayConstructors.forEach(constructor => { + var obj = new constructor(4); + obj[0] = 100; + obj[1] = 200; + + for (var v of [20, 300, -10, Math.pow(2, 32), -Math.pow(2, 32), NaN]) { + Object.defineProperty(obj, 0, {value: v}); + obj[1] = v; + assertEq(obj[0], obj[1]); + } +}); + +reportCompare(true, true, "test"); diff --git a/js/src/tests/non262/TypedArray/of-validation.js b/js/src/tests/non262/TypedArray/of-validation.js new file mode 100644 index 0000000000..e7ab905195 --- /dev/null +++ b/js/src/tests/non262/TypedArray/of-validation.js @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.of. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayArgs = [[], [123], [123, ...Array(1023).fill(0)]]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.of, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.of, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].of, + error: otherGlobal.TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, ...args), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + // Passes when the length is zero. + if (args.length === 0) { + let result = method.call(species, ...args); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(species, ...args), error); + } + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + let result = method.call(species, ...args); + assertEq(result.length, args.length + extraLength); + assertEq(result[0], (args.length === 0 ? 0 : 123)); + assertEq(result[args.length + extraLength - 1], 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, ...args), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/of.js b/js/src/tests/non262/TypedArray/of.js new file mode 100644 index 0000000000..af9c112023 --- /dev/null +++ b/js/src/tests/non262/TypedArray/of.js @@ -0,0 +1,92 @@ +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.of.length, 0); + + assertDeepEq(Object.getOwnPropertyDescriptor(constructor.__proto__, "of"), { + value: constructor.of, + writable: true, + enumerable: false, + configurable: true + }); + + // Basic tests. + assertEq(constructor.of().constructor, constructor); + assertEq(constructor.of() instanceof constructor, true); + assertDeepEq(constructor.of(10), new constructor([10])); + assertDeepEq(constructor.of(1, 2, 3), new constructor([1, 2, 3])); + assertDeepEq(constructor.of("1", "2", "3"), new constructor([1, 2, 3])); + + // This method can't be transplanted to other constructors. + assertThrowsInstanceOf(() => constructor.of.call(Array), TypeError); + assertThrowsInstanceOf(() => constructor.of.call(Array, 1, 2, 3), TypeError); + + var hits = 0; + assertDeepEq(constructor.of.call(function(len) { + assertEq(arguments.length, 1); + assertEq(len, 3); + hits++; + return new constructor(len); + }, 10, 20, 30), new constructor([10, 20, 30])); + assertEq(hits, 1); + + // Behavior across compartments. + if (typeof newGlobal === "function") { + var newC = newGlobal()[constructor.name]; + assertEq(newC.of() instanceof newC, true); + assertEq(newC.of() instanceof constructor, false); + assertEq(newC.of.call(constructor) instanceof constructor, true); + } + + // Throws if `this` isn't a constructor. + var invalidConstructors = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + constructor.of, () => {}]; + invalidConstructors.forEach(C => { + assertThrowsInstanceOf(() => { + constructor.of.call(C); + }, TypeError); + }); + + // Throw if `this` is a method definition or a getter/setter function. + assertThrowsInstanceOf(() => { + constructor.of.call({method() {}}.method); + }, TypeError); + assertThrowsInstanceOf(() => { + constructor.of.call(Object.getOwnPropertyDescriptor({get getter() {}}, "getter").get); + }, TypeError); + + // Generators are not legal constructors. + assertThrowsInstanceOf(() => { + constructor.of.call(function*(len) { + return len; + }, "a") + }, TypeError); + + // An exception might be thrown in a strict assignment to the new object's indexed properties. + assertThrowsInstanceOf(() => { + constructor.of.call(function() { + return {get 0() {}}; + }, "a"); + }, TypeError); + + assertThrowsInstanceOf(() => { + constructor.of.call(function() { + return Object("1"); + }, "a"); + }, TypeError); + + assertThrowsInstanceOf(() => { + constructor.of.call(function() { + return Object.create({ + set 0(v) { + throw new TypeError; + } + }); + }, "a"); + }, TypeError); +} + +for (let constructor of anyTypedArrayConstructors.filter(isFloatConstructor)) { + assertDeepEq(constructor.of(0.1, null, undefined, NaN), new constructor([0.1, 0, NaN, NaN])); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/prototype-constructor-identity.js b/js/src/tests/non262/TypedArray/prototype-constructor-identity.js new file mode 100644 index 0000000000..10324f5720 --- /dev/null +++ b/js/src/tests/non262/TypedArray/prototype-constructor-identity.js @@ -0,0 +1,55 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var gTestfile = 'prototype-constructor-identity.js'; +//----------------------------------------------------------------------------- +var BUGNUMBER = 896116; +var summary = + "Typed array prototypes/constructors should be largely empty, inheriting " + "most functionality from %TypedArray% and %TypedArray%.prototype"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var TypedArray = Object.getPrototypeOf(Int8Array); + +assertEq(TypedArray !== Function.prototype, true, + "%TypedArray% should be in constructors' [[Prototype]] chains"); +assertEq(Object.getPrototypeOf(TypedArray), Function.prototype, + "%TypedArray%.prototype should inherit from Function.prototype"); + +assertEq(TypedArray.prototype.constructor, TypedArray, + "bad %TypedArray%.prototype.constructor"); + +// Check a few different functions we implement are here, as a sanity check. +var typedArrayProps = Object.getOwnPropertyNames(TypedArray.prototype); +assertEq(typedArrayProps.indexOf("copyWithin") >= 0, true); +assertEq(typedArrayProps.indexOf("subarray") >= 0, true); +assertEq(typedArrayProps.indexOf("set") >= 0, true); + +anyTypedArrayConstructors.forEach(function(ctor) { + assertEq(Object.getPrototypeOf(ctor), TypedArray); + + var proto = ctor.prototype; + + // Inherited functions aren't present. + var props = Object.getOwnPropertyNames(proto).sort(); + assertEq(props[0], "BYTES_PER_ELEMENT"); + assertEq(props[1], "constructor"); + assertEq(props.length, 2); + + // The inheritance chain should be set up properly. + assertEq(Object.getPrototypeOf(proto), TypedArray.prototype, + "prototype should inherit from %TypedArray%.prototype"); +}); + +/******************************************************************************/ + +reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/TypedArray/reduce-and-reduceRight.js b/js/src/tests/non262/TypedArray/reduce-and-reduceRight.js new file mode 100644 index 0000000000..21a7d99e90 --- /dev/null +++ b/js/src/tests/non262/TypedArray/reduce-and-reduceRight.js @@ -0,0 +1,188 @@ +// Tests for TypedArray#reduce. +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.reduce.length, 1); + + // Basic tests. + var arr = new constructor([1, 2, 3, 4, 5]); + + assertEq(arr.reduce((previous, current) => previous + current), 15); + assertEq(arr.reduce((previous, current) => current - previous), 3); + + var count = 0; + var sum = 0; + assertEq(arr.reduce((previous, current, index, array) => { + count++; + sum += current; + assertEq(current - 1, index); + assertEq(current, arr[index]); + assertEq(array, arr); + return previous * current; + }), 120); + assertEq(count, 4); + assertEq(sum, 14); + + // Tests for `initialValue` argument. + assertEq(arr.reduce((previous, current) => previous + current, -15), 0); + assertEq(arr.reduce((previous, current) => previous + current, ""), "12345"); + assertDeepEq(arr.reduce((previous, current) => previous.concat(current), []), [1, 2, 3, 4, 5]); + + // Tests for `this` value. + var global = this; + arr.reduce(function(){ + assertEq(this, global); + }); + arr.reduce(function(){ + "use strict"; + assertEq(this, undefined); + }); + arr.reduce(() => assertEq(this, global)); + + // Throw an exception in the callback. + var count = 0; + var sum = 0; + assertThrowsInstanceOf(() => { + arr.reduce((previous, current, index, array) => { + count++; + sum += current; + if (index === 3) { + throw TypeError("reduce"); + } + }) + }, TypeError); + assertEq(count, 3); + assertEq(sum, 9); + + // There is no callback or callback is not a function. + assertThrowsInstanceOf(() => { + arr.reduce(); + }, TypeError); + var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidCallbacks.forEach(callback => { + assertThrowsInstanceOf(() => { + arr.reduce(callback); + }, TypeError); + }) + + // Callback is a generator. + arr.reduce(function*(){ + throw "This line will not be executed"; + }); + + // Called from other globals. + if (typeof newGlobal === "function") { + var reduce = newGlobal()[constructor.name].prototype.reduce; + assertEq(reduce.call(arr, (previous, current) => Math.min(previous, current)), 1); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(3), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.reduce.call(invalidReceiver, () => {}); + }, TypeError, "Assert that reduce fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + assertEq(Object.defineProperty(arr, "length", { + get() { + throw new Error("length accessor called"); + } + }).reduce((previous, current) => Math.max(previous, current)), 5); +} + +// Tests for TypedArray#reduceRight. +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.reduceRight.length, 1); + + // Basic tests. + var arr = new constructor([1, 2, 3, 4, 5]); + + assertEq(arr.reduceRight((previous, current) => previous + current), 15); + assertEq(arr.reduceRight((previous, current) => current - previous), 3); + + var count = 0; + var sum = 0; + assertEq(arr.reduceRight((previous, current, index, array) => { + count++; + sum += current; + assertEq(current - 1, index); + assertEq(current, arr[index]); + assertEq(array, arr); + return previous * current; + }), 120); + assertEq(count, 4); + assertEq(sum, 10); + + // Tests for `initialValue` argument. + assertEq(arr.reduceRight((previous, current) => previous + current, -15), 0); + assertEq(arr.reduceRight((previous, current) => previous + current, ""), "54321"); + assertDeepEq(arr.reduceRight((previous, current) => previous.concat(current), []), [5, 4, 3, 2, 1]); + + // Tests for `this` value. + var global = this; + arr.reduceRight(function(){ + assertEq(this, global); + }); + arr.reduceRight(function(){ + "use strict"; + assertEq(this, undefined); + }); + arr.reduceRight(() => assertEq(this, global)); + + // Throw an exception in the callback. + var count = 0; + var sum = 0; + assertThrowsInstanceOf(() => { + arr.reduceRight((previous, current, index, array) => { + count++; + sum += current; + if (index === 1) { + throw TypeError("reduceRight"); + } + }) + }, TypeError); + assertEq(count, 3); + assertEq(sum, 9); + + // There is no callback or callback is not a function. + assertThrowsInstanceOf(() => { + arr.reduceRight(); + }, TypeError); + var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidCallbacks.forEach(callback => { + assertThrowsInstanceOf(() => { + arr.reduceRight(callback); + }, TypeError); + }) + + // Callback is a generator. + arr.reduceRight(function*(){ + throw "This line will not be executed"; + }); + + // Called from other globals. + if (typeof newGlobal === "function") { + var reduceRight = newGlobal()[constructor.name].prototype.reduceRight; + assertEq(reduceRight.call(arr, (previous, current) => Math.min(previous, current)), 1); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(3), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.reduceRight.call(invalidReceiver, () => {}); + }, TypeError, "Assert that reduceRight fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + assertEq(Object.defineProperty(arr, "length", { + get() { + throw new Error("length accessor called"); + } + }).reduceRight((previous, current) => Math.max(previous, current)), 5); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/reverse.js b/js/src/tests/non262/TypedArray/reverse.js new file mode 100644 index 0000000000..f8146506c6 --- /dev/null +++ b/js/src/tests/non262/TypedArray/reverse.js @@ -0,0 +1,38 @@ +for (var constructor of anyTypedArrayConstructors) { + assertDeepEq(constructor.prototype.reverse.length, 0); + + assertDeepEq(new constructor().reverse(), new constructor()); + assertDeepEq(new constructor(10).reverse(), new constructor(10)); + assertDeepEq(new constructor([]).reverse(), new constructor([])); + assertDeepEq(new constructor([1]).reverse(), new constructor([1])); + assertDeepEq(new constructor([1, 2]).reverse(), new constructor([2, 1])); + assertDeepEq(new constructor([1, 2, 3]).reverse(), new constructor([3, 2, 1])); + assertDeepEq(new constructor([1, 2, 3, 4]).reverse(), new constructor([4, 3, 2, 1])); + assertDeepEq(new constructor([1, 2, 3, 4, 5]).reverse(), new constructor([5, 4, 3, 2, 1])); + assertDeepEq(new constructor([.1, .2, .3]).reverse(), new constructor([.3, .2, .1])); + + // Called from other globals. + if (typeof newGlobal === "function") { + var reverse = newGlobal()[constructor.name].prototype.reverse; + assertDeepEq(reverse.call(new constructor([3, 2, 1])), new constructor([1, 2, 3])); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.reverse.call(invalidReceiver); + }, TypeError, "Assert that reverse fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).reverse(); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/seal-and-freeze.js b/js/src/tests/non262/TypedArray/seal-and-freeze.js new file mode 100644 index 0000000000..f90efb866f --- /dev/null +++ b/js/src/tests/non262/TypedArray/seal-and-freeze.js @@ -0,0 +1,49 @@ +"use strict"; + +// Seal +assertEq(Object.isSealed(new Int32Array(2)), false); +assertEq(Object.isSealed(new Int32Array(0)), false); + +var array = new Int32Array(0); +Object.preventExtensions(array); +assertEq(Object.isSealed(array), true); + +// Non-empty typed arrays can never be sealed, because the elements stay configurable. +array = new Int32Array(1); +array.b = "test"; +Object.preventExtensions(array); +assertEq(Object.isSealed(array), false); +Object.defineProperty(array, "b", {configurable: false}); +assertEq(Object.isSealed(array), false); + +array = new Int32Array(2); +array.b = "test"; +assertThrowsInstanceOf(() => Object.seal(array), TypeError); +assertEq(Object.isSealed(array), false); +assertThrowsInstanceOf(() => array.c = 15, TypeError); + +// Freeze +assertEq(Object.isFrozen(new Int32Array(2)), false); +assertEq(Object.isFrozen(new Int32Array(0)), false); + +// Empty non-extensible typed-array is trivially frozen +var array = new Int32Array(0); +Object.preventExtensions(array); +assertEq(Object.isFrozen(array), true); + +array = new Int32Array(0); +array.b = "test"; +assertEq(Object.isFrozen(array), false); +Object.preventExtensions(array); +assertEq(Object.isFrozen(array), false); +Object.defineProperty(array, "b", {configurable: false, writable: false}); +assertEq(Object.isFrozen(array), true); + +// Non-empty typed arrays can never be frozen, because the elements stay writable +array = new Int32Array(1); +assertThrowsInstanceOf(() => Object.freeze(array), TypeError); +assertEq(Object.isExtensible(array), false); +assertEq(Object.isFrozen(array), false); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/set-detached-bigint.js b/js/src/tests/non262/TypedArray/set-detached-bigint.js new file mode 100644 index 0000000000..c54f4b867f --- /dev/null +++ b/js/src/tests/non262/TypedArray/set-detached-bigint.js @@ -0,0 +1,19 @@ +let ta = new BigInt64Array(10); + +let obj = { + get length() { + detachArrayBuffer(ta.buffer); + return 1; + }, + 0: { + valueOf() { + return "huzzah!"; + } + }, +}; + +// Throws a SyntaxError, because "huzzah!" can't be parsed as a BigInt. +assertThrowsInstanceOf(() => ta.set(obj), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/set-detached.js b/js/src/tests/non262/TypedArray/set-detached.js new file mode 100644 index 0000000000..4f9226c608 --- /dev/null +++ b/js/src/tests/non262/TypedArray/set-detached.js @@ -0,0 +1,265 @@ +// Tests for detached ArrayBuffer checks in %TypedArray%.prototype.set(array|typedArray, offset). + +function* createTypedArrays(lengths = [0, 1, 4, 4096]) { + for (let length of lengths) { + let buffer = new ArrayBuffer(length * Int32Array.BYTES_PER_ELEMENT); + let typedArray = new Int32Array(buffer); + + yield {typedArray, buffer}; + } +} + +if (typeof detachArrayBuffer === "function") { + class ExpectedError extends Error {} + + // No detached check on function entry. + for (let {typedArray, buffer} of createTypedArrays()) { + detachArrayBuffer(buffer); + + assertThrowsInstanceOf(() => typedArray.set(null, { + valueOf() { + throw new ExpectedError(); + } + }), ExpectedError); + } + + // Check for detached buffer after calling ToInteger(offset). Test with: + // - valid offset, + // - too large offset, + // - and negative offset. + for (let [offset, error] of [[0, TypeError], [1000000, TypeError], [-1, RangeError]]) { + for (let source of [[], [0], new Int32Array(0), new Int32Array(1)]) { + for (let {typedArray, buffer} of createTypedArrays()) { + assertThrowsInstanceOf(() => typedArray.set(source, { + valueOf() { + detachArrayBuffer(buffer); + return offset; + } + }), error); + } + } + } + + // Tests when called with detached typed array as source. + for (let {typedArray} of createTypedArrays()) { + for (let {typedArray: source, buffer: sourceBuffer} of createTypedArrays()) { + detachArrayBuffer(sourceBuffer); + + assertThrowsInstanceOf(() => typedArray.set(source, { + valueOf() { + throw new ExpectedError(); + } + }), ExpectedError); + } + } + + // Check when detaching source buffer in ToInteger(offset). Test with: + // - valid offset, + // - too large offset, + // - and negative offset. + for (let [offset, error] of [[0, TypeError], [1000000, TypeError], [-1, RangeError]]) { + for (let {typedArray} of createTypedArrays()) { + for (let {typedArray: source, buffer: sourceBuffer} of createTypedArrays()) { + assertThrowsInstanceOf(() => typedArray.set(source, { + valueOf() { + detachArrayBuffer(sourceBuffer); + return offset; + } + }), error); + } + } + } + + // Test when target and source use the same underlying buffer and + // ToInteger(offset) detaches the buffer. Test with: + // - same typed array, + // - different typed array, but same element type, + // - and different element type. + for (let src of [ta => ta, ta => new Int32Array(ta.buffer), ta => new Float32Array(ta.buffer)]) { + for (let {typedArray, buffer} of createTypedArrays()) { + let source = src(typedArray); + assertThrowsInstanceOf(() => typedArray.set(source, { + valueOf() { + detachArrayBuffer(buffer); + return 0; + } + }), TypeError); + } + } + + // Test when Get(src, "length") detaches the buffer, but srcLength is 0. + // Also use different offsets to ensure bounds checks use the typed array's + // length value from before detaching the buffer. + for (let offset of [() => 0, ta => Math.min(1, ta.length), ta => Math.max(0, ta.length - 1)]) { + for (let {typedArray, buffer} of createTypedArrays()) { + let source = { + get length() { + detachArrayBuffer(buffer); + return 0; + } + }; + typedArray.set(source, offset(typedArray)); + } + } + + // Test when ToLength(Get(src, "length")) detaches the buffer, but + // srcLength is 0. Also use different offsets to ensure bounds checks use + // the typed array's length value from before detaching the buffer. + for (let offset of [() => 0, ta => Math.min(1, ta.length), ta => Math.max(0, ta.length - 1)]) { + for (let {typedArray, buffer} of createTypedArrays()) { + let source = { + length: { + valueOf() { + detachArrayBuffer(buffer); + return 0; + } + } + }; + typedArray.set(source, offset(typedArray)); + } + } + + // Test no TypeError is thrown when the typed array is detached and + // srcLength > 0. + for (let {typedArray, buffer} of createTypedArrays()) { + let source = { + length: { + valueOf() { + detachArrayBuffer(buffer); + return 1; + } + } + }; + if (typedArray.length === 0) { + assertThrowsInstanceOf(() => typedArray.set(source), RangeError); + } else { + typedArray.set(source); + } + } + + // Same as above, but with side-effect when executing Get(src, "0"). + for (let {typedArray, buffer} of createTypedArrays()) { + let source = { + get 0() { + throw new ExpectedError(); + }, + length: { + valueOf() { + detachArrayBuffer(buffer); + return 1; + } + } + }; + let err = typedArray.length === 0 ? RangeError : ExpectedError; + assertThrowsInstanceOf(() => typedArray.set(source), err); + } + + // Same as above, but with side-effect when executing ToNumber(Get(src, "0")). + for (let {typedArray, buffer} of createTypedArrays()) { + let source = { + get 0() { + return { + valueOf() { + throw new ExpectedError(); + } + }; + }, + length: { + valueOf() { + detachArrayBuffer(buffer); + return 1; + } + } + }; + let err = typedArray.length === 0 ? RangeError : ExpectedError; + assertThrowsInstanceOf(() => typedArray.set(source), err); + } + + // Side-effects when getting the source elements detach the buffer. + for (let {typedArray, buffer} of createTypedArrays()) { + let source = Object.defineProperties([], { + 0: { + get() { + detachArrayBuffer(buffer); + return 1; + } + } + }); + if (typedArray.length === 0) { + assertThrowsInstanceOf(() => typedArray.set(source), RangeError); + } else { + typedArray.set(source); + } + } + + // Side-effects when getting the source elements detach the buffer. Also + // ensure other elements are accessed. + for (let {typedArray, buffer} of createTypedArrays()) { + let accessed = false; + let source = Object.defineProperties([], { + 0: { + get() { + detachArrayBuffer(buffer); + return 1; + } + }, + 1: { + get() { + assertEq(accessed, false); + accessed = true; + return 2; + } + } + }); + if (typedArray.length <= 1) { + assertThrowsInstanceOf(() => typedArray.set(source), RangeError); + } else { + assertEq(accessed, false); + typedArray.set(source); + assertEq(accessed, true); + } + } + + // Side-effects when converting the source elements detach the buffer. + for (let {typedArray, buffer} of createTypedArrays()) { + let source = [{ + valueOf() { + detachArrayBuffer(buffer); + return 1; + } + }]; + if (typedArray.length === 0) { + assertThrowsInstanceOf(() => typedArray.set(source), RangeError); + } else { + typedArray.set(source); + } + } + + // Side-effects when converting the source elements detach the buffer. Also + // ensure other elements are accessed. + for (let {typedArray, buffer} of createTypedArrays()) { + let accessed = false; + let source = [{ + valueOf() { + detachArrayBuffer(buffer); + return 1; + } + }, { + valueOf() { + assertEq(accessed, false); + accessed = true; + return 2; + } + }]; + if (typedArray.length <= 1) { + assertThrowsInstanceOf(() => typedArray.set(source), RangeError); + } else { + assertEq(accessed, false); + typedArray.set(source); + assertEq(accessed, true); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/set-negative-offset.js b/js/src/tests/non262/TypedArray/set-negative-offset.js new file mode 100644 index 0000000000..6d9c45ffb2 --- /dev/null +++ b/js/src/tests/non262/TypedArray/set-negative-offset.js @@ -0,0 +1,35 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var gTestfile = "set-negative-offset.js"; +//----------------------------------------------------------------------------- +var BUGNUMBER = 1140752; +var summary = + "%TypedArray%.prototype.set must throw a RangeError when passed a negative " + + "offset"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +try +{ + new Uint8Array().set([], -1); + throw new Error("didn't throw at all"); +} +catch (e) +{ + assertEq(e instanceof RangeError, true, + "expected RangeError, instead got: " + e); +} + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/TypedArray/set-same-buffer-different-source-target-types.js b/js/src/tests/non262/TypedArray/set-same-buffer-different-source-target-types.js new file mode 100644 index 0000000000..9084ce883a --- /dev/null +++ b/js/src/tests/non262/TypedArray/set-same-buffer-different-source-target-types.js @@ -0,0 +1,41 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var gTestfile = "set-same-buffer-different-source-target-types.js"; +//----------------------------------------------------------------------------- +var BUGNUMBER = 896116; +var summary = + "When setting a typed array from an overlapping typed array of different " + + "element type, copy the source elements into properly-sized temporary " + + "memory, and properly copy them into the target without overflow (of " + + "either source *or* target) when finished"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +// smallest 2**n triggering segfaults in a pre-patch build locally, then +// quadrupled in case the boundary ever changes, or is different in some other +// memory-allocating implementation +var srclen = 65536; + +var ta = new Uint8Array(srclen * Float64Array.BYTES_PER_ELEMENT); +var ta2 = new Float64Array(ta.buffer, 0, srclen); +ta.set(ta2); + +// This test mostly exists to check for no crash above, but it's worth testing +// for no uninitialized memory (in case of buffer overflow) being copied into +// the array, too. +for (var i = 0, len = ta.length; i < len; i++) + assertEq(ta[i], 0, "zero-bits double should convert to zero"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/TypedArray/set-tointeger.js b/js/src/tests/non262/TypedArray/set-tointeger.js new file mode 100644 index 0000000000..8c1e9c2418 --- /dev/null +++ b/js/src/tests/non262/TypedArray/set-tointeger.js @@ -0,0 +1,95 @@ +// Test ToInteger conversion in %TypedArray%.prototype.set(array|typedArray, offset). + +let ta = new Int32Array(4); + +// %TypedArray%.prototype.set has two different implementations for typed array +// and non-typed array arguments. Test with both input types. +let emptySources = [[], new Int32Array(0)]; +let nonEmptySource = [[0], new Int32Array(1)]; +let sources = [...emptySources, ...nonEmptySource]; + +// Test when ToInteger(offset) is in (-1, 4). +let validOffsets = [ + // Values in [+0, 4). + 0, + 0.1, + 3, + 3.9, + + // Values in (-1, -0]. + -0, + -0.1, + -0.9, + + NaN, + + // Also include some non-number values. + undefined, + null, + true, + "", + "3", + " 1\t\n", + "some string", + {valueOf() { return 2; }}, +]; + +for (let offset of validOffsets) { + for (let source of sources) { + ta.set(source, offset); + } +} + +// Test when ToInteger(offset) isn't in (-1, 4). +let invalidOffsets = [ + // Values exceeding the typed array's length. + 5, + 2147483647, + 2147483648, + 2147483649, + 4294967295, + 4294967296, + 4294967297, + Infinity, + + // Negative values. + -1, + -1.1, + -2147483647, + -2147483648, + -2147483649, + -4294967295, + -4294967296, + -4294967297, + -Infinity, + + // Also include some non-number values. + "8", + "Infinity", + " Infinity ", + {valueOf() { return 10; }}, +]; + +for (let offset of invalidOffsets) { + for (let source of sources) { + assertThrowsInstanceOf(() => ta.set(source, offset), RangeError); + } +} + +// Test when ToInteger(offset) is in [4, 5). +for (let source of emptySources) { + ta.set(source, 4); + ta.set(source, 4.9); +} +for (let source of nonEmptySource) { + assertThrowsInstanceOf(() => ta.set(source, 4), RangeError); + assertThrowsInstanceOf(() => ta.set(source, 4.9), RangeError); +} + +// ToInteger(symbol value) throws a TypeError. +for (let source of sources) { + assertThrowsInstanceOf(() => ta.set(source, Symbol()), TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/set-toobject.js b/js/src/tests/non262/TypedArray/set-toobject.js new file mode 100644 index 0000000000..360c5dde7e --- /dev/null +++ b/js/src/tests/non262/TypedArray/set-toobject.js @@ -0,0 +1,53 @@ +// Test ToObject in %TypedArray%.prototype.set(array|typedArray, offset). + +let ta = new Int32Array(4); + +for (let nullOrUndefined of [null, undefined]) { + // ToObject(array) throws a TypeError when |array| is null or undefined. + assertThrowsInstanceOf(() => ta.set(nullOrUndefined), TypeError); + + // ToInteger(offset) is called before ToObject(array). + class ExpectedError extends Error {} + assertThrowsInstanceOf(() => ta.set(nullOrUndefined, { + valueOf() { + throw new ExpectedError(); + } + }), ExpectedError); +} + +// Ensure ta is still initialized with zeros. +assertEqArray(ta, [0, 0, 0, 0]); + +// %TypedArray%.prototype.set can be called with a string primitive values. +ta.set(""); +assertEqArray(ta, [0, 0, 0, 0]); + +ta.set("123"); +assertEqArray(ta, [1, 2, 3, 0]); + +// Throws a RangeError if the length is too large. +assertThrowsInstanceOf(() => ta.set("456789"), RangeError); +assertEqArray(ta, [1, 2, 3, 0]); + +// When called with other primitive values the typed array contents don't +// change since ToObject(<primitive>).length is zero, i.e. the source object is +// treated the same as an empty array. +for (let value of [true, false, 0, NaN, 123, Infinity, Symbol()]) { + ta.set(value); + assertEqArray(ta, [1, 2, 3, 0]); +} + +// Repeat test from above when the primitive wrapper prototype has been changed +// to include "length" and an indexed property. +Number.prototype.length = 4; +Number.prototype[3] = -1; +try { + ta.set(456); + assertEqArray(ta, [0, 0, 0, -1]); +} finally { + delete Number.prototype.length; + delete Number.prototype[3]; +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/set-with-receiver.js b/js/src/tests/non262/TypedArray/set-with-receiver.js new file mode 100644 index 0000000000..dc01c0766f --- /dev/null +++ b/js/src/tests/non262/TypedArray/set-with-receiver.js @@ -0,0 +1,28 @@ +for (var constructor of anyTypedArrayConstructors) { + var receiver = {}; + + var ta = new constructor(1); + assertEq(Reflect.set(ta, 0, 47, receiver), true); + assertEq(ta[0], 0); + assertEq(receiver[0], 47); + + // Out-of-bounds + assertEq(Reflect.set(ta, 10, 47, receiver), true); + assertEq(ta[10], undefined); + assertEq(receiver[10], 47); + + // Detached + if (typeof detachArrayBuffer === "function" && + !isSharedConstructor(constructor)) + { + detachArrayBuffer(ta.buffer) + + assertEq(ta[0], undefined); + assertEq(Reflect.set(ta, 0, 42, receiver), true); + assertEq(ta[0], undefined); + assertEq(receiver[0], 42); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/set-wrapped.js b/js/src/tests/non262/TypedArray/set-wrapped.js new file mode 100644 index 0000000000..30a242bf0b --- /dev/null +++ b/js/src/tests/non262/TypedArray/set-wrapped.js @@ -0,0 +1,81 @@ +// Test %TypedArray%.prototype.set(typedArray, offset) when called with wrapped +// typed array. + +if (typeof newGlobal === "function") { + var otherGlobal = newGlobal(); + + function taintLengthProperty(obj) { + Object.defineProperty(obj, "length", { + get() { + assertEq(true, false); + } + }); + } + + for (var TA of anyTypedArrayConstructors) { + var target = new TA(4); + var source = new otherGlobal[TA.name]([10, 20]); + + // Ensure "length" getter accessor isn't called. + taintLengthProperty(source); + + assertEqArray(target, [0, 0, 0, 0]); + target.set(source, 1); + assertEqArray(target, [0, 10, 20, 0]); + } + + // Detachment checks are also applied correctly for wrapped typed arrays. + if (typeof detachArrayBuffer === "function") { + // Create typed array from different global (explicit constructor call). + for (var TA of typedArrayConstructors) { + var target = new TA(4); + var source = new otherGlobal[TA.name](1); + taintLengthProperty(source); + + // Called with wrapped typed array, array buffer already detached. + otherGlobal.detachArrayBuffer(source.buffer); + assertThrowsInstanceOf(() => target.set(source), TypeError); + + var source = new otherGlobal[TA.name](1); + taintLengthProperty(source); + + // Called with wrapped typed array, array buffer detached when + // processing offset parameter. + var offset = { + valueOf() { + otherGlobal.detachArrayBuffer(source.buffer); + return 0; + } + }; + assertThrowsInstanceOf(() => target.set(source, offset), TypeError); + } + + // Create typed array from different global (implictly created when + // ArrayBuffer is a CCW). + for (var TA of typedArrayConstructors) { + var target = new TA(4); + var source = new TA(new otherGlobal.ArrayBuffer(1 * TA.BYTES_PER_ELEMENT)); + taintLengthProperty(source); + + // Called with wrapped typed array, array buffer already detached. + otherGlobal.detachArrayBuffer(source.buffer); + assertThrowsInstanceOf(() => target.set(source), TypeError); + + var source = new TA(new otherGlobal.ArrayBuffer(1 * TA.BYTES_PER_ELEMENT)); + taintLengthProperty(source); + + // Called with wrapped typed array, array buffer detached when + // processing offset parameter. + var offset = { + valueOf() { + otherGlobal.detachArrayBuffer(source.buffer); + return 0; + } + }; + assertThrowsInstanceOf(() => target.set(source, offset), TypeError); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/set.js b/js/src/tests/non262/TypedArray/set.js new file mode 100644 index 0000000000..2af5a129a8 --- /dev/null +++ b/js/src/tests/non262/TypedArray/set.js @@ -0,0 +1,21 @@ +const TypedArrayPrototype = Object.getPrototypeOf(Int8Array.prototype); + +// %TypedArrayPrototype% has an own "set" function property. +assertEq(TypedArrayPrototype.hasOwnProperty("set"), true); +assertEq(typeof TypedArrayPrototype.set, "function"); + +// The concrete TypedArray prototypes do not have an own "set" property. +assertEq(anyTypedArrayConstructors.every(c => !c.hasOwnProperty("set")), true); + +assertDeepEq(Object.getOwnPropertyDescriptor(TypedArrayPrototype, "set"), { + value: TypedArrayPrototype.set, + writable: true, + enumerable: false, + configurable: true, +}); + +assertEq(TypedArrayPrototype.set.name, "set"); +assertEq(TypedArrayPrototype.set.length, 1); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/shell.js b/js/src/tests/non262/TypedArray/shell.js new file mode 100644 index 0000000000..2fb0ab8f1d --- /dev/null +++ b/js/src/tests/non262/TypedArray/shell.js @@ -0,0 +1,114 @@ +(function(global) { + "use strict"; + + const { + Float32Array, Float64Array, Object, Reflect, SharedArrayBuffer, WeakMap, + assertEq + } = global; + const { + apply: Reflect_apply, + construct: Reflect_construct, + } = Reflect; + const { + get: WeakMap_prototype_get, + has: WeakMap_prototype_has, + } = WeakMap.prototype; + + const sharedConstructors = new WeakMap(); + + // Synthesize a constructor for a shared memory array from the constructor + // for unshared memory. This has "good enough" fidelity for many uses. In + // cases where it's not good enough, call isSharedConstructor for local + // workarounds. + function sharedConstructor(baseConstructor) { + // Create SharedTypedArray as a subclass of %TypedArray%, following the + // built-in %TypedArray% subclasses. + class SharedTypedArray extends Object.getPrototypeOf(baseConstructor) { + constructor(...args) { + var array = Reflect_construct(baseConstructor, args); + var {buffer, byteOffset, length} = array; + var sharedBuffer = new SharedArrayBuffer(buffer.byteLength); + var sharedArray = Reflect_construct(baseConstructor, + [sharedBuffer, byteOffset, length], + new.target); + for (var i = 0; i < length; i++) + sharedArray[i] = array[i]; + assertEq(sharedArray.buffer, sharedBuffer); + return sharedArray; + } + } + + // 22.2.5.1 TypedArray.BYTES_PER_ELEMENT + Object.defineProperty(SharedTypedArray, "BYTES_PER_ELEMENT", + {__proto__: null, value: baseConstructor.BYTES_PER_ELEMENT}); + + // 22.2.6.1 TypedArray.prototype.BYTES_PER_ELEMENT + Object.defineProperty(SharedTypedArray.prototype, "BYTES_PER_ELEMENT", + {__proto__: null, value: baseConstructor.BYTES_PER_ELEMENT}); + + // Share the same name with the base constructor to avoid calling + // isSharedConstructor() in multiple places. + Object.defineProperty(SharedTypedArray, "name", + {__proto__: null, value: baseConstructor.name}); + + sharedConstructors.set(SharedTypedArray, baseConstructor); + + return SharedTypedArray; + } + + /** + * All TypedArray constructors for unshared memory. + */ + const typedArrayConstructors = Object.freeze([ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + ]); + + /** + * All TypedArray constructors for shared memory. + */ + const sharedTypedArrayConstructors = Object.freeze( + typeof SharedArrayBuffer === "function" + ? typedArrayConstructors.map(sharedConstructor) + : [] + ); + + /** + * All TypedArray constructors for unshared and shared memory. + */ + const anyTypedArrayConstructors = Object.freeze([ + ...typedArrayConstructors, ...sharedTypedArrayConstructors, + ]); + + /** + * Returns `true` if `constructor` is a TypedArray constructor for shared + * memory. + */ + function isSharedConstructor(constructor) { + return Reflect_apply(WeakMap_prototype_has, sharedConstructors, [constructor]); + } + + /** + * Returns `true` if `constructor` is a TypedArray constructor for shared + * or unshared memory, with an underlying element type of either Float32 or + * Float64. + */ + function isFloatConstructor(constructor) { + if (isSharedConstructor(constructor)) + constructor = Reflect_apply(WeakMap_prototype_get, sharedConstructors, [constructor]); + return constructor == Float32Array || constructor == Float64Array; + } + + global.typedArrayConstructors = typedArrayConstructors; + global.sharedTypedArrayConstructors = sharedTypedArrayConstructors; + global.anyTypedArrayConstructors = anyTypedArrayConstructors; + global.isSharedConstructor = isSharedConstructor; + global.isFloatConstructor = isFloatConstructor; +})(this); diff --git a/js/src/tests/non262/TypedArray/slice-bitwise.js b/js/src/tests/non262/TypedArray/slice-bitwise.js new file mode 100644 index 0000000000..ea0a670b18 --- /dev/null +++ b/js/src/tests/non262/TypedArray/slice-bitwise.js @@ -0,0 +1,153 @@ +// Copies bytes bit-wise if source and target type are the same. +// Only detectable when using floating point typed arrays. +const float32Constructors = anyTypedArrayConstructors.filter(isFloatConstructor) + .filter(c => c.BYTES_PER_ELEMENT === 4); +const float64Constructors = anyTypedArrayConstructors.filter(isFloatConstructor) + .filter(c => c.BYTES_PER_ELEMENT === 8); + +// Also test with cross-compartment typed arrays. +const otherGlobal = newGlobal(); +float32Constructors.push(otherGlobal.Float32Array); +float64Constructors.push(otherGlobal.Float64Array); + +function* p(xs, ys) { + for (let x of xs) { + for (let y of ys) { + yield [x, y]; + } + } +} + +const isLittleEndian = new Uint8Array(new Uint16Array([1]).buffer)[0] !== 0; + +function geti64(i32, i) { + return [i32[2 * i + isLittleEndian], i32[2 * i + !isLittleEndian]]; +} + +function seti64(i32, i, [hi, lo]) { + i32[i * 2 + isLittleEndian] = hi; + i32[i * 2 + !isLittleEndian] = lo; +} + +const NaNs = { + Float32: [ + 0x7F800001|0, // smallest SNaN + 0x7FBFFFFF|0, // largest SNaN + 0x7FC00000|0, // smallest QNaN + 0x7FFFFFFF|0, // largest QNaN + 0xFF800001|0, // smallest SNaN, sign-bit set + 0xFFBFFFFF|0, // largest SNaN, sign-bit set + 0xFFC00000|0, // smallest QNaN, sign-bit set + 0xFFFFFFFF|0, // largest QNaN, sign-bit set + ], + Float64: [ + [0x7FF00000|0, 0x00000001|0], // smallest SNaN + [0x7FF7FFFF|0, 0xFFFFFFFF|0], // largest SNaN + [0x7FF80000|0, 0x00000000|0], // smallest QNaN + [0x7FFFFFFF|0, 0xFFFFFFFF|0], // largest QNaN + [0xFFF00000|0, 0x00000001|0], // smallest SNaN, sign-bit set + [0xFFF7FFFF|0, 0xFFFFFFFF|0], // largest SNaN, sign-bit set + [0xFFF80000|0, 0x00000000|0], // smallest QNaN, sign-bit set + [0xFFFFFFFF|0, 0xFFFFFFFF|0], // largest QNaN, sign-bit set + ], +}; + +const cNaN = { + Float32: new Int32Array(new Float32Array([NaN]).buffer)[0], + Float64: geti64(new Int32Array(new Float64Array([NaN]).buffer), 0), +}; + +// Float32 -> Float32 +for (let [sourceConstructor, targetConstructor] of p(float32Constructors, float32Constructors)) { + let len = NaNs.Float32.length; + let f32 = new sourceConstructor(len); + let i32 = new Int32Array(f32.buffer); + f32.constructor = targetConstructor; + + for (let i = 0; i < len; ++i) { + i32[i] = NaNs.Float32[i]; + } + + let rf32 = f32.slice(0); + let ri32 = new Int32Array(rf32.buffer); + + assertEq(rf32.length, len); + assertEq(ri32.length, len); + + // Same bits. + for (let i = 0; i < len; ++i) { + assertEq(ri32[i], NaNs.Float32[i]); + } +} + +// Float32 -> Float64 +for (let [sourceConstructor, targetConstructor] of p(float32Constructors, float64Constructors)) { + let len = NaNs.Float32.length; + let f32 = new sourceConstructor(len); + let i32 = new Int32Array(f32.buffer); + f32.constructor = targetConstructor; + + for (let i = 0; i < len; ++i) { + i32[i] = NaNs.Float32[i]; + } + + let rf64 = f32.slice(0); + let ri32 = new Int32Array(rf64.buffer); + + assertEq(rf64.length, len); + assertEq(ri32.length, 2 * len); + + // NaN bits canonicalized. + for (let i = 0; i < len; ++i) { + assertEqArray(geti64(ri32, i), cNaN.Float64); + } +} + +// Float64 -> Float64 +for (let [sourceConstructor, targetConstructor] of p(float64Constructors, float64Constructors)) { + let len = NaNs.Float64.length; + let f64 = new sourceConstructor(len); + let i32 = new Int32Array(f64.buffer); + f64.constructor = targetConstructor; + + for (let i = 0; i < len; ++i) { + seti64(i32, i, NaNs.Float64[i]); + } + + let rf64 = f64.slice(0); + let ri32 = new Int32Array(rf64.buffer); + + assertEq(rf64.length, len); + assertEq(ri32.length, 2 * len); + + // Same bits. + for (let i = 0; i < len; ++i) { + assertEqArray(geti64(ri32, i), NaNs.Float64[i]); + } +} + +// Float64 -> Float32 +for (let [sourceConstructor, targetConstructor] of p(float64Constructors, float32Constructors)) { + let len = NaNs.Float64.length; + let f64 = new sourceConstructor(len); + let i32 = new Int32Array(f64.buffer); + f64.constructor = targetConstructor; + + for (let i = 0; i < len; ++i) { + seti64(i32, i, NaNs.Float64[i]); + } + + let rf32 = f64.slice(0); + let ri32 = new Int32Array(rf32.buffer); + + assertEq(rf32.length, len); + assertEq(ri32.length, len); + + // NaN bits canonicalized. + for (let i = 0; i < len; ++i) { + assertEqArray(ri32[i], cNaN.Float32); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/slice-conversion.js b/js/src/tests/non262/TypedArray/slice-conversion.js new file mode 100644 index 0000000000..bc44d798c5 --- /dev/null +++ b/js/src/tests/non262/TypedArray/slice-conversion.js @@ -0,0 +1,515 @@ +// Test conversions to different element types. +let tests = [ + /* Int8Array */ + { + from: Int8Array, + to: Int8Array, + values: [-129, -128, -127, -1, 0, 1, 127, 128, 129], + expected: [127, -128, -127, -1, 0, 1, 127, -128, -127], + }, + { + from: Int8Array, + to: Uint8Array, + values: [-129, -128, -127, -1, 0, 1, 127, 128, 129], + expected: [127, 128, 129, 255, 0, 1, 127, 128, 129], + }, + { + from: Int8Array, + to: Uint8ClampedArray, + values: [-129, -128, -127, -1, 0, 1, 127, 128, 129], + expected: [127, 0, 0, 0, 0, 1, 127, 0, 0], + }, + { + from: Int8Array, + to: Int16Array, + values: [-129, -128, -127, -1, 0, 1, 127, 128, 129], + expected: [127, -128, -127, -1, 0, 1, 127, -128, -127], + }, + { + from: Int8Array, + to: Uint16Array, + values: [-129, -128, -127, -1, 0, 1, 127, 128, 129], + expected: [127, 65408, 65409, 65535, 0, 1, 127, 65408, 65409], + }, + { + from: Int8Array, + to: Int32Array, + values: [-129, -128, -127, -1, 0, 1, 127, 128, 129], + expected: [127, -128, -127, -1, 0, 1, 127, -128, -127], + }, + { + from: Int8Array, + to: Uint32Array, + values: [-129, -128, -127, -1, 0, 1, 127, 128, 129], + expected: [127, 4294967168, 4294967169, 4294967295, 0, 1, 127, 4294967168, 4294967169], + }, + { + from: Int8Array, + to: Float32Array, + values: [-129, -128, -127, -1, 0, 1, 127, 128, 129], + expected: [127, -128, -127, -1, 0, 1, 127, -128, -127], + }, + { + from: Int8Array, + to: Float64Array, + values: [-129, -128, -127, -1, 0, 1, 127, 128, 129], + expected: [127, -128, -127, -1, 0, 1, 127, -128, -127], + }, + + /* Uint8Array */ + { + from: Uint8Array, + to: Int8Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, -128, -127, -2, -1, 0], + }, + { + from: Uint8Array, + to: Uint8Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 0], + }, + { + from: Uint8Array, + to: Uint8ClampedArray, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 0], + }, + { + from: Uint8Array, + to: Int16Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 0], + }, + { + from: Uint8Array, + to: Uint16Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 0], + }, + { + from: Uint8Array, + to: Int32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 0], + }, + { + from: Uint8Array, + to: Uint32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 0], + }, + { + from: Uint8Array, + to: Float32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 0], + }, + { + from: Uint8Array, + to: Float64Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 0], + }, + + /* Uint8ClampedArray */ + { + from: Uint8ClampedArray, + to: Int8Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, -128, -127, -2, -1, -1], + }, + { + from: Uint8ClampedArray, + to: Uint8Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 255], + }, + { + from: Uint8ClampedArray, + to: Uint8ClampedArray, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 255], + }, + { + from: Uint8ClampedArray, + to: Int16Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 255], + }, + { + from: Uint8ClampedArray, + to: Uint16Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 255], + }, + { + from: Uint8ClampedArray, + to: Int32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 255], + }, + { + from: Uint8ClampedArray, + to: Uint32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 255], + }, + { + from: Uint8ClampedArray, + to: Float32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 255], + }, + { + from: Uint8ClampedArray, + to: Float64Array, + values: [0, 1, 127, 128, 129, 254, 255, 256], + expected: [0, 1, 127, 128, 129, 254, 255, 255], + }, + + /* Int16Array */ + { + from: Int16Array, + to: Int8Array, + values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769], + expected: [-1, 0, 1, 127, -128, -127, -1, 0, 1, 127, -128, -127, -1, 0, 1], + }, + { + from: Int16Array, + to: Uint8Array, + values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769], + expected: [255, 0, 1, 127, 128, 129, 255, 0, 1, 127, 128, 129, 255, 0, 1], + }, + { + from: Int16Array, + to: Uint8ClampedArray, + values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769], + expected: [255, 0, 0, 0, 0, 0, 0, 0, 1, 127, 128, 129, 255, 0, 0], + }, + { + from: Int16Array, + to: Int16Array, + values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769], + expected: [32767, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, -32768, -32767], + }, + { + from: Int16Array, + to: Uint16Array, + values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769], + expected: [32767, 32768, 32769, 65407, 65408, 65409, 65535, 0, 1, 127, 128, 129, 32767, 32768, 32769], + }, + { + from: Int16Array, + to: Int32Array, + values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769], + expected: [32767, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, -32768, -32767], + }, + { + from: Int16Array, + to: Uint32Array, + values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769], + expected: [32767, 4294934528, 4294934529, 4294967167, 4294967168, 4294967169, 4294967295, 0, 1, 127, 128, 129, 32767, 4294934528, 4294934529], + }, + { + from: Int16Array, + to: Float32Array, + values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769], + expected: [32767, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, -32768, -32767], + }, + { + from: Int16Array, + to: Float64Array, + values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769], + expected: [32767, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, -32768, -32767], + }, + + /* Uint16Array */ + { + from: Uint16Array, + to: Int8Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536], + expected: [0, 1, 127, -128, -127, -2, -1, 0, -1, 0, 1, -2, -1, 0], + }, + { + from: Uint16Array, + to: Uint8Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536], + expected: [0, 1, 127, 128, 129, 254, 255, 0, 255, 0, 1, 254, 255, 0], + }, + { + from: Uint16Array, + to: Uint8ClampedArray, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536], + expected: [0, 1, 127, 128, 129, 254, 255, 255, 255, 255, 255, 255, 255, 0], + }, + { + from: Uint16Array, + to: Int16Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, -32768, -32767, -2, -1, 0], + }, + { + from: Uint16Array, + to: Uint16Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0], + }, + { + from: Uint16Array, + to: Int32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0], + }, + { + from: Uint16Array, + to: Uint32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0], + }, + { + from: Uint16Array, + to: Float32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0], + }, + { + from: Uint16Array, + to: Float64Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0], + }, + + /* Int32Array */ + { + from: Int32Array, + to: Int8Array, + values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649], + expected: [-1, 0, 1, -1, 0, 1, 127, -128, -127, -1, 0, 1, 127, -128, -127, -1, 0, 1, -1, 0, 1], + }, + { + from: Int32Array, + to: Uint8Array, + values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649], + expected: [255, 0, 1, 255, 0, 1, 127, 128, 129, 255, 0, 1, 127, 128, 129, 255, 0, 1, 255, 0, 1], + }, + { + from: Int32Array, + to: Uint8ClampedArray, + values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649], + expected: [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 127, 128, 129, 255, 255, 255, 255, 0, 0], + }, + { + from: Int32Array, + to: Int16Array, + values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649], + expected: [-1, 0, 1, 32767, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, -32768, -32767, -1, 0, 1], + }, + { + from: Int32Array, + to: Uint16Array, + values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649], + expected: [65535, 0, 1, 32767, 32768, 32769, 65407, 65408, 65409, 65535, 0, 1, 127, 128, 129, 32767, 32768, 32769, 65535, 0, 1], + }, + { + from: Int32Array, + to: Int32Array, + values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649], + expected: [2147483647, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, -2147483648, -2147483647], + }, + { + from: Int32Array, + to: Uint32Array, + values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649], + expected: [2147483647, 2147483648, 2147483649, 4294934527, 4294934528, 4294934529, 4294967167, 4294967168, 4294967169, 4294967295, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649], + }, + { + from: Int32Array, + to: Float32Array, + values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649], + expected: [2147483648, -2147483648, -2147483648, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483648, -2147483648, -2147483648], + }, + { + from: Int32Array, + to: Float64Array, + values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649], + expected: [2147483647, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, -2147483648, -2147483647], + }, + + /* Uint32Array */ + { + from: Uint32Array, + to: Int8Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296], + expected: [0, 1, 127, -128, -127, -2, -1, 0, -1, 0, 1, -2, -1, 0, -1, 0, 1, -2, -1, 0], + }, + { + from: Uint32Array, + to: Uint8Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296], + expected: [0, 1, 127, 128, 129, 254, 255, 0, 255, 0, 1, 254, 255, 0, 255, 0, 1, 254, 255, 0], + }, + { + from: Uint32Array, + to: Uint8ClampedArray, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296], + expected: [0, 1, 127, 128, 129, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0], + }, + { + from: Uint32Array, + to: Int16Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, -32768, -32767, -2, -1, 0, -1, 0, 1, -2, -1, 0], + }, + { + from: Uint32Array, + to: Uint16Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0, 65535, 0, 1, 65534, 65535, 0], + }, + { + from: Uint32Array, + to: Int32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, -2147483648, -2147483647, -2, -1, 0], + }, + { + from: Uint32Array, + to: Uint32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 0], + }, + { + from: Uint32Array, + to: Float32Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483648, 2147483648, 2147483648, 4294967296, 4294967296, 0], + }, + { + from: Uint32Array, + to: Float64Array, + values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296], + expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 0], + }, + + /* Float32Array */ + { + from: Float32Array, + to: Int8Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 0, 0, 0, -1, 0, 1, 127, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, -128, -127, -1, 0, 1, 0, 0, 0, 0, 0], + }, + { + from: Float32Array, + to: Uint8Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 0, 0, 0, 255, 0, 1, 127, 128, 129, 255, 255, 255, 255, 0, 0, 1, 1, 1, 1, 127, 128, 129, 255, 0, 1, 0, 0, 0, 0, 0], + }, + { + from: Float32Array, + to: Uint8ClampedArray, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 127, 128, 129, 255, 255, 255, 255, 255, 255, 255, 0], + }, + { + from: Float32Array, + to: Int16Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 0, 0, 0, 32767, -32768, -32767, -129, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, -32768, -32767, 0, 0, 0, 0, 0], + }, + { + from: Float32Array, + to: Uint16Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 0, 0, 0, 32767, 32768, 32769, 65407, 65408, 65409, 65535, 65535, 65535, 65535, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, 0, 0, 0, 0, 0], + }, + { + from: Float32Array, + to: Int32Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, -2147483648, -2147483648, -2147483648, -32769, -32768, -32767, -129, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, -2147483648, -2147483648, -2147483648, 0, 0], + }, + { + from: Float32Array, + to: Uint32Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 2147483648, 2147483648, 2147483648, 4294934527, 4294934528, 4294934529, 4294967167, 4294967168, 4294967169, 4294967295, 4294967295, 4294967295, 4294967295, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, 2147483648, 2147483648, 2147483648, 0, 0], + }, + { + from: Float32Array, + to: Float32Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [-Infinity, -2147483648, -2147483648, -2147483648, -32769, -32768, -32767, -129, -128, -127, -1.600000023841858, -1.5, -1.399999976158142, -1, -0, 0, 1, 1.399999976158142, 1.5, 1.600000023841858, 127, 128, 129, 32767, 32768, 32769, 2147483648, 2147483648, 2147483648, Infinity, NaN], + }, + { + from: Float32Array, + to: Float64Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [-Infinity, -2147483648, -2147483648, -2147483648, -32769, -32768, -32767, -129, -128, -127, -1.600000023841858, -1.5, -1.399999976158142, -1, -0, 0, 1, 1.399999976158142, 1.5, 1.600000023841858, 127, 128, 129, 32767, 32768, 32769, 2147483648, 2147483648, 2147483648, Infinity, NaN], + }, + + /* Float64Array */ + { + from: Float64Array, + to: Int8Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, -1, 0, 1, -1, 0, 1, 127, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, -128, -127, -1, 0, 1, -1, 0, 1, 0, 0], + }, + { + from: Float64Array, + to: Uint8Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 255, 0, 1, 255, 0, 1, 127, 128, 129, 255, 255, 255, 255, 0, 0, 1, 1, 1, 1, 127, 128, 129, 255, 0, 1, 255, 0, 1, 0, 0], + }, + { + from: Float64Array, + to: Uint8ClampedArray, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 127, 128, 129, 255, 255, 255, 255, 255, 255, 255, 0], + }, + { + from: Float64Array, + to: Int16Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, -1, 0, 1, 32767, -32768, -32767, -129, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, -32768, -32767, -1, 0, 1, 0, 0], + }, + { + from: Float64Array, + to: Uint16Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 65535, 0, 1, 32767, 32768, 32769, 65407, 65408, 65409, 65535, 65535, 65535, 65535, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, 65535, 0, 1, 0, 0], + }, + { + from: Float64Array, + to: Int32Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 2147483647, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, -2147483648, -2147483647, 0, 0], + }, + { + from: Float64Array, + to: Uint32Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [0, 2147483647, 2147483648, 2147483649, 4294934527, 4294934528, 4294934529, 4294967167, 4294967168, 4294967169, 4294967295, 4294967295, 4294967295, 4294967295, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, 0, 0], + }, + { + from: Float64Array, + to: Float32Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [-Infinity, -2147483648, -2147483648, -2147483648, -32769, -32768, -32767, -129, -128, -127, -1.600000023841858, -1.5, -1.399999976158142, -1, -0, 0, 1, 1.399999976158142, 1.5, 1.600000023841858, 127, 128, 129, 32767, 32768, 32769, 2147483648, 2147483648, 2147483648, Infinity, NaN], + }, + { + from: Float64Array, + to: Float64Array, + values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + expected: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN], + }, +]; + +for (let {from, to, values, expected} of tests) { + let ta = new from(values); + ta.constructor = to; + assertEqArray(ta.slice(0), expected, `${from.name} -> ${to.name}`); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/slice-detached.js b/js/src/tests/non262/TypedArray/slice-detached.js new file mode 100644 index 0000000000..be16653154 --- /dev/null +++ b/js/src/tests/non262/TypedArray/slice-detached.js @@ -0,0 +1,103 @@ +// Tests for detached ArrayBuffer checks in %TypedArray%.prototype.slice ( start, end ). + +function* createTypedArrays(lengths = [0, 1, 4, 4096]) { + // Test with eagerly created ArrayBuffer. + for (let length of lengths) { + let buffer = new ArrayBuffer(length * Int32Array.BYTES_PER_ELEMENT); + let typedArray = new Int32Array(buffer); + + yield {typedArray, length, buffer() { return buffer; }}; + } + + // Test with lazily created ArrayBuffer. + for (let length of lengths) { + let typedArray = new Int32Array(length); + + yield {typedArray, length, buffer() { return typedArray.buffer; }}; + } +} + +if (typeof detachArrayBuffer === "function") { + // ArrayBuffer is detached when entering slice(). + for (let {typedArray, buffer} of createTypedArrays()) { + detachArrayBuffer(buffer()); + assertThrowsInstanceOf(() => { + typedArray.slice(0); + }, TypeError, "ArrayBuffer is detached on function entry"); + } + + // ArrayBuffer is detached when computing ToInteger(start). + for (let {typedArray, length, buffer} of createTypedArrays()) { + let detached = false; + let start = { + valueOf() { + assertEq(detached, false); + detachArrayBuffer(buffer()); + assertEq(detached, false); + detached = true; + return 0; + } + }; + + // Doesn't throw an error when no bytes are copied. + if (length === 0) { + typedArray.slice(start); + } else { + assertThrowsInstanceOf(() => { + typedArray.slice(start); + }, TypeError, "ArrayBuffer is detached in ToInteger(start)"); + } + assertEq(detached, true, "detachArrayBuffer was called"); + } + + // ArrayBuffer is detached when computing ToInteger(end). + for (let {typedArray, length, buffer} of createTypedArrays()) { + let detached = false; + let end = { + valueOf() { + assertEq(detached, false); + detachArrayBuffer(buffer()); + assertEq(detached, false); + detached = true; + return length; + } + }; + + // Doesn't throw an error when no bytes are copied. + if (length === 0) { + typedArray.slice(0, end); + } else { + assertThrowsInstanceOf(() => { + typedArray.slice(0, end); + }, TypeError, "ArrayBuffer is detached in ToInteger(end)"); + } + assertEq(detached, true, "detachArrayBuffer was called"); + } + + // ArrayBuffer is detached in species constructor. + for (let {typedArray, length, buffer} of createTypedArrays()) { + let detached = false; + typedArray.constructor = { + [Symbol.species]: function(...args) { + assertEq(detached, false); + detachArrayBuffer(buffer()); + assertEq(detached, false); + detached = true; + return new Int32Array(...args); + } + }; + + // Doesn't throw an error when no bytes are copied. + if (length === 0) { + typedArray.slice(0); + } else { + assertThrowsInstanceOf(() => { + typedArray.slice(0); + }, TypeError, "ArrayBuffer is detached in TypedArraySpeciesCreate(...)"); + } + assertEq(detached, true, "detachArrayBuffer was called"); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/slice-memcpy.js b/js/src/tests/non262/TypedArray/slice-memcpy.js new file mode 100644 index 0000000000..a20010ed16 --- /dev/null +++ b/js/src/tests/non262/TypedArray/slice-memcpy.js @@ -0,0 +1,84 @@ +const otherGlobal = newGlobal(); + +// Create with new ArrayBuffer and offset. +for (var constructor of typedArrayConstructors) { + const elementSize = constructor.BYTES_PER_ELEMENT; + let ab = new constructor(10).map((v, i) => i).buffer; + let ta = new constructor(ab, 2 * elementSize, 6); + ta.constructor = { + [Symbol.species]: function(len) { + return new constructor(new ArrayBuffer((len + 4) * elementSize), 4 * elementSize); + } + }; + let tb = ta.slice(0); + + assertEqArray(ta, [2, 3, 4, 5, 6, 7]); + assertEqArray(tb, [2, 3, 4, 5, 6, 7]); +} + +// Source and target arrays use the same ArrayBuffer, target range before source range. +for (var constructor of typedArrayConstructors) { + const elementSize = constructor.BYTES_PER_ELEMENT; + let ab = new constructor(10).map((v, i) => i).buffer; + let ta = new constructor(ab, 2 * elementSize, 6); + ta.constructor = { + [Symbol.species]: function(len) { + return new constructor(ab, 0, len); + } + }; + let tb = ta.slice(0); + + assertEqArray(ta, [4, 5, 6, 7, 6, 7]); + assertEqArray(tb, [2, 3, 4, 5, 6, 7]); + assertEqArray(new constructor(ab), [2, 3, 4, 5, 6, 7, 6, 7, 8, 9]); +} + +// Source and target arrays use the same ArrayBuffer, target range after source range. +for (var constructor of typedArrayConstructors) { + const elementSize = constructor.BYTES_PER_ELEMENT; + let ab = new constructor(10).map((v, i) => i + 1).buffer; + let ta = new constructor(ab, 0, 6); + ta.constructor = { + [Symbol.species]: function(len) { + return new constructor(ab, 2 * elementSize, len); + } + }; + let tb = ta.slice(0); + + assertEqArray(ta, [1, 2, 1, 2, 1, 2]); + assertEqArray(tb, [1, 2, 1, 2, 1, 2]); + assertEqArray(new constructor(ab), [1, 2, 1, 2, 1, 2, 1, 2, 9, 10]); +} + +// Tricky: Same as above, but with SharedArrayBuffer and different compartments. +if (this.setSharedObject && this.SharedArrayBuffer) { + for (var constructor of typedArrayConstructors) { + const elementSize = constructor.BYTES_PER_ELEMENT; + let ts = new constructor(new SharedArrayBuffer(10 * elementSize)); + for (let i = 0; i < ts.length; i++) { + ts[i] = i + 1; + } + let ab = ts.buffer; + let ta = new constructor(ab, 0, 6); + ta.constructor = { + [Symbol.species]: function(len) { + setSharedObject(ab); + try { + return otherGlobal.eval(` + new ${constructor.name}(getSharedObject(), ${2 * elementSize}, ${len}); + `); + } finally { + setSharedObject(null); + } + } + }; + let tb = ta.slice(0); + + assertEqArray(ta, [1, 2, 1, 2, 1, 2]); + assertEqArray(tb, [1, 2, 1, 2, 1, 2]); + assertEqArray(new constructor(ab), [1, 2, 1, 2, 1, 2, 1, 2, 9, 10]); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/slice-species.js b/js/src/tests/non262/TypedArray/slice-species.js new file mode 100644 index 0000000000..8a03f2f6ee --- /dev/null +++ b/js/src/tests/non262/TypedArray/slice-species.js @@ -0,0 +1,49 @@ +for (var constructor of typedArrayConstructors) { + // Basic tests for our SpeciesConstructor implementation. + var undefConstructor = new constructor(2); + undefConstructor.constructor = undefined; + assertDeepEq(undefConstructor.slice(1), new constructor(1)); + + assertThrowsInstanceOf(() => { + var strConstructor = new constructor; + strConstructor.constructor = "not a constructor"; + strConstructor.slice(123); + }, TypeError, "Assert that we have an invalid constructor"); + + // If obj.constructor[@@species] is undefined or null then the default + // constructor is used. + var mathConstructor = new constructor(8); + mathConstructor.constructor = Math.sin; + assertDeepEq(mathConstructor.slice(4), new constructor(4)); + + var undefSpecies = new constructor(2); + undefSpecies.constructor = { [Symbol.species]: undefined }; + assertDeepEq(undefSpecies.slice(1), new constructor(1)); + + var nullSpecies = new constructor(2); + nullSpecies.constructor = { [Symbol.species]: null }; + assertDeepEq(nullSpecies.slice(1), new constructor(1)); + + // If obj.constructor[@@species] is different constructor, it should be + // used. + for (var constructor2 of typedArrayConstructors) { + var modifiedConstructor = new constructor(2); + modifiedConstructor.constructor = constructor2; + assertDeepEq(modifiedConstructor.slice(1), new constructor2(1)); + + var modifiedSpecies = new constructor(2); + modifiedSpecies.constructor = { [Symbol.species]: constructor2 }; + assertDeepEq(modifiedSpecies.slice(1), new constructor2(1)); + } + + // If obj.constructor[@@species] is neither undefined nor null, and it's + // not a constructor, TypeError should be thrown. + assertThrowsInstanceOf(() => { + var strSpecies = new constructor; + strSpecies.constructor = { [Symbol.species]: "not a constructor" }; + strSpecies.slice(123); + }, TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/slice-validation.js b/js/src/tests/non262/TypedArray/slice-validation.js new file mode 100644 index 0000000000..28d50e36f4 --- /dev/null +++ b/js/src/tests/non262/TypedArray/slice-validation.js @@ -0,0 +1,194 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.prototype.slice. + +const otherGlobal = newGlobal({newCompartment: true}); +const typedArrayLengths = [0, 1, 1024]; + +// Note: slice uses CallTypedArrayMethodIfWrapped, which results in throwing +// a TypeError from the wrong Realm if cross-compartment. The browser +// runner doesn't support the "newCompartment" option, so it can't create +// cross-compartment globals, which means it throws the error from the +// correct Realm. +const eitherGlobalTypeError = { + [Symbol.hasInstance](obj) { + return obj instanceof TypeError || obj instanceof otherGlobal.TypeError; + } +}; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.prototype.slice, + error: TypeError, + }); + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.prototype.slice, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].prototype.slice, + error: eitherGlobalTypeError, + }); + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + + // Passes when the length is zero. + if (length === 0) { + let result = method.call(ta, 0); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(ta, 0), error); + } + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, 0, 0); + assertEq(result.length, 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, 0); + assertEq(result.length, length + extraLength); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, 0, 0); + assertEq(result.length, extraLength); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/slice.js b/js/src/tests/non262/TypedArray/slice.js new file mode 100644 index 0000000000..0a5ce4c50b --- /dev/null +++ b/js/src/tests/non262/TypedArray/slice.js @@ -0,0 +1,45 @@ +for (var constructor of anyTypedArrayConstructors) { + assertDeepEq(constructor.prototype.slice.length, 2); + + assertDeepEq(new constructor().slice(0), new constructor()); + assertDeepEq(new constructor().slice(0, 4), new constructor()); + assertDeepEq(new constructor(10).slice(0, 2), new constructor(2)); + + assertDeepEq(new constructor([1, 2]).slice(1), new constructor([2])); + assertDeepEq(new constructor([1, 2]).slice(0), new constructor([1, 2])); + assertDeepEq(new constructor([1, 2, 3]).slice(-1), new constructor([3])); + assertDeepEq(new constructor([1, 2, 3, 4]).slice(-3, -1), new constructor([2, 3])); + assertDeepEq(new constructor([.1, .2]).slice(0), new constructor([.1, .2])); + + assertDeepEq(new constructor([1, 2]).slice(-3), new constructor([1, 2])); + assertDeepEq(new constructor([1, 2]).slice(0, -3), new constructor()); + assertDeepEq(new constructor([1, 2]).slice(4), new constructor()); + assertDeepEq(new constructor([1, 2]).slice(1, 5), new constructor([2])); + + // Called from other globals. + if (typeof newGlobal === "function") { + var slice = newGlobal()[constructor.name].prototype.slice; + assertDeepEq(slice.call(new constructor([3, 2, 1]), 1), + new constructor([2, 1])); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.slice.call(invalidReceiver, 0); + }, TypeError, "Assert that slice fails if this value is not a TypedArray"); + }); + + // Test that the length getter is never called. + Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).slice(2); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); + diff --git a/js/src/tests/non262/TypedArray/sort-negative-nan.js b/js/src/tests/non262/TypedArray/sort-negative-nan.js new file mode 100644 index 0000000000..cf9a67714b --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort-negative-nan.js @@ -0,0 +1,106 @@ +// Test with all floating point typed arrays. +const floatConstructors = anyTypedArrayConstructors.filter(isFloatConstructor); + +// Also test with cross-compartment wrapped typed arrays. +if (typeof newGlobal === "function") { + const otherGlobal = newGlobal(); + floatConstructors.push(otherGlobal.Float32Array); + floatConstructors.push(otherGlobal.Float64Array); +} + +function* prod(xs, ys) { + for (let x of xs) { + for (let y of ys) { + yield [x, y]; + } + } +} + +const isLittleEndian = new Uint8Array(new Uint16Array([1]).buffer)[0] !== 0; + +function seti32(i32, i, v) { + i32[i] = v; +} + +function seti64(i32, i, [hi, lo]) { + i32[i * 2 + isLittleEndian] = hi; + i32[i * 2 + !isLittleEndian] = lo; +} + +const setInt = { + Float32: seti32, + Float64: seti64, +}; + +const NaNs = { + Float32: [ + 0x7F800001|0, // smallest SNaN + 0x7FBFFFFF|0, // largest SNaN + 0x7FC00000|0, // smallest QNaN + 0x7FFFFFFF|0, // largest QNaN + 0xFF800001|0, // smallest SNaN, sign-bit set + 0xFFBFFFFF|0, // largest SNaN, sign-bit set + 0xFFC00000|0, // smallest QNaN, sign-bit set + 0xFFFFFFFF|0, // largest QNaN, sign-bit set + ], + Float64: [ + [0x7FF00000|0, 0x00000001|0], // smallest SNaN + [0x7FF7FFFF|0, 0xFFFFFFFF|0], // largest SNaN + [0x7FF80000|0, 0x00000000|0], // smallest QNaN + [0x7FFFFFFF|0, 0xFFFFFFFF|0], // largest QNaN + [0xFFF00000|0, 0x00000001|0], // smallest SNaN, sign-bit set + [0xFFF7FFFF|0, 0xFFFFFFFF|0], // largest SNaN, sign-bit set + [0xFFF80000|0, 0x00000000|0], // smallest QNaN, sign-bit set + [0xFFFFFFFF|0, 0xFFFFFFFF|0], // largest QNaN, sign-bit set + ], +}; + +// %TypedArray%.prototype.sort +const TypedArraySort = Int32Array.prototype.sort; + +// Test with small and large typed arrays. +const typedArrayLengths = [16, 4096]; + +for (const [TA, taLength] of prod(floatConstructors, typedArrayLengths)) { + let type = TA.name.slice(0, -"Array".length); + let nansLength = NaNs[type].length; + let fta = new TA(taLength); + let i32 = new Int32Array(fta.buffer); + + // Add NaNs in various representations at the start of the typed array. + for (let i = 0; i < nansLength; ++i) { + setInt[type](i32, i, NaNs[type][i]); + } + + // Also add two non-NaN values for testing. + fta[nansLength] = 123; + fta[nansLength + 1] = -456; + + // Sort the array and validate sort() sorted all elements correctly. + TypedArraySort.call(fta); + + // |-456| should be sorted to the start. + assertEq(fta[0], -456); + + // Followed by a bunch of zeros, + const zeroOffset = 1; + const zeroCount = taLength - nansLength - 2; + for (let i = 0; i < zeroCount; ++i) { + assertEq(fta[zeroOffset + i], 0, `At offset: ${zeroOffset + i}`); + } + + // and then |123|. + assertEq(fta[zeroOffset + zeroCount], 123); + + // And finally the NaNs. + const nanOffset = zeroCount + 2; + for (let i = 0; i < nansLength; ++i) { + // We don't assert a specific NaN value is present, because this is + // not required by the spec and we don't provide any guarantees NaN + // values are either unchanged or canonicalized in sort(). + assertEq(fta[nanOffset + i], NaN, `At offset: ${nanOffset + i}`); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort-non-function.js b/js/src/tests/non262/TypedArray/sort-non-function.js new file mode 100644 index 0000000000..bea810457a --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort-non-function.js @@ -0,0 +1,22 @@ +// %TypedArray%.prototype.sort throws if the comparator is neither undefined nor +// a callable object. + +// Use a zero length typed array, so we can provide any kind of callable object +// without worrying that the function is actually a valid comparator function. +const typedArray = new Int32Array(0); + +// Throws if the comparator is neither undefined nor callable. +for (let invalidComparator of [null, 0, true, Symbol(), {}, []]) { + assertThrowsInstanceOf(() => typedArray.sort(invalidComparator), TypeError); +} + +// Doesn't throw if the comparator is undefined or a callable object. +for (let validComparator of [undefined, () => {}, Math.max, class {}, new Proxy(function(){}, {})]) { + typedArray.sort(validComparator); +} + +// Also doesn't throw if no comparator was provided at all. +typedArray.sort(); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/sort_basics.js b/js/src/tests/non262/TypedArray/sort_basics.js new file mode 100644 index 0000000000..3338861507 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_basics.js @@ -0,0 +1,73 @@ +// Note: failed runs should include their "SEED" value in error messages, +// setting "const SEED" to that value will recreate the data from any such run. +const SEED = (Math.random() * 10) + 1; + +// Fill up an array buffer with random values and return it in raw form. +// 'size' is the desired length of the view we will place atop the buffer, +// 'width' is the bit-width of the view we plan on placing atop the buffer, +// and 'seed' is an initial value supplied to a pseudo-random number generator. +function genRandomArrayBuffer(size, width, seed) { + let buf = new ArrayBuffer((width / 8) * size); + let arr = new Uint8Array(buf); + let len = 0; + // We generate a random number, n, where 0 <= n <= 255 for every space + // available in our buffer. + for (let n of XorShiftGenerator(seed, buf.byteLength)) + arr[len++] = n; + return buf; +} + +// Because we can generate any possible combination of bits, some floating point +// entries will take on -Infinity, Infinity, and NaN values. This function ensures +// that a is <= b, where, like the default comparator, -Infinity < Infinity and +// every non-NaN < NaN. +function lte(a, b) { + if (isNaN(b)) + return true; + return a <= b; +} + +// A a >= b counterpart to the helper function above. +function gte(a, b) { + return lte(b, a); +} + +// A custom comparator. +function cmp(a, b) { + return lte(a, b) ? gte(a, b) ? 0 : -1 : 1; +} + +function SortTest(dataType, dataSource) { + let typedArray = new dataType(dataSource); + let originalValues = Array.from(typedArray); + + // Test the default comparator + typedArray.sort(); + + // Test against regular array sort + assertEqArray(Array.from(typedArray), Array.from(originalValues).sort(cmp), + `The array is not properly sorted! seed: ${SEED}`); + + // Another sanity check + for (let i=0; i < typedArray.length - 1; i++) + assertEq(lte(typedArray[i], typedArray[i + 1]), true, + `The array is not properly sorted! ${typedArray[i]} > ${typedArray[i + 1]}, seed: ${SEED}`) + + // Test custom comparators + typedArray.sort((x, y) => cmp(y, x)); + + // The array should be in reverse order + for (let i=typedArray.length - 2; i >= 0; i--) + assertEq(gte(typedArray[i], typedArray[i + 1]), true, + `The array is not properly sorted! ${typedArray[i]} < ${typedArray[i + 1]}, seed: ${SEED}`) +} + +for (let constructor of anyTypedArrayConstructors) { + for (let arrayLength of [512, 256, 16, 0]) { + let source = genRandomArrayBuffer(arrayLength, constructor.BYTES_PER_ELEMENT * 8, SEED); + SortTest(constructor, source); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_byteoffset.js b/js/src/tests/non262/TypedArray/sort_byteoffset.js new file mode 100644 index 0000000000..b4e262c450 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_byteoffset.js @@ -0,0 +1,30 @@ +// Ensure that when sorting TypedArrays we don't +// ignore byte offsets (bug 1290579). + +var sortFunctions = [Int32Array.prototype.sort]; + +// Also test with cross-compartment wrapped typed arrays. +if (typeof newGlobal === "function") { + var otherGlobal = newGlobal(); + sortFunctions.push(newGlobal().Int32Array.prototype.sort); +} + +// The bug manifests itself only with Float arrays, +// but checking everything here just for sanity. + +for (var ctor of anyTypedArrayConstructors) { + var ab = new ArrayBuffer(1025 * ctor.BYTES_PER_ELEMENT); + var ta = new ctor(ab, ctor.BYTES_PER_ELEMENT, 1024); + + // |testArray[0]| shouldn't be modified when sort() is called below. + var testArray = new ctor(ab, 0, 1); + testArray[0] = 1; + + for (var sortFn of sortFunctions) { + sortFn.call(ta); + assertEq(testArray[0], 1); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_comparators.js b/js/src/tests/non262/TypedArray/sort_comparators.js new file mode 100644 index 0000000000..ca190948fc --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_comparators.js @@ -0,0 +1,32 @@ +// Ensure that sorts finish even if a comparator adds items +// Note: the array is not expected to be properly sorted. +let outsideArray = new Int32Array([1, 99, 2]); +function addingComparator(x, y) { + if (x == 99 || y == 99) { + outsideArray[0] = 101; + outsideArray[outsideArray.length - 1] = 102; + } + return x - y; +} +outsideArray.sort(addingComparator); +assertEq(outsideArray.every(x => [1, 2, 99, 101, 102].includes(x)), true); + +// Ensure that sorts finish even if a comparator calls sort again +// Note: the array is not expected to be properly sorted. +outsideArray = new Int32Array([1, 99, 2]); +function recursiveComparator(x, y) { + outsideArray.sort(); + return x - y; +} +outsideArray.sort(recursiveComparator); +assertEq(outsideArray.every(x => outsideArray.includes(x)), true) + +// Ensure that NaN's returned from custom comparators behave as / are converted +// to +0s. +let nanComparatorData = [2112, 42, 1111, 34]; +let nanComparatorArray = new Int32Array(nanComparatorData); +nanComparatorArray.sort((x, y) => NaN); +assertEq(nanComparatorData.every(x => nanComparatorArray.includes(x)), true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_compare_nan.js b/js/src/tests/non262/TypedArray/sort_compare_nan.js new file mode 100644 index 0000000000..c9af7dff77 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_compare_nan.js @@ -0,0 +1,12 @@ +// Returning zero from the sort comparator... +let ta = new Int32Array([0, 1]).sort(() => 0); +assertEq(ta[0], 0); +assertEq(ta[1], 1); + +// ... should give the same result as returning NaN. +let tb = new Int32Array([0, 1]).sort(() => NaN); +assertEq(tb[0], 0); +assertEq(tb[1], 1); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_errors.js b/js/src/tests/non262/TypedArray/sort_errors.js new file mode 100644 index 0000000000..f3bdc05b20 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_errors.js @@ -0,0 +1,87 @@ +// Ensure that TypedArrays throw when attempting to sort a detached ArrayBuffer +if (typeof detachArrayBuffer === "function") { + assertThrowsInstanceOf(() => { + let buffer = new ArrayBuffer(32); + let array = new Int32Array(buffer); + detachArrayBuffer(buffer); + array.sort(); + }, TypeError); +} + +// Ensure detaching buffer in comparator doesn't throw an error. +if (typeof detachArrayBuffer === "function") { + let detached = false; + let ta = new Int32Array(3); + ta.sort(function(a, b) { + if (!detached) { + detached = true; + detachArrayBuffer(ta.buffer); + } + return a - b; + }); + assertEq(detached, true); +} + +// Ensure detachment check doesn't choke on wrapped typed array. +if (typeof newGlobal === "function") { + let ta = new Int32Array(3); + let otherGlobal = newGlobal(); + otherGlobal.Int32Array.prototype.sort.call(ta, function(a, b) { + return a - b; + }); +} + +// Ensure detaching buffer in comparator doesn't throw an error when the typed array is wrapped. +if (typeof newGlobal === "function" && typeof detachArrayBuffer === "function") { + let detached = false; + let ta = new Int32Array(3); + let otherGlobal = newGlobal(); + otherGlobal.Int32Array.prototype.sort.call(ta, function(a,b) { + if (!detached) { + detached = true; + detachArrayBuffer(ta.buffer); + } + return a - b; + }); + assertEq(detached, true); +} + +// Ensure that TypedArray.prototype.sort will not sort non-TypedArrays +assertThrowsInstanceOf(() => { + let array = [4, 3, 2, 1]; + Int32Array.prototype.sort.call(array); +}, TypeError); + +assertThrowsInstanceOf(() => { + Int32Array.prototype.sort.call({a: 1, b: 2}); +}, TypeError); + +assertThrowsInstanceOf(() => { + Int32Array.prototype.sort.call(Int32Array.prototype); +}, TypeError); + +assertThrowsInstanceOf(() => { + let buf = new ArrayBuffer(32); + Int32Array.prototype.sort.call(buf); +}, TypeError); + +// Ensure that comparator errors are propagataed +function badComparator(x, y) { + if (x == 99 && y == 99) + throw new TypeError; + return x - y; +} + +assertThrowsInstanceOf(() => { + let array = new Uint8Array([99, 99, 99, 99]); + array.sort(badComparator); +}, TypeError); + +assertThrowsInstanceOf(() => { + let array = new Uint8Array([1, 99, 2, 99]); + array.sort(badComparator); +}, TypeError); + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_globals.js b/js/src/tests/non262/TypedArray/sort_globals.js new file mode 100644 index 0000000000..6bab6a3913 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_globals.js @@ -0,0 +1,9 @@ +// TypedArray.prototype.sort should work across globals +let g2 = newGlobal(); +assertEqArray( + Int32Array.prototype.sort.call(new g2.Int32Array([3, 2, 1])), + new Int32Array([1, 2, 3]) +); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_modifications.js b/js/src/tests/non262/TypedArray/sort_modifications.js new file mode 100644 index 0000000000..2a6ed49eb7 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_modifications.js @@ -0,0 +1,73 @@ +const TAConstructors = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Uint8ClampedArray, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +// Use different size classes to catch any implementation-specific +// optimisations. +const sizes = [ + 4, 8, 64, 128, 1024 +]; + +function ToNumeric(TA) { + if (TA === BigInt64Array || TA === BigUint64Array) { + return BigInt; + } + return Number; +} + +function ascending(a, b) { + return a < b ? -1 : a > b ? 1 : 0; +} + +function descending(a, b) { + return -ascending(a, b); +} + +for (let TA of TAConstructors) { + let toNumeric = ToNumeric(TA); + for (let size of sizes) { + let sorted = new TA(size); + + // Fill with |1..size| and then sort to account for wrap-arounds. + for (let i = 0; i < size; ++i) { + sorted[i] = toNumeric(i + 1); + } + sorted.sort(); + + // Create a copy in descending order. + let ta = new TA(sorted); + ta.sort(descending); + + // Sort the copy in ascending order and on the first call reset all + // elements to zero. + let called = false; + ta.sort(function(a, b) { + if (!called) { + called = true; + ta.fill(toNumeric(0)); + } + return ascending(a, b); + }); + + // Ensure the comparator function was called. + assertEq(called, true); + + // All elements should be sorted correctly. No elements should be zero. + for (let i = 0; i < size; ++i) { + assertEq(ta[i], sorted[i], `${TA.name} at index ${i} for size ${size}`); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_modifications_concurrent.js b/js/src/tests/non262/TypedArray/sort_modifications_concurrent.js new file mode 100644 index 0000000000..99493df800 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_modifications_concurrent.js @@ -0,0 +1,145 @@ +// |reftest| skip-if(!xulRuntime.shell) + +if (helperThreadCount() === 0) { + if (typeof reportCompare === "function") + reportCompare(true, true); + quit(); +} + +const TAConstructors = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Uint8ClampedArray, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +// Use different size classes to catch any implementation-specific +// optimisations. +const sizes = [ + 4, 8, 64, 128, 1024 +]; + +function ToNumeric(TA) { + if (TA === BigInt64Array || TA === BigUint64Array) { + return BigInt; + } + return Number; +} + +function ToAtomicTA(TA) { + switch (TA) { + case Int8Array: + case Int16Array: + case Int32Array: + case Uint8Array: + case Uint16Array: + case Uint32Array: + case BigInt64Array: + case BigUint64Array: + return TA; + case Uint8ClampedArray: + return Uint8Array; + case Float32Array: + return Uint32Array; + case Float64Array: + return BigUint64Array; + } + throw new Error("Invalid typed array kind"); +} + +function ascending(a, b) { + return a < b ? -1 : a > b ? 1 : 0; +} + +function descending(a, b) { + return -ascending(a, b); +} + +for (let TA of TAConstructors) { + let toNumeric = ToNumeric(TA); + for (let size of sizes) { + let sorted = new TA(size); + + // Fill with |1..size| and then sort to account for wrap-arounds. + for (let i = 0; i < size; ++i) { + sorted[i] = toNumeric(i + 1); + } + sorted.sort(); + + let extra = Math.max(TA.BYTES_PER_ELEMENT, Int32Array.BYTES_PER_ELEMENT); + let buffer = new SharedArrayBuffer(size * TA.BYTES_PER_ELEMENT + extra); + let controller = new Int32Array(buffer, 0, 1); + + // Create a copy in descending order. + let ta = new TA(buffer, extra, size); + ta.set(sorted) + ta.sort(descending); + + // Worker code expects that the last element changes when sorted. + assertEq(ta[size - 1] === sorted[size - 1], false); + + setSharedObject(buffer); + + evalInWorker(` + const ToNumeric = ${ToNumeric}; + const ToAtomicTA = ${ToAtomicTA}; + const TA = ${TA.name}; + const AtomicTA = ToAtomicTA(TA); + + let size = ${size}; + let extra = ${extra}; + + let toNumeric = ToNumeric(AtomicTA); + let buffer = getSharedObject(); + let controller = new Int32Array(buffer, 0, 1); + let ta = new AtomicTA(buffer, extra, size); + + let value = Atomics.load(ta, size - 1); + + // Coordinate with main thread. + while (Atomics.notify(controller, 0, 1) < 1) ; + + // Wait until modification of the last element. + // + // Sorting writes in ascending indexed ordered, so when the last element + // was modified, we know that the sort operation has finished. + while (Atomics.load(ta, size - 1) === value) ; + + // Set all elements to zero. + ta.fill(toNumeric(0)); + + // Sleep for 50 ms. + const amount = 0.05; + + // Coordinate with main thread. + while (Atomics.notify(controller, 0, 1) < 1) { + sleep(amount); + } + `); + + // Wait until worker is set-up. + assertEq(Atomics.wait(controller, 0, 0), "ok"); + + // Sort the array in ascending order. + ta.sort(); + + // Wait until worker has finished. + assertEq(Atomics.wait(controller, 0, 0), "ok"); + + // All elements have been set to zero. + let zero = toNumeric(0); + for (let i = 0; i < size; ++i) { + assertEq(ta[i], zero, `${TA.name} at index ${i} for size ${size}`); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_modifications_concurrent_radixsort.js b/js/src/tests/non262/TypedArray/sort_modifications_concurrent_radixsort.js new file mode 100644 index 0000000000..c6de0f1ece --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_modifications_concurrent_radixsort.js @@ -0,0 +1,116 @@ +// |reftest| skip-if(!xulRuntime.shell) + +if (helperThreadCount() === 0) { + if (typeof reportCompare === "function") + reportCompare(true, true); + quit(); +} + +// TypedArray constructors which can use radix sort. +const TAConstructors = [ + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, +]; + +// Use a large enough size to ensure concurrent accesses can be detected. +const size = 0x4000; + +function ToAtomicTA(TA) { + switch (TA) { + case Int16Array: + case Int32Array: + case Uint16Array: + case Uint32Array: + return TA; + case Float32Array: + return Uint32Array; + } + throw new Error("Invalid typed array kind"); +} + +function ascending(a, b) { + return a < b ? -1 : a > b ? 1 : 0; +} + +function descending(a, b) { + return -ascending(a, b); +} + +for (let TA of TAConstructors) { + let sorted = new TA(size); + + // Fill with |1..size| and then sort to account for wrap-arounds. + for (let i = 0; i < size; ++i) { + sorted[i] = i + 1; + } + sorted.sort(); + + let extra = Math.max(TA.BYTES_PER_ELEMENT, Int32Array.BYTES_PER_ELEMENT); + let buffer = new SharedArrayBuffer(size * TA.BYTES_PER_ELEMENT + extra); + let controller = new Int32Array(buffer, 0, 1); + + // Create a copy in descending order. + let ta = new TA(buffer, extra, size); + ta.set(sorted) + ta.sort(descending); + + // Worker code expects that the last element changes when sorted. + assertEq(ta[size - 1] === sorted[size - 1], false); + + setSharedObject(buffer); + + evalInWorker(` + const ToAtomicTA = ${ToAtomicTA}; + const TA = ${TA.name}; + const AtomicTA = ToAtomicTA(TA); + + let size = ${size}; + let extra = ${extra}; + + let buffer = getSharedObject(); + let controller = new Int32Array(buffer, 0, 1); + let ta = new AtomicTA(buffer, extra, size); + + let value = Atomics.load(ta, size - 1); + + // Coordinate with main thread. + while (Atomics.notify(controller, 0, 1) < 1) ; + + // Wait until modification of the last element. + // + // Sorting writes in ascending indexed ordered, so when the last element + // was modified, we know that the sort operation has finished. + while (Atomics.load(ta, size - 1) === value) ; + + // Set all elements to zero. + ta.fill(0); + + // Sleep for 50 ms. + const amount = 0.05; + + // Coordinate with main thread. + while (Atomics.notify(controller, 0, 1) < 1) { + sleep(amount); + } + `); + + // Wait until worker is set-up. + assertEq(Atomics.wait(controller, 0, 0), "ok"); + + // Sort the array in ascending order. + ta.sort(); + + // Wait until worker has finished. + assertEq(Atomics.wait(controller, 0, 0), "ok"); + + // All elements have been set to zero. + for (let i = 0; i < size; ++i) { + assertEq(ta[i], 0, `${TA.name} at index ${i} for size ${size}`); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_small.js b/js/src/tests/non262/TypedArray/sort_small.js new file mode 100644 index 0000000000..22f9dc69a6 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_small.js @@ -0,0 +1,38 @@ +const testCases = { + // Pre-sorted test data, it's important that these arrays remain in ascending order. + [Int8Array.name]: [[-128, 127]], + [Int16Array.name]: [[-32768, -999, 1942, 32767]], + [Int32Array.name]: [[-2147483648, -320000, -244000, 2147483647]], + [Uint8Array.name]: [[255]], + [Uint16Array.name]: [[0, 65535, 65535]], + [Uint32Array.name]: [[0, 987632, 4294967295]], + [Uint8ClampedArray.name]: [[255]], + + // Test the behavior in the default comparator as described in 22.2.3.26. + // The spec boils down to, -0s come before +0s, and NaNs always come last. + // Float Arrays are used because all other types convert -0 and NaN to +0. + [Float32Array.name]: [ + [-2147483647, -2147483646.99, -0, 0, 2147483646.99, NaN], + [1/undefined, NaN, Number.NaN] + ], + [Float64Array.name]: [ + [-2147483646.99, -0, 0, 4147483646.99, NaN], + [1/undefined, NaN, Number.NaN] + ], +}; + +// Sort every possible permutation of an arrays +function sortAllPermutations(dataType, testData) { + let reference = new dataType(testData); + for (let permutation of Permutations(testData)) + assertDeepEq((new dataType(permutation)).sort(), reference); +} + +for (let constructor of sharedTypedArrayConstructors) { + for (let data of testCases[constructor.name]) { + sortAllPermutations(constructor, data); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_snans.js b/js/src/tests/non262/TypedArray/sort_snans.js new file mode 100644 index 0000000000..50f99b2205 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_snans.js @@ -0,0 +1,79 @@ +// Ensure that signaling NaN's don't cause problems while sorting + +function getNaNArray(length) { + let a = []; + for (let i = 0; i < length; i++) + a.push(NaN); + return a; +} + +// Test every skipNth value in some range n, where start <= n <= end +// and start/stop should be 32-bit integers with bit patterns that +// form Float32 NaNs. +function testFloat32NaNRanges(start, end) { + let skipN = 10e3; + + // sample the space of possible NaNs to save time + let sampleSize = Math.floor((end - start)/ skipN); + + let NaNArray = new Float32Array(getNaNArray(sampleSize)); + let buffer = new ArrayBuffer(4 * sampleSize); + let uintView = new Uint32Array(buffer); + let floatView = new Float32Array(buffer); + + uintView[0] = start; + for (let i = 1; i < sampleSize; i++) { + uintView[i] = uintView[0] + (i * skipN); + } + + floatView.sort(); + assertDeepEq(floatView, NaNArray); +} + +// Test every skipNth value in some range n, where start <= n <= end +// and startHi, startLow and endHi, endLow should be 32-bit integers which, +// when combined (Hi + Low), form Float64 NaNs. +function testFloat64NaNRanges(startHi, startLow, endHi, endLow) { + + // Swap on big endian platforms + if (new Uint32Array(new Uint8Array([1,2,3,4]).buffer)[0] === 0x01020304) { + [startHi, startLow] = [startLow, startHi]; + [endHi, endLow] = [endLow, endHi]; + } + + let skipN = 10e6; + + let sampleSizeHi = Math.floor((endHi - startHi)/skipN); + let sampleSizeLow = Math.floor((endLow - startLow)/skipN); + + let NaNArray = new Float64Array(getNaNArray(sampleSizeHi + sampleSizeLow)); + let buffer = new ArrayBuffer(8 * (sampleSizeHi + sampleSizeLow)); + let uintView = new Uint32Array(buffer); + let floatView = new Float64Array(buffer); + + // Fill in all of the low bits first. + for (let i = 0; i <= sampleSizeLow; i++) { + uintView[i * 2] = startLow + (i * skipN); + uintView[(i * 2) + 1] = startHi; + } + + // Then the high bits. + for (let i = sampleSizeLow; i <= sampleSizeLow + sampleSizeHi; i++) { + uintView[i * 2] = endLow; + uintView[(i * 2) + 1] = startHi + ((i - sampleSizeLow) * skipN); + } + + floatView.sort(); + assertDeepEq(floatView, NaNArray); +} + +// Float32 Signaling NaN ranges +testFloat32NaNRanges(0x7F800001, 0x7FBFFFFF); +testFloat32NaNRanges(0xFF800001, 0xFFBFFFFF); + +// Float64 Signaling NaN ranges +testFloat64NaNRanges(0x7FF00000, 0x00000001, 0x7FF7FFFF, 0xFFFFFFFF); +testFloat64NaNRanges(0xFFF00000, 0x00000001, 0xFFF7FFFF, 0xFFFFFFFF); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_sorted.js b/js/src/tests/non262/TypedArray/sort_sorted.js new file mode 100644 index 0000000000..cb55ec3399 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_sorted.js @@ -0,0 +1,30 @@ +function SortedAscending(length) { + var array = new Int32Array(length); + for (var i = 0; i < length; ++i) + array[i] = i + 1; + + array.sort((x, y) => x - y); + + for (var i = 0; i < length; ++i) + assertEq(i + 1, array[i], `Mismatch at index=${i}, length=${length}`); +} + +for (var i = 0; i < 256; ++i) + SortedAscending(i); + +function SortedDescending(length) { + var array = new Int32Array(length); + for (var i = 0; i < length; ++i) + array[i] = length - i; + + array.sort((x, y) => x - y); + + for (var i = 0; i < length; ++i) + assertEq(i + 1, array[i], `Mismatch at index=${i}, length=${length}`); +} + +for (var i = 0; i < 256; ++i) + SortedDescending(i); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sort_stable.js b/js/src/tests/non262/TypedArray/sort_stable.js new file mode 100644 index 0000000000..d945e774c1 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_stable.js @@ -0,0 +1,23 @@ +// Test with different lengths to cover the case when InsertionSort is resp. +// is not called. +for (let i = 2; i <= 10; ++i) { + let length = 2 ** i; + let ta = new Int8Array(length); + + ta[0] = 2; + ta[1] = 1; + ta[2] = 0; + + for (let i = 3; i < length; ++i) { + ta[i] = 4; + } + + ta.sort((a, b) => (a/4|0) - (b/4|0)); + + assertEq(ta[0], 2); + assertEq(ta[1], 1); + assertEq(ta[2], 0); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/sorting_buffer_access.js b/js/src/tests/non262/TypedArray/sorting_buffer_access.js new file mode 100644 index 0000000000..c31f4ca167 --- /dev/null +++ b/js/src/tests/non262/TypedArray/sorting_buffer_access.js @@ -0,0 +1,15 @@ +// Ensure that when sorting arrays of size greater than 128, which +// calls RadixSort under the hood, we don't access the 'buffer' +// property of the typed array directly. + + +// The buggy behavior in the RadixSort is only exposed when we use +// float arrays, but checking everything just to be sure. +for (var ctor of anyTypedArrayConstructors) { + var testArray = new ctor(1024); + Object.defineProperty(testArray, "buffer", { get() { throw new Error("FAIL: Buffer accessed directly"); } }); + testArray.sort(); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/subarray-species.js b/js/src/tests/non262/TypedArray/subarray-species.js new file mode 100644 index 0000000000..91c34e80cc --- /dev/null +++ b/js/src/tests/non262/TypedArray/subarray-species.js @@ -0,0 +1,63 @@ +function test(constructor, constructor2, from=[1, 2, 3, 4, 5], to=[3, 4], begin=2, end=4) { + var modifiedConstructor = new constructor(from); + modifiedConstructor.constructor = constructor2; + assertDeepEq(modifiedConstructor.subarray(begin, end), new constructor2(to)); + var modifiedSpecies = new constructor(from); + modifiedSpecies.constructor = { [Symbol.species]: constructor2 }; + assertDeepEq(modifiedSpecies.subarray(begin, end), new constructor2(to)); +} + +// same size, same sign + +test(Int8Array, Uint8Array); +test(Int8Array, Uint8ClampedArray); + +test(Uint8Array, Int8Array); +test(Uint8Array, Uint8ClampedArray); + +test(Uint8ClampedArray, Int8Array); +test(Uint8ClampedArray, Uint8Array); + +test(Int16Array, Uint16Array); +test(Uint16Array, Int16Array); + +test(Int32Array, Uint32Array); +test(Uint32Array, Int32Array); + +// same size, different sign + +test(Int8Array, Uint8Array, [-1, -2, -3, -4, -5], [0xFD, 0xFC]); +test(Int8Array, Uint8ClampedArray, [-1, -2, -3, -4, -5], [0xFD, 0xFC]); + +test(Uint8Array, Int8Array, [0xFF, 0xFE, 0xFD, 0xFC, 0xFB], [-3, -4]); +test(Uint8ClampedArray, Int8Array, [0xFF, 0xFE, 0xFD, 0xFC, 0xFB], [-3, -4]); + +test(Int16Array, Uint16Array, [-1, -2, -3, -4, -5], [0xFFFD, 0xFFFC]); +test(Uint16Array, Int16Array, [0xFFFF, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFB], [-3, -4]); + +test(Int32Array, Uint32Array, [-1, -2, -3, -4, -5], [0xFFFFFFFD, 0xFFFFFFFC]); +test(Uint32Array, Int32Array, [0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFC, 0xFFFFFFFB], [-3, -4]); + +// different size + +// To avoid handling endian, use ArrayBuffer as an argument. +var a = new Int8Array([0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, + 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x0F]); + +test(Uint8Array, Uint16Array, a.buffer, a.slice(2, 6).buffer); +test(Uint16Array, Uint8Array, a.buffer, a.slice(4, 6).buffer); + +test(Uint8Array, Uint32Array, a.buffer, a.slice(4, 12).buffer, 4, 6); +test(Uint32Array, Uint8Array, a.buffer, a.slice(8, 10).buffer); + +test(Uint16Array, Uint32Array, a.buffer, a.slice(4, 12).buffer); +test(Uint32Array, Uint16Array, a.buffer, a.slice(8, 12).buffer); + +test(Float32Array, Float64Array, a.buffer, a.slice(8, 24).buffer); +test(Float64Array, Float32Array, a.buffer, a.slice(16, 24).buffer); + +if (typeof reportCompare === "function") + reportCompare(true, true); + diff --git a/js/src/tests/non262/TypedArray/subarray-validation.js b/js/src/tests/non262/TypedArray/subarray-validation.js new file mode 100644 index 0000000000..4ad8c86095 --- /dev/null +++ b/js/src/tests/non262/TypedArray/subarray-validation.js @@ -0,0 +1,117 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.prototype.subarray. + +const otherGlobal = newGlobal({newCompartment: true}); +const typedArrayLengths = [0, 1, 1024]; + +// Note: subarray uses CallTypedArrayMethodIfWrapped, which results in throwing +// a TypeError from the wrong Realm if cross-compartment. The browser +// runner doesn't support the "newCompartment" option, so it can't create +// cross-compartment globals, which means it throws the error from the +// correct Realm. +const eitherGlobalTypeError = { + [Symbol.hasInstance](obj) { + return obj instanceof TypeError || obj instanceof otherGlobal.TypeError; + } +}; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.prototype.subarray, + error: TypeError, + }); + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.prototype.subarray, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].prototype.subarray, + error: eitherGlobalTypeError, + }); + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + // Note: TypedArray |a| is (currently) created in this global, not + // |otherGlobal|, because a typed array and its buffer must + // use the same compartment. + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/subarray.js b/js/src/tests/non262/TypedArray/subarray.js new file mode 100644 index 0000000000..a7bfab0efd --- /dev/null +++ b/js/src/tests/non262/TypedArray/subarray.js @@ -0,0 +1,29 @@ +// Bug 1291003 +if (typeof detachArrayBuffer === "function") { + for (let constructor of typedArrayConstructors) { + const elementSize = constructor.BYTES_PER_ELEMENT; + + let targetOffset; + let buffer = new ArrayBuffer(2 * elementSize); + let typedArray = new constructor(buffer, 1 * elementSize, 1); + typedArray.constructor = { + [Symbol.species]: function(ab, offset, length) { + targetOffset = offset; + return new constructor(1); + } + }; + + let beginIndex = { + valueOf() { + detachArrayBuffer(buffer); + return 0; + } + }; + typedArray.subarray(beginIndex); + + assertEq(targetOffset, 1 * elementSize); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/test-integrity-level-detached.js b/js/src/tests/non262/TypedArray/test-integrity-level-detached.js new file mode 100644 index 0000000000..68f772de61 --- /dev/null +++ b/js/src/tests/non262/TypedArray/test-integrity-level-detached.js @@ -0,0 +1,104 @@ +const EMPTY = 0; +const INLINE_STORAGE = 10; +const NON_INLINE_STORAGE = 1024; + +class DetachedInt32Array extends Int32Array { + constructor(...args) { + super(...args); + detachArrayBuffer(this.buffer); + } +} + +function throwsTypeError(fn) { + try { + fn(); + } catch (e) { + assertEq(e instanceof TypeError, true); + return true; + } + return false; +} + +// Non-standard: Accessing elements of detached array buffers should throw, but +// this is currently not implemented. +const ACCESS_ON_DETACHED_ARRAY_BUFFER_THROWS = (() => { + let ta = new DetachedInt32Array(10); + let throws = throwsTypeError(() => ta[0]); + // Ensure [[Get]] and [[GetOwnProperty]] return consistent results. + assertEq(throwsTypeError(() => Object.getOwnPropertyDescriptor(ta, 0)), throws); + return throws; +})(); + +function maybeThrowOnDetached(fn, returnValue) { + if (ACCESS_ON_DETACHED_ARRAY_BUFFER_THROWS) { + assertThrowsInstanceOf(fn, TypeError); + return returnValue; + } + return fn(); +} + +// Empty typed arrays can be sealed. +{ + let ta = new DetachedInt32Array(EMPTY); + Object.seal(ta); + + assertEq(Object.isExtensible(ta), false); + assertEq(Object.isSealed(ta), true); + assertEq(Object.isFrozen(ta), true); +} + +// Non-empty typed arrays can be sealed, but calling TestIntegrityLevel will +// throw on detached typed arrays. +for (let length of [INLINE_STORAGE, NON_INLINE_STORAGE]) { + let ta = new DetachedInt32Array(length); + Object.seal(ta); + + assertEq(Object.isExtensible(ta), false); + assertEq(maybeThrowOnDetached(() => Object.isSealed(ta), true), true); + assertEq(maybeThrowOnDetached(() => Object.isFrozen(ta), true), true); +} + +// Empty typed arrays can be frozen. +{ + let ta = new DetachedInt32Array(EMPTY); + Object.freeze(ta); + + assertEq(Object.isExtensible(ta), false); + assertEq(Object.isSealed(ta), true); + assertEq(Object.isFrozen(ta), true); +} + +// Non-empty typed arrays cannot be frozen. +for (let length of [INLINE_STORAGE, NON_INLINE_STORAGE]) { + let ta = new DetachedInt32Array(length); + maybeThrowOnDetached(() => Object.freeze(ta)); + + assertEq(Object.isExtensible(ta), false); + assertEq(maybeThrowOnDetached(() => Object.isSealed(ta), true), true); + assertEq(maybeThrowOnDetached(() => Object.isFrozen(ta), true), true); +} + +// Non-extensible empty typed arrays are sealed and frozen. +{ + let ta = new DetachedInt32Array(EMPTY); + Object.preventExtensions(ta); + + assertEq(Object.isExtensible(ta), false); + assertEq(Object.isSealed(ta), true); + assertEq(Object.isFrozen(ta), true); +} + +// Calling TestIntegrityLevel will throw on detached typed arrays with non-zero +// length. +for (let length of [INLINE_STORAGE, NON_INLINE_STORAGE]) { + let ta = new DetachedInt32Array(length); + Object.preventExtensions(ta); + + assertEq(Object.isExtensible(ta), false); + assertEq(maybeThrowOnDetached(() => Object.isSealed(ta), true), true); + assertEq(maybeThrowOnDetached(() => Object.isFrozen(ta), true), true); +} + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/test-integrity-level.js b/js/src/tests/non262/TypedArray/test-integrity-level.js new file mode 100644 index 0000000000..c382b4967c --- /dev/null +++ b/js/src/tests/non262/TypedArray/test-integrity-level.js @@ -0,0 +1,67 @@ +const EMPTY = 0; +const INLINE_STORAGE = 10; +const NON_INLINE_STORAGE = 1024; + +// Empty typed arrays can be sealed. +{ + let ta = new Int32Array(EMPTY); + Object.seal(ta); + + assertEq(Object.isExtensible(ta), false); + assertEq(Object.isSealed(ta), true); + assertEq(Object.isFrozen(ta), true); +} + +// Non-empty typed arrays cannot be sealed. +for (let length of [INLINE_STORAGE, NON_INLINE_STORAGE]) { + let ta = new Int32Array(length); + assertThrowsInstanceOf(() => Object.seal(ta), TypeError); + + assertEq(Object.isExtensible(ta), false); + assertEq(Object.isSealed(ta), false); + assertEq(Object.isFrozen(ta), false); +} + +// Empty typed arrays can be frozen. +{ + let ta = new Int32Array(EMPTY); + Object.freeze(ta); + + assertEq(Object.isExtensible(ta), false); + assertEq(Object.isSealed(ta), true); + assertEq(Object.isFrozen(ta), true); +} + +// Non-empty typed arrays cannot be frozen. +for (let length of [INLINE_STORAGE, NON_INLINE_STORAGE]) { + let ta = new Int32Array(length); + assertThrowsInstanceOf(() => Object.freeze(ta), TypeError); + + assertEq(Object.isExtensible(ta), false); + assertEq(Object.isSealed(ta), false); + assertEq(Object.isFrozen(ta), false); +} + +// Non-extensible empty typed arrays are sealed and frozen. +{ + let ta = new Int32Array(EMPTY); + Object.preventExtensions(ta); + + assertEq(Object.isExtensible(ta), false); + assertEq(Object.isSealed(ta), true); + assertEq(Object.isFrozen(ta), true); +} + +// Non-extensible non-empty typed arrays are neither sealed nor frozen. +for (let length of [INLINE_STORAGE, NON_INLINE_STORAGE]) { + let ta = new Int32Array(length); + Object.preventExtensions(ta); + + assertEq(Object.isExtensible(ta), false); + assertEq(Object.isSealed(ta), false); + assertEq(Object.isFrozen(ta), false); +} + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/toLocaleString-detached.js b/js/src/tests/non262/TypedArray/toLocaleString-detached.js new file mode 100644 index 0000000000..fe95ae689c --- /dev/null +++ b/js/src/tests/non262/TypedArray/toLocaleString-detached.js @@ -0,0 +1,38 @@ +if (typeof detachArrayBuffer === "function") { + const originalNumberToLocaleString = Number.prototype.toLocaleString; + + // Throws if array buffer is detached. + for (let constructor of typedArrayConstructors) { + let typedArray = new constructor(42); + detachArrayBuffer(typedArray.buffer); + assertThrowsInstanceOf(() => typedArray.toLocaleString(), TypeError); + } + + // Throws a TypeError if detached in Number.prototype.toLocaleString. + for (let constructor of typedArrayConstructors) { + Number.prototype.toLocaleString = function() { + "use strict"; + if (!detached) { + detachArrayBuffer(typedArray.buffer); + detached = true; + } + return this; + }; + + // No error for single element arrays. + let detached = false; + let typedArray = new constructor(1); + assertEq(typedArray.toLocaleString(), "0"); + assertEq(detached, true); + + // TypeError if more than one element is present. + detached = false; + typedArray = new constructor(2); + assertThrowsInstanceOf(() => typedArray.toLocaleString(), TypeError); + assertEq(detached, true); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/toLocaleString-nointl.js b/js/src/tests/non262/TypedArray/toLocaleString-nointl.js new file mode 100644 index 0000000000..f2870efbe6 --- /dev/null +++ b/js/src/tests/non262/TypedArray/toLocaleString-nointl.js @@ -0,0 +1,40 @@ +if (typeof Intl !== "object") { + const localeSep = [,,].toLocaleString(); + + const originalNumberToLocaleString = Number.prototype.toLocaleString; + + // Ensure no arguments are passed to the array elements. + for (let constructor of anyTypedArrayConstructors) { + Number.prototype.toLocaleString = function() { + assertEq(arguments.length, 0); + return "pass"; + }; + + // Single element case. + assertEq(new constructor(1).toLocaleString(), "pass"); + + // More than one element. + assertEq(new constructor(2).toLocaleString(), "pass" + localeSep + "pass"); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; + + // Ensure no arguments are passed to the array elements even if supplied. + for (let constructor of anyTypedArrayConstructors) { + Number.prototype.toLocaleString = function() { + assertEq(arguments.length, 0); + return "pass"; + }; + let locales = {}; + let options = {}; + + // Single element case. + assertEq(new constructor(1).toLocaleString(locales, options), "pass"); + + // More than one element. + assertEq(new constructor(2).toLocaleString(locales, options), "pass" + localeSep + "pass"); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/toLocaleString.js b/js/src/tests/non262/TypedArray/toLocaleString.js new file mode 100644 index 0000000000..78049ec225 --- /dev/null +++ b/js/src/tests/non262/TypedArray/toLocaleString.js @@ -0,0 +1,80 @@ +const TypedArrayPrototype = Object.getPrototypeOf(Int8Array.prototype); + +// %TypedArrayPrototype% has an own "toLocaleString" function property. +assertEq(TypedArrayPrototype.hasOwnProperty("toLocaleString"), true); +assertEq(typeof TypedArrayPrototype.toLocaleString, "function"); + +// The initial value of %TypedArrayPrototype%.toLocaleString is not Array.prototype.toLocaleString. +assertEq(TypedArrayPrototype.toLocaleString === Array.prototype.toLocaleString, false); + +// The concrete TypedArray prototypes do not have an own "toLocaleString" property. +assertEq(anyTypedArrayConstructors.every(c => !c.hasOwnProperty("toLocaleString")), true); + +assertDeepEq(Object.getOwnPropertyDescriptor(TypedArrayPrototype, "toLocaleString"), { + value: TypedArrayPrototype.toLocaleString, + writable: true, + enumerable: false, + configurable: true, +}); + +assertEq(TypedArrayPrototype.toLocaleString.name, "toLocaleString"); +assertEq(TypedArrayPrototype.toLocaleString.length, 0); + +// It's not a generic method. +assertThrowsInstanceOf(() => TypedArrayPrototype.toLocaleString.call(), TypeError); +for (let invalid of [void 0, null, {}, [], function(){}, true, 0, "", Symbol()]) { + assertThrowsInstanceOf(() => TypedArrayPrototype.toLocaleString.call(invalid), TypeError); +} + +const localeOne = 1..toLocaleString(), + localeTwo = 2..toLocaleString(), + localeSep = [,,].toLocaleString(); + +for (let constructor of anyTypedArrayConstructors) { + assertEq(new constructor([]).toLocaleString(), ""); + assertEq(new constructor([1]).toLocaleString(), localeOne); + assertEq(new constructor([1, 2]).toLocaleString(), localeOne + localeSep + localeTwo); +} + +const originalNumberToLocaleString = Number.prototype.toLocaleString; + +// Calls Number.prototype.toLocaleString on each element. +for (let constructor of anyTypedArrayConstructors) { + Number.prototype.toLocaleString = function() { + "use strict"; + + // Ensure this-value is not boxed. + assertEq(typeof this, "number"); + + // Test ToString is applied. + return { + valueOf: () => { + throw new Error("valueOf called"); + }, + toString: () => { + return this + 10; + } + }; + }; + let typedArray = new constructor([1, 2]); + assertEq(typedArray.toLocaleString(), "11" + localeSep + "12"); +} +Number.prototype.toLocaleString = originalNumberToLocaleString; + +// Calls Number.prototype.toLocaleString from the current Realm. +const otherGlobal = newGlobal(); +for (let constructor of anyTypedArrayConstructors) { + Number.prototype.toLocaleString = function() { + "use strict"; + called = true; + return this; + }; + let typedArray = new otherGlobal[constructor.name]([1]); + let called = false; + assertEq(TypedArrayPrototype.toLocaleString.call(typedArray), "1"); + assertEq(called, true); +} +Number.prototype.toLocaleString = originalNumberToLocaleString; + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/toReversed-detached.js b/js/src/tests/non262/TypedArray/toReversed-detached.js new file mode 100644 index 0000000000..ae2a784180 --- /dev/null +++ b/js/src/tests/non262/TypedArray/toReversed-detached.js @@ -0,0 +1,10 @@ +// |reftest| shell-option(--enable-change-array-by-copy) skip-if(!Int32Array.prototype.toReversed) + +var ta = new Int32Array([3, 2, 1]); + +detachArrayBuffer(ta.buffer); + +assertThrowsInstanceOf(() => ta.toReversed(), TypeError); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/toSorted-detached.js b/js/src/tests/non262/TypedArray/toSorted-detached.js new file mode 100644 index 0000000000..a4c5ba2252 --- /dev/null +++ b/js/src/tests/non262/TypedArray/toSorted-detached.js @@ -0,0 +1,10 @@ +// |reftest| shell-option(--enable-change-array-by-copy) skip-if(!Int32Array.prototype.toSorted) + +var ta = new Int32Array([3, 2, 1]); + +detachArrayBuffer(ta.buffer); + +assertThrowsInstanceOf(() => ta.toSorted(), TypeError); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/toString.js b/js/src/tests/non262/TypedArray/toString.js new file mode 100644 index 0000000000..0879b8b31d --- /dev/null +++ b/js/src/tests/non262/TypedArray/toString.js @@ -0,0 +1,69 @@ +const TypedArrayPrototype = Object.getPrototypeOf(Int8Array.prototype); + +// %TypedArrayPrototype% has an own "toString" property. +assertEq(TypedArrayPrototype.hasOwnProperty("toString"), true); + +// The initial value of %TypedArrayPrototype%.toString is Array.prototype.toString. +assertEq(TypedArrayPrototype.toString, Array.prototype.toString); + +// The concrete TypedArray prototypes do not have an own "toString" property. +assertEq(anyTypedArrayConstructors.every(c => !c.hasOwnProperty("toString")), true); + +assertDeepEq(Object.getOwnPropertyDescriptor(TypedArrayPrototype, "toString"), { + value: TypedArrayPrototype.toString, + writable: true, + enumerable: false, + configurable: true, +}); + +for (let constructor of anyTypedArrayConstructors) { + assertEq(new constructor([]).toString(), ""); + assertEq(new constructor([1]).toString(), "1"); + assertEq(new constructor([1, 2]).toString(), "1,2"); +} + +const testCases = { + [Int8Array.name]: { + array: [-1, 2, -3, 4, NaN], + expected: "-1,2,-3,4,0", + }, + [Int16Array.name]: { + array: [-1, 2, -3, 4, NaN], + expected: "-1,2,-3,4,0", + }, + [Int32Array.name]: { + array: [-1, 2, -3, 4, NaN], + expected: "-1,2,-3,4,0", + }, + [Uint8Array.name]: { + array: [255, 2, 3, 4, NaN], + expected: "255,2,3,4,0", + }, + [Uint16Array.name]: { + array: [-1, 2, 3, 4, NaN], + expected: "65535,2,3,4,0", + }, + [Uint32Array.name]: { + array: [-1, 2, 3, 4, NaN], + expected: "4294967295,2,3,4,0", + }, + [Uint8ClampedArray.name]: { + array: [255, 256, 2, 3, 4, NaN], + expected: "255,255,2,3,4,0", + }, + [Float32Array.name]: { + array: [-0, 0, 0.5, -0.5, NaN, Infinity, -Infinity], + expected: "0,0,0.5,-0.5,NaN,Infinity,-Infinity", + }, + [Float64Array.name]: { + array: [-0, 0, 0.5, -0.5, NaN, Infinity, -Infinity], + expected: "0,0,0.5,-0.5,NaN,Infinity,-Infinity", + }, +}; +for (let constructor of anyTypedArrayConstructors) { + let {array, expected} = testCases[constructor.name]; + assertEq(new constructor(array).toString(), expected); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/toStringTag-cross-compartment.js b/js/src/tests/non262/TypedArray/toStringTag-cross-compartment.js new file mode 100644 index 0000000000..8f0875721c --- /dev/null +++ b/js/src/tests/non262/TypedArray/toStringTag-cross-compartment.js @@ -0,0 +1,12 @@ +const TypedArrayPrototype = Object.getPrototypeOf(Int8Array.prototype); +const {get: toStringTag} = Object.getOwnPropertyDescriptor(TypedArrayPrototype, Symbol.toStringTag); + +const otherGlobal = newGlobal(); + +for (let constructor of anyTypedArrayConstructors) { + let ta = new otherGlobal[constructor.name](0); + assertEq(toStringTag.call(ta), constructor.name); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/uint8clamped-constructor.js b/js/src/tests/non262/TypedArray/uint8clamped-constructor.js new file mode 100644 index 0000000000..3eefe70f1f --- /dev/null +++ b/js/src/tests/non262/TypedArray/uint8clamped-constructor.js @@ -0,0 +1,9 @@ +for (var v of [-300, 255.6, 300, 3.5, -3.9]) { + var a = new Uint8ClampedArray([v]); + var b = new Uint8ClampedArray(1); + b[0] = v; + + assertEq(a[0], b[0]); +} + +reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/values.js b/js/src/tests/non262/TypedArray/values.js new file mode 100644 index 0000000000..bf486bfa0a --- /dev/null +++ b/js/src/tests/non262/TypedArray/values.js @@ -0,0 +1,37 @@ +for (var constructor of anyTypedArrayConstructors) { + assertEq(constructor.prototype.values.length, 0); + assertEq(constructor.prototype.values.name, "values"); + assertEq(constructor.prototype.values, constructor.prototype[Symbol.iterator]); + + assertDeepEq([...new constructor(0).values()], []); + assertDeepEq([...new constructor(1).values()], [0]); + assertDeepEq([...new constructor(2).values()], [0, 0]); + assertDeepEq([...new constructor([15]).values()], [15]); + + var arr = new constructor([1, 2, 3]); + var iterator = arr.values(); + assertDeepEq(iterator.next(), {value: 1, done: false}); + assertDeepEq(iterator.next(), {value: 2, done: false}); + assertDeepEq(iterator.next(), {value: 3, done: false}); + assertDeepEq(iterator.next(), {value: undefined, done: true}); + + // Called from other globals. + if (typeof newGlobal === "function") { + var values = newGlobal()[constructor.name].prototype.values; + assertDeepEq([...values.call(new constructor([42, 36]))], [42, 36]); + arr = new (newGlobal()[constructor.name])([42, 36]); + assertEq([...constructor.prototype.values.call(arr)].toString(), "42,36"); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.values.call(invalidReceiver); + }, TypeError, "Assert that values fails if this value is not a TypedArray"); + }); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/TypedArray/with-detached.js b/js/src/tests/non262/TypedArray/with-detached.js new file mode 100644 index 0000000000..76711a0ee5 --- /dev/null +++ b/js/src/tests/non262/TypedArray/with-detached.js @@ -0,0 +1,10 @@ +// |reftest| shell-option(--enable-change-array-by-copy) skip-if(!Int32Array.prototype.with) + +var ta = new Int32Array([3, 2, 1]); + +detachArrayBuffer(ta.buffer); + +assertThrowsInstanceOf(() => ta.with(0, 0), TypeError); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/with.js b/js/src/tests/non262/TypedArray/with.js new file mode 100644 index 0000000000..c240f7084e --- /dev/null +++ b/js/src/tests/non262/TypedArray/with.js @@ -0,0 +1,34 @@ +// |reftest| shell-option(--enable-change-array-by-copy) skip-if(!Int32Array.prototype.with) + +class Err {} + +const indices = [ + -Infinity, -10, -0.5, -0, 0, 0.5, 10, Infinity, NaN +]; + +let value = { + valueOf() { + throw new Err; + } +}; + +let ta = new Int32Array(5); +for (let index of indices) { + assertThrowsInstanceOf(() => ta.with(index, value), Err); +} + +for (let index of indices) { + let ta = new Int32Array(5); + + let value = { + valueOf() { + detachArrayBuffer(ta.buffer); + return 0; + } + }; + + assertThrowsInstanceOf(() => ta.with(index, value), RangeError); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/TypedArray/write-out-of-bounds-tonumber.js b/js/src/tests/non262/TypedArray/write-out-of-bounds-tonumber.js new file mode 100644 index 0000000000..48a538eab7 --- /dev/null +++ b/js/src/tests/non262/TypedArray/write-out-of-bounds-tonumber.js @@ -0,0 +1,60 @@ +// ToNumber(value) is executed for OOB writes when using a direct assignment. +function plainSet() { + var callCount = 0; + var value = { + valueOf() { + callCount++; + return 1; + } + }; + + var N = 100; + var ta = new Int32Array(0); + for (var i = 0; i < N; ++i) + ta[0] = value + + assertEq(callCount, N); +} +for (var i = 0; i < 2; ++i) plainSet(); + +// ToNumber(value) is executed for OOB writes when using Reflect.set(...). +function reflectSet() { + var callCount = 0; + var value = { + valueOf() { + callCount++; + return 1; + } + }; + + var N = 100; + var ta = new Int32Array(0); + for (var i = 0; i < N; ++i) + assertEq(Reflect.set(ta, 0, value), true); + + assertEq(callCount, N); +} +for (var i = 0; i < 2; ++i) reflectSet(); + +// ToNumber(value) is not executed for OOB writes when using Reflect.defineProperty(...). +function defineProp() { + var callCount = 0; + var value = { + valueOf() { + callCount++; + return 1; + } + }; + var desc = {value, writable: true, enumerable: true, configurable: true}; + + var N = 100; + var ta = new Int32Array(0); + for (var i = 0; i < N; ++i) + assertEq(Reflect.defineProperty(ta, 0, desc), false); + + assertEq(callCount, 0); +} +for (var i = 0; i < 2; ++i) defineProp(); + +if (typeof reportCompare === "function") + reportCompare(true, true); |