diff options
Diffstat (limited to 'js/src/tests/ecma_6/Generators')
21 files changed, 1833 insertions, 0 deletions
diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-1.js b/js/src/tests/ecma_6/Generators/delegating-yield-1.js new file mode 100644 index 000000000..f9df1d7f8 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-1.js @@ -0,0 +1,42 @@ +// This file was written by Andy Wingo <wingo@igalia.com> and originally +// contributed to V8 as generators-objects.js, available here: +// +// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-objects.js + +// Test that yield* re-yields received results without re-boxing. + +function results(results) { + var i = 0; + function next() { + return results[i++]; + } + var iter = { next: next } + var ret = {}; + ret[Symbol.iterator] = function () { return iter; } + return ret; +} + +function* yield_results(expected) { + return yield* results(expected); +} + +function collect_results(iterable) { + var ret = []; + var result; + var iter = iterable[Symbol.iterator](); + do { + result = iter.next(); + ret.push(result); + } while (!result.done); + return ret; +} + +// We have to put a full result for the end, because the return will re-box. +var expected = [{value: 1}, {value: 34, done: true}]; + +// Sanity check. +assertDeepEq(expected, collect_results(results(expected))); +assertDeepEq(expected, collect_results(yield_results(expected))); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-10.js b/js/src/tests/ecma_6/Generators/delegating-yield-10.js new file mode 100644 index 000000000..1f317023e --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-10.js @@ -0,0 +1,49 @@ +// Errors accessing next, done, or value don't cause an exception to be +// thrown into the iterator of a yield*. + +function* g(n) { for (var i=0; i<n; i++) yield i; } +function* delegate(iter) { return yield* iter; } + +var log = "", inner, outer; + +// That var is poisoooooon, p-poison poison... +var Poison = new Error; + +function log_calls(method) { + return function () { + log += "x" + return method.call(this); + } +} + +function poison(receiver, prop) { + Object.defineProperty(receiver, prop, { get: function () { throw Poison } }); +} + +// Poison inner.next. +inner = g(10); +outer = delegate(inner); +inner.throw = log_calls(inner.throw); +poison(inner, 'next') +assertThrowsValue(outer.next.bind(outer), Poison); +assertEq(log, ""); + +// Poison result value from inner. +inner = g(10); +outer = delegate(inner); +inner.next = function () { return { done: true, get value() { throw Poison} } }; +inner.throw = log_calls(inner.throw); +assertThrowsValue(outer.next.bind(outer), Poison); +assertEq(log, ""); + +// Poison result done from inner. +inner = g(10); +outer = delegate(inner); +inner.next = function () { return { get done() { throw Poison }, value: 42 } }; +inner.throw = log_calls(inner.throw); +assertThrowsValue(outer.next.bind(outer), Poison); +assertEq(log, ""); + +// mischief managed. +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-11.js b/js/src/tests/ecma_6/Generators/delegating-yield-11.js new file mode 100644 index 000000000..f7e6650fb --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-11.js @@ -0,0 +1,20 @@ +// The first call to yield* passes one arg to "next". + +function Iter() { + function next() { + if (arguments.length != 1) + throw Error; + return { value: 42, done: true } + } + + this.next = next; + this[Symbol.iterator] = function () { return this; } +} + +function* delegate(iter) { return yield* iter; } + +var iter = delegate(new Iter()); +assertDeepEq(iter.next(), {value:42, done:true}); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-12.js b/js/src/tests/ecma_6/Generators/delegating-yield-12.js new file mode 100644 index 000000000..9ed7666e1 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-12.js @@ -0,0 +1,49 @@ +// yield* calls @@iterator on the iterable to produce the iterator. + +var log = ''; + +function IteratorWrapper(iterator) { + return { + next: function (val) { + log += 'n'; + return iterator.next(val); + }, + + throw: function (exn) { + log += 't'; + return iterator.throw(exn); + } + }; +} + +function IterableWrapper(iterable) { + var ret = {}; + + ret[Symbol.iterator] = function () { + log += 'i'; + return IteratorWrapper(iterable[Symbol.iterator]()); + } + + return ret; +} + +function* delegate(iter) { return yield* iter; } + +var iter = delegate(IterableWrapper([1, 2, 3])); +assertIteratorNext(iter, 1); +assertIteratorNext(iter, 2); +assertIteratorNext(iter, 3); +assertIteratorDone(iter, undefined); + +assertEq(log, 'innnn'); + +iter = delegate([1, 2, 3]); +assertIteratorNext(iter, 1); +assertIteratorNext(iter, 2); +assertIteratorNext(iter, 3); +assertIteratorDone(iter, undefined); + +assertEq(log, 'innnn'); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-2.js b/js/src/tests/ecma_6/Generators/delegating-yield-2.js new file mode 100644 index 000000000..918fb33e1 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-2.js @@ -0,0 +1,71 @@ +// Test yield* with iter.throw and monkeypatching. + +function* g1() { return (yield 1); } +function* g2() { try { yield 1; } catch (e) { yield e; } } +function* delegate(iter) { return yield* iter; } +var GeneratorObjectPrototype = Object.getPrototypeOf(g1).prototype; +var GeneratorObjectPrototype_throw = GeneratorObjectPrototype.throw; + +// An uncaught delegated throw. +var inner = g1(); +var outer = delegate(inner); +assertIteratorNext(outer, 1); +assertThrowsValue(function () { outer.throw(42) }, 42); +assertThrowsValue(function () { outer.throw(42) }, 42); + +// A caught delegated throw. +inner = g2(); +outer = delegate(inner); +assertIteratorNext(outer, 1); +assertIteratorResult(outer.throw(42), 42, false); +assertThrowsValue(function () { outer.throw(42) }, 42); +assertThrowsValue(function () { outer.throw(42) }, 42); + +// What would be an uncaught delegated throw, but with a monkeypatched iterator. +inner = g1(); +outer = delegate(inner); +assertIteratorNext(outer, 1); +inner.throw = function(e) { return e*2; }; +assertEq(84, outer.throw(42)); +assertIteratorDone(outer, undefined); + +// Monkeypatching inner.next. +inner = g1(); +outer = delegate(inner); +inner.next = function() { return { value: 13, done: true } }; +assertIteratorDone(outer, 13); + +// What would be a caught delegated throw, but with a monkeypunched prototype. +inner = g2(); +outer = delegate(inner); +assertIteratorNext(outer, 1); +delete GeneratorObjectPrototype.throw; +var outer_throw_42 = GeneratorObjectPrototype_throw.bind(outer, 42); +assertThrowsValue(outer_throw_42, 42); +assertThrowsValue(outer_throw_42, 42); + +// Monkeypunch a different throw handler. +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()); +// This continues indefinitely. +assertEq(84, outer_throw_42()); +assertIteratorDone(outer, undefined); + +// The same, but restoring the original pre-monkey throw. +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()); +GeneratorObjectPrototype.throw = GeneratorObjectPrototype_throw; +assertIteratorResult(outer_throw_42(), 42, false); +assertIteratorDone(outer, undefined); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-3.js b/js/src/tests/ecma_6/Generators/delegating-yield-3.js new file mode 100644 index 000000000..34800f1b0 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-3.js @@ -0,0 +1,40 @@ +// Test yield* with iter.next and monkeypatching. + +function* g(n) { for (var i=0; i<n; i++) yield i; } +function* delegate(iter) { return yield* iter; } +var GeneratorObjectPrototype = Object.getPrototypeOf(g).prototype; +var GeneratorObjectPrototype_next = GeneratorObjectPrototype.next; + +// Monkeypatch next on an iterator. +var inner = g(20); +var outer = delegate(inner); +assertIteratorNext(outer, 0); +assertIteratorNext(outer, 1); +inner.next = function() { return { patched: true }; }; +// 42 yielded directly without re-boxing. +assertEq(true, outer.next().patched); +// Outer generator not terminated. +assertEq(true, outer.next().patched); +// Restore. +inner.next = GeneratorObjectPrototype_next; +assertIteratorNext(outer, 2); +// Repatch. +inner.next = function() { return { value: 42, done: true }; }; +assertIteratorDone(outer, 42); + +// Monkeypunch next on the prototype. +var inner = g(20); +var outer = delegate(inner); +assertIteratorNext(outer, 0); +assertIteratorNext(outer, 1); +GeneratorObjectPrototype.next = function() { return { patched: true }; }; +// 42 yielded directly without re-boxing. +assertEq(true, GeneratorObjectPrototype_next.call(outer).patched); +// Outer generator not terminated. +assertEq(true, GeneratorObjectPrototype_next.call(outer).patched); +// Restore. +GeneratorObjectPrototype.next = GeneratorObjectPrototype_next; +assertIteratorNext(outer, 2); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-4.js b/js/src/tests/ecma_6/Generators/delegating-yield-4.js new file mode 100644 index 000000000..c07673172 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-4.js @@ -0,0 +1,18 @@ +// With yield*, inner and outer iterators can be invoked separately. + +function* g(n) { for (var i=0; i<n; i++) yield i; } +function* delegate(iter) { return yield* iter; } + +var inner = g(20); +var outer1 = delegate(inner); +var outer2 = delegate(inner); + +assertIteratorNext(outer1, 0); +assertIteratorNext(outer2, 1); +assertIteratorNext(inner, 2); +assertIteratorNext(outer1, 3); +assertIteratorNext(outer2, 4); +assertIteratorNext(inner, 5); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-5.js b/js/src/tests/ecma_6/Generators/delegating-yield-5.js new file mode 100644 index 000000000..f4376743b --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-5.js @@ -0,0 +1,37 @@ +// Test that a deep yield* chain re-yields received results without +// re-boxing. + +function results(results) { + var i = 0; + function next() { + return results[i++]; + } + var iter = { next: next }; + var ret = {}; + ret[Symbol.iterator] = function () { return iter; } + return ret; +} + +function* yield_results(expected, n) { + return yield* n ? yield_results(expected, n - 1) : results(expected); +} + +function collect_results(iterable) { + var ret = []; + var result; + var iter = iterable[Symbol.iterator](); + do { + result = iter.next(); + ret.push(result); + } while (!result.done); + return ret; +} + +// We have to put a full result for the end, because the return will re-box. +var expected = [{value: 1}, {value: 34, done: true}]; + +assertDeepEq(expected, collect_results(results(expected))); +assertDeepEq(expected, collect_results(yield_results(expected, 20))); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-6.js b/js/src/tests/ecma_6/Generators/delegating-yield-6.js new file mode 100644 index 000000000..8b358525c --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-6.js @@ -0,0 +1,56 @@ +// Test that each yield* loop just checks "done", and "value" is only +// fetched once at the end. + +var log = ""; + +function collect_results(iter) { + var ret = []; + var result; + do { + result = iter.next(); + ret.push(result); + } while (!result.done); + return ret; +} + +function Iter(val, count) { + function next() { + log += 'n'; + return { + get done() { log += "d"; return count-- == 0; }, + get value() { log += "v"; return val; } + } + } + + function iterator() { + log += 'i'; + return this; + } + + this.next = next; + this[Symbol.iterator] = iterator; +} + +function* delegate(iter) { return yield* iter; } + +var inner = new Iter(42, 5); +var outer = delegate(inner); + +// Five values, and one terminal value. +outer.next(); +outer.next(); +outer.next(); +outer.next(); +outer.next(); +outer.next(); + +assertEq(log, "indndndndndndv"); + +// Outer's dead, man. Outer's dead. +assertDeepEq(outer.next(), {value: undefined, done: true}); + +// No more checking the iterator. +assertEq(log, "indndndndndndv"); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-7.js b/js/src/tests/ecma_6/Generators/delegating-yield-7.js new file mode 100644 index 000000000..f7c9e86cc --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-7.js @@ -0,0 +1,38 @@ +// The iteratee of yield* can be a proxy. + +function results(results) { + var i = 0; + function iterator() { + return this; + } + function next() { + return results[i++]; + } + var ret = { next: next } + ret[Symbol.iterator] = iterator; + return ret; +} + +function* yield_results(expected) { + return yield* new Proxy(results(expected), {}); +} + +function collect_results(iter) { + var ret = []; + var result; + do { + result = iter.next(); + ret.push(result); + } while (!result.done); + return ret; +} + +// We have to put a full result for the end, because the return will re-box. +var expected = [{value: 1}, {value: 34, done: true}]; + +// Sanity check. +assertDeepEq(expected, collect_results(results(expected))); +assertDeepEq(expected, collect_results(yield_results(expected))); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-8.js b/js/src/tests/ecma_6/Generators/delegating-yield-8.js new file mode 100644 index 000000000..c17d6ea26 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-8.js @@ -0,0 +1,44 @@ +// Test that yield* can appear in a loop, and alongside yield. + +function* countdown(n) { + while (n > 0) { + yield n; + yield* countdown(--n); + } + return 34; +} + +function collect_results(iter) { + var ret = []; + var result; + do { + result = iter.next(); + ret.push(result); + } while (!result.done); + return ret; +} + +var expected = [ + // yield in countdown(3), n == 3 + {value: 3, done: false}, + // yield in yield* countdown(2), n == 2 + {value: 2, done: false}, + // yield in nested yield* countdown(1), n == 1 + {value: 1, done: false}, + // countdown(0) yields no values + // second go-through of countdown(2) loop, n == 1 + {value: 1, done: false}, + // second go-through of countdown(3) loop, n == 2 + {value: 2, done: false}, + // yield in yield* countdown(1), n == 1 + {value: 1, done: false}, + // third go-through of countdown(3) loop, n == 1 + {value: 1, done: false}, + // done + {value: 34, done: true} +]; + +assertDeepEq(expected, collect_results(countdown(3))); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/delegating-yield-9.js b/js/src/tests/ecma_6/Generators/delegating-yield-9.js new file mode 100644 index 000000000..60c35b94d --- /dev/null +++ b/js/src/tests/ecma_6/Generators/delegating-yield-9.js @@ -0,0 +1,37 @@ +// Test that yield* can appear in a loop, and inside yield. + +function* countdown(n) { + while (n > 0) { + yield (yield* countdown(--n)); + } + return 34; +} + +function collect_results(iter) { + var ret = []; + var result; + do { + result = iter.next(); + ret.push(result); + } while (!result.done); + return ret; +} + +var expected = [ + // Only 34 yielded from the "yield" and the last return make it out. + // Three yields in countdown(3), two in countdown(2), and one in + // countdown(1) (called twice). + {value: 34, done: false}, + {value: 34, done: false}, + {value: 34, done: false}, + {value: 34, done: false}, + {value: 34, done: false}, + {value: 34, done: false}, + {value: 34, done: false}, + {value: 34, done: true}, // final +]; + +assertDeepEq(collect_results(countdown(3)), expected); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/iteration.js b/js/src/tests/ecma_6/Generators/iteration.js new file mode 100644 index 000000000..6eb6a51aa --- /dev/null +++ b/js/src/tests/ecma_6/Generators/iteration.js @@ -0,0 +1,574 @@ +// This file was written by Andy Wingo <wingo@igalia.com> and originally +// contributed to V8 as generators-objects.js, available here: +// +// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-objects.js + +// Test aspects of the generator runtime. + + +var GeneratorFunction = (function*(){yield 1;}).constructor; + + +function TestGeneratorResultPrototype() { + function* g() { yield 1; } + var iter = g(); + assertIteratorNext(iter, 1); + assertIteratorDone(iter, undefined); + assertIteratorDone(iter, undefined); +} +TestGeneratorResultPrototype(); + +function TestGenerator(g, expected_values_for_next, + send_val, expected_values_for_send) { + function testNext(thunk) { + var iter = thunk(); + for (var i = 0; i < expected_values_for_next.length; i++) { + assertIteratorResult(iter.next(), expected_values_for_next[i], + i == expected_values_for_next.length - 1); + } + assertIteratorDone(iter, undefined); + } + function testSend(thunk) { + var iter = thunk(); + for (var i = 0; i < expected_values_for_send.length; i++) { + assertIteratorResult(iter.next(send_val), + expected_values_for_send[i], + i == expected_values_for_send.length - 1); + } + assertIteratorDone(iter, undefined); + } + function testThrow(thunk) { + for (var i = 0; i < expected_values_for_next.length; i++) { + var iter = thunk(); + for (var j = 0; j < i; j++) { + assertIteratorResult(iter.next(), + expected_values_for_next[j], + j == expected_values_for_next.length - 1); + } + var Sentinel = function () {} + assertThrowsInstanceOf(function () { iter.throw(new Sentinel); }, Sentinel); + assertIteratorDone(iter, undefined); + } + } + + testNext(g); + testSend(g); + testThrow(g); + + testNext(function*() { return yield* g(); }); + testSend(function*() { return yield* g(); }); + testThrow(function*() { return yield* g(); }); + + if (g instanceof GeneratorFunction) { + testNext(function() { return g(); }); + testSend(function() { return g(); }); + testThrow(function() { return g(); }); + } +} + +TestGenerator(function* g1() { }, + [undefined], + "foo", + [undefined]); + +TestGenerator(function* g2() { yield 1; }, + [1, undefined], + "foo", + [1, undefined]); + +TestGenerator(function* g3() { yield 1; yield 2; }, + [1, 2, undefined], + "foo", + [1, 2, undefined]); + +TestGenerator(function* g4() { yield 1; yield 2; return 3; }, + [1, 2, 3], + "foo", + [1, 2, 3]); + +TestGenerator(function* g5() { return 1; }, + [1], + "foo", + [1]); + +TestGenerator(function* g6() { var x = yield 1; return x; }, + [1, undefined], + "foo", + [1, "foo"]); + +TestGenerator(function* g7() { var x = yield 1; yield 2; return x; }, + [1, 2, undefined], + "foo", + [1, 2, "foo"]); + +TestGenerator(function* g8() { for (var x = 0; x < 4; x++) { yield x; } }, + [0, 1, 2, 3, undefined], + "foo", + [0, 1, 2, 3, undefined]); + +// Generator with arguments. +TestGenerator( + function g9() { + return (function*(a, b, c, d) { + yield a; yield b; yield c; yield d; + })("fee", "fi", "fo", "fum"); + }, + ["fee", "fi", "fo", "fum", undefined], + "foo", + ["fee", "fi", "fo", "fum", undefined]); + +// Too few arguments. +TestGenerator( + function g10() { + return (function*(a, b, c, d) { + yield a; yield b; yield c; yield d; + })("fee", "fi"); + }, + ["fee", "fi", undefined, undefined, undefined], + "foo", + ["fee", "fi", undefined, undefined, undefined]); + +// Too many arguments. +TestGenerator( + function g11() { + return (function*(a, b, c, d) { + yield a; yield b; yield c; yield d; + })("fee", "fi", "fo", "fum", "I smell the blood of an Englishman"); + }, + ["fee", "fi", "fo", "fum", undefined], + "foo", + ["fee", "fi", "fo", "fum", undefined]); + +// The arguments object. +TestGenerator( + function g12() { + return (function*(a, b, c, d) { + for (var i = 0; i < arguments.length; i++) { + yield arguments[i]; + } + })("fee", "fi", "fo", "fum", "I smell the blood of an Englishman"); + }, + ["fee", "fi", "fo", "fum", "I smell the blood of an Englishman", + undefined], + "foo", + ["fee", "fi", "fo", "fum", "I smell the blood of an Englishman", + undefined]); + +// Access to captured free variables. +TestGenerator( + function g13() { + return (function(a, b, c, d) { + return (function*() { + yield a; yield b; yield c; yield d; + })(); + })("fee", "fi", "fo", "fum"); + }, + ["fee", "fi", "fo", "fum", undefined], + "foo", + ["fee", "fi", "fo", "fum", undefined]); + +// Abusing the arguments object. +TestGenerator( + function g14() { + return (function*(a, b, c, d) { + arguments[0] = "Be he live"; + arguments[1] = "or be he dead"; + arguments[2] = "I'll grind his bones"; + arguments[3] = "to make my bread"; + yield a; yield b; yield c; yield d; + })("fee", "fi", "fo", "fum"); + }, + ["Be he live", "or be he dead", "I'll grind his bones", "to make my bread", + undefined], + "foo", + ["Be he live", "or be he dead", "I'll grind his bones", "to make my bread", + undefined]); + +// Abusing the arguments object: strict mode. +TestGenerator( + function g15() { + return (function*(a, b, c, d) { + "use strict"; + arguments[0] = "Be he live"; + arguments[1] = "or be he dead"; + arguments[2] = "I'll grind his bones"; + arguments[3] = "to make my bread"; + yield a; yield b; yield c; yield d; + })("fee", "fi", "fo", "fum"); + }, + ["fee", "fi", "fo", "fum", undefined], + "foo", + ["fee", "fi", "fo", "fum", undefined]); + +// GC. +if (typeof gc == 'function') { + TestGenerator(function* g16() { yield "baz"; gc(); yield "qux"; }, + ["baz", "qux", undefined], + "foo", + ["baz", "qux", undefined]); +} + +// Receivers. +TestGenerator( + function g17() { + function* g() { yield this.x; yield this.y; } + var o = { start: g, x: 1, y: 2 }; + return o.start(); + }, + [1, 2, undefined], + "foo", + [1, 2, undefined]); + +// FIXME: Capture the generator object as "this" in new g(). Bug 907742. +// TestGenerator( +// function g18() { +// function* g() { yield this.x; yield this.y; } +// var iter = new g; +// iter.x = 1; +// iter.y = 2; +// return iter; +// }, +// [1, 2, undefined], +// "foo", +// [1, 2, undefined]); + +TestGenerator( + function* g19() { + var x = 1; + yield x; + with({x:2}) { yield x; } + yield x; + }, + [1, 2, 1, undefined], + "foo", + [1, 2, 1, undefined]); + +TestGenerator( + function* g20() { yield (1 + (yield 2) + 3); }, + [2, NaN, undefined], + "foo", + [2, "1foo3", undefined]); + +TestGenerator( + function* g21() { return (1 + (yield 2) + 3); }, + [2, NaN], + "foo", + [2, "1foo3"]); + +TestGenerator( + function* g22() { yield (1 + (yield 2) + 3); yield (4 + (yield 5) + 6); }, + [2, NaN, 5, NaN, undefined], + "foo", + [2, "1foo3", 5, "4foo6", undefined]); + +TestGenerator( + function* g23() { + return (yield (1 + (yield 2) + 3)) + (yield (4 + (yield 5) + 6)); + }, + [2, NaN, 5, NaN, NaN], + "foo", + [2, "1foo3", 5, "4foo6", "foofoo"]); + +// Rewind a try context with and without operands on the stack. +TestGenerator( + function* g24() { + try { + return (yield (1 + (yield 2) + 3)) + (yield (4 + (yield 5) + 6)); + } catch (e) { + throw e; + } + }, + [2, NaN, 5, NaN, NaN], + "foo", + [2, "1foo3", 5, "4foo6", "foofoo"]); + +// Yielding in a catch context, with and without operands on the stack. +TestGenerator( + function* g25() { + try { + throw (yield (1 + (yield 2) + 3)) + } catch (e) { + if (typeof e == 'object') throw e; + return e + (yield (4 + (yield 5) + 6)); + } + }, + [2, NaN, 5, NaN, NaN], + "foo", + [2, "1foo3", 5, "4foo6", "foofoo"]); + +// Generator function instances. +TestGenerator(GeneratorFunction(), + [undefined], + "foo", + [undefined]); + +TestGenerator(new GeneratorFunction(), + [undefined], + "foo", + [undefined]); + +TestGenerator(GeneratorFunction('yield 1;'), + [1, undefined], + "foo", + [1, undefined]); + +TestGenerator( + function() { return GeneratorFunction('x', 'y', 'yield x + y;')(1, 2) }, + [3, undefined], + "foo", + [3, undefined]); + +// Access to this with formal arguments. +TestGenerator( + function () { + return ({ x: 42, g: function* (a) { yield this.x } }).g(0); + }, + [42, undefined], + "foo", + [42, undefined]); + +function TestTryCatch(instantiate) { + function* g() { yield 1; try { yield 2; } catch (e) { yield e; } yield 3; } + function Sentinel() {} + + function Test1(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + assertIteratorNext(iter, 3); + assertIteratorDone(iter, undefined); + assertIteratorDone(iter, undefined); + } + Test1(instantiate(g)); + + function Test2(iter) { + assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel); + assertIteratorDone(iter, undefined); + } + Test2(instantiate(g)); + + function Test3(iter) { + assertIteratorNext(iter, 1); + assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel); + assertIteratorDone(iter, undefined); + } + Test3(instantiate(g)); + + function Test4(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + var exn = new Sentinel; + assertIteratorResult(iter.throw(exn), exn, false); + assertIteratorNext(iter, 3); + assertIteratorDone(iter, undefined); + assertIteratorDone(iter, undefined); + } + Test4(instantiate(g)); + + function Test5(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + var exn = new Sentinel; + assertIteratorResult(iter.throw(exn), exn, false); + assertIteratorNext(iter, 3); + assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel); + assertIteratorDone(iter, undefined); + + } + Test5(instantiate(g)); + + function Test6(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + var exn = new Sentinel; + assertIteratorResult(iter.throw(exn), exn, false); + assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel); + assertIteratorDone(iter, undefined); + } + Test6(instantiate(g)); +} +TestTryCatch(function (g) { return g(); }); +TestTryCatch(function* (g) { return yield* g(); }); + +function TestTryFinally(instantiate) { + function* g() { yield 1; try { yield 2; } finally { yield 3; } yield 4; } + function Sentinel() {} + function Sentinel2() {} + + function Test1(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + assertIteratorNext(iter, 3); + assertIteratorNext(iter, 4); + assertIteratorDone(iter, undefined); + assertIteratorDone(iter, undefined); + } + Test1(instantiate(g)); + + function Test2(iter) { + assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel); + assertIteratorDone(iter, undefined); + } + Test2(instantiate(g)); + + function Test3(iter) { + assertIteratorNext(iter, 1); + assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel); + assertIteratorDone(iter, undefined); + } + Test3(instantiate(g)); + + function Test4(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + assertIteratorResult(iter.throw(new Sentinel), 3, false); + assertThrowsInstanceOf(function() { iter.next(); }, Sentinel); + assertIteratorDone(iter, undefined); + + } + Test4(instantiate(g)); + + function Test5(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + assertIteratorResult(iter.throw(new Sentinel), 3, false); + assertThrowsInstanceOf(function() { iter.throw(new Sentinel2); }, Sentinel2); + assertIteratorDone(iter, undefined); + } + Test5(instantiate(g)); + + function Test6(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + assertIteratorNext(iter, 3); + assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel); + assertIteratorDone(iter, undefined); + } + Test6(instantiate(g)); + + function Test7(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + assertIteratorNext(iter, 3); + assertIteratorNext(iter, 4); + assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel); + assertIteratorDone(iter, undefined); + } + Test7(instantiate(g)); +} +TestTryFinally(function (g) { return g(); }); +TestTryFinally(function* (g) { return yield* g(); }); + +function TestNestedTry(instantiate) { + function* g() { + try { + yield 1; + try { yield 2; } catch (e) { yield e; } + yield 3; + } finally { + yield 4; + } + yield 5; + } + function Sentinel() {} + function Sentinel2() {} + + function Test1(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + assertIteratorNext(iter, 3); + assertIteratorNext(iter, 4); + assertIteratorNext(iter, 5); + assertIteratorDone(iter, undefined); + assertIteratorDone(iter, undefined); + } + Test1(instantiate(g)); + + function Test2(iter) { + assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel); + assertIteratorDone(iter, undefined); + } + Test2(instantiate(g)); + + function Test3(iter) { + assertIteratorNext(iter, 1); + assertIteratorResult(iter.throw(new Sentinel), 4, false); + assertThrowsInstanceOf(function() { iter.next(); }, Sentinel); + assertIteratorDone(iter, undefined); + } + Test3(instantiate(g)); + + function Test4(iter) { + assertIteratorNext(iter, 1); + assertIteratorResult(iter.throw(new Sentinel), 4, false); + assertThrowsInstanceOf(function() { iter.throw(new Sentinel2); }, Sentinel2); + assertIteratorDone(iter, undefined); + } + Test4(instantiate(g)); + + function Test5(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + var exn = new Sentinel; + assertIteratorResult(iter.throw(exn), exn, false); + assertIteratorNext(iter, 3); + assertIteratorNext(iter, 4); + assertIteratorNext(iter, 5); + assertIteratorDone(iter, undefined); + assertIteratorDone(iter, undefined); + + } + Test5(instantiate(g)); + + function Test6(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + var exn = new Sentinel; + assertIteratorResult(iter.throw(exn), exn, false); + assertIteratorResult(iter.throw(new Sentinel2), 4, false); + assertThrowsInstanceOf(function() { iter.next(); }, Sentinel2); + assertIteratorDone(iter, undefined); + } + Test6(instantiate(g)); + + function Test7(iter) { + assertIteratorNext(iter, 1); + assertIteratorNext(iter, 2); + var exn = new Sentinel; + assertIteratorResult(iter.throw(exn), exn, false); + assertIteratorNext(iter, 3); + assertIteratorResult(iter.throw(new Sentinel2), 4, false); + assertThrowsInstanceOf(function() { iter.next(); }, Sentinel2); + assertIteratorDone(iter, undefined); + + } + Test7(instantiate(g)); + + // That's probably enough. +} +TestNestedTry(function (g) { return g(); }); +TestNestedTry(function* (g) { return yield* g(); }); + +function TestRecursion() { + function TestNextRecursion() { + function* g() { yield iter.next(); } + var iter = g(); + return iter.next(); + } + function TestSendRecursion() { + function* g() { yield iter.next(42); } + var iter = g(); + return iter.next(); + } + function TestThrowRecursion() { + function* g() { yield iter.throw(1); } + var iter = g(); + return iter.next(); + } + assertThrowsInstanceOf(TestNextRecursion, TypeError); + assertThrowsInstanceOf(TestSendRecursion, TypeError); + assertThrowsInstanceOf(TestThrowRecursion, TypeError); +} +TestRecursion(); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/iterator-next-non-object.js b/js/src/tests/ecma_6/Generators/iterator-next-non-object.js new file mode 100644 index 000000000..453f4cd87 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/iterator-next-non-object.js @@ -0,0 +1,64 @@ +var BUGNUMBER = 1016936; +var summary = "IteratorNext should throw if the value returned by iterator.next() is not an object."; + +print(BUGNUMBER + ": " + summary); + +var nonobjs = [ + null, + undefined, + 1, + true, + "a", + Symbol.iterator, +]; + +function createIterable(v) { + var iterable = {}; + iterable[Symbol.iterator] = function () { + return { + next: function () { + return v; + } + }; + }; + return iterable; +} + +function f() { +} + +for (var nonobj of nonobjs) { + var iterable = createIterable(nonobj); + + assertThrowsInstanceOf(() => [...iterable], TypeError); + assertThrowsInstanceOf(() => f(...iterable), TypeError); + + assertThrowsInstanceOf(() => { for (var x of iterable) {} }, TypeError); + + assertThrowsInstanceOf(() => { + var [a] = iterable; + }, TypeError); + assertThrowsInstanceOf(() => { + var [...a] = iterable; + }, TypeError); + + assertThrowsInstanceOf(() => Array.from(iterable), TypeError); + assertThrowsInstanceOf(() => new Map(iterable), TypeError); + assertThrowsInstanceOf(() => new Set(iterable), TypeError); + assertThrowsInstanceOf(() => new WeakMap(iterable), TypeError); + assertThrowsInstanceOf(() => new WeakSet(iterable), TypeError); + // FIXME: bug 1232266 + // assertThrowsInstanceOf(() => new Int8Array(iterable), TypeError); + assertThrowsInstanceOf(() => Int8Array.from(iterable), TypeError); + + assertThrowsInstanceOf(() => { + var g = function*() { + yield* iterable; + }; + var v = g(); + v.next(); + }, TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/objects.js b/js/src/tests/ecma_6/Generators/objects.js new file mode 100644 index 000000000..1ffdf48fa --- /dev/null +++ b/js/src/tests/ecma_6/Generators/objects.js @@ -0,0 +1,49 @@ +// This file was written by Andy Wingo <wingo@igalia.com> and originally +// contributed to V8 as generators-objects.js, available here: +// +// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-objects.js + +// Test aspects of the generator runtime. + +// Test the properties and prototype of a generator object. +function TestGeneratorObject() { + function* g() { yield 1; } + + var iter = g(); + assertEq(Object.getPrototypeOf(iter), g.prototype); + assertTrue(iter instanceof g); + assertEq(String(iter), "[object Generator]"); + assertDeepEq(Object.getOwnPropertyNames(iter), []); + assertNotEq(g(), iter); +} +TestGeneratorObject(); + + +// Test the methods of generator objects. +function TestGeneratorObjectMethods() { + function* g() { yield 1; } + var iter = g(); + + assertEq(iter.next.length, 1); + assertEq(iter.return.length, 1); + assertEq(iter.throw.length, 1); + + function TestNonGenerator(non_generator) { + assertThrowsInstanceOf(function() { iter.next.call(non_generator); }, TypeError); + assertThrowsInstanceOf(function() { iter.next.call(non_generator, 1); }, TypeError); + assertThrowsInstanceOf(function() { iter.return.call(non_generator, 1); }, TypeError); + assertThrowsInstanceOf(function() { iter.throw.call(non_generator, 1); }, TypeError); + assertThrowsInstanceOf(function() { iter.close.call(non_generator); }, TypeError); + } + + TestNonGenerator(1); + TestNonGenerator({}); + TestNonGenerator(function(){}); + TestNonGenerator(g); + TestNonGenerator(g.prototype); +} +TestGeneratorObjectMethods(); + + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/return-finally.js b/js/src/tests/ecma_6/Generators/return-finally.js new file mode 100644 index 000000000..9e4c6c8f3 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/return-finally.js @@ -0,0 +1,309 @@ +var BUGNUMBER = 1202134; +var summary = "Return value should not be overwritten by finally block with normal execution."; + +print(BUGNUMBER + ": " + summary); + +// ==== single ==== + +var f, g, v; +f = function*() { + // F.[[type]] is normal + // B.[[type]] is return + try { + return 42; + } finally { + } +}; +g = f(); +v = g.next(); +assertEq(v.value, 42); +assertEq(v.done, true); + +f = function*() { + // F.[[type]] is return + try { + return 42; + } finally { + return 43; + } +}; +g = f(); +v = g.next(); +assertEq(v.value, 43); +assertEq(v.done, true); + +f = function*() { + // F.[[type]] is throw + try { + return 42; + } finally { + throw 43; + } +}; +var caught = false; +g = f(); +try { + v = g.next(); +} catch (e) { + assertEq(e, 43); + caught = true; +} +assertEq(caught, true); + +f = function*() { + // F.[[type]] is break + do try { + return 42; + } finally { + break; + } while (false); + return 43; +}; +g = f(); +v = g.next(); +assertEq(v.value, 43); +assertEq(v.done, true); + +f = function*() { + // F.[[type]] is break + L: try { + return 42; + } finally { + break L; + } + return 43; +}; +g = f(); +v = g.next(); +assertEq(v.value, 43); +assertEq(v.done, true); + +f = function*() { + // F.[[type]] is continue + do try { + return 42; + } finally { + continue; + } while (false); + return 43; +}; +g = f(); +v = g.next(); +assertEq(v.value, 43); +assertEq(v.done, true); + +// ==== nested ==== + +f = function*() { + // F.[[type]] is normal + // B.[[type]] is return + try { + return 42; + } finally { + // F.[[type]] is break + do try { + return 43; + } finally { + break; + } while (0); + } +}; +g = f(); +v = g.next(); +assertEq(v.value, 42); +assertEq(v.done, true); + +f = function*() { + // F.[[type]] is normal + // B.[[type]] is return + try { + return 42; + } finally { + // F.[[type]] is break + L: try { + return 43; + } finally { + break L; + } + } +} +g = f(); +v = g.next(); +assertEq(v.value, 42); +assertEq(v.done, true); + +f = function*() { + // F.[[type]] is normal + // B.[[type]] is return + try { + return 42; + } finally { + // F.[[type]] is continue + do try { + return 43; + } finally { + continue; + } while (0); + } +}; +g = f(); +v = g.next(); +assertEq(v.value, 42); +assertEq(v.done, true); + +f = function*() { + // F.[[type]] is normal + // B.[[type]] is return + try { + return 42; + } finally { + // F.[[type]] is normal + // B.[[type]] is normal + try { + // F.[[type]] is throw + try { + return 43; + } finally { + throw 9; + } + } catch (e) { + } + } +}; +g = f(); +v = g.next(); +assertEq(v.value, 42); +assertEq(v.done, true); + +f = function*() { + // F.[[type]] is return + try { + return 41; + } finally { + // F.[[type]] is normal + // B.[[type]] is return + try { + return 42; + } finally { + // F.[[type]] is break + do try { + return 43; + } finally { + break; + } while (0); + } + } +}; +g = f(); +v = g.next(); +assertEq(v.value, 42); +assertEq(v.done, true); + +// ==== with yield ==== + +f = function*() { + // F.[[type]] is normal + // B.[[type]] is return + try { + return 42; + } finally { + yield 43; + } +}; +g = f(); +v = g.next(); +assertEq(v.value, 43); +assertEq(v.done, false); +v = g.next(); +assertEq(v.value, 42); +assertEq(v.done, true); + +// ==== throw() ==== + +f = function*() { + // F.[[type]] is throw + try { + return 42; + } finally { + yield 43; + } +}; +caught = false; +g = f(); +v = g.next(); +assertEq(v.value, 43); +assertEq(v.done, false); +try { + v = g.throw(44); +} catch (e) { + assertEq(e, 44); + caught = true; +} +assertEq(caught, true); + +f = function*() { + // F.[[type]] is normal + try { + return 42; + } finally { + // F.[[type]] is normal + // B.[[type]] is throw + try { + yield 43; + } catch (e) { + } + } +}; +caught = false; +g = f(); +v = g.next(); +assertEq(v.value, 43); +assertEq(v.done, false); +v = g.throw(44); +assertEq(v.value, 42); +assertEq(v.done, true); + +// ==== return() ==== + +f = function*() { + // F.[[type]] is return + try { + return 42; + } finally { + yield 43; + } +}; +caught = false; +g = f(); +v = g.next(); +assertEq(v.value, 43); +assertEq(v.done, false); +v = g.return(44); +assertEq(v.value, 44); +assertEq(v.done, true); + +f = function*() { + // F.[[type]] is normal + // B.[[type]] is return + try { + yield 42; + } finally { + // F.[[type]] is continue + do try { + return 43; + } finally { + continue; + } while (0); + } +}; +caught = false; +g = f(); +v = g.next(); +assertEq(v.value, 42); +assertEq(v.done, false); +v = g.return(44); +assertEq(v.value, 44); +assertEq(v.done, true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/runtime.js b/js/src/tests/ecma_6/Generators/runtime.js new file mode 100644 index 000000000..c4d3bb6a6 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/runtime.js @@ -0,0 +1,132 @@ +// This file was written by Andy Wingo <wingo@igalia.com> and originally +// contributed to V8 as generators-runtime.js, available here: +// +// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-runtime.js + +// Test aspects of the generator runtime. + +// See http://people.mozilla.org/~jorendorff/es6-draft.html#sec-15.19.3. + +function assertSyntaxError(str) { + assertThrowsInstanceOf(Function(str), SyntaxError); +} + + +function f() { } +function* g() { yield 1; } +var GeneratorFunctionPrototype = Object.getPrototypeOf(g); +var GeneratorFunction = GeneratorFunctionPrototype.constructor; +var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype; + + +// A generator function should have the same set of properties as any +// other function. +function TestGeneratorFunctionInstance() { + var f_own_property_names = Object.getOwnPropertyNames(f); + var g_own_property_names = Object.getOwnPropertyNames(g); + + f_own_property_names.sort(); + g_own_property_names.sort(); + + assertDeepEq(f_own_property_names, g_own_property_names); + var i; + for (i = 0; i < f_own_property_names.length; i++) { + var prop = f_own_property_names[i]; + var f_desc = Object.getOwnPropertyDescriptor(f, prop); + var g_desc = Object.getOwnPropertyDescriptor(g, prop); + assertEq(f_desc.configurable, g_desc.configurable, prop); + assertEq(f_desc.writable, g_desc.writable, prop); + assertEq(f_desc.enumerable, g_desc.enumerable, prop); + } +} +TestGeneratorFunctionInstance(); + + +// Generators have an additional object interposed in the chain between +// themselves and Function.prototype. +function TestGeneratorFunctionPrototype() { + // Sanity check. + assertEq(Object.getPrototypeOf(f), Function.prototype); + assertNotEq(GeneratorFunctionPrototype, Function.prototype); + assertEq(Object.getPrototypeOf(GeneratorFunctionPrototype), + Function.prototype); + assertEq(Object.getPrototypeOf(function* () {}), + GeneratorFunctionPrototype); +} +TestGeneratorFunctionPrototype(); + + +// Functions that we associate with generator objects are actually defined by +// a common prototype. +function TestGeneratorObjectPrototype() { + // %GeneratorPrototype% must inherit from %IteratorPrototype%. + var iterProto = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())); + assertEq(Object.getPrototypeOf(GeneratorObjectPrototype), + iterProto); + assertEq(Object.getPrototypeOf((function*(){yield 1}).prototype), + GeneratorObjectPrototype); + + var expected_property_names = ["next", "return", "throw", "constructor"]; + var found_property_names = + Object.getOwnPropertyNames(GeneratorObjectPrototype); + + expected_property_names.sort(); + found_property_names.sort(); + + assertDeepEq(found_property_names, expected_property_names); + assertDeepEq(Object.getOwnPropertySymbols(GeneratorObjectPrototype), [Symbol.toStringTag]); +} +TestGeneratorObjectPrototype(); + + +// This tests the object that would be called "GeneratorFunction", if it were +// like "Function". +function TestGeneratorFunction() { + assertEq(GeneratorFunctionPrototype, GeneratorFunction.prototype); + assertTrue(g instanceof GeneratorFunction); + + assertEq(Function, Object.getPrototypeOf(GeneratorFunction)); + assertTrue(g instanceof Function); + + assertEq("function* g() { yield 1; }", g.toString()); + + // Not all functions are generators. + assertTrue(f instanceof Function); // Sanity check. + assertFalse(f instanceof GeneratorFunction); + + assertTrue((new GeneratorFunction()) instanceof GeneratorFunction); + assertTrue(GeneratorFunction() instanceof GeneratorFunction); + + assertTrue(GeneratorFunction('yield 1') instanceof GeneratorFunction); + assertTrue(GeneratorFunction('return 1') instanceof GeneratorFunction); + assertTrue(GeneratorFunction('a', 'yield a') instanceof GeneratorFunction); + assertTrue(GeneratorFunction('a', 'return a') instanceof GeneratorFunction); + assertTrue(GeneratorFunction('a', 'return a') instanceof GeneratorFunction); + assertSyntaxError("GeneratorFunction('yield', 'return yield')"); + assertTrue(GeneratorFunction('with (x) return foo;') instanceof GeneratorFunction); + assertSyntaxError("GeneratorFunction('\"use strict\"; with (x) return foo;')"); + + // Doesn't matter particularly what string gets serialized, as long + // as it contains "function*" and "yield 10". + assertEq(GeneratorFunction('yield 10').toString(), + "function* anonymous() {\nyield 10\n}"); +} +TestGeneratorFunction(); + + +function TestPerGeneratorPrototype() { + assertNotEq((function*(){}).prototype, (function*(){}).prototype); + assertNotEq((function*(){}).prototype, g.prototype); + assertEq(typeof GeneratorFunctionPrototype, "object"); + assertEq(g.prototype.__proto__.constructor, GeneratorFunctionPrototype, "object"); + assertEq(Object.getPrototypeOf(g.prototype), GeneratorObjectPrototype); + assertFalse(g.prototype instanceof Function); + assertEq(typeof (g.prototype), "object"); + + assertDeepEq(Object.getOwnPropertyNames(g.prototype), []); +} +TestPerGeneratorPrototype(); + + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/shell.js b/js/src/tests/ecma_6/Generators/shell.js new file mode 100644 index 000000000..db15c606c --- /dev/null +++ b/js/src/tests/ecma_6/Generators/shell.js @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function assertFalse(a) { assertEq(a, false) } +function assertTrue(a) { assertEq(a, true) } +function assertNotEq(found, not_expected) { assertFalse(found === expected) } +function assertIteratorResult(result, value, done) { + assertDeepEq(result.value, value); + assertEq(result.done, done); +} +function assertIteratorNext(iter, value) { + assertIteratorResult(iter.next(), value, false); +} +function assertIteratorDone(iter, value) { + assertIteratorResult(iter.next(), value, true); +} diff --git a/js/src/tests/ecma_6/Generators/syntax.js b/js/src/tests/ecma_6/Generators/syntax.js new file mode 100644 index 000000000..b2fec3bcc --- /dev/null +++ b/js/src/tests/ecma_6/Generators/syntax.js @@ -0,0 +1,140 @@ +// This file was written by Andy Wingo <wingo@igalia.com> and originally +// contributed to V8 as generators-parsing.js, available here: +// +// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-parsing.js + +function assertSyntaxError(str) { + var msg; + var evil = eval; + try { + // Non-direct eval. + evil(str); + } catch (exc) { + if (exc instanceof SyntaxError) + return; + msg = "Assertion failed: expected SyntaxError, got " + exc; + } + if (msg === undefined) + msg = "Assertion failed: expected SyntaxError, but no exception thrown"; + throw new Error(msg + " - " + str); +} + +// Yield statements. +function* g() { yield 3; yield 4; } + +// Yield expressions. +function* g() { (yield 3) + (yield 4); } + +// Yield without a RHS. +function* g() { yield; } +function* g() { yield } +function* g() { + yield +} +function* g() { (yield) } +function* g() { [yield] } +function* g() { {yield} } +function* g() { (yield), (yield) } +function* g() { yield; yield } +function* g() { (yield) ? yield : yield } +function* g() { + (yield) + ? yield + : yield +} + +// If yield has a RHS, it needs to start on the same line. The * in a +// yield* counts as starting the RHS. +function* g() { + yield * + foo +} +assertThrows("function* g() { yield\n* foo }", SyntaxError); +assertIteratorNext(function*(){ + yield + 3 + }(), undefined) + +// A YieldExpression is not a LogicalORExpression. +assertThrows("function* g() { yield ? yield : yield }", SyntaxError); + +// You can have a generator in strict mode. +function* g() { "use strict"; yield 3; yield 4; } + +// Generators can have return statements also, which internally parse to a kind +// of yield expression. +function* g() { yield 1; return; } +function* g() { yield 1; return 2; } +function* g() { yield 1; return 2; yield "dead"; } + +// Generator expression. +(function* () { yield 3; }); + +// Named generator expression. +(function* g() { yield 3; }); + +// Generators do not have to contain yield expressions. +function* g() { } + +// YieldExpressions can occur in the RHS of a YieldExpression. +function* g() { yield yield 1; } +function* g() { yield 3 + (yield 4); } + +// Generator definitions with a name of "yield" are not specifically ruled out +// by the spec, as the `yield' name is outside the generator itself. However, +// in strict-mode, "yield" is an invalid identifier. +function* yield() { (yield 3) + (yield 4); } +assertSyntaxError("function* yield() { 'use strict'; (yield 3) + (yield 4); }"); + +// In classic mode, yield is a normal identifier, outside of generators. +function yield(yield) { yield: yield (yield + yield (0)); } + +// Yield is always valid as a key in an object literal. +({ yield: 1 }); +function* g() { yield ({ yield: 1 }) } +function* g() { yield ({ get yield() { return 1; }}) } + +// Yield is a valid property name. +function* g(obj) { yield obj.yield; } + +// Checks that yield is a valid label in classic mode, but not valid in a strict +// mode or in generators. +function f() { yield: 1 } +assertSyntaxError("function f() { 'use strict'; yield: 1 }") +assertSyntaxError("function* g() { yield: 1 }") + +// Yield is only a keyword in the body of the generator, not in nested +// functions. +function* g() { function f(yield) { yield (yield + yield (0)); } } + +// Yield in a generator is not an identifier. +assertSyntaxError("function* g() { yield = 10; }"); + +// Yield binds very loosely, so this parses as "yield (3 + yield 4)", which is +// invalid. +assertSyntaxError("function* g() { yield 3 + yield 4; }"); + +// Yield is still a future-reserved-word in strict mode +assertSyntaxError("function f() { 'use strict'; var yield = 13; }"); + +// The name of the NFE isn't let-bound in F/G, so this is valid. +function f() { (function yield() {}); } +function* g() { (function yield() {}); } + +// The name of the NFE is let-bound in the function/generator expression, so this is invalid. +assertSyntaxError("function f() { (function* yield() {}); }"); +assertSyntaxError("function* g() { (function* yield() {}); }"); + +// The name of the declaration is let-bound in F, so this is valid. +function f() { function yield() {} } +function f() { function* yield() {} } + +// The name of the declaration is let-bound in G, so this is invalid. +assertSyntaxError("function* g() { function yield() {} }"); +assertSyntaxError("function* g() { function* yield() {} }"); + +// In generators, yield is invalid as a formal argument name. +assertSyntaxError("function* g(yield) { yield (10); }"); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/yield-non-regexp.js b/js/src/tests/ecma_6/Generators/yield-non-regexp.js new file mode 100644 index 000000000..d9fbcd359 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/yield-non-regexp.js @@ -0,0 +1,16 @@ +var BUGNUMBER = 1099956; +var summary = + "The token next to yield should be tokenized as non-operand if yield is " + + "a valid name"; + +printBugNumber(BUGNUMBER); +printStatus(summary); + +var yield = 12, a = 3, b = 6, g = 2; +var yieldParsedAsIdentifier = false; + +yield /a; yieldParsedAsIdentifier = true; b/g; + +assertEq(yieldParsedAsIdentifier, true); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/yield-star-iterator-primitive.js b/js/src/tests/ecma_6/Generators/yield-star-iterator-primitive.js new file mode 100644 index 000000000..90d9e7578 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/yield-star-iterator-primitive.js @@ -0,0 +1,31 @@ +var BUGNUMBER = 1021835; +var summary = "Returning non-object from @@iterator should throw"; + +print(BUGNUMBER + ": " + summary); + +let primitives = [ + 1, + true, + undefined, + null, + "foo", + Symbol.iterator +]; + +for (let primitive of primitives) { + let obj = { + [Symbol.iterator]() { + return primitive; + } + }; + assertThrowsInstanceOf(() => { + function* g() { + yield* obj; + } + for (let x of g()) { + } + }, TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); |