diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/jit-test/tests/for-of | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
92 files changed, 1506 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/for-of/arguments-1.js b/js/src/jit-test/tests/for-of/arguments-1.js new file mode 100644 index 0000000000..fddb047e1e --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-1.js @@ -0,0 +1,21 @@ +// for-of can iterate arguments objects. + +load(libdir + "iteration.js"); + +// Arguments objects do not have a .@@iterator() method by default. +// Install one on Object.prototype. +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; + +var s; +function test() { + for (var v of arguments) + s += v; +} + +s = ''; +test(); +assertEq(s, ''); + +s = ''; +test('x', 'y'); +assertEq(s, 'xy'); diff --git a/js/src/jit-test/tests/for-of/arguments-2.js b/js/src/jit-test/tests/for-of/arguments-2.js new file mode 100644 index 0000000000..65abd6ddc5 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-2.js @@ -0,0 +1,14 @@ +// for-of can iterate arguments objects after returning. + +load(libdir + "iteration.js"); + +function f() { + return arguments; +} + +var s = ''; +var args = f('a', 'b', 'c'); +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; +for (var v of args) + s += v; +assertEq(s, 'abc'); diff --git a/js/src/jit-test/tests/for-of/arguments-3.js b/js/src/jit-test/tests/for-of/arguments-3.js new file mode 100644 index 0000000000..7c5b0488f6 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-3.js @@ -0,0 +1,20 @@ +// for-of can iterate strict arguments objects. + +load(libdir + "iteration.js"); + +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; + +var s; +function test() { + "use strict"; + for (var v of arguments) + s += v; +} + +s = ''; +test(); +assertEq(s, ''); + +s = ''; +test('a', 'b'); +assertEq(s, 'ab'); diff --git a/js/src/jit-test/tests/for-of/arguments-4.js b/js/src/jit-test/tests/for-of/arguments-4.js new file mode 100644 index 0000000000..79b836d084 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-4.js @@ -0,0 +1,19 @@ +// for-of can iterate arguments objects for other active frames. + +load(libdir + "iteration.js"); + +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; + +var s; +function g(obj) { + for (var v of obj) + s += v; +} + +function f() { + g(arguments); +} + +s = ''; +f(1, 2, 3); +assertEq(s, '123'); diff --git a/js/src/jit-test/tests/for-of/arguments-5.js b/js/src/jit-test/tests/for-of/arguments-5.js new file mode 100644 index 0000000000..37272d3a78 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-5.js @@ -0,0 +1,20 @@ +// for-of can iterate strict arguments objects in non-strict code. + +load(libdir + "iteration.js"); + +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; + +var s; +function g(obj) { + for (var v of obj) + s += v; +} + +function f() { + "use strict"; + g(arguments); +} + +s = ''; +f(1, 2, 3); +assertEq(s, '123'); diff --git a/js/src/jit-test/tests/for-of/arguments-6.js b/js/src/jit-test/tests/for-of/arguments-6.js new file mode 100644 index 0000000000..b8129561d2 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-6.js @@ -0,0 +1,16 @@ +// Changing arguments.length affects a for-of loop iterating over arguments. + +load(libdir + "iteration.js"); + +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; + +var s; +function f() { + arguments.length = 2; + for (var v of arguments) + s += v; +} + +s = ''; +f('a', 'b', 'c', 'd', 'e'); +assertEq(s, 'ab'); diff --git a/js/src/jit-test/tests/for-of/arguments-7.js b/js/src/jit-test/tests/for-of/arguments-7.js new file mode 100644 index 0000000000..cf58c2ba50 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-7.js @@ -0,0 +1,17 @@ +// Changing arguments.length during a for-of loop iterating over arguments affects the loop. + +load(libdir + "iteration.js"); + +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; + +var s; +function f() { + for (var v of arguments) { + s += v; + arguments.length--; + } +} + +s = ''; +f('a', 'b', 'c', 'd', 'e'); +assertEq(s, 'abc'); diff --git a/js/src/jit-test/tests/for-of/array-holes-1.js b/js/src/jit-test/tests/for-of/array-holes-1.js new file mode 100644 index 0000000000..3d86122192 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-1.js @@ -0,0 +1,10 @@ +// for-of does not skip Array holes. The value at a hole is undefined. + +var a = [0, , 2, 3]; +var log = []; +for (var x of a) { + assertEq(x, a[log.length]); + log.push(x); +} +assertEq(log[1], undefined); +assertEq(log.join(), "0,,2,3"); diff --git a/js/src/jit-test/tests/for-of/array-holes-2.js b/js/src/jit-test/tests/for-of/array-holes-2.js new file mode 100644 index 0000000000..cc37aedf2a --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-2.js @@ -0,0 +1,8 @@ +// for-of consults Object.prototype when it encounters a hole. + +Object.prototype[1] = 'peek'; +var log = []; +for (var x of [0, , 2, 3]) + log.push(x); +assertEq(log[1], 'peek'); +assertEq(log.join(), "0,peek,2,3"); diff --git a/js/src/jit-test/tests/for-of/array-holes-3.js b/js/src/jit-test/tests/for-of/array-holes-3.js new file mode 100644 index 0000000000..9b4d087bde --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-3.js @@ -0,0 +1,8 @@ +// for-of consults Array.prototype when it encounters a hole. + +Array.prototype[1] = 'peek'; +var log = []; +for (var x of [0, , 2, 3]) + log.push(x); +assertEq(log[1], 'peek'); +assertEq(log.join(), "0,peek,2,3"); diff --git a/js/src/jit-test/tests/for-of/array-holes-4.js b/js/src/jit-test/tests/for-of/array-holes-4.js new file mode 100644 index 0000000000..e4a8770260 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-4.js @@ -0,0 +1,13 @@ +// for-of on an Array consults the prototype chain when it encounters a hole. + +load(libdir + "iteration.js"); + +var m = {1: 'peek'}; +var a = [0, , 2, 3]; +a.__proto__ = m; +var log = []; +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; +for (var x of a) + log.push(x); +assertEq(log[1], 'peek'); +assertEq(log.join(), "0,peek,2,3"); diff --git a/js/src/jit-test/tests/for-of/array-holes-5.js b/js/src/jit-test/tests/for-of/array-holes-5.js new file mode 100644 index 0000000000..3f70ed123b --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-5.js @@ -0,0 +1,6 @@ +// for-of does not skip trailing holes; the value is undefined. + +var log = ""; +for (var x of [1, 2, 3,,]) + log += x; +assertEq(log, "123undefined"); diff --git a/js/src/jit-test/tests/for-of/array-holes-6.js b/js/src/jit-test/tests/for-of/array-holes-6.js new file mode 100644 index 0000000000..2b4703fbce --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-6.js @@ -0,0 +1,8 @@ +// for-of visits each hole in an array full of holes. + +var n = 0; +for (var x of Array(5)) { + assertEq(x, undefined); + n++; +} +assertEq(n, 5); diff --git a/js/src/jit-test/tests/for-of/array-holes-slow.js b/js/src/jit-test/tests/for-of/array-holes-slow.js new file mode 100644 index 0000000000..7478ee6ac4 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-slow.js @@ -0,0 +1,13 @@ +// for-of on a slow Array consults the prototype chain when it encounters a hole. + +var a = [0, , , 3]; +a.slow = true; +Object.prototype[1] = 'peek1'; +Array.prototype[2] = 'peek2'; + +var log = []; +for (var x of a) + log.push(x); +assertEq(log[1], 'peek1'); +assertEq(log[2], 'peek2'); +assertEq(log.join(), "0,peek1,peek2,3"); diff --git a/js/src/jit-test/tests/for-of/array-iterator-changing.js b/js/src/jit-test/tests/for-of/array-iterator-changing.js new file mode 100644 index 0000000000..43a7aa40bf --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-iterator-changing.js @@ -0,0 +1,29 @@ +// Array iterators reflect changes to elements of the underlying array. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var arr = [0, 1, 2]; +var it = arr[Symbol.iterator](); +arr[0] = 1000; +arr[2] = 2000; +assertIteratorNext(it, 1000); +assertIteratorNext(it, 1); +assertIteratorNext(it, 2000); +assertIteratorDone(it, undefined); + +// test that .keys() and .entries() have the same behaviour + +arr = [0, 1, 2]; +var ki = arr.keys(); +var ei = arr.entries(); +arr[0] = 1000; +arr[2] = 2000; +assertIteratorNext(ki, 0); +assertIteratorNext(ei, [0, 1000]); +assertIteratorNext(ki, 1); +assertIteratorNext(ei, [1, 1]); +assertIteratorNext(ki, 2); +assertIteratorNext(ei, [2, 2000]); +assertIteratorDone(ki, undefined); +assertIteratorDone(ei, undefined); diff --git a/js/src/jit-test/tests/for-of/array-iterator-empty.js b/js/src/jit-test/tests/for-of/array-iterator-empty.js new file mode 100644 index 0000000000..5b448befae --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-iterator-empty.js @@ -0,0 +1,11 @@ +// Array.keys() and .entries() on an empty array produce empty iterators + +var arr = []; +var ki = arr.keys(), ei = arr.entries(); +var p = Object.getPrototypeOf(ki); +assertEq(Object.getPrototypeOf(ei), p); + +for (let k of ki) + throw "FAIL"; +for (let [k, v] of ei) + throw "FAIL"; diff --git a/js/src/jit-test/tests/for-of/array-iterator-generic.js b/js/src/jit-test/tests/for-of/array-iterator-generic.js new file mode 100644 index 0000000000..8e19bf8370 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-iterator-generic.js @@ -0,0 +1,33 @@ +// Array.prototype.iterator is generic. +// That is, it can be applied to arraylike objects and strings, not just arrays. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +function test(obj) { + var it = Array.prototype[Symbol.iterator].call(obj); + var ki = Array.prototype.keys.call(obj); + var ei = Array.prototype.entries.call(obj); + for (var i = 0; i < (obj.length >>> 0); i++) { + assertIteratorNext(it, obj[i]); + assertIteratorNext(ki, i); + assertIteratorNext(ei, [i, obj[i]]); + } + assertIteratorDone(it, undefined); + assertIteratorDone(ki, undefined); + assertIteratorDone(ei, undefined); +} + +test({length: 0}); +test({length: 0, 0: 'x', 1: 'y'}); +test({length: 2, 0: 'x', 1: 'y'}); +test(Object.create(['x', 'y', 'z'])); +test(Object.create({length: 2, 0: 'x', 1: 'y'})); +test(""); +test("ponies"); + +// Perverse length values. +test({length: "011", 9: 9, 10: 10, 11: 11}); +test({length: -0}); +test({length: 2.7, 0: 0, 1: 1, 2: 2}); +test({length: {valueOf: function () { return 3; }}, 0: 0, 1: 1, 2: 2}); diff --git a/js/src/jit-test/tests/for-of/array-iterator-growing-1.js b/js/src/jit-test/tests/for-of/array-iterator-growing-1.js new file mode 100644 index 0000000000..73637a4da4 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-iterator-growing-1.js @@ -0,0 +1,26 @@ +// If an array with an active iterator is lengthened, the iterator visits the new elements. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var arr = [0, 1]; +var it = arr[Symbol.iterator](); +var ki = arr.keys(); +var ei = arr.entries(); +assertIteratorNext(it, 0); +assertIteratorNext(ki, 0); +assertIteratorNext(ei, [0, 0]); +assertIteratorNext(it, 1); +assertIteratorNext(ki, 1); +assertIteratorNext(ei, [1, 1]); +arr[2] = 2; +arr.length = 4; +assertIteratorNext(it, 2); +assertIteratorNext(ki, 2); +assertIteratorNext(ei, [2, 2]); +assertIteratorNext(it, undefined); +assertIteratorNext(ki, 3); +assertIteratorNext(ei, [3, undefined]); +assertIteratorDone(it, undefined); +assertIteratorDone(ki, undefined); +assertIteratorDone(ei, undefined); diff --git a/js/src/jit-test/tests/for-of/array-iterator-keys-entries.js b/js/src/jit-test/tests/for-of/array-iterator-keys-entries.js new file mode 100644 index 0000000000..e3315488da --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-iterator-keys-entries.js @@ -0,0 +1,16 @@ +// array.keys() returns an iterator over the index of elements +// and array.entries() returns an iterator that yields pairs [index, element]. + +load(libdir + "iteration.js"); + +var data = [1, 2, 3, "abc"]; + +var ki = data.keys(); +for (var i = 0; i < data.length; i++) + assertIteratorResult(ki.next(), i, false); +assertIteratorDone(ki, undefined); + +var ei = data.entries(); +for (var i = 0; i < data.length; i++) + assertIteratorResult(ei.next(), [i, data[i]], false); +assertIteratorDone(ei, undefined); diff --git a/js/src/jit-test/tests/for-of/array-iterator-null.js b/js/src/jit-test/tests/for-of/array-iterator-null.js new file mode 100644 index 0000000000..6382f0701e --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-iterator-null.js @@ -0,0 +1,11 @@ +// Array.prototype.iterator applied to undefined or null throws directly. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +for (var v of [undefined, null]) { + // ES6 draft 2013-09-05 section 22.1.5.1. + assertThrowsInstanceOf(function () { Array.prototype[Symbol.iterator].call(v); }, TypeError); + assertThrowsInstanceOf(function () { Array.prototype.keys.call(v); }, TypeError); + assertThrowsInstanceOf(function () { Array.prototype.entries.call(v); }, TypeError); +} diff --git a/js/src/jit-test/tests/for-of/array-iterator-proxy.js b/js/src/jit-test/tests/for-of/array-iterator-proxy.js new file mode 100644 index 0000000000..f410b5fc66 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-iterator-proxy.js @@ -0,0 +1,47 @@ +// An array iterator for a proxy calls the traps in a predictable order. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var s = ''; + +var handler = { + get: function (recipient, name) { + if (name == 'length') { + s += 'L'; + return 2; + } else { + s += name; + return name; + } + } +}; + +var it = Array.prototype[Symbol.iterator].call(new Proxy([0, 1], handler)); + +assertIteratorNext(it, "0"); +s += ' '; +assertIteratorNext(it, "1"); +s += ' '; +assertIteratorDone(it, undefined); +assertEq(s, "L0 L1 L"); + +s = ''; +var ki = Array.prototype.keys.call(new Proxy([0, 1], handler)); + +assertIteratorNext(ki, 0); +s += ' '; +assertIteratorNext(ki, 1); +s += ' '; +assertIteratorDone(ki, undefined); +assertEq(s, "L L L"); + +s = ''; +var ei = Array.prototype.entries.call(new Proxy([0, 1], handler)); + +assertIteratorNext(ei, [0, "0"]); +s += ' '; +assertIteratorNext(ei, [1, "1"]); +s += ' '; +assertIteratorDone(ei, undefined); +assertEq(s, "L0 L1 L"); diff --git a/js/src/jit-test/tests/for-of/array-iterator-shrinking.js b/js/src/jit-test/tests/for-of/array-iterator-shrinking.js new file mode 100644 index 0000000000..3963129dde --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-iterator-shrinking.js @@ -0,0 +1,20 @@ +// If an array is truncated to the left of an iterator it, it.next() returns { done: true }. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var arr = [0, 1, 2]; +var it = arr[Symbol.iterator](); +var ki = arr.keys(); +var ei = arr.entries(); + +assertIteratorNext(it, 0); +assertIteratorNext(it, 1); +assertIteratorNext(ki, 0); +assertIteratorNext(ki, 1); +assertIteratorNext(ei, [0, 0]); +assertIteratorNext(ei, [1, 1]); +arr.length = 1; +assertIteratorDone(it, undefined); +assertIteratorDone(ki, undefined); +assertIteratorDone(ei, undefined); diff --git a/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js b/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js new file mode 100644 index 0000000000..6757acb3e5 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js @@ -0,0 +1,17 @@ +// Superficial tests of the Array.prototype[@@iterator] builtin function and its workalikes. + +load(libdir + "iteration.js"); + +var constructors = [Array, String, Uint8Array, Uint8ClampedArray]; +for (var c of constructors) { + assertEq(c.prototype[Symbol.iterator].length, 0); + + var loc = (c === Array || c === String) + ? c.prototype + : Object.getPrototypeOf(c.prototype); + + var desc = Object.getOwnPropertyDescriptor(loc, Symbol.iterator); + assertEq(desc.configurable, true); + assertEq(desc.enumerable, false); + assertEq(desc.writable, true); +} diff --git a/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js b/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js new file mode 100644 index 0000000000..a58ebef379 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js @@ -0,0 +1,31 @@ +// Superficial tests for iterators created by Array.prototype.iterator + +load(libdir + "iteration.js"); + +var proto = Object.getPrototypeOf([][Symbol.iterator]()); +var iterProto = Object.getPrototypeOf(proto); +proto = Object.getPrototypeOf([].keys()); +assertEq(Object.getPrototypeOf(proto), iterProto); +proto = Object.getPrototypeOf([].entries()); +assertEq(Object.getPrototypeOf(proto), iterProto); + +function check(it) { + assertEq(typeof it, 'object'); + assertEq(Object.getPrototypeOf(it), proto); + assertEq(Object.getOwnPropertyNames(it).length, 0); + assertEq(it[Symbol.iterator](), it); + + // for-in enumerates the iterator's properties. + it.x = 0; + var s = ''; + for (var p in it) + s += p + '.'; + assertEq(s, 'x.'); +} + +check([][Symbol.iterator]()); +check(Array.prototype[Symbol.iterator].call({})); +check([].keys()); +check(Array.prototype.keys.call({})); +check([].entries()); +check(Array.prototype.entries.call({})); diff --git a/js/src/jit-test/tests/for-of/array-jit.js b/js/src/jit-test/tests/for-of/array-jit.js new file mode 100644 index 0000000000..3f139cc2bd --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-jit.js @@ -0,0 +1,6 @@ +var arr = [1, 2, 3]; +var y = 0; +for (var i = 0; i < 10; i++) + for (var x of arr) + y += x; +assertEq(y, 60); diff --git a/js/src/jit-test/tests/for-of/array-prototype.js b/js/src/jit-test/tests/for-of/array-prototype.js new file mode 100644 index 0000000000..074420e871 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-prototype.js @@ -0,0 +1,11 @@ +// for-of works on Array.prototype. + +var v; +for (v of Array.prototype) + throw "FAIL"; + +var s = ''; +Array.prototype.push('a', 'b'); +for (v of Array.prototype) + s += v; +assertEq(s, 'ab'); diff --git a/js/src/jit-test/tests/for-of/arrays-1.js b/js/src/jit-test/tests/for-of/arrays-1.js new file mode 100644 index 0000000000..b75a78d191 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-1.js @@ -0,0 +1,7 @@ +// for-of works on arrays. + +var a = ['a', 'b', 'c']; +var s = ''; +for (var v of a) + s += v; +assertEq(s, 'abc'); diff --git a/js/src/jit-test/tests/for-of/arrays-2.js b/js/src/jit-test/tests/for-of/arrays-2.js new file mode 100644 index 0000000000..df20f92232 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-2.js @@ -0,0 +1,10 @@ +// A for-of loop over an array does not take a snapshot of the array elements. +// Instead, each time the loop needs an element from the array, it gets its current value. + +var a = [3, 5, 5, 4, 0, 5]; +var s = ''; +for (var i of a) { + s += i; + a[i] = 'X'; +} +assertEq(s, '355X0X'); diff --git a/js/src/jit-test/tests/for-of/arrays-3.js b/js/src/jit-test/tests/for-of/arrays-3.js new file mode 100644 index 0000000000..b0dedbcc10 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-3.js @@ -0,0 +1,9 @@ +// Two for-of loops on the same array get distinct iterators. + +var a = [1, 2, 3]; +var s = ''; +for (var x of a) + s += x; +for (var y of a) + s += y; +assertEq(s, '123123'); diff --git a/js/src/jit-test/tests/for-of/arrays-4.js b/js/src/jit-test/tests/for-of/arrays-4.js new file mode 100644 index 0000000000..8567f2b60e --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-4.js @@ -0,0 +1,8 @@ +// Nested for-of loops on the same array get distinct iterators. + +var a = [1, 2, 3]; +var s = ''; +for (var x of a) + for (var y of a) + s += '' + x + y + ','; +assertEq(s, '11,12,13,21,22,23,31,32,33,'); diff --git a/js/src/jit-test/tests/for-of/arrays-5.js b/js/src/jit-test/tests/for-of/arrays-5.js new file mode 100644 index 0000000000..266eb1d948 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-5.js @@ -0,0 +1,4 @@ +// for-of on an empty array does nothing. + +for (var x of []) + fail(); diff --git a/js/src/jit-test/tests/for-of/arrays-growing-1.js b/js/src/jit-test/tests/for-of/arrays-growing-1.js new file mode 100644 index 0000000000..4b5c19e1ea --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-growing-1.js @@ -0,0 +1,10 @@ +// A for-of loop over an array continues to the end if the array grows during iteration. + +var a = [0, 1, 1, 0, 1, 0, 0]; +var s = ''; +for (var v of a) { + s += v; + if (v === 1) + a.push(2); +} +assertEq(s, '0110100222'); diff --git a/js/src/jit-test/tests/for-of/arrays-growing-2.js b/js/src/jit-test/tests/for-of/arrays-growing-2.js new file mode 100644 index 0000000000..e9867714ce --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-growing-2.js @@ -0,0 +1,10 @@ +// Inserting values in an array does not change the next index of an existing iterator. + +var a = [1, 2, 3, 4]; +var s = ''; +for (var v of a) { + s += v; + if (s.length === 2) + a.unshift('x'); +} +assertEq(s, '12234'); diff --git a/js/src/jit-test/tests/for-of/arrays-shrinking-1.js b/js/src/jit-test/tests/for-of/arrays-shrinking-1.js new file mode 100644 index 0000000000..b7210c29c0 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-shrinking-1.js @@ -0,0 +1,13 @@ +// A for-of loop over an array stops at the new end of the array if it shrinks during iteration. + +function ispal(arr) { + for (var v of arr) { + if (v !== arr.pop()) + return false; + } + return true; +} + +assertEq(ispal([1, 2, 3, 4, 3, 2, 1]), true); +assertEq(ispal([1, 2, 3, 3, 2, 1]), true); +assertEq(ispal([1, 2, 3, 4, 2, 1]), false); diff --git a/js/src/jit-test/tests/for-of/arrays-shrinking-2.js b/js/src/jit-test/tests/for-of/arrays-shrinking-2.js new file mode 100644 index 0000000000..86c77303d0 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-shrinking-2.js @@ -0,0 +1,9 @@ +// Using shift to cut values out of an array does not change the next index of an existing iterator. + +var a = [1, 2, 3, 4, 5, 6, 7, 8]; +var s = ''; +for (var v of a) { + s += v; + a.shift(); +} +assertEq(s, '1357'); diff --git a/js/src/jit-test/tests/for-of/arrays-slow-1.js b/js/src/jit-test/tests/for-of/arrays-slow-1.js new file mode 100644 index 0000000000..cab5a49a09 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-slow-1.js @@ -0,0 +1,8 @@ +// for-of works on slow arrays. + +var a = ['a', 'b', 'c']; +a.slow = true; +var log = ''; +for (var x of a) + log += x; +assertEq(log, 'abc'); diff --git a/js/src/jit-test/tests/for-of/arrays-slow-2.js b/js/src/jit-test/tests/for-of/arrays-slow-2.js new file mode 100644 index 0000000000..dc4cf4fa79 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-slow-2.js @@ -0,0 +1,10 @@ +// Two for-of loops on the same slow array get distinct iterators. + +var a = [1, 2, 3]; +a.slow = true; +var s = ''; +for (var x of a) + s += x; +for (var y of a) + s += y; +assertEq(s, '123123'); diff --git a/js/src/jit-test/tests/for-of/arrays-slow-3.js b/js/src/jit-test/tests/for-of/arrays-slow-3.js new file mode 100644 index 0000000000..8a00fd51b0 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-slow-3.js @@ -0,0 +1,9 @@ +// Nested for-of loops on the same slow array get distinct iterators. + +var a = [1, 2, 3]; +a.slow = true; +var s = ''; +for (var x of a) + for (var y of a) + s += '' + x + y + ','; +assertEq(s, '11,12,13,21,22,23,31,32,33,'); diff --git a/js/src/jit-test/tests/for-of/arrays-slow-4.js b/js/src/jit-test/tests/for-of/arrays-slow-4.js new file mode 100644 index 0000000000..d2bceef876 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-slow-4.js @@ -0,0 +1,6 @@ +// for-of on an empty slow array does nothing. + +var a = []; +a.slow = true; +for (var x of a) + fail(); diff --git a/js/src/jit-test/tests/for-of/arrays-slow-5.js b/js/src/jit-test/tests/for-of/arrays-slow-5.js new file mode 100644 index 0000000000..36173e352b --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-slow-5.js @@ -0,0 +1,10 @@ +// Slowifying an array while it is being iterated does not affect iteration. + +var a = [9, 8, 7, 6, 5, 4, 3]; +var log = ''; +for (var x of a) { + log += x; + if (x === 6) + a.slow = true; +} +assertEq(log, "9876543"); diff --git a/js/src/jit-test/tests/for-of/break-1.js b/js/src/jit-test/tests/for-of/break-1.js new file mode 100644 index 0000000000..1a1e528eb8 --- /dev/null +++ b/js/src/jit-test/tests/for-of/break-1.js @@ -0,0 +1,9 @@ +// A break statement leaves a for-of loop. + +var log = ''; +for (var x of ['a', 'b', 'c']) { + log += x; + if (x === 'b') + break; +} +assertEq(log, "ab"); diff --git a/js/src/jit-test/tests/for-of/break-2.js b/js/src/jit-test/tests/for-of/break-2.js new file mode 100644 index 0000000000..4eea55bfac --- /dev/null +++ b/js/src/jit-test/tests/for-of/break-2.js @@ -0,0 +1,10 @@ +// A break statement leaves only a single for-of loop. + +var log = ''; +for (var x of [1, 2, 3]) { + for (var y of ['.', ':']) { + log += x + y; + break; + } +} +assertEq(log, "1.2.3."); diff --git a/js/src/jit-test/tests/for-of/break-3.js b/js/src/jit-test/tests/for-of/break-3.js new file mode 100644 index 0000000000..f5f873197f --- /dev/null +++ b/js/src/jit-test/tests/for-of/break-3.js @@ -0,0 +1,12 @@ +// A labeled break statement can leave multiple for-loops + +var log = ''; +for (var i = 0; i < 3; i++) { + a: for (var x of [1, 2, 3]) { + for (var y of ['.', ':']) { + log += x + y; + break a; + } + } +} +assertEq(log, "1.1.1."); diff --git a/js/src/jit-test/tests/for-of/bug-1331444.js b/js/src/jit-test/tests/for-of/bug-1331444.js new file mode 100644 index 0000000000..9770c584bc --- /dev/null +++ b/js/src/jit-test/tests/for-of/bug-1331444.js @@ -0,0 +1,7 @@ +// |jit-test| error: ReferenceError + +symbols = [Symbol]; +for (comparator of[, ]) + for (a of symbols) + for (;;) + expect; diff --git a/js/src/jit-test/tests/for-of/bug-1341339.js b/js/src/jit-test/tests/for-of/bug-1341339.js new file mode 100644 index 0000000000..1f88acdafa --- /dev/null +++ b/js/src/jit-test/tests/for-of/bug-1341339.js @@ -0,0 +1,9 @@ +let m = parseModule(` +function* values() {} +var iterator = values(); +for (var i=0; i < 10000; ++i) { + for (var x of iterator) {} +} +`); +m.declarationInstantiation(); +m.evaluation(); diff --git a/js/src/jit-test/tests/for-of/bug-728079-js17-1.js b/js/src/jit-test/tests/for-of/bug-728079-js17-1.js new file mode 100644 index 0000000000..1bc920dab9 --- /dev/null +++ b/js/src/jit-test/tests/for-of/bug-728079-js17-1.js @@ -0,0 +1,21 @@ +// for-of does not trigger the JS 1.7 for-in destructuring special case. + +var data = [[1, 2, 3], [4, 5, 6, 7]]; + +function test(vars, expr, result) { + var s = ''; + eval("for (" + vars + " of data) s += (" + expr + ") + ';';"); + assertEq(s, result); +} + +for (var prefix of ["var ", "let ", ""]) { + test(prefix + "[a, b, c]", + "a + ',' + b + ',' + c", + "1,2,3;4,5,6;"); +} + +test("var [a]", "a", "1;4;"); +test("var {length: len}", "len", "3;4;"); +test("var {length}", "length", "3;4;"); +test("{}", "0", "0;0;"); + diff --git a/js/src/jit-test/tests/for-of/bug-728079-js17-4.js b/js/src/jit-test/tests/for-of/bug-728079-js17-4.js new file mode 100644 index 0000000000..3c3917f905 --- /dev/null +++ b/js/src/jit-test/tests/for-of/bug-728079-js17-4.js @@ -0,0 +1,3 @@ +// Test case from bug 785989 comment 3. + +Reflect.parse("for (let [a, b] of c) ;"); diff --git a/js/src/jit-test/tests/for-of/bug1519700.js b/js/src/jit-test/tests/for-of/bug1519700.js new file mode 100644 index 0000000000..84302c6143 --- /dev/null +++ b/js/src/jit-test/tests/for-of/bug1519700.js @@ -0,0 +1,23 @@ +function* wrapNoThrow() { + let iter = { + [Symbol.iterator]() { + return this; + }, + next() { + return {}; + }, + return () {} + }; + for (const i of iter) + yield i; +} +function foo() { + l2: for (j of wrapNoThrow()) { + for (i of [1, 2, 3]) + break l2; + return false; + } +} +try { + foo(); +} catch {} diff --git a/js/src/jit-test/tests/for-of/completion.js b/js/src/jit-test/tests/for-of/completion.js new file mode 100644 index 0000000000..91dc96e571 --- /dev/null +++ b/js/src/jit-test/tests/for-of/completion.js @@ -0,0 +1,6 @@ +// The completion value of a for-of loop is the completion value of the +// last evaluation of the body, or undefined. + +assertEq(eval("for (let x of [1, 2, 3]) { x }"), 3); +assertEq(eval("for (let x of [1, 2, 3]) { x * 2 }"), 6); +assertEq(eval("for (let x of []) { x }"), undefined); diff --git a/js/src/jit-test/tests/for-of/decompiler.js b/js/src/jit-test/tests/for-of/decompiler.js new file mode 100644 index 0000000000..98e124b3eb --- /dev/null +++ b/js/src/jit-test/tests/for-of/decompiler.js @@ -0,0 +1,28 @@ +// The decompiler correctly handles for-of loops. + +function tokens(code) { + var arr = []; + var s = code.replace(/\w+|[^\s]/g, function (tok) { arr.push(tok); return ""; }); + assertEq(s.trim(), "", "tokens() should find all tokens in code: " + JSON.stringify(code)); + return arr; +} + +function test(code) { + var before = "function f() { " + code + " }"; + var after = eval("(" + before + ")").toString(); + assertEq(tokens(before).join(" "), tokens(after).join(" "), "decompiler failed to round-trip"); +} + +// statements +test("for (a of b) { f(a); }"); +test("for (a of b) { f(a); g(a); }"); + +// for-of with "in" operator nearby +test("for (a of b in c ? c : c.items()) { f(a); }"); + +// destructuring +test("for ([a, b] of c) { a.m(b); }"); + +// for-let-of +test("for (let a of b) { f(a); }"); +test("for (let [a, b] of c) { a.m(b); }"); diff --git a/js/src/jit-test/tests/for-of/generators-1.js b/js/src/jit-test/tests/for-of/generators-1.js new file mode 100644 index 0000000000..417465a508 --- /dev/null +++ b/js/src/jit-test/tests/for-of/generators-1.js @@ -0,0 +1,11 @@ +// for-of works with generators. + +function* range(n) { + for (var i = 0; i < n; i++) + yield i; +} + +var s = ''; +for (var a of range(4)) + s += a; +assertEq(s, '0123'); diff --git a/js/src/jit-test/tests/for-of/generators-2.js b/js/src/jit-test/tests/for-of/generators-2.js new file mode 100644 index 0000000000..e142b206de --- /dev/null +++ b/js/src/jit-test/tests/for-of/generators-2.js @@ -0,0 +1,15 @@ +// Generator-iterators are consumed the first time they are iterated. + +function* range(n) { + for (var i = 0; i < n; i++) + yield i; +} + +var r = range(10); +var i = 0; +for (var x of r) + assertEq(x, i++); +assertEq(i, 10); +for (var y of r) + throw "FAIL"; +assertEq(y, undefined); diff --git a/js/src/jit-test/tests/for-of/generators-3.js b/js/src/jit-test/tests/for-of/generators-3.js new file mode 100644 index 0000000000..6472f4df29 --- /dev/null +++ b/js/src/jit-test/tests/for-of/generators-3.js @@ -0,0 +1,18 @@ +// Nested for-of loops can use the same generator-iterator. + +function* range(n) { + for (var i = 0; i < n; i++) + yield i; +} + +var r = range(10); +for (var a of r) + for (var b of r) + for (var c of r) + for (var d of r) + ; + +assertEq(a, 0); +assertEq(b, 1); +assertEq(c, 2); +assertEq(d, 9); diff --git a/js/src/jit-test/tests/for-of/generators-5.js b/js/src/jit-test/tests/for-of/generators-5.js new file mode 100644 index 0000000000..4ce7166249 --- /dev/null +++ b/js/src/jit-test/tests/for-of/generators-5.js @@ -0,0 +1,20 @@ +// Breaking out of a for-of loop over a generator-iterator closes the generator. + +load(libdir + "iteration.js"); + +function* range(n) { + for (var i = 0; i < n; i++) + yield i; +} + +var r = range(10); +var s = ''; +for (var x of r) { + s += x; + if (x == 4) + break; +} +s += '/'; +for (var y of r) + s += y; +assertEq(s, '01234/'); diff --git a/js/src/jit-test/tests/for-of/manual-advance.js b/js/src/jit-test/tests/for-of/manual-advance.js new file mode 100644 index 0000000000..b4059251db --- /dev/null +++ b/js/src/jit-test/tests/for-of/manual-advance.js @@ -0,0 +1,15 @@ +// Manually advancing the iterator. + +load(libdir + 'iteration.js'); + +function* g(n) { for (var i=0; i<n; i++) yield i; } + +var inner = g(20); + +var n = 0; +for (var x of inner) { + assertEq(x, n * 2); + assertIteratorNext(inner, n * 2 + 1); + n++; +} +assertEq(n, 10); diff --git a/js/src/jit-test/tests/for-of/next-3.js b/js/src/jit-test/tests/for-of/next-3.js new file mode 100644 index 0000000000..529d44dafa --- /dev/null +++ b/js/src/jit-test/tests/for-of/next-3.js @@ -0,0 +1,10 @@ +// Iterators from another compartment work with both their own .next method +// with the other compartment's .next method. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var g = newGlobal(); +g.eval(`var it = [1, 2][Symbol.iterator]();`); +assertIteratorNext(g.it, 1); +assertDeepEq([][Symbol.iterator]().next.call(g.it), { value: 2, done: false }) diff --git a/js/src/jit-test/tests/for-of/next-arity.js b/js/src/jit-test/tests/for-of/next-arity.js new file mode 100644 index 0000000000..43990fd237 --- /dev/null +++ b/js/src/jit-test/tests/for-of/next-arity.js @@ -0,0 +1,22 @@ +// For-of passes one arg to "next". + +load(libdir + 'iteration.js') + +var log = ''; + +function Iter() { + function next() { + log += 'n'; + assertEq(arguments.length, 0) + assertEq(arguments[0], undefined) + return { get value() { throw 42; }, done: true } + } + + this[Symbol.iterator] = function () { return this; } + this.next = next; +} + +for (var x of new Iter()) + throw 'not reached'; + +assertEq(log, 'n'); diff --git a/js/src/jit-test/tests/for-of/next-shenanigans.js b/js/src/jit-test/tests/for-of/next-shenanigans.js new file mode 100644 index 0000000000..f57b04e12e --- /dev/null +++ b/js/src/jit-test/tests/for-of/next-shenanigans.js @@ -0,0 +1,27 @@ +// Test for-of with iter.next and monkeypatching. + +function* g(n) { for (var i=0; i<n; i++) yield i; } +var GeneratorObjectPrototype = Object.getPrototypeOf(g).prototype; +var GeneratorObjectPrototype_next = GeneratorObjectPrototype.next; + +// Monkeypatch next on an iterator. +var inner = g(20); +var n = 0; +for (let x of inner) { + assertEq(x, n++); + if (n == 1) { + inner.next = function() { throw 'not reached'; }; + } +} +assertEq(n, 20); + +// Monkeypatch next on the prototype. +var inner = g(20); +var n = 0; +for (let x of inner) { + assertEq(x, n++); + if (n == 1) { + GeneratorObjectPrototype.next = function() { throw 'not reached'; }; + } +} +assertEq(n, 20); diff --git a/js/src/jit-test/tests/for-of/non-iterable.js b/js/src/jit-test/tests/for-of/non-iterable.js new file mode 100644 index 0000000000..99f6b07802 --- /dev/null +++ b/js/src/jit-test/tests/for-of/non-iterable.js @@ -0,0 +1,25 @@ +// Iterating over non-iterable values throws a TypeError. + +load(libdir + "asserts.js"); + +var misc = [ + {}, {x: 1}, Math, isNaN, + Object.create(null), + null, undefined, + true, 0, 3.1416, + new Boolean(true), new Number(0), + {iterator: function () { return undefined; }}, + {iterator: function () { return null; }}, + {iterator: function () { return true; }}, + {iterator: function () { return 17; }}, +]; + +for (var i = 0; i < misc.length; i++) { + let v = misc[i]; + var testfn = function () { + for (var _ of v) + throw 'FAIL'; + throw 'BAD'; + }; + assertThrowsInstanceOf(testfn, TypeError); +} diff --git a/js/src/jit-test/tests/for-of/proxy-1.js b/js/src/jit-test/tests/for-of/proxy-1.js new file mode 100644 index 0000000000..abf5a31ff7 --- /dev/null +++ b/js/src/jit-test/tests/for-of/proxy-1.js @@ -0,0 +1,13 @@ +// Basic for-of test with Proxy. + +var s = ''; +var arr = ['a', 'b', 'c', 'd']; +var p = new Proxy(arr, {}); + +// Test the same proxy twice. +for (var i = 0; i < 2; i++) { + var j = 0; + for (var x of p) + assertEq(x, arr[j++]); + assertEq(j, arr.length); +} diff --git a/js/src/jit-test/tests/for-of/proxy-2.js b/js/src/jit-test/tests/for-of/proxy-2.js new file mode 100644 index 0000000000..e0c7b116ca --- /dev/null +++ b/js/src/jit-test/tests/for-of/proxy-2.js @@ -0,0 +1,18 @@ +// Basic for-of test with Proxy whose iterator method is a generator. + +var arr = ['a', 'b', 'c', 'd']; +var proxy = new Proxy(arr, { + get(target, property, receiver) { + if (property === Symbol.iterator) { + return function* () { + for (var i = 0; i < arr.length; i++) + yield arr[i]; + } + } + + return Reflect.get(target, property, receiver); + } +}); + +for (var i = 0; i < 2; i++) + assertEq([...proxy].join(","), "a,b,c,d"); diff --git a/js/src/jit-test/tests/for-of/proxy-3.js b/js/src/jit-test/tests/for-of/proxy-3.js new file mode 100644 index 0000000000..1d7b84847e --- /dev/null +++ b/js/src/jit-test/tests/for-of/proxy-3.js @@ -0,0 +1,13 @@ +// An exception thrown from a proxy trap while getting the .iterator method is propagated. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var p = new Proxy({}, { + get(target, property) { + if (property === Symbol.iterator) + throw "fit"; + return undefined; + } +}); +assertThrowsValue(function () { for (var v of p) {} }, "fit"); diff --git a/js/src/jit-test/tests/for-of/return.js b/js/src/jit-test/tests/for-of/return.js new file mode 100644 index 0000000000..3e39df49cf --- /dev/null +++ b/js/src/jit-test/tests/for-of/return.js @@ -0,0 +1,14 @@ +// Control can exit a for-of loop via return. + +function f() { + for (var a of [1, 2, 3]) { + for (var b of [1, 2, 3]) { + for (var c of [1, 2, 3]) { + if (a !== b && b !== c && c !== a) + return "" + a + b + c; + } + } + } +} + +assertEq(f(), "123"); diff --git a/js/src/jit-test/tests/for-of/semantics-01.js b/js/src/jit-test/tests/for-of/semantics-01.js new file mode 100644 index 0000000000..6da9883723 --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-01.js @@ -0,0 +1,13 @@ +// for-of is defined in terms of basic operations on objects, particularly +// [[Get]] for properties named "iterator" and "next", and [[Call]]. These +// "semantics" tests check that for-of really does appear to be implemented in +// terms of those more basic operations, as required by the spec, even in +// unusual cases. + +// Deleting Array.prototype.iterator makes for-of stop working on arrays. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +delete Array.prototype[Symbol.iterator]; +assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError); diff --git a/js/src/jit-test/tests/for-of/semantics-02.js b/js/src/jit-test/tests/for-of/semantics-02.js new file mode 100644 index 0000000000..27b1323298 --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-02.js @@ -0,0 +1,12 @@ +// Replacing Array.prototype.iterator with something non-callable makes for-of throw. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +function test(v) { + Array.prototype[Symbol.iterator] = v; + assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError); +} +test(undefined); +test(null); +test({}); diff --git a/js/src/jit-test/tests/for-of/semantics-03.js b/js/src/jit-test/tests/for-of/semantics-03.js new file mode 100644 index 0000000000..069b7fa749 --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-03.js @@ -0,0 +1,13 @@ +// Replacing Array.prototype.iterator with a generator affects for-of behavior. + +load(libdir + "iteration.js"); + +Array.prototype[Symbol.iterator] = function* () { + for (var i = this.length; --i >= 0; ) + yield this[i]; +}; + +var s = ''; +for (var v of ['a', 'b', 'c', 'd']) + s += v; +assertEq(s, 'dcba'); diff --git a/js/src/jit-test/tests/for-of/semantics-04.js b/js/src/jit-test/tests/for-of/semantics-04.js new file mode 100644 index 0000000000..df7036de7a --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-04.js @@ -0,0 +1,17 @@ +// Giving an Array an own .iterator property affects for-of. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var a = []; +a[Symbol.iterator] = function* () { + yield 'o'; + yield 'k'; +}; +var s = ''; +for (var v of a) + s += v; +assertEq(s, 'ok'); + +a[Symbol.iterator] = undefined; +assertThrowsInstanceOf(function () { for (var v of a) ; }, TypeError); diff --git a/js/src/jit-test/tests/for-of/semantics-05.js b/js/src/jit-test/tests/for-of/semantics-05.js new file mode 100644 index 0000000000..be5bd7f6e2 --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-05.js @@ -0,0 +1,8 @@ +// Deleting String.prototype.iterator makes for-of stop working on strings. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +delete String.prototype[Symbol.iterator]; +assertThrowsInstanceOf(function () { for (var v of "abc") ; }, TypeError); +assertThrowsInstanceOf(function () { for (var v of new String("abc")) ; }, TypeError); diff --git a/js/src/jit-test/tests/for-of/semantics-06.js b/js/src/jit-test/tests/for-of/semantics-06.js new file mode 100644 index 0000000000..1ff3d9570e --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-06.js @@ -0,0 +1,8 @@ +// Deleting the .next method makes for-of stop working on arrays. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var iterProto = Object.getPrototypeOf([][Symbol.iterator]()); +delete iterProto.next; +assertThrowsInstanceOf(function () { for (var v of []) ; }, TypeError); diff --git a/js/src/jit-test/tests/for-of/semantics-07.js b/js/src/jit-test/tests/for-of/semantics-07.js new file mode 100644 index 0000000000..e89cf1f053 --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-07.js @@ -0,0 +1,15 @@ +// Deleting the .next method of an iterator in the middle of a for-of loop +// doesn't cause a TypeError at the next iteration. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var iterProto = Object.getPrototypeOf([][Symbol.iterator]()); +var s = ''; +for (var v of ['duck', 'duck', 'duck', 'goose', 'and now you\'re it']) { + s += v; + if (v === 'goose') + delete iterProto.next; + s += '.'; +} +assertEq(s, 'duck.duck.duck.goose.and now you\'re it.'); diff --git a/js/src/jit-test/tests/for-of/semantics-08.js b/js/src/jit-test/tests/for-of/semantics-08.js new file mode 100644 index 0000000000..11b96fd12b --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-08.js @@ -0,0 +1,12 @@ +// Results from another compartment are correctly interpreted by for-of. + +load(libdir + "iteration.js"); + +var g = newGlobal(); +g.eval(` + var obj = {}; + obj[Symbol.iterator] = function () { return this; }; + obj.next = function () { return { done: true }; }; +`); +for (x of g.obj) + throw 'FAIL'; diff --git a/js/src/jit-test/tests/for-of/semantics-09.js b/js/src/jit-test/tests/for-of/semantics-09.js new file mode 100644 index 0000000000..3efa0b3a9f --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-09.js @@ -0,0 +1,25 @@ +// The LHS of a for-of loop is not evaluated until after the .next() method returns. + +var s; +function f() { + s += 'f'; + return {}; +} + +// Test 1: .next() throws StopIteration right away. f is never called. +s = ''; +for (f().x of []) + s += '.'; +assertEq(s, ''); + +// Test 2: check proper interleaving of f calls, iterator.next() calls, and the loop body. +function* g() { + s += 'g'; + yield 0; + s += 'g'; + yield 1; + s += 'g'; +} +for (f().x of g()) + s += '.'; +assertEq(s, 'gf.gf.g'); diff --git a/js/src/jit-test/tests/for-of/semantics-10.js b/js/src/jit-test/tests/for-of/semantics-10.js new file mode 100644 index 0000000000..03f6bc3cfc --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-10.js @@ -0,0 +1,31 @@ +// The LHS of a for-loop is not bound to a particular scope until after the .next() method returns. + +var obj = {}; + +// Test 1 +function* g() { + obj.x = 0; + yield 1; +} +var x = 2, n = 0; +with (obj) { + for (x of g()) // g().next() inserts a binding for x on obj + n++; +} +assertEq(x, 2); +assertEq(obj.x, 1); +assertEq(n, 1); + +// Test 2 +function* h() { + delete obj.x; + yield 3; +} +n = 0; +with (obj) { + for (x of h()) // h().next() deletes the binding for x on obj + n++; +} +assertEq(x, 3); +assertEq("x" in obj, false); +assertEq(n, 1); diff --git a/js/src/jit-test/tests/for-of/semantics-11.js b/js/src/jit-test/tests/for-of/semantics-11.js new file mode 100644 index 0000000000..f806478bef --- /dev/null +++ b/js/src/jit-test/tests/for-of/semantics-11.js @@ -0,0 +1,43 @@ +// for-of on a proxy causes a predictable sequence of trap calls. + +load(libdir + "iteration.js"); + +var s = ''; + +var i = 0; +var next_fn = new Proxy(function() {}, { + apply() { + s += "n"; + if (i == 3) + return { value: undefined, done: true }; + return { value: i++, done: false }; + } +}); + +var it = new Proxy({}, { + get(target, property, receiver) { + assertEq(property, "next"); + s += "N"; + return next_fn; + } +}); + +var iterator_fn = new Proxy(function() {}, { + apply() { + s += 'i'; + return it; + } +}); + +var obj = new Proxy({}, { + get: function (receiver, name) { + assertEq(name, Symbol.iterator); + s += "I"; + return iterator_fn; + } +}); + +for (var v of obj) + s += v; + +assertEq(s, 'IiNn0n1n2n'); diff --git a/js/src/jit-test/tests/for-of/string-iterator-generic.js b/js/src/jit-test/tests/for-of/string-iterator-generic.js new file mode 100644 index 0000000000..e1573dd179 --- /dev/null +++ b/js/src/jit-test/tests/for-of/string-iterator-generic.js @@ -0,0 +1,25 @@ +// String.prototype.iterator is generic. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); +load(libdir + "string.js"); + +function test(obj) { + var it = String.prototype[Symbol.iterator].call(obj); + var s = String(obj); + for (var i = 0, length = s.length; i < length;) { + var r = s[i++]; + if (isHighSurrogate(r) && i < length && isLowSurrogate(s[i])) { + r += s[i++]; + } + assertIteratorNext(it, r); + } + assertIteratorDone(it, undefined); +} + +test({toString: () => ""}); +test({toString: () => "xyz"}); +test({toString: () => "\ud808\udf45"}); +test({valueOf: () => ""}); +test({valueOf: () => "xyz"}); +test({valueOf: () => "\ud808\udf45"}); diff --git a/js/src/jit-test/tests/for-of/string-iterator-surfaces.js b/js/src/jit-test/tests/for-of/string-iterator-surfaces.js new file mode 100644 index 0000000000..293f1b8b3c --- /dev/null +++ b/js/src/jit-test/tests/for-of/string-iterator-surfaces.js @@ -0,0 +1,83 @@ +// String.prototype[@@iterator] and StringIterator.prototype surface tests + +load(libdir + "array-compare.js"); +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +function assertDataDescriptor(actual, expected) { + assertEq(actual.value, expected.value); + assertEq(actual.writable, expected.writable); + assertEq(actual.enumerable, expected.enumerable); + assertEq(actual.configurable, expected.configurable); +} + +function isConstructor(o) { + try { + new (new Proxy(o, {construct: () => ({})})); + return true; + } catch(e) { + return false; + } +} + +function assertBuiltinFunction(o, name, arity) { + var fn = o[name]; + assertDataDescriptor(Object.getOwnPropertyDescriptor(o, name), { + value: fn, + writable: true, + enumerable: false, + configurable: true, + }); + + assertEq(typeof fn, "function"); + assertEq(Object.getPrototypeOf(fn), Function.prototype); + assertEq(isConstructor(fn), false); + + assertEq(arraysEqual(Object.getOwnPropertyNames(fn).sort(), ["length", "name"].sort()), true); + + assertDataDescriptor(Object.getOwnPropertyDescriptor(fn, "length"), { + value: arity, + writable: false, + enumerable: false, + configurable: true + }); + + var functionName = typeof name === "symbol" + ? String(name).replace(/^Symbol\((.+)\)$/, "[$1]") + : name; + assertDataDescriptor(Object.getOwnPropertyDescriptor(fn, "name"), { + value: functionName, + writable: false, + enumerable: false, + configurable: true + }); +} + + +// String.prototype[@@iterator] is a built-in function +assertBuiltinFunction(String.prototype, Symbol.iterator, 0); + +// Test StringIterator.prototype surface +var iter = ""[Symbol.iterator](); +var iterProto = Object.getPrototypeOf(iter); + +// StringIterator.prototype inherits from %IteratorPrototype%. Check it's the +// same object as %ArrayIteratorPrototype%'s proto. +assertEq(Object.getPrototypeOf(iterProto), + Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); + +// Own properties for StringIterator.prototype: "next" +assertEq(arraysEqual(Object.getOwnPropertyNames(iterProto).sort(), ["next"]), true); + +// StringIterator.prototype.next is a built-in function +assertBuiltinFunction(iterProto, "next", 0); + +// StringIterator.prototype[@@iterator] is generic and returns |this| +for (var v of [void 0, null, true, false, "", 0, 1, {}, [], iter, iterProto]) { + assertEq(iterProto[Symbol.iterator].call(v), v); +} + +// StringIterator.prototype.next is not generic +for (var v of [void 0, null, true, false, "", 0, 1, {}, [], iterProto]) { + assertThrowsInstanceOf(() => iterProto.next.call(v), TypeError); +} diff --git a/js/src/jit-test/tests/for-of/strings.js b/js/src/jit-test/tests/for-of/strings.js new file mode 100644 index 0000000000..1dab295b01 --- /dev/null +++ b/js/src/jit-test/tests/for-of/strings.js @@ -0,0 +1,47 @@ +// for-of works on strings and String objects. + +load(libdir + "string.js"); + +function test(s, expectedCodePoints) { + var copy = ''; + var codepoints = 0; + var singleHighSurrogate = false; + for (var v of s) { + assertEq(typeof v, 'string'); + assertEq(v.length, isSurrogatePair(v) ? 2 : 1); + assertEq(false, singleHighSurrogate && isLowSurrogate(v)); + copy += v; + codepoints += 1; + singleHighSurrogate = !isSurrogatePair(v) && isHighSurrogate(v); + } + assertEq(copy, String(s)); + assertEq(codepoints, expectedCodePoints); +} + +test('', 0); +test('abc', 3); +test('a \0 \ufffe \ufeff', 7); + +// Non-BMP characters are generally passed to JS in UTF-16, as surrogate pairs. +// ES6 requires that such pairs be treated as a single code point in for-of. +test('\ud808\udf45', 1); + +// Also test invalid surrogate pairs: +// (1) High surrogate not followed by low surrogate +test('\ud808', 1); +test('\ud808\u0000', 2); +// (2) Low surrogate not preceded by high surrogate +test('\udf45', 1); +test('\u0000\udf45', 2); +// (3) Low surrogate followed by high surrogate +test('\udf45\ud808', 2); + +test(new String(''), 0); +test(new String('abc'), 3); +test(new String('a \0 \ufffe \ufeff'), 7); +test(new String('\ud808\udf45'), 1); +test(new String('\ud808'), 1); +test(new String('\ud808\u0000'), 2); +test(new String('\udf45'), 1); +test(new String('\u0000\udf45'), 2); +test(new String('\udf45\ud808'), 2); diff --git a/js/src/jit-test/tests/for-of/syntax-1.js b/js/src/jit-test/tests/for-of/syntax-1.js new file mode 100644 index 0000000000..7cc393ec12 --- /dev/null +++ b/js/src/jit-test/tests/for-of/syntax-1.js @@ -0,0 +1,20 @@ +// We correctly reject bogus for-of loop syntax. + +load(libdir + "asserts.js"); + +function assertSyntaxError(code) { + assertThrowsInstanceOf(function () { Function(code); }, SyntaxError, "Function:" + code); + assertThrowsInstanceOf(function () { eval(code); }, SyntaxError, "eval:" + code); + var ieval = eval; + assertThrowsInstanceOf(function () { ieval(code); }, SyntaxError, "indirect eval:" + code); +} + +function test(badForHead) { + assertSyntaxError(badForHead + " {}"); // apply directly to forHead + assertSyntaxError("[0 " + badForHead + "];"); +} + +var a, b, c; +test("for (a in b of c)"); +test("for (a of b of c)"); +test("for (let {a: 1} of b)"); diff --git a/js/src/jit-test/tests/for-of/syntax-2.js b/js/src/jit-test/tests/for-of/syntax-2.js new file mode 100644 index 0000000000..2241a39214 --- /dev/null +++ b/js/src/jit-test/tests/for-of/syntax-2.js @@ -0,0 +1,7 @@ +// "of" is not a keyword. + +var of; + +Function("var of;"); + +function of(of) {} diff --git a/js/src/jit-test/tests/for-of/syntax-3.js b/js/src/jit-test/tests/for-of/syntax-3.js new file mode 100644 index 0000000000..01b7cce1c1 --- /dev/null +++ b/js/src/jit-test/tests/for-of/syntax-3.js @@ -0,0 +1,19 @@ +// For-of can't have initializers. + +load(libdir + 'asserts.js'); + +function assertSyntaxError(str) { + assertThrowsInstanceOf(function () { return Function(str); }, SyntaxError); +} + +assertSyntaxError("for (var x = 1 of []) {}"); +assertSyntaxError("for (var [x] = 1 of []) {}"); +assertSyntaxError("for (var {x} = 1 of []) {}"); + +assertSyntaxError("for (let x = 1 of []) {}"); +assertSyntaxError("for (let [x] = 1 of []) {}"); +assertSyntaxError("for (let {x} = 1 of []) {}"); + +assertSyntaxError("for (const x = 1 of []) {}"); +assertSyntaxError("for (const [x] = 1 of []) {}"); +assertSyntaxError("for (const {x} = 1 of []) {}"); diff --git a/js/src/jit-test/tests/for-of/syntax-4.js b/js/src/jit-test/tests/for-of/syntax-4.js new file mode 100644 index 0000000000..a6c3214840 --- /dev/null +++ b/js/src/jit-test/tests/for-of/syntax-4.js @@ -0,0 +1,19 @@ +// The right-hand-side of a for-of is an assignment expression. + +load(libdir + 'asserts.js'); + +function assertSyntaxError(str) { + assertThrowsInstanceOf(function () { return Function(str); }, SyntaxError); +} + +assertSyntaxError("for (var x of 1, 2) {}"); +assertSyntaxError("for (var [x] of 1, 2) {}"); +assertSyntaxError("for (var {x} of 1, 2) {}"); + +assertSyntaxError("for (let x of 1, 2) {}"); +assertSyntaxError("for (let [x] of 1, 2) {}"); +assertSyntaxError("for (let {x} of 1, 2) {}"); + +assertSyntaxError("for (const x of 1, 2) {}"); +assertSyntaxError("for (const [x] of 1, 2) {}"); +assertSyntaxError("for (const {x} of 1, 2) {}"); diff --git a/js/src/jit-test/tests/for-of/throw-during-break.js b/js/src/jit-test/tests/for-of/throw-during-break.js new file mode 100644 index 0000000000..352f6017df --- /dev/null +++ b/js/src/jit-test/tests/for-of/throw-during-break.js @@ -0,0 +1,42 @@ +var whoCaught = "nobody" + +function* wrapNoThrow() { + let iter = { + [Symbol.iterator]() { + return this; + }, + next() { + return { value: 10, done: false }; + }, + return() { throw "nonsense"; } + }; + for (const i of iter) + yield i; + } +function foo() { + try { + l2: + for (j of wrapNoThrow()) { + try { + for (i of [1,2,3]) { + try { + break l2; + } catch(e) { + whoCaught = "inner" + } + } + } catch (e) { + whoCaught = "inner2" + } + } + } catch (e) { + whoCaught = "correct" + } +} + +try { + foo(); +} catch (e) { whoCaught = "outer" } + + +assertEq(whoCaught, "correct"); diff --git a/js/src/jit-test/tests/for-of/throw-during-nested-break.js b/js/src/jit-test/tests/for-of/throw-during-nested-break.js new file mode 100644 index 0000000000..64e2d182b2 --- /dev/null +++ b/js/src/jit-test/tests/for-of/throw-during-nested-break.js @@ -0,0 +1,29 @@ +var progress = ""; + +function* wrapNoThrow() { + let iter = { + [Symbol.iterator]() { return this; }, + next() { return { value: 10, iter: false }; }, + return() { progress += " throw"; throw "nonsense"; } + }; + for (const i of iter) + yield i; +} + +function foo() { + try { + var badIter = wrapNoThrow(); + loop: for (var i of badIter) { + progress += "outerloop"; + try { + for (i of [1,2,3]) { + progress += " innerloop"; + break loop; + } + } catch (e) { progress += " BAD CATCH"; } + } + } catch (e) { progress += " goodcatch"; } +} + +foo(); +assertEq(progress, "outerloop innerloop throw goodcatch"); diff --git a/js/src/jit-test/tests/for-of/throw.js b/js/src/jit-test/tests/for-of/throw.js new file mode 100644 index 0000000000..2576ce1356 --- /dev/null +++ b/js/src/jit-test/tests/for-of/throw.js @@ -0,0 +1,20 @@ +// Control can exit a for-of loop via throw. + +function f() { + for (var a of [1, 2, 3]) { + for (var b of [1, 2, 3]) { + for (var c of [1, 2, 3]) { + if (a !== b && b !== c && c !== a) + throw [a, b, c]; + } + } + } +} + +var x = null; +try { + f(); +} catch (exc) { + x = exc.join(""); +} +assertEq(x, "123"); diff --git a/js/src/jit-test/tests/for-of/typedarrays-1.js b/js/src/jit-test/tests/for-of/typedarrays-1.js new file mode 100644 index 0000000000..d6b7f50383 --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-1.js @@ -0,0 +1,7 @@ +// for-of can iterate over typed arrays. + +var a = new Int8Array([0, 1, -7, 3]) +var s = ''; +for (var v of a) + s += v + ','; +assertEq(s, '0,1,-7,3,'); diff --git a/js/src/jit-test/tests/for-of/typedarrays-2.js b/js/src/jit-test/tests/for-of/typedarrays-2.js new file mode 100644 index 0000000000..dddc8aab73 --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-2.js @@ -0,0 +1,11 @@ +// The body of a for-of loop does not run if the target is an empty typed array. + +for (x of new Int16Array(0)) + throw "FAIL"; +for (x of new Float32Array(0)) + throw "FAIL"; + +var a = new Int8Array([0, 1, 2, 3]).subarray(2, 2); +assertEq(a.length, 0); +for (v of a) + throw "FAIL"; diff --git a/js/src/jit-test/tests/for-of/typedarrays-3.js b/js/src/jit-test/tests/for-of/typedarrays-3.js new file mode 100644 index 0000000000..beb205d48e --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-3.js @@ -0,0 +1,4 @@ +// Destructuring does not occur when the target of for-of is an empty typed array. + +for (var [[x]] of new Int32Array(0)) + throw "FAIL"; diff --git a/js/src/jit-test/tests/for-of/typedarrays-4.js b/js/src/jit-test/tests/for-of/typedarrays-4.js new file mode 100644 index 0000000000..ec2f8ffb54 --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-4.js @@ -0,0 +1,7 @@ +// for-of throws if the target is a typed array prototype object. + +load(libdir + "asserts.js"); +assertThrowsInstanceOf(function () { + for (var v of Int8Array.prototype) + throw "FAIL"; +}, TypeError); diff --git a/js/src/jit-test/tests/for-of/typedarrays-5.js b/js/src/jit-test/tests/for-of/typedarrays-5.js new file mode 100644 index 0000000000..6ba70e1210 --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-5.js @@ -0,0 +1,7 @@ +// for-of throws if the target is an ArrayBuffer. + +load(libdir + "asserts.js"); +assertThrowsInstanceOf(function () { + for (var v of new Int8Array([0, 1, 2, 3]).buffer) + throw "FAIL"; +}, TypeError); diff --git a/js/src/jit-test/tests/for-of/typedarrays-6.js b/js/src/jit-test/tests/for-of/typedarrays-6.js new file mode 100644 index 0000000000..90853f30fe --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-6.js @@ -0,0 +1,9 @@ +// for-of can iterate over float typed arrays containing infinities or NaNs. + +var values = [Infinity, -Infinity, -0, NaN]; +for (var C of [Float32Array, Float64Array]) { + var i = 0; + for (var v of new C(values)) + assertEq(v, values[i++]); + assertEq(i, values.length); +} diff --git a/js/src/jit-test/tests/for-of/value-done-access.js b/js/src/jit-test/tests/for-of/value-done-access.js new file mode 100644 index 0000000000..49028a7746 --- /dev/null +++ b/js/src/jit-test/tests/for-of/value-done-access.js @@ -0,0 +1,23 @@ +// Test that each yield* loop just checks "done", and "value" is only +// fetched once at the end. + +load(libdir + 'iteration.js'); + +var log = ""; + +function Iter(val, count) { + function next() { + return { + get done() { log += "d"; return count-- == 0; }, + get value() { log += "v"; return val; } + } + } + + this[Symbol.iterator] = function() { return this; }; + this.next = next; +} + +for (var x of new Iter(42, 5)) + assertEq(x, 42); + +assertEq(log, "dvdvdvdvdvd"); diff --git a/js/src/jit-test/tests/for-of/wrapper-1.js b/js/src/jit-test/tests/for-of/wrapper-1.js new file mode 100644 index 0000000000..90adb11b08 --- /dev/null +++ b/js/src/jit-test/tests/for-of/wrapper-1.js @@ -0,0 +1,7 @@ +// for-of works on cross-compartment wrappers of Arrays. + +var g = newGlobal(); +var s = ''; +for (var x of g.Array(1, 1, 2, 3, 5)) + s += x; +assertEq(s, '11235'); |