summaryrefslogtreecommitdiffstats
path: root/js/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests')
-rw-r--r--js/src/tests/ecma_6/Destructuring/array-iterator-close.js93
-rw-r--r--js/src/tests/ecma_6/Generators/delegating-yield-2.js20
-rw-r--r--js/src/tests/ecma_6/Generators/yield-iterator-close.js58
-rw-r--r--js/src/tests/ecma_6/Generators/yield-star-iterator-close.js153
-rw-r--r--js/src/tests/ecma_6/Statements/for-inof-finally.js78
-rw-r--r--js/src/tests/ecma_6/Statements/for-of-iterator-close-throw.js35
-rw-r--r--js/src/tests/ecma_6/Statements/for-of-iterator-close.js102
-rw-r--r--js/src/tests/ecma_6/shell.js52
8 files changed, 567 insertions, 24 deletions
diff --git a/js/src/tests/ecma_6/Destructuring/array-iterator-close.js b/js/src/tests/ecma_6/Destructuring/array-iterator-close.js
new file mode 100644
index 000000000..ed35135db
--- /dev/null
+++ b/js/src/tests/ecma_6/Destructuring/array-iterator-close.js
@@ -0,0 +1,93 @@
+// Tests that IteratorClose is called in array destructuring patterns.
+
+function test() {
+ var returnCalled = 0;
+ var returnCalledExpected = 0;
+ var iterable = {};
+
+ // empty [] calls IteratorClose regardless of "done" on the result.
+ iterable[Symbol.iterator] = makeIterator({
+ next: function() {
+ return { done: true };
+ },
+ ret: function() {
+ returnCalled++;
+ return {};
+ }
+ });
+ var [] = iterable;
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ iterable[Symbol.iterator] = makeIterator({
+ ret: function() {
+ returnCalled++;
+ return {};
+ }
+ });
+ var [] = iterable;
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // Non-empty destructuring calls IteratorClose if iterator is not done by
+ // the end of destructuring.
+ var [a,b] = iterable;
+ assertEq(returnCalled, ++returnCalledExpected);
+ var [c,] = iterable;
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // throw in lhs ref calls IteratorClose
+ function throwlhs() {
+ throw "in lhs";
+ }
+ assertThrowsValue(function() {
+ 0, [...{}[throwlhs()]] = iterable;
+ }, "in lhs");
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // throw in lhs ref calls IteratorClose with falsy "done".
+ iterable[Symbol.iterator] = makeIterator({
+ next: function() {
+ // "done" is undefined.
+ return {};
+ },
+ ret: function() {
+ returnCalled++;
+ return {};
+ }
+ });
+ assertThrowsValue(function() {
+ 0, [...{}[throwlhs()]] = iterable;
+ }, "in lhs");
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // throw in iter.next doesn't call IteratorClose
+ iterable[Symbol.iterator] = makeIterator({
+ next: function() {
+ throw "in next";
+ },
+ ret: function() {
+ returnCalled++;
+ return {};
+ }
+ });
+ assertThrowsValue(function() {
+ var [d] = iterable;
+ }, "in next");
+ assertEq(returnCalled, returnCalledExpected);
+
+ // "return" must return an Object.
+ iterable[Symbol.iterator] = makeIterator({
+ ret: function() {
+ returnCalled++;
+ return 42;
+ }
+ });
+ assertThrowsInstanceOf(function() {
+ var [] = iterable;
+ }, TypeError);
+ assertEq(returnCalled, ++returnCalledExpected);
+}
+
+test();
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-2.js b/js/src/tests/ecma_6/Generators/delegating-yield-2.js
index 918fb33e1..34cb3f4a9 100644
--- a/js/src/tests/ecma_6/Generators/delegating-yield-2.js
+++ b/js/src/tests/ecma_6/Generators/delegating-yield-2.js
@@ -25,8 +25,8 @@ assertThrowsValue(function () { outer.throw(42) }, 42);
inner = g1();
outer = delegate(inner);
assertIteratorNext(outer, 1);
-inner.throw = function(e) { return e*2; };
-assertEq(84, outer.throw(42));
+inner.throw = function(e) { return { value: e*2 }; };
+assertEq(84, outer.throw(42).value);
assertIteratorDone(outer, undefined);
// Monkeypatching inner.next.
@@ -41,7 +41,9 @@ outer = delegate(inner);
assertIteratorNext(outer, 1);
delete GeneratorObjectPrototype.throw;
var outer_throw_42 = GeneratorObjectPrototype_throw.bind(outer, 42);
-assertThrowsValue(outer_throw_42, 42);
+// yield* protocol violation: no 'throw' method
+assertThrowsInstanceOf(outer_throw_42, TypeError);
+// Now done, so just throws.
assertThrowsValue(outer_throw_42, 42);
// Monkeypunch a different throw handler.
@@ -49,11 +51,11 @@ inner = g2();
outer = delegate(inner);
outer_throw_42 = GeneratorObjectPrototype_throw.bind(outer, 42);
assertIteratorNext(outer, 1);
-GeneratorObjectPrototype.throw = function(e) { return e*2; }
-assertEq(84, outer_throw_42());
-assertEq(84, outer_throw_42());
+GeneratorObjectPrototype.throw = function(e) { return { value: e*2 }; }
+assertEq(84, outer_throw_42().value);
+assertEq(84, outer_throw_42().value);
// This continues indefinitely.
-assertEq(84, outer_throw_42());
+assertEq(84, outer_throw_42().value);
assertIteratorDone(outer, undefined);
// The same, but restoring the original pre-monkey throw.
@@ -61,8 +63,8 @@ inner = g2();
outer = delegate(inner);
outer_throw_42 = GeneratorObjectPrototype_throw.bind(outer, 42);
assertIteratorNext(outer, 1);
-assertEq(84, outer_throw_42());
-assertEq(84, outer_throw_42());
+assertEq(84, outer_throw_42().value);
+assertEq(84, outer_throw_42().value);
GeneratorObjectPrototype.throw = GeneratorObjectPrototype_throw;
assertIteratorResult(outer_throw_42(), 42, false);
assertIteratorDone(outer, undefined);
diff --git a/js/src/tests/ecma_6/Generators/yield-iterator-close.js b/js/src/tests/ecma_6/Generators/yield-iterator-close.js
new file mode 100644
index 000000000..970ad494d
--- /dev/null
+++ b/js/src/tests/ecma_6/Generators/yield-iterator-close.js
@@ -0,0 +1,58 @@
+// Test that IteratorClose is called when a Generator is abruptly completed by
+// Generator.return.
+
+var returnCalled = 0;
+function* wrapNoThrow() {
+ let iter = {
+ [Symbol.iterator]() {
+ return this;
+ },
+ next() {
+ return { value: 10, done: false };
+ },
+ return() {
+ returnCalled++;
+ return {};
+ }
+ };
+ for (const i of iter) {
+ yield i;
+ }
+}
+
+// Breaking calls Generator.return, which causes the yield above to resume with
+// an abrupt completion of kind "return", which then calls
+// iter.return.
+for (const i of wrapNoThrow()) {
+ break;
+}
+assertEq(returnCalled, 1);
+
+function* wrapThrow() {
+ let iter = {
+ [Symbol.iterator]() {
+ return this;
+ },
+ next() {
+ return { value: 10, done: false };
+ },
+ return() {
+ throw 42;
+ }
+ };
+ for (const i of iter) {
+ yield i;
+ }
+}
+
+// Breaking calls Generator.return, which, like above, calls iter.return. If
+// iter.return throws, since the yield is resuming with an abrupt completion of
+// kind "return", the newly thrown value takes precedence over returning.
+assertThrowsValue(() => {
+ for (const i of wrapThrow()) {
+ break;
+ }
+}, 42);
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/Generators/yield-star-iterator-close.js b/js/src/tests/ecma_6/Generators/yield-star-iterator-close.js
new file mode 100644
index 000000000..91ad31cb6
--- /dev/null
+++ b/js/src/tests/ecma_6/Generators/yield-star-iterator-close.js
@@ -0,0 +1,153 @@
+// Tests that the "return" method on iterators is called in yield*
+// expressions.
+
+function test() {
+ var returnCalled = 0;
+ var returnCalledExpected = 0;
+ var nextCalled = 0;
+ var nextCalledExpected = 0;
+ var throwCalled = 0;
+ var throwCalledExpected = 0;
+ var iterable = {};
+ iterable[Symbol.iterator] = makeIterator({
+ next: function() {
+ nextCalled++;
+ return { done: false };
+ },
+ ret: function() {
+ returnCalled++;
+ return { done: true, value: "iter.return" };
+ }
+ });
+
+ function* y() {
+ yield* iterable;
+ }
+
+ // G.p.throw on an iterator without "throw" calls IteratorClose.
+ var g1 = y();
+ g1.next();
+ assertThrowsInstanceOf(function() {
+ g1.throw("foo");
+ }, TypeError);
+ assertEq(returnCalled, ++returnCalledExpected);
+ assertEq(nextCalled, ++nextCalledExpected);
+ g1.next();
+ assertEq(nextCalled, nextCalledExpected);
+
+ // G.p.return calls "return", and if the result.done is true, return the
+ // result.
+ var g2 = y();
+ g2.next();
+ var v2 = g2.return("test return");
+ assertEq(v2.done, true);
+ assertEq(v2.value, "iter.return");
+ assertEq(returnCalled, ++returnCalledExpected);
+ assertEq(nextCalled, ++nextCalledExpected);
+ g2.next();
+ assertEq(nextCalled, nextCalledExpected);
+
+ // G.p.return calls "return", and if the result.done is false, continue
+ // yielding.
+ iterable[Symbol.iterator] = makeIterator({
+ next: function() {
+ nextCalled++;
+ return { done: false };
+ },
+ ret: function() {
+ returnCalled++;
+ return { done: false, value: "iter.return" };
+ }
+ });
+ var g3 = y();
+ g3.next();
+ var v3 = g3.return("test return");
+ assertEq(v3.done, false);
+ assertEq(v3.value, "iter.return");
+ assertEq(returnCalled, ++returnCalledExpected);
+ assertEq(nextCalled, ++nextCalledExpected);
+ g3.next();
+ assertEq(nextCalled, ++nextCalledExpected);
+
+ // G.p.return throwing does not re-call iter.return.
+ iterable[Symbol.iterator] = makeIterator({
+ ret: function() {
+ returnCalled++;
+ throw "in iter.return";
+ }
+ });
+ var g4 = y();
+ g4.next();
+ assertThrowsValue(function() {
+ g4.return("in test");
+ }, "in iter.return");
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // G.p.return expects iter.return to return an Object.
+ iterable[Symbol.iterator] = makeIterator({
+ ret: function() {
+ returnCalled++;
+ return 42;
+ }
+ });
+ var g5 = y();
+ g5.next();
+ assertThrowsInstanceOf(function() {
+ g5.return("foo");
+ }, TypeError);
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // IteratorClose expects iter.return to return an Object.
+ var g6 = y();
+ g6.next();
+ var exc;
+ try {
+ g6.throw("foo");
+ } catch (e) {
+ exc = e;
+ } finally {
+ assertEq(exc instanceof TypeError, true);
+ // The message test is here because instanceof TypeError doesn't
+ // distinguish the non-Object return TypeError and the
+ // throw-method-is-not-defined iterator protocol error.
+ assertEq(exc.toString().indexOf("non-object") > 0, true);
+ }
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // G.p.return passes its argument to "return".
+ iterable[Symbol.iterator] = makeIterator({
+ ret: function(x) {
+ assertEq(x, "in test");
+ returnCalled++;
+ return { done: true };
+ }
+ });
+ var g7 = y();
+ g7.next();
+ g7.return("in test");
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // If a throw method is present, do not call "return".
+ iterable[Symbol.iterator] = makeIterator({
+ throw: function(e) {
+ throwCalled++;
+ throw e;
+ },
+ ret: function(x) {
+ returnCalled++;
+ return { done: true };
+ }
+ });
+ var g8 = y();
+ g8.next();
+ assertThrowsValue(function() {
+ g8.throw("foo");
+ }, "foo");
+ assertEq(throwCalled, ++throwCalledExpected);
+ assertEq(returnCalled, returnCalledExpected);
+}
+
+test();
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/Statements/for-inof-finally.js b/js/src/tests/ecma_6/Statements/for-inof-finally.js
new file mode 100644
index 000000000..e1f0c77e1
--- /dev/null
+++ b/js/src/tests/ecma_6/Statements/for-inof-finally.js
@@ -0,0 +1,78 @@
+var BUGNUMBER = 1332881;
+var summary =
+ "Leaving for-in and try should handle stack value in correct order";
+
+print(BUGNUMBER + ": " + summary);
+
+var called = 0;
+function reset() {
+ called = 0;
+}
+var obj = {
+ [Symbol.iterator]() {
+ return {
+ next() {
+ return { value: 10, done: false };
+ },
+ return() {
+ called++;
+ return {};
+ }
+ };
+ }
+};
+
+var a = (function () {
+ for (var x in [0]) {
+ try {} finally {
+ return 11;
+ }
+ }
+})();
+assertEq(a, 11);
+
+reset();
+var b = (function () {
+ for (var x of obj) {
+ try {} finally {
+ return 12;
+ }
+ }
+})();
+assertEq(called, 1);
+assertEq(b, 12);
+
+reset();
+var c = (function () {
+ for (var x in [0]) {
+ for (var y of obj) {
+ try {} finally {
+ return 13;
+ }
+ }
+ }
+})();
+assertEq(called, 1);
+assertEq(c, 13);
+
+reset();
+var d = (function () {
+ for (var x in [0]) {
+ for (var y of obj) {
+ try {} finally {
+ for (var z in [0]) {
+ for (var w of obj) {
+ try {} finally {
+ return 14;
+ }
+ }
+ }
+ }
+ }
+ }
+})();
+assertEq(called, 2);
+assertEq(d, 14);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/Statements/for-of-iterator-close-throw.js b/js/src/tests/ecma_6/Statements/for-of-iterator-close-throw.js
new file mode 100644
index 000000000..1974e416b
--- /dev/null
+++ b/js/src/tests/ecma_6/Statements/for-of-iterator-close-throw.js
@@ -0,0 +1,35 @@
+function test() {
+ var returnCalled = 0;
+ var returnCalledExpected = 0;
+ var catchEntered = 0;
+ var finallyEntered = 0;
+ var finallyEnteredExpected = 0;
+ var iterable = {};
+ iterable[Symbol.iterator] = makeIterator({
+ ret: function() {
+ returnCalled++;
+ throw 42;
+ }
+ });
+
+ // inner try cannot catch IteratorClose throwing
+ assertThrowsValue(function() {
+ for (var x of iterable) {
+ try {
+ return;
+ } catch (e) {
+ catchEntered++;
+ } finally {
+ finallyEntered++;
+ }
+ }
+ }, 42);
+ assertEq(returnCalled, ++returnCalledExpected);
+ assertEq(catchEntered, 0);
+ assertEq(finallyEntered, ++finallyEnteredExpected);
+}
+
+test();
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/Statements/for-of-iterator-close.js b/js/src/tests/ecma_6/Statements/for-of-iterator-close.js
new file mode 100644
index 000000000..b28895d8f
--- /dev/null
+++ b/js/src/tests/ecma_6/Statements/for-of-iterator-close.js
@@ -0,0 +1,102 @@
+// Tests that IteratorReturn is called when a for-of loop has an abrupt
+// completion value during non-iterator code.
+
+function test() {
+ var returnCalled = 0;
+ var returnCalledExpected = 0;
+ var iterable = {};
+ iterable[Symbol.iterator] = makeIterator({
+ ret: function() {
+ returnCalled++;
+ return {};
+ }
+ });
+
+ // break calls iter.return
+ for (var x of iterable)
+ break;
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // throw in body calls iter.return
+ assertThrowsValue(function() {
+ for (var x of iterable)
+ throw "in body";
+ }, "in body");
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // throw in lhs ref calls iter.return
+ function throwlhs() {
+ throw "in lhs";
+ }
+ assertThrowsValue(function() {
+ for ((throwlhs()) of iterable)
+ continue;
+ }, "in lhs");
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // throw in iter.return doesn't re-call iter.return
+ iterable[Symbol.iterator] = makeIterator({
+ ret: function() {
+ returnCalled++;
+ throw "in iter.return";
+ }
+ });
+ assertThrowsValue(function() {
+ for (var x of iterable)
+ break;
+ }, "in iter.return");
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // throw in iter.next doesn't call IteratorClose
+ iterable[Symbol.iterator] = makeIterator({
+ next: function() {
+ throw "in next";
+ }
+ });
+ assertThrowsValue(function() {
+ for (var x of iterable)
+ break;
+ }, "in next");
+ assertEq(returnCalled, returnCalledExpected);
+
+ // "return" must return an Object.
+ iterable[Symbol.iterator] = makeIterator({
+ ret: function() {
+ returnCalled++;
+ return 42;
+ }
+ });
+ assertThrowsInstanceOf(function() {
+ for (var x of iterable)
+ break;
+ }, TypeError);
+ assertEq(returnCalled, ++returnCalledExpected);
+
+ // continue doesn't call iter.return for the loop it's continuing
+ var i = 0;
+ iterable[Symbol.iterator] = makeIterator({
+ next: function() {
+ return { done: i++ > 5 };
+ },
+ ret: function() {
+ returnCalled++;
+ return {};
+ }
+ });
+ for (var x of iterable)
+ continue;
+ assertEq(returnCalled, returnCalledExpected);
+
+ // continue does call iter.return for loops it skips
+ i = 0;
+ L: do {
+ for (var x of iterable)
+ continue L;
+ } while (false);
+ assertEq(returnCalled, ++returnCalledExpected);
+}
+
+test();
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/shell.js b/js/src/tests/ecma_6/shell.js
index 06be6f2db..756da9f36 100644
--- a/js/src/tests/ecma_6/shell.js
+++ b/js/src/tests/ecma_6/shell.js
@@ -3,21 +3,43 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(global) {
- /** Yield every permutation of the elements in some array. */
- global.Permutations = function* Permutations(items) {
- if (items.length == 0) {
- yield [];
- } else {
- items = items.slice(0);
- for (let i = 0; i < items.length; i++) {
- let swap = items[0];
- items[0] = items[i];
- items[i] = swap;
- for (let e of Permutations(items.slice(1, items.length)))
- yield [items[0]].concat(e);
- }
- }
- };
+ /** Yield every permutation of the elements in some array. */
+ global.Permutations = function* Permutations(items) {
+ if (items.length == 0) {
+ yield [];
+ } else {
+ items = items.slice(0);
+ for (let i = 0; i < items.length; i++) {
+ let swap = items[0];
+ items[0] = items[i];
+ items[i] = swap;
+ for (let e of Permutations(items.slice(1, items.length)))
+ yield [items[0]].concat(e);
+ }
+ }
+ };
+
+ /** Make an iterator with a return method. */
+ global.makeIterator = function makeIterator(overrides) {
+ var throwMethod;
+ if (overrides && overrides.throw)
+ throwMethod = overrides.throw;
+ var iterator = {
+ throw: throwMethod,
+ next: function(x) {
+ if (overrides && overrides.next)
+ return overrides.next(x);
+ return { done: false };
+ },
+ return: function(x) {
+ if (overrides && overrides.ret)
+ return overrides.ret(x);
+ return { done: true };
+ }
+ };
+
+ return function() { return iterator; };
+ };
})(this);
if (typeof assertThrowsInstanceOf === 'undefined') {