diff options
Diffstat (limited to 'js/src/tests/non262/generators')
48 files changed, 3157 insertions, 0 deletions
diff --git a/js/src/tests/non262/generators/326466-01.js b/js/src/tests/non262/generators/326466-01.js new file mode 100644 index 0000000000..a5123ecf71 --- /dev/null +++ b/js/src/tests/non262/generators/326466-01.js @@ -0,0 +1,45 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 326466; +var summary = 'Implement Pythonic generators and iteration protocol support'; +var actual; +var expect; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +function* fib() +{ + var a = 0, b = 1; + + while (true) + { + yield a; + var t = a; + a = b; + b += t; + } +} + +var g = fib(); + +expect = '[object Generator]'; +actual = g.toString(); +reportCompare(expect, actual, summary); + +var actual = []; +var expect = [0, 1, 1, 2, 3, 5, 8, 13]; +actual.push(g.next().value); +actual.push(g.next().value); +actual.push(g.next().value); +actual.push(g.next().value); +actual.push(g.next().value); +actual.push(g.next().value); +actual.push(g.next().value); +actual.push(g.next().value); +reportCompare(expect.join(), actual.join(), summary); + diff --git a/js/src/tests/non262/generators/browser.js b/js/src/tests/non262/generators/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/generators/browser.js diff --git a/js/src/tests/non262/generators/construct-newtarget.js b/js/src/tests/non262/generators/construct-newtarget.js new file mode 100644 index 0000000000..f2087852b0 --- /dev/null +++ b/js/src/tests/non262/generators/construct-newtarget.js @@ -0,0 +1,79 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const GeneratorFunction = function*(){}.constructor; + + +// Test subclassing %GeneratorFunction% works correctly. +class MyGenerator extends GeneratorFunction {} + +var fn = new MyGenerator(); +assertEq(fn instanceof MyGenerator, true); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), MyGenerator.prototype); + +fn = Reflect.construct(MyGenerator, []); +assertEq(fn instanceof MyGenerator, true); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), MyGenerator.prototype); + +fn = Reflect.construct(MyGenerator, [], MyGenerator); +assertEq(fn instanceof MyGenerator, true); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), MyGenerator.prototype); + +fn = Reflect.construct(MyGenerator, [], GeneratorFunction); +assertEq(fn instanceof MyGenerator, false); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype); + + +// Set a different constructor as NewTarget. +fn = Reflect.construct(MyGenerator, [], Array); +assertEq(fn instanceof MyGenerator, false); +assertEq(fn instanceof GeneratorFunction, false); +assertEq(Object.getPrototypeOf(fn), Array.prototype); + +fn = Reflect.construct(GeneratorFunction, [], Array); +assertEq(fn instanceof GeneratorFunction, false); +assertEq(Object.getPrototypeOf(fn), Array.prototype); + + +// The prototype defaults to %GeneratorFunctionPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +fn = Reflect.construct(GeneratorFunction, [], NewTargetNullPrototype); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype); + +fn = Reflect.construct(MyGenerator, [], NewTargetNullPrototype); +assertEq(fn instanceof MyGenerator, false); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(GeneratorFunction, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +fn = Reflect.construct(GeneratorFunction, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/generators/create-function-parse-before-getprototype.js b/js/src/tests/non262/generators/create-function-parse-before-getprototype.js new file mode 100644 index 0000000000..1373e6fdb3 --- /dev/null +++ b/js/src/tests/non262/generators/create-function-parse-before-getprototype.js @@ -0,0 +1,19 @@ +var getProtoCalled = false; + +var newTarget = Object.defineProperty(function(){}.bind(), "prototype", { + get() { + getProtoCalled = true; + return null; + } +}); + +var Generator = function*(){}.constructor; + +assertThrowsInstanceOf(() => { + Reflect.construct(Generator, ["@error"], newTarget); +}, SyntaxError); + +assertEq(getProtoCalled, false); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/generators/delegating-yield-1.js b/js/src/tests/non262/generators/delegating-yield-1.js new file mode 100644 index 0000000000..f9df1d7f89 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/delegating-yield-10.js b/js/src/tests/non262/generators/delegating-yield-10.js new file mode 100644 index 0000000000..1f317023e4 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/delegating-yield-11.js b/js/src/tests/non262/generators/delegating-yield-11.js new file mode 100644 index 0000000000..f7e6650fb9 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/delegating-yield-12.js b/js/src/tests/non262/generators/delegating-yield-12.js new file mode 100644 index 0000000000..9ed7666e11 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/delegating-yield-2.js b/js/src/tests/non262/generators/delegating-yield-2.js new file mode 100644 index 0000000000..34cb3f4a9f --- /dev/null +++ b/js/src/tests/non262/generators/delegating-yield-2.js @@ -0,0 +1,73 @@ +// 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 { value: e*2 }; }; +assertEq(84, outer.throw(42).value); +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); +// 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. +inner = g2(); +outer = delegate(inner); +outer_throw_42 = GeneratorObjectPrototype_throw.bind(outer, 42); +assertIteratorNext(outer, 1); +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().value); +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().value); +assertEq(84, outer_throw_42().value); +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/non262/generators/delegating-yield-3.js b/js/src/tests/non262/generators/delegating-yield-3.js new file mode 100644 index 0000000000..624c12104c --- /dev/null +++ b/js/src/tests/non262/generators/delegating-yield-3.js @@ -0,0 +1,44 @@ +// 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); +var innerNextFn = GeneratorObjectPrototype_next; +inner.next = function() { return innerNextFn.call(this); }; +assertIteratorNext(outer, 0); +assertIteratorNext(outer, 1); +innerNextFn = 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. +innerNextFn = GeneratorObjectPrototype_next; +assertIteratorNext(outer, 2); +// Repatch. +innerNextFn = function() { return { value: 42, done: true }; }; +assertIteratorDone(outer, 42); + +// Monkeypunch next on the prototype. +var inner = g(20); +var outer = delegate(inner); +var innerNextFn = GeneratorObjectPrototype_next; +GeneratorObjectPrototype.next = function() { return innerNextFn.call(this); }; +assertIteratorNext(outer, 0); +assertIteratorNext(outer, 1); +innerNextFn = 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. +innerNextFn = GeneratorObjectPrototype_next; +assertIteratorNext(outer, 2); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/generators/delegating-yield-4.js b/js/src/tests/non262/generators/delegating-yield-4.js new file mode 100644 index 0000000000..c076731727 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/delegating-yield-5.js b/js/src/tests/non262/generators/delegating-yield-5.js new file mode 100644 index 0000000000..f4376743b0 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/delegating-yield-6.js b/js/src/tests/non262/generators/delegating-yield-6.js new file mode 100644 index 0000000000..8b358525c4 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/delegating-yield-7.js b/js/src/tests/non262/generators/delegating-yield-7.js new file mode 100644 index 0000000000..f7c9e86cc5 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/delegating-yield-8.js b/js/src/tests/non262/generators/delegating-yield-8.js new file mode 100644 index 0000000000..c17d6ea26a --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/delegating-yield-9.js b/js/src/tests/non262/generators/delegating-yield-9.js new file mode 100644 index 0000000000..60c35b94d7 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/fibonacci-matrix-generator.js b/js/src/tests/non262/generators/fibonacci-matrix-generator.js new file mode 100644 index 0000000000..dbfee58272 --- /dev/null +++ b/js/src/tests/non262/generators/fibonacci-matrix-generator.js @@ -0,0 +1,62 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var summary = "Fibonacci generator by matrix multiplication"; +var actual, expect; + +printStatus(summary); + +/************** + * BEGIN TEST * + **************/ + +function* fib() +{ + var init = [1, 0]; + var mx = [[1, 1], [1, 0]]; + while (true) + { + yield init[1]; + var tmp = [,]; + tmp[0] = + mx[0][0]*init[0] + mx[0][1]*init[1]; + tmp[1] = + mx[1][0]*init[0] + mx[1][1]*init[1]; + init = tmp; + } +} + +var failed = false; +var it = fib(); + +try +{ + if (it.next().value != 0) + throw "F_0 failed"; + if (it.next().value != 1) + throw "F_1 failed"; + if (it.next().value != 1) + throw "F_2 failed"; + if (it.next().value != 2) + throw "F_3 failed"; + if (it.next().value != 3) + throw "F_4 failed"; + if (it.next().value != 5) + throw "F_5 failed"; + if (it.next().value != 8) + throw "F_6 failed"; +} +catch (e) +{ + failed = e; +} + + + +expect = false; +actual = failed; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/generators/forbidden-as-consequent.js b/js/src/tests/non262/generators/forbidden-as-consequent.js new file mode 100644 index 0000000000..13647e154b --- /dev/null +++ b/js/src/tests/non262/generators/forbidden-as-consequent.js @@ -0,0 +1,11 @@ +/* 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/. */ + +assertThrowsInstanceOf(() => eval("if (1) function* foo() {}"), + SyntaxError); +assertThrowsInstanceOf(() => eval("'use strict'; if (1) function* foo() {}"), + SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/generators/gen-with-call-obj.js b/js/src/tests/non262/generators/gen-with-call-obj.js new file mode 100644 index 0000000000..9f7751a3dc --- /dev/null +++ b/js/src/tests/non262/generators/gen-with-call-obj.js @@ -0,0 +1,36 @@ +var foo; + +function* gen() { + var x = 0; + foo = function() { return x++; } + for (var i = 0; i < 10; ++i) + yield x++; +} + +var j = 0; +for (i of gen()) + assertEq(i, j++); + +// now mess up the stack + +function f1(x) { + var a, b, c, d, e, f, g; + return x <= 0 ? 0 : f1(x-1); +} +f1(10); +function f2(x) { + var a = x, b = x; + return x <= 0 ? 0 : f2(x-1); +} +f2(10); + +// now observe gen's call object (which should have been put) + +gc(); +assertEq(foo(), 10); +gc(); +assertEq(foo(), 11); +gc(); +assertEq(foo(), 12); + +reportCompare(true,true); diff --git a/js/src/tests/non262/generators/iteration.js b/js/src/tests/non262/generators/iteration.js new file mode 100644 index 0000000000..6eb6a51aa3 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/iterator-next-non-object.js b/js/src/tests/non262/generators/iterator-next-non-object.js new file mode 100644 index 0000000000..453f4cd876 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/iterator-toString.js b/js/src/tests/non262/generators/iterator-toString.js new file mode 100644 index 0000000000..78e4943950 --- /dev/null +++ b/js/src/tests/non262/generators/iterator-toString.js @@ -0,0 +1,41 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var summary = "gen.toString() omitting 'yield' from value"; +var actual, expect; + +printStatus(summary); + +/************** + * BEGIN TEST * + **************/ + +var failed = false; + +function* gen() +{ + yield 17; +} + +try +{ + var str = gen.toString(); + var index = str.search(/yield/); + + if (index < 0) + throw "yield not found in str: " + str; +} +catch (e) +{ + failed = e; +} + + + +expect = false; +actual = failed; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/generators/nested-yield.js b/js/src/tests/non262/generators/nested-yield.js new file mode 100644 index 0000000000..24729f5f37 --- /dev/null +++ b/js/src/tests/non262/generators/nested-yield.js @@ -0,0 +1,44 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var summary = "YieldExpression is and contains an AssignmentExpression"; +var actual, expect; + +printStatus(summary); + +/************** + * BEGIN TEST * + **************/ + +var failed = false; + +function* gen() +{ + yield (yield (yield 7)); +} + +var it = gen(); + +try +{ + if (it.next().value != 7) + throw "7 not yielded"; + if (it.next(17).value != 17) + throw "passed-in 17 not yielded"; + if (it.next(undefined).value !== undefined) + throw "should be able to yield undefined"; + + it.next(); +} +catch (e) +{ + failed = e; +} + +expect = false; +actual = failed; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/generators/objects.js b/js/src/tests/non262/generators/objects.js new file mode 100644 index 0000000000..1ffdf48fab --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/pi-generator.js b/js/src/tests/non262/generators/pi-generator.js new file mode 100644 index 0000000000..b5d4bf47b8 --- /dev/null +++ b/js/src/tests/non262/generators/pi-generator.js @@ -0,0 +1,57 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var summary = "A (slow) generator of pi"; +var actual, expect; + +printStatus(summary); + +/************** + * BEGIN TEST * + **************/ + +function* pi() +{ + var val = 0; + var curr = 1; + var isNeg = false; + while (true) + { + if (isNeg) + yield val -= 4/curr; + else + yield val += 4/curr; + curr += 2; + isNeg = !isNeg; + } +} + +var failed = false; +var it = pi(); + +var vals = + [4, + 4 - 4/3, + 4 - 4/3 + 4/5, + 4 - 4/3 + 4/5 - 4/7]; + +try +{ + for (var i = 0, sz = vals.length; i < sz; i++) + if (it.next().value != vals[i]) + throw vals[i]; +} +catch (e) +{ + failed = e; +} + + + +expect = false; +actual = failed; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/generators/properties.js b/js/src/tests/non262/generators/properties.js new file mode 100644 index 0000000000..7782e64c06 --- /dev/null +++ b/js/src/tests/non262/generators/properties.js @@ -0,0 +1,111 @@ +/* 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 assertOwnDescriptor(object, propertyKey, expected) { + var desc = Object.getOwnPropertyDescriptor(object, propertyKey); + if (desc === undefined) { + assertEq(expected, undefined, "Property shouldn't be present"); + return; + } + + assertEq(desc.enumerable, expected.enumerable, `${String(propertyKey)}.[[Enumerable]]`); + assertEq(desc.configurable, expected.configurable, `${String(propertyKey)}.[[Configurable]]`); + + if (Object.prototype.hasOwnProperty.call(desc, "value")) { + assertEq(desc.value, expected.value, `${String(propertyKey)}.[[Value]]`); + assertEq(desc.writable, expected.writable, `${String(propertyKey)}.[[Writable]]`); + } else { + assertEq(desc.get, expected.get, `${String(propertyKey)}.[[Get]]`); + assertEq(desc.set, expected.set, `${String(propertyKey)}.[[Set]]`); + } +} + +function* generator(){} +var GeneratorFunctionPrototype = Object.getPrototypeOf(generator); +var GeneratorFunction = GeneratorFunctionPrototype.constructor; +var GeneratorPrototype = GeneratorFunctionPrototype.prototype; + + +// ES2017, 25.2.2 Properties of the GeneratorFunction Constructor + +assertEqArray(Object.getOwnPropertyNames(GeneratorFunction).sort(), ["length", "name", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(GeneratorFunction), []); + +assertOwnDescriptor(GeneratorFunction, "length", { + value: 1, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunction, "name", { + value: "GeneratorFunction", writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunction, "prototype", { + value: GeneratorFunctionPrototype, writable: false, enumerable: false, configurable: false +}); + + +// ES2017, 25.2.3 Properties of the GeneratorFunction Prototype Object + +assertEqArray(Object.getOwnPropertyNames(GeneratorFunctionPrototype).sort(), ["constructor", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(GeneratorFunctionPrototype), [Symbol.toStringTag]); + +assertOwnDescriptor(GeneratorFunctionPrototype, "constructor", { + value: GeneratorFunction, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunctionPrototype, "prototype", { + value: GeneratorPrototype, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunctionPrototype, Symbol.toStringTag, { + value: "GeneratorFunction", writable: false, enumerable: false, configurable: true +}); + + +// ES2017, 25.2.4 GeneratorFunction Instances + +assertEqArray(Object.getOwnPropertyNames(generator).sort(), ["length", "name", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(generator), []); + +assertOwnDescriptor(generator, "length", { + value: 0, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(generator, "name", { + value: "generator", writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(generator, "prototype", { + value: generator.prototype, writable: true, enumerable: false, configurable: false +}); + + +// ES2017, 25.3.1 Properties of Generator Prototype + +assertEqArray(Object.getOwnPropertyNames(GeneratorPrototype).sort(), ["constructor", "next", "return", "throw"]); +assertEqArray(Object.getOwnPropertySymbols(GeneratorPrototype), [Symbol.toStringTag]); + +assertOwnDescriptor(GeneratorPrototype, "constructor", { + value: GeneratorFunctionPrototype, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, "next", { + value: GeneratorPrototype.next, writable: true, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, "return", { + value: GeneratorPrototype.return, writable: true, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, "throw", { + value: GeneratorPrototype.throw, writable: true, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, Symbol.toStringTag, { + value: "Generator", writable: false, enumerable: false, configurable: true +}); + + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/generators/regress-345855.js b/js/src/tests/non262/generators/regress-345855.js new file mode 100644 index 0000000000..682848f6be --- /dev/null +++ b/js/src/tests/non262/generators/regress-345855.js @@ -0,0 +1,90 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 345855; +var summary = 'Blank yield expressions are not syntax errors'; +var actual = ''; +var expect = 'No Error'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = "SyntaxError"; + try + { + eval('(function*() {x = 12 + yield;})'); + actual = 'No Error'; + } + catch(ex) + { + actual = ex.name; + } + reportCompare(expect, actual, summary + ': function*() {x = 12 + yield;}'); + + expect = "SyntaxError"; + try + { + eval('(function*() {x = 12 + yield 42})'); + actual = 'No Error'; + } + catch(ex) + { + actual = ex.name; + } + reportCompare(expect, actual, summary + ': function*() {x = 12 + yield 42}'); + + expect = 'No Error'; + try + { + eval('(function*() {x = 12 + (yield);})'); + actual = 'No Error'; + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': function*() {x = 12 + (yield);}'); + + try + { + eval('(function* () {foo((yield))})'); + actual = 'No Error'; + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': function* () {foo((yield))}'); + + try + { + eval('(function*() {x = 12 + (yield 42)})'); + actual = 'No Error'; + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': function*() {x = 12 + (yield 42)}'); + + try + { + eval('(function* (){foo((yield 42))})'); + actual = 'No Error'; + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': function* (){foo((yield 42))}'); +} diff --git a/js/src/tests/non262/generators/regress-345879-01.js b/js/src/tests/non262/generators/regress-345879-01.js new file mode 100644 index 0000000000..993244f803 --- /dev/null +++ b/js/src/tests/non262/generators/regress-345879-01.js @@ -0,0 +1,30 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 345879; +var summary = 'Crash when calling a function from a generator with less arguments than its arity '; +var actual = 'No Crash'; +var expect = 'No Crash'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + function* gen() { + yield isNaN(); + } + + f = gen(); + f.next(); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/generators/regress-349362.js b/js/src/tests/non262/generators/regress-349362.js new file mode 100644 index 0000000000..af22f0c69a --- /dev/null +++ b/js/src/tests/non262/generators/regress-349362.js @@ -0,0 +1,26 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 349362; +var summary = 'generator toString should be [object Generator]'; +var actual = ''; +var expect = '[object Generator]'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + var y = function*(){ yield 3}; + actual = y().toString(); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/generators/regress-349851.js b/js/src/tests/non262/generators/regress-349851.js new file mode 100644 index 0000000000..86b35cb24a --- /dev/null +++ b/js/src/tests/non262/generators/regress-349851.js @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 349851; +var summary = 'decompilation of yield \\n, 3'; +var actual = ''; +var expect = 'SyntaxError'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + // note the newline after yield is required for this test + try + { + var f = eval('function(){ yield \n,3 }'); + } + catch(ex) + { + actual = ex.name; + } + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/generators/regress-350809.js b/js/src/tests/non262/generators/regress-350809.js new file mode 100644 index 0000000000..f34546740d --- /dev/null +++ b/js/src/tests/non262/generators/regress-350809.js @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 350809; +var summary = 'Do not assertion: if yield in xml filtering predicate'; +var actual = 'No Crash'; +var expect = 'No Crash'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + try + { + eval('(function(){ <x/>.(yield 4) })().next();'); + } + catch(ex) + { + actual = expect = + 'InternalError: yield not yet supported from filtering predicate'; + } + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/generators/regress-351120.js b/js/src/tests/non262/generators/regress-351120.js new file mode 100644 index 0000000000..9d9ba0e411 --- /dev/null +++ b/js/src/tests/non262/generators/regress-351120.js @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 351120; +var summary = 'Incorrect error messages with yield expressions'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = /TypeError:.*(is not a function|Cannot find function).*/; + actual = ''; + try + { + (function*() { yield [].z({}); })().next(); + } + catch(ex) + { + actual = ex + ''; + } + reportMatch(expect, actual, summary); +} diff --git a/js/src/tests/non262/generators/regress-359062.js b/js/src/tests/non262/generators/regress-359062.js new file mode 100644 index 0000000000..656b34124e --- /dev/null +++ b/js/src/tests/non262/generators/regress-359062.js @@ -0,0 +1,38 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 359062; +var summary = 'Access generator local variables from nested functions'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = "Generator string"; + + var scope = "Global"; + + function* gen() { + var scope = "Generator"; + function inner() { + actual = scope + " " + typeof scope; + } + inner(); + yield; + } + + gen().next(); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/generators/regress-366941.js b/js/src/tests/non262/generators/regress-366941.js new file mode 100644 index 0000000000..6997aae044 --- /dev/null +++ b/js/src/tests/non262/generators/regress-366941.js @@ -0,0 +1,80 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: Robert Sayre + */ + + +//----------------------------------------------------------------------------- +var BUGNUMBER = 366941; +var summary = 'Destructuring enumerations, iterations'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + var list1 = [[1,2],[3,4],[5,6]]; + var list2 = [[1,2,3],[4,5,6],[7,8,9]]; + + expect = '1,2;3,4;5,6;'; + actual = ''; + + for (var [foo, bar] of list1) { + actual += foo + "," + bar + ";"; + } + + reportCompare(expect, actual, summary + ': 1'); + + expect = '1,2,3;4,5,6;7,8,9;'; + actual = ''; + for (var [foo, bar, baz] of list2) { + actual += foo + "," + bar + "," + baz + ";"; + } + + reportCompare(expect, actual, summary + ': 2'); + + function* gen(list) { + for (var test of list) { + yield test; + } + } + + var iter1 = gen(list1); + + expect = '1,2;3,4;5,6;'; + actual = ''; + + for (var [foo, bar] of iter1) { + actual += foo + "," + bar + ";"; + } + + reportCompare(expect, actual, summary + ': 3'); + + // Before JS1.7's destructuring for…in was fixed to match JS1.8's, + // the expected result was a SyntaxError about the for…in loop's lhs. + var iter2 = gen(list2); + expect = '1,2,3;4,5,6;7,8,9;'; + actual = ''; + + try + { + eval('for (var [foo, bar, baz] of iter2) {' + + 'actual += foo + "," + bar + "," + baz + ";";' + + '}'); + } + catch(ex) + { + actual = ex + ''; + } + + reportCompare(expect, actual, summary + ': 4'); +} diff --git a/js/src/tests/non262/generators/regress-384991.js b/js/src/tests/non262/generators/regress-384991.js new file mode 100644 index 0000000000..25439d907c --- /dev/null +++ b/js/src/tests/non262/generators/regress-384991.js @@ -0,0 +1,56 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 384991; +var summary = ' w(yield) should not cause "yield expression must be parenthesized" syntax error'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = 'No Error'; + + try + { + actual = 'No Error'; + (function*() { w((yield)); }); + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': 1'); + + try + { + actual = 'No Error'; + (function*() { w(1 ? yield : 0); }); + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': 2'); + + try + { + actual = 'No Error'; + (function* () { f(x = yield); const x = undefined; }); + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': 3'); +} diff --git a/js/src/tests/non262/generators/regress-466206.js b/js/src/tests/non262/generators/regress-466206.js new file mode 100644 index 0000000000..76c297038b --- /dev/null +++ b/js/src/tests/non262/generators/regress-466206.js @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 466206; +var summary = 'Do not crash due to unrooted function variables'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + var f; + + function* g() { + var x = {}; + f = function () { x.y; }; + if (0) yield; + } + + try { g().next(); } catch (e) {} + gc(); + f(); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/generators/return-finally.js b/js/src/tests/non262/generators/return-finally.js new file mode 100644 index 0000000000..9e4c6c8f3b --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/runtime.js b/js/src/tests/non262/generators/runtime.js new file mode 100644 index 0000000000..7146eef9ff --- /dev/null +++ b/js/src/tests/non262/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(\n) {\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/non262/generators/shell.js b/js/src/tests/non262/generators/shell.js new file mode 100644 index 0000000000..791e824841 --- /dev/null +++ b/js/src/tests/non262/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) { assertEq(Object.is(found, not_expected), false) } +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/non262/generators/simple-fib.js b/js/src/tests/non262/generators/simple-fib.js new file mode 100644 index 0000000000..487b656ab1 --- /dev/null +++ b/js/src/tests/non262/generators/simple-fib.js @@ -0,0 +1,55 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 326466; // bug 326466, comment 1 +var summary = "Simple Fibonacci iterator"; +var actual, expect; + +printBugNumber(BUGNUMBER); +printStatus(summary); + +/************** + * BEGIN TEST * + **************/ + +function* fib() +{ + var a = 0, b = 1; + while (true) + { + yield a; + var t = a; + a = b; + b += t; + } +} + +var failed = false; + +try +{ + var g = fib(); + + if (g.next().value != 0) + throw "F_0 = 0"; + if (g.next().value != 1) + throw "F_1 = 1"; + if (g.next().value != 1) + throw "F_2 = 1"; + if (g.next().value != 2) + throw "F_3 = 2"; +} +catch (e) +{ + failed = e; +} + + + +expect = false; +actual = failed; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/generators/subclass.js b/js/src/tests/non262/generators/subclass.js new file mode 100644 index 0000000000..f93f4df4db --- /dev/null +++ b/js/src/tests/non262/generators/subclass.js @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const GeneratorFunction = function*(){}.constructor; + +class MyGen extends GeneratorFunction {} + +// MyGen inherits from %GeneratorFunction%. +assertEq(Object.getPrototypeOf(MyGen), GeneratorFunction); + +// MyGen.prototype inherits from %Generator%. +assertEq(Object.getPrototypeOf(MyGen.prototype), GeneratorFunction.prototype); + +var fn = new MyGen("yield* [1, 2, 3]"); + +// fn inherits from MyGen.prototype. +assertEq(Object.getPrototypeOf(fn), MyGen.prototype); + +// fn.prototype inherits from %GeneratorPrototype%. +assertEq(Object.getPrototypeOf(fn.prototype), GeneratorFunction.prototype.prototype); + +// Ensure the new generator function can be executed. +var it = fn(); + +// it inherits from fn.prototype. +assertEq(Object.getPrototypeOf(it), fn.prototype); + +// Computes the expected result. +assertEqArray([...it], [1, 2, 3]); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/generators/syntax.js b/js/src/tests/non262/generators/syntax.js new file mode 100644 index 0000000000..21ecf02875 --- /dev/null +++ b/js/src/tests/non262/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 +} +assertThrowsInstanceOf(() => Function("function* g() { yield\n* foo }"), SyntaxError); +assertIteratorNext(function*(){ + yield + 3 + }(), undefined) + +// A YieldExpression is not a LogicalORExpression. +assertThrowsInstanceOf(() => Function("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/non262/generators/yield-error.js b/js/src/tests/non262/generators/yield-error.js new file mode 100644 index 0000000000..ac915ebcd7 --- /dev/null +++ b/js/src/tests/non262/generators/yield-error.js @@ -0,0 +1,32 @@ +var BUGNUMBER = 1384299; +var summary = "yield outside of generators should provide better error"; + +print(BUGNUMBER + ": " + summary); + +let caught = false; +try { + eval("yield 10"); +} catch(e) { + assertEq(e.message, "yield expression is only valid in generators"); + caught = true; +} +assertEq(caught, true); + +try { + eval("function f() { yield 10; }"); +} catch(e) { + assertEq(e.message, "yield expression is only valid in generators"); + caught = true; +} +assertEq(caught, true); + +try { + eval("async function f() { yield 10; }"); +} catch(e) { + assertEq(e.message, "yield expression is only valid in generators"); + caught = true; +} +assertEq(caught, true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/generators/yield-iterator-close.js b/js/src/tests/non262/generators/yield-iterator-close.js new file mode 100644 index 0000000000..970ad494d2 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/yield-non-regexp.js b/js/src/tests/non262/generators/yield-non-regexp.js new file mode 100644 index 0000000000..d9fbcd3598 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/yield-star-iterator-close.js b/js/src/tests/non262/generators/yield-star-iterator-close.js new file mode 100644 index 0000000000..91ad31cb64 --- /dev/null +++ b/js/src/tests/non262/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/non262/generators/yield-star-iterator-primitive.js b/js/src/tests/non262/generators/yield-star-iterator-primitive.js new file mode 100644 index 0000000000..90d9e75785 --- /dev/null +++ b/js/src/tests/non262/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); diff --git a/js/src/tests/non262/generators/yield-star-throw-htmldda.js b/js/src/tests/non262/generators/yield-star-throw-htmldda.js new file mode 100644 index 0000000000..a903da02f7 --- /dev/null +++ b/js/src/tests/non262/generators/yield-star-throw-htmldda.js @@ -0,0 +1,28 @@ +function* g(iter) { + yield* iter; +} + +var calledReturn = false; + +var it = g({ + [Symbol.iterator]() { + return this; + }, + next() { + return {done: false}; + }, + throw: createIsHTMLDDA(), + return() { + calledReturn = true; + return {done: false}; + } +}); + +it.next(); + +assertThrowsInstanceOf(() => it.throw(""), TypeError); + +assertEq(calledReturn, false); + +if (typeof reportCompare === "function") + reportCompare(0, 0); |