summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/for-of
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/for-of')
-rw-r--r--js/src/jit-test/tests/for-of/arguments-1.js21
-rw-r--r--js/src/jit-test/tests/for-of/arguments-2.js14
-rw-r--r--js/src/jit-test/tests/for-of/arguments-3.js20
-rw-r--r--js/src/jit-test/tests/for-of/arguments-4.js19
-rw-r--r--js/src/jit-test/tests/for-of/arguments-5.js20
-rw-r--r--js/src/jit-test/tests/for-of/arguments-6.js16
-rw-r--r--js/src/jit-test/tests/for-of/arguments-7.js17
-rw-r--r--js/src/jit-test/tests/for-of/array-holes-1.js10
-rw-r--r--js/src/jit-test/tests/for-of/array-holes-2.js8
-rw-r--r--js/src/jit-test/tests/for-of/array-holes-3.js8
-rw-r--r--js/src/jit-test/tests/for-of/array-holes-4.js13
-rw-r--r--js/src/jit-test/tests/for-of/array-holes-5.js6
-rw-r--r--js/src/jit-test/tests/for-of/array-holes-6.js8
-rw-r--r--js/src/jit-test/tests/for-of/array-holes-slow.js13
-rw-r--r--js/src/jit-test/tests/for-of/array-iterator-changing.js29
-rw-r--r--js/src/jit-test/tests/for-of/array-iterator-empty.js11
-rw-r--r--js/src/jit-test/tests/for-of/array-iterator-generic.js33
-rw-r--r--js/src/jit-test/tests/for-of/array-iterator-growing-1.js26
-rw-r--r--js/src/jit-test/tests/for-of/array-iterator-keys-entries.js16
-rw-r--r--js/src/jit-test/tests/for-of/array-iterator-null.js11
-rw-r--r--js/src/jit-test/tests/for-of/array-iterator-proxy.js47
-rw-r--r--js/src/jit-test/tests/for-of/array-iterator-shrinking.js20
-rw-r--r--js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js17
-rw-r--r--js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js31
-rw-r--r--js/src/jit-test/tests/for-of/array-jit.js6
-rw-r--r--js/src/jit-test/tests/for-of/array-prototype.js11
-rw-r--r--js/src/jit-test/tests/for-of/arrays-1.js7
-rw-r--r--js/src/jit-test/tests/for-of/arrays-2.js10
-rw-r--r--js/src/jit-test/tests/for-of/arrays-3.js9
-rw-r--r--js/src/jit-test/tests/for-of/arrays-4.js8
-rw-r--r--js/src/jit-test/tests/for-of/arrays-5.js4
-rw-r--r--js/src/jit-test/tests/for-of/arrays-growing-1.js10
-rw-r--r--js/src/jit-test/tests/for-of/arrays-growing-2.js10
-rw-r--r--js/src/jit-test/tests/for-of/arrays-shrinking-1.js13
-rw-r--r--js/src/jit-test/tests/for-of/arrays-shrinking-2.js9
-rw-r--r--js/src/jit-test/tests/for-of/arrays-slow-1.js8
-rw-r--r--js/src/jit-test/tests/for-of/arrays-slow-2.js10
-rw-r--r--js/src/jit-test/tests/for-of/arrays-slow-3.js9
-rw-r--r--js/src/jit-test/tests/for-of/arrays-slow-4.js6
-rw-r--r--js/src/jit-test/tests/for-of/arrays-slow-5.js10
-rw-r--r--js/src/jit-test/tests/for-of/break-1.js9
-rw-r--r--js/src/jit-test/tests/for-of/break-2.js10
-rw-r--r--js/src/jit-test/tests/for-of/break-3.js12
-rw-r--r--js/src/jit-test/tests/for-of/bug-1331444.js7
-rw-r--r--js/src/jit-test/tests/for-of/bug-1341339.js9
-rw-r--r--js/src/jit-test/tests/for-of/bug-728079-js17-1.js21
-rw-r--r--js/src/jit-test/tests/for-of/bug-728079-js17-4.js3
-rw-r--r--js/src/jit-test/tests/for-of/bug1519700.js23
-rw-r--r--js/src/jit-test/tests/for-of/bug1773496.js47
-rw-r--r--js/src/jit-test/tests/for-of/completion.js6
-rw-r--r--js/src/jit-test/tests/for-of/decompiler.js28
-rw-r--r--js/src/jit-test/tests/for-of/generators-1.js11
-rw-r--r--js/src/jit-test/tests/for-of/generators-2.js15
-rw-r--r--js/src/jit-test/tests/for-of/generators-3.js18
-rw-r--r--js/src/jit-test/tests/for-of/generators-5.js20
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-dynamic-slot-throw.js55
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-dynamic-slot.js47
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-extra-args-throw.js49
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-extra-args.js41
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-generator-throw.js46
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-generator.js38
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-invalid-return-throw.js48
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-invalid-return.js46
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-invalidate-with-catch.js54
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-invalidate-with-finally.js55
-rw-r--r--js/src/jit-test/tests/for-of/iterclose-invalidate.js53
-rw-r--r--js/src/jit-test/tests/for-of/manual-advance.js15
-rw-r--r--js/src/jit-test/tests/for-of/next-3.js10
-rw-r--r--js/src/jit-test/tests/for-of/next-arity.js22
-rw-r--r--js/src/jit-test/tests/for-of/next-shenanigans.js27
-rw-r--r--js/src/jit-test/tests/for-of/non-iterable.js25
-rw-r--r--js/src/jit-test/tests/for-of/proxy-1.js13
-rw-r--r--js/src/jit-test/tests/for-of/proxy-2.js18
-rw-r--r--js/src/jit-test/tests/for-of/proxy-3.js13
-rw-r--r--js/src/jit-test/tests/for-of/return.js14
-rw-r--r--js/src/jit-test/tests/for-of/semantics-01.js13
-rw-r--r--js/src/jit-test/tests/for-of/semantics-02.js12
-rw-r--r--js/src/jit-test/tests/for-of/semantics-03.js13
-rw-r--r--js/src/jit-test/tests/for-of/semantics-04.js17
-rw-r--r--js/src/jit-test/tests/for-of/semantics-05.js8
-rw-r--r--js/src/jit-test/tests/for-of/semantics-06.js8
-rw-r--r--js/src/jit-test/tests/for-of/semantics-07.js15
-rw-r--r--js/src/jit-test/tests/for-of/semantics-08.js12
-rw-r--r--js/src/jit-test/tests/for-of/semantics-09.js25
-rw-r--r--js/src/jit-test/tests/for-of/semantics-10.js31
-rw-r--r--js/src/jit-test/tests/for-of/semantics-11.js43
-rw-r--r--js/src/jit-test/tests/for-of/string-iterator-generic.js25
-rw-r--r--js/src/jit-test/tests/for-of/string-iterator-surfaces.js83
-rw-r--r--js/src/jit-test/tests/for-of/strings.js47
-rw-r--r--js/src/jit-test/tests/for-of/syntax-1.js20
-rw-r--r--js/src/jit-test/tests/for-of/syntax-2.js7
-rw-r--r--js/src/jit-test/tests/for-of/syntax-3.js19
-rw-r--r--js/src/jit-test/tests/for-of/syntax-4.js19
-rw-r--r--js/src/jit-test/tests/for-of/throw-during-break.js42
-rw-r--r--js/src/jit-test/tests/for-of/throw-during-nested-break.js29
-rw-r--r--js/src/jit-test/tests/for-of/throw.js20
-rw-r--r--js/src/jit-test/tests/for-of/typedarrays-1.js7
-rw-r--r--js/src/jit-test/tests/for-of/typedarrays-2.js11
-rw-r--r--js/src/jit-test/tests/for-of/typedarrays-3.js4
-rw-r--r--js/src/jit-test/tests/for-of/typedarrays-4.js7
-rw-r--r--js/src/jit-test/tests/for-of/typedarrays-5.js7
-rw-r--r--js/src/jit-test/tests/for-of/typedarrays-6.js9
-rw-r--r--js/src/jit-test/tests/for-of/value-done-access.js23
-rw-r--r--js/src/jit-test/tests/for-of/wrapper-1.js7
104 files changed, 2085 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..cd5f5ff90c
--- /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) {}
+}
+`);
+moduleLink(m);
+moduleEvaluate(m);
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/bug1773496.js b/js/src/jit-test/tests/for-of/bug1773496.js
new file mode 100644
index 0000000000..91ec0b4c8c
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/bug1773496.js
@@ -0,0 +1,47 @@
+var shouldBail = false;
+function foo() {
+ if (shouldBail) {
+ bailout();
+ return 0;
+ }
+ for (var i = 0; i < 1000; i++) {}
+ return { value: undefined, done: true }
+}
+
+const iterable = {
+ [Symbol.iterator]() {
+ return {
+ i: 0,
+ next() {
+ return { value: this.i++, done: false }
+ },
+ return() {
+ return foo();
+ }
+ };
+ }
+}
+
+function closeIter() {
+ with ({}) {}
+ for (var x of iterable) {
+ if (x == 2) {
+ break;
+ }
+ }
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ closeIter();
+}
+
+shouldBail = true;
+
+caught = false;
+try {
+ closeIter();
+} catch {
+ caught = true;
+}
+assertEq(caught, true);
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/iterclose-dynamic-slot-throw.js b/js/src/jit-test/tests/for-of/iterclose-dynamic-slot-throw.js
new file mode 100644
index 0000000000..5f91160da2
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-dynamic-slot-throw.js
@@ -0,0 +1,55 @@
+var counter = 0;
+const iterable = {
+ [Symbol.iterator]() {
+ return {
+ i: 0,
+
+ // Force the return method into a dynamic slot.
+ x0: 0, x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0,
+ x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0,
+ x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0,
+
+ next() {
+ return { value: this.i++, done: false }
+ },
+ return() {
+ counter += 1;
+ return { value: undefined, done: true };
+ }
+ };
+ }
+}
+
+function closeIter(o) {
+ for (var x of o) {
+ if (x == 2) {
+ throw "good";
+ }
+ }
+}
+
+function test() {
+ with ({}) {}
+ counter = 0;
+ for (var i = 0; i < 100; i++) {
+ var caught = "bad";
+ try {
+ closeIter(iterable)
+ } catch (e) {
+ caught = e;
+ }
+ assertEq(caught, "good");
+ }
+ assertEq(counter, 100)
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+try {
+ closeIter([1,2,3]);
+} catch {}
+
+test();
diff --git a/js/src/jit-test/tests/for-of/iterclose-dynamic-slot.js b/js/src/jit-test/tests/for-of/iterclose-dynamic-slot.js
new file mode 100644
index 0000000000..170d60999d
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-dynamic-slot.js
@@ -0,0 +1,47 @@
+var counter = 0;
+const iterable = {
+ [Symbol.iterator]() {
+ return {
+ i: 0,
+
+ // Force the return method into a dynamic slot.
+ x0: 0, x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0,
+ x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0,
+ x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0,
+
+ next() {
+ return { value: this.i++, done: false }
+ },
+ return() {
+ counter += 1;
+ return { value: undefined, done: true };
+ }
+ };
+ }
+}
+
+function closeIter(o) {
+ for (var x of o) {
+ if (x == 2) {
+ break;
+ }
+ }
+}
+
+function test() {
+ with ({}) {}
+ counter = 0;
+ for (var i = 0; i < 100; i++) {
+ closeIter(iterable)
+ }
+ assertEq(counter, 100)
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+closeIter([1,2,3]);
+
+test();
diff --git a/js/src/jit-test/tests/for-of/iterclose-extra-args-throw.js b/js/src/jit-test/tests/for-of/iterclose-extra-args-throw.js
new file mode 100644
index 0000000000..3f7346ca2f
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-extra-args-throw.js
@@ -0,0 +1,49 @@
+const iterable = {
+ [Symbol.iterator]() {
+ return {
+ i: 0,
+ next() {
+ return { value: this.i++, done: false }
+ },
+ return(a, b, c, d) {
+ assertEq(a, undefined);
+ assertEq(b, undefined);
+ assertEq(c, undefined);
+ assertEq(d, undefined);
+ return { value: "return", done: true };
+ }
+ };
+ }
+}
+
+function closeIter(o) {
+ for (var x of o) {
+ if (x == 2) {
+ throw "good";
+ }
+ }
+}
+
+function test() {
+ with ({}) {}
+ for (var i = 0; i < 100; i++) {
+ var caught = "bad";
+ try {
+ closeIter(iterable)
+ } catch (e) {
+ caught = e;
+ }
+ assertEq(caught, "good");
+ }
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+try {
+ closeIter([1,2,3,4,5]);
+} catch {}
+
+test();
diff --git a/js/src/jit-test/tests/for-of/iterclose-extra-args.js b/js/src/jit-test/tests/for-of/iterclose-extra-args.js
new file mode 100644
index 0000000000..e0564d14d6
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-extra-args.js
@@ -0,0 +1,41 @@
+const iterable = {
+ [Symbol.iterator]() {
+ return {
+ i: 0,
+ next() {
+ return { value: this.i++, done: false }
+ },
+ return(a, b, c, d) {
+ assertEq(a, undefined);
+ assertEq(b, undefined);
+ assertEq(c, undefined);
+ assertEq(d, undefined);
+ return { value: "return", done: true };
+ }
+ };
+ }
+}
+
+function closeIter(o) {
+ for (var x of o) {
+ if (x == 2) {
+ break;
+ }
+ }
+}
+
+function test() {
+ with ({}) {}
+ for (var i = 0; i < 100; i++) {
+ closeIter(iterable)
+ }
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+closeIter([1,2,3,4,5]);
+
+test();
diff --git a/js/src/jit-test/tests/for-of/iterclose-generator-throw.js b/js/src/jit-test/tests/for-of/iterclose-generator-throw.js
new file mode 100644
index 0000000000..f380b9796d
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-generator-throw.js
@@ -0,0 +1,46 @@
+var finallyCount = 0;
+
+function* gen(o) {
+ try {
+ yield 1;
+ yield 2;
+ yield 3;
+ } finally {
+ finallyCount++;
+ }
+}
+
+function closeIter(o) {
+ for (var x of o) {
+ if (x == 2) {
+ throw "good"
+ }
+ }
+}
+
+function test() {
+ with ({}) {}
+ finallyCount = 0;
+
+ for (var i = 0; i < 100; i++) {
+ var caught = "bad";
+ try {
+ closeIter(gen());
+ } catch (e) {
+ caught = e;
+ }
+ assertEq(caught, "good");
+ }
+ assertEq(finallyCount, 100);
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+try {
+ closeIter([1,2,3,4,5]);
+} catch {}
+
+test();
diff --git a/js/src/jit-test/tests/for-of/iterclose-generator.js b/js/src/jit-test/tests/for-of/iterclose-generator.js
new file mode 100644
index 0000000000..3068e7c8ed
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-generator.js
@@ -0,0 +1,38 @@
+var finallyCount = 0;
+
+function* gen(o) {
+ try {
+ yield 1;
+ yield 2;
+ yield 3;
+ } finally {
+ finallyCount++;
+ }
+}
+
+function closeIter(o) {
+ for (var x of o) {
+ if (x == 2) {
+ break;
+ }
+ }
+}
+
+function test() {
+ with ({}) {}
+ finallyCount = 0;
+
+ for (var i = 0; i < 100; i++) {
+ closeIter(gen());
+ }
+ assertEq(finallyCount, 100);
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+closeIter([1,2,3,4,5]);
+
+test();
diff --git a/js/src/jit-test/tests/for-of/iterclose-invalid-return-throw.js b/js/src/jit-test/tests/for-of/iterclose-invalid-return-throw.js
new file mode 100644
index 0000000000..599b6479fa
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-invalid-return-throw.js
@@ -0,0 +1,48 @@
+var returnInvalid = false;
+
+const iterable = {
+ [Symbol.iterator]() {
+ return {
+ i: 0,
+ next() {
+ return { value: this.i++, done: false }
+ },
+ return() {
+ return returnInvalid ? undefined : { value: "return", done: true };
+ }
+ };
+ }
+}
+
+function closeIter(o) {
+ for (var x of o) {
+ if (x == 2) {
+ throw "good";
+ }
+ }
+}
+
+function test() {
+ with ({}) {}
+ for (var i = 0; i < 1000; i++) {
+ returnInvalid = i % 10 == 0;
+ var caught = "bad";
+ try {
+ closeIter(iterable);
+ } catch (e) {
+ caught = e;
+ }
+ assertEq(caught, "good");
+ }
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+try {
+ closeIter([1,2,3,4,5]);
+} catch {}
+
+test();
diff --git a/js/src/jit-test/tests/for-of/iterclose-invalid-return.js b/js/src/jit-test/tests/for-of/iterclose-invalid-return.js
new file mode 100644
index 0000000000..bda448edc7
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-invalid-return.js
@@ -0,0 +1,46 @@
+var returnInvalid = false;
+
+const iterable = {
+ [Symbol.iterator]() {
+ return {
+ i: 0,
+ next() {
+ return { value: this.i++, done: false }
+ },
+ return() {
+ return returnInvalid ? undefined : { value: "return", done: true };
+ }
+ };
+ }
+}
+
+function closeIter(o) {
+ for (var x of o) {
+ if (x == 2) {
+ break;
+ }
+ }
+}
+
+function test() {
+ with ({}) {}
+ for (var i = 0; i < 1000; i++) {
+ returnInvalid = i % 10 == 0;
+ var caught = false;
+ try {
+ closeIter(iterable);
+ } catch {
+ caught = true;
+ }
+ assertEq(caught, returnInvalid);
+ }
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+closeIter([1,2,3,4,5]);
+
+test();
diff --git a/js/src/jit-test/tests/for-of/iterclose-invalidate-with-catch.js b/js/src/jit-test/tests/for-of/iterclose-invalidate-with-catch.js
new file mode 100644
index 0000000000..5f5cdd7645
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-invalidate-with-catch.js
@@ -0,0 +1,54 @@
+var invalidate = false;
+var caught = false;
+
+const iterable = {
+ [Symbol.iterator]() {
+ return {
+ i: 0,
+ next() {
+ return { value: this.i++, done: false }
+ },
+ return() {
+ if (invalidate) {
+ // Invalidate Ion scripts.
+ gc(closeIter, 'shrinking');
+ return undefined;
+ }
+ return { value: "return", done: true };
+ }
+ };
+ }
+}
+
+function closeIter(o) {
+ try {
+ for (var x of o) {
+ if (x == 2) {
+ break;
+ }
+ }
+ } catch(e) {
+ caught = true;
+ }
+}
+
+function test() {
+ with ({}) {}
+ for (var i = 0; i < 100; i++) {
+ closeIter(iterable);
+ }
+ invalidate = true;
+ closeIter(iterable);
+ assertEq(caught, true);
+ invalidate = false;
+ caught = false;
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+closeIter([1,2,3,4,5]);
+
+test();
diff --git a/js/src/jit-test/tests/for-of/iterclose-invalidate-with-finally.js b/js/src/jit-test/tests/for-of/iterclose-invalidate-with-finally.js
new file mode 100644
index 0000000000..fde21bd6b9
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-invalidate-with-finally.js
@@ -0,0 +1,55 @@
+var invalidate = false;
+var finallyCount = 0;
+
+const iterable = {
+ [Symbol.iterator]() {
+ return {
+ i: 0,
+ next() {
+ return { value: this.i++, done: false }
+ },
+ return() {
+ if (invalidate) {
+ // Invalidate Ion scripts.
+ gc(closeIter, 'shrinking');
+ return undefined;
+ }
+ return { value: "return", done: true };
+ }
+ };
+ }
+}
+
+function closeIter(o) {
+ try {
+ for (var x of o) {
+ if (x == 2) {
+ break;
+ }
+ }
+ } finally {
+ finallyCount++;
+ }
+}
+
+function test() {
+ with ({}) {}
+ for (var i = 0; i < 100; i++) {
+ closeIter(iterable);
+ }
+ invalidate = true;
+ try {
+ closeIter(iterable);
+ } catch {}
+ invalidate = false;
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+closeIter([1,2,3,4,5]);
+
+test();
+assertEq(finallyCount, 203);
diff --git a/js/src/jit-test/tests/for-of/iterclose-invalidate.js b/js/src/jit-test/tests/for-of/iterclose-invalidate.js
new file mode 100644
index 0000000000..ac23362924
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/iterclose-invalidate.js
@@ -0,0 +1,53 @@
+var returnInvalid = false;
+
+const iterable = {
+ [Symbol.iterator]() {
+ return {
+ i: 0,
+ next() {
+ return { value: this.i++, done: false }
+ },
+ return() {
+ if (returnInvalid) {
+ // Invalidate Ion scripts.
+ gc(closeIter, 'shrinking');
+ return undefined;
+ }
+ return { value: "return", done: true };
+ }
+ };
+ }
+}
+
+function closeIter(o) {
+ for (var x of o) {
+ if (x == 2) {
+ break;
+ }
+ }
+}
+
+function test() {
+ with ({}) {}
+ for (var i = 0; i < 100; i++) {
+ closeIter(iterable);
+ }
+ returnInvalid = true;
+ var caught = false;
+ try {
+ closeIter(iterable);
+ } catch {
+ caught = true;
+ }
+ assertEq(caught, true);
+ returnInvalid = false;
+}
+
+with ({}) {}
+
+test();
+
+// Force an IC in Ion.
+closeIter([1,2,3,4,5]);
+
+test();
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');