Bug 958951 - Return IteratorResult object for completed generators instead of throwing. r=jorendorff
authorTill Schneidereit <till@tillschneidereit.net>
Sat, 01 Feb 2014 23:20:14 +0100
changeset 182543 7f177e032c15ac8d275ba005122d84a11df65271
parent 182542 09013fef24b123f128bf424822fe4151b8bf32cf
child 182544 30d4356308c79f88a8733b1e54609b7439a84f84
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs958951
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 958951 - Return IteratorResult object for completed generators instead of throwing. r=jorendorff
js/src/jit-test/tests/debug/resumption-06.js
js/src/jit-test/tests/generators/next-on-finished.js
js/src/jit-test/tests/generators/throw-on-finished.js
js/src/jsiter.cpp
js/src/tests/ecma_6/Generators/delegating-yield-2.js
js/src/tests/ecma_6/Generators/delegating-yield-6.js
js/src/tests/ecma_6/Generators/iteration.js
--- a/js/src/jit-test/tests/debug/resumption-06.js
+++ b/js/src/jit-test/tests/debug/resumption-06.js
@@ -12,9 +12,9 @@ g.eval("var dbg = new Debugger(debuggeeG
 function* gen() {
     yield '1';
     debugger;  // Force return here. The value is ignored.
     yield '2';
 }
 var iter = gen();
 assertIteratorNext(iter, '1');
 assertEq(iter.next(), '!');
-assertThrowsInstanceOf(iter.next.bind(iter), TypeError);
+assertIteratorDone(iter);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/generators/next-on-finished.js
@@ -0,0 +1,6 @@
+function*g(){ };
+o = g();
+o.next();
+result = o.next();
+assertEq(result.done, true);
+assertEq(o.value, undefined);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/generators/throw-on-finished.js
@@ -0,0 +1,7 @@
+load(libdir + "asserts.js");
+
+function*g(){ };
+o = g();
+o.next();
+function TestException() {};
+assertThrowsInstanceOf(() => o.throw(new TestException()), TestException);
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1705,18 +1705,21 @@ SendToGenerator(JSContext *cx, JSGenerat
 
 MOZ_ALWAYS_INLINE bool
 star_generator_next(JSContext *cx, CallArgs args)
 {
     RootedObject thisObj(cx, &args.thisv().toObject());
     JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
 
     if (gen->state == JSGEN_CLOSED) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_GENERATOR_FINISHED);
-        return false;
+        RootedObject obj(cx, CreateItrResultObject(cx, JS::UndefinedHandleValue, true));
+        if (!obj)
+            return false;
+        args.rval().setObject(*obj);
+        return true;
     }
 
     if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
         RootedValue val(cx, args[0]);
         js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
                             JSDVG_SEARCH_STACK, val, js::NullPtr());
         return false;
     }
@@ -1727,17 +1730,17 @@ star_generator_next(JSContext *cx, CallA
 
 MOZ_ALWAYS_INLINE bool
 star_generator_throw(JSContext *cx, CallArgs args)
 {
     RootedObject thisObj(cx, &args.thisv().toObject());
 
     JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
     if (gen->state == JSGEN_CLOSED) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_GENERATOR_FINISHED);
+        cx->setPendingException(args.get(0));
         return false;
     }
 
     return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), StarGenerator,
                            args.rval());
 }
 
 MOZ_ALWAYS_INLINE bool
--- a/js/src/tests/ecma_6/Generators/delegating-yield-2.js
+++ b/js/src/tests/ecma_6/Generators/delegating-yield-2.js
@@ -6,25 +6,25 @@ function* delegate(iter) { return yield*
 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);
-assertThrowsInstanceOf(function () { outer.throw(42) }, TypeError);
+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);
-assertThrowsInstanceOf(function () { outer.throw(42) }, TypeError);
+assertThrowsValue(function () { outer.throw(42) }, 42);
 
 // What would be an uncaught delegated throw, but with a monkeypatched iterator.
 inner = g1();
 outer = delegate(inner);
 assertIteratorNext(outer, 1);
 inner.throw = function(e) { return e*2; };
 assertEq(84, outer.throw(42));
 assertIteratorDone(outer, undefined);
@@ -37,17 +37,17 @@ assertIteratorDone(outer, 13);
 
 // What would be a caught delegated throw, but with a monkeypunched prototype.
 inner = g2();
 outer = delegate(inner);
 assertIteratorNext(outer, 1);
 delete GeneratorObjectPrototype.throw;
 var outer_throw_42 = GeneratorObjectPrototype_throw.bind(outer, 42);
 assertThrowsValue(outer_throw_42, 42);
-assertThrowsInstanceOf(outer_throw_42, TypeError);
+assertThrowsValue(outer_throw_42, 42);
 
 // Monkeypunch a different throw handler.
 inner = g2();
 outer = delegate(inner);
 outer_throw_42 = GeneratorObjectPrototype_throw.bind(outer, 42);
 assertIteratorNext(outer, 1);
 GeneratorObjectPrototype.throw = function(e) { return e*2; }
 assertEq(84, outer_throw_42());
--- a/js/src/tests/ecma_6/Generators/delegating-yield-6.js
+++ b/js/src/tests/ecma_6/Generators/delegating-yield-6.js
@@ -42,15 +42,15 @@ outer.next();
 outer.next();
 outer.next();
 outer.next();
 outer.next();
 
 assertEq(log, "indndndndndndv");
 
 // Outer's dead, man.  Outer's dead.
-assertThrowsInstanceOf(outer.next.bind(outer), TypeError);
+assertDeepEq(outer.next(), {value: undefined, done: true});
 
 // No more checking the iterator.
 assertEq(log, "indndndndndndv");
 
 if (typeof reportCompare == "function")
     reportCompare(true, true);
--- a/js/src/tests/ecma_6/Generators/iteration.js
+++ b/js/src/tests/ecma_6/Generators/iteration.js
@@ -9,50 +9,50 @@
 var GeneratorFunction = (function*(){yield 1;}).constructor;
 
 
 function TestGeneratorResultPrototype() {
     function* g() { yield 1; }
     var iter = g();
     assertIteratorNext(iter, 1);
     assertIteratorDone(iter, undefined);
-    assertThrowsInstanceOf(function() { iter.next() }, TypeError);
+    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);
         }
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        assertIteratorDone(iter, undefined);
     }
     function testSend(thunk) {
         var iter = thunk();
         for (var i = 0; i < expected_values_for_send.length; i++) {
             assertIteratorResult(i ? iter.next(send_val) : iter.next(),
                                  expected_values_for_send[i],
                                  i == expected_values_for_send.length - 1);
         }
-        assertThrowsInstanceOf(function() { iter.next(send_val); }, TypeError);
+        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);
-            assertThrowsInstanceOf(function () { iter.next(); }, TypeError);
+            assertIteratorDone(iter, undefined);
         }
     }
 
     testNext(g);
     testSend(g);
     testThrow(g);
 
     testNext(function*() { return yield* g(); });
@@ -331,63 +331,63 @@ 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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        assertIteratorDone(iter, undefined);
     }
     Test1(instantiate(g));
 
     function Test2(iter) {
         assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        assertIteratorDone(iter, undefined);
     }
     Test2(instantiate(g));
 
     function Test3(iter) {
         assertIteratorNext(iter, 1);
         assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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; }
@@ -395,68 +395,68 @@ function TestTryFinally(instantiate) {
     function Sentinel2() {}
 
     function Test1(iter) {
         assertIteratorNext(iter, 1);
         assertIteratorNext(iter, 2);
         assertIteratorNext(iter, 3);
         assertIteratorNext(iter, 4);
         assertIteratorDone(iter, undefined);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        assertIteratorDone(iter, undefined);
     }
     Test1(instantiate(g));
 
     function Test2(iter) {
         assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        assertIteratorDone(iter, undefined);
     }
     Test2(instantiate(g));
 
     function Test3(iter) {
         assertIteratorNext(iter, 1);
         assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        assertIteratorDone(iter, undefined);
     }
     Test7(instantiate(g));
 }
 TestTryFinally(function (g) { return g(); });
 TestTryFinally(function* (g) { return yield* g(); });
 
 function TestNestedTry(instantiate) {
     function* g() {
@@ -474,76 +474,76 @@ function TestNestedTry(instantiate) {
 
     function Test1(iter) {
         assertIteratorNext(iter, 1);
         assertIteratorNext(iter, 2);
         assertIteratorNext(iter, 3);
         assertIteratorNext(iter, 4);
         assertIteratorNext(iter, 5);
         assertIteratorDone(iter, undefined);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        assertIteratorDone(iter, undefined);
     }
     Test1(instantiate(g));
 
     function Test2(iter) {
         assertThrowsInstanceOf(function() { iter.throw(new Sentinel); }, Sentinel);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        assertIteratorDone(iter, undefined);
     }
     Test2(instantiate(g));
 
     function Test3(iter) {
         assertIteratorNext(iter, 1);
         assertIteratorResult(iter.throw(new Sentinel), 4, false);
         assertThrowsInstanceOf(function() { iter.next(); }, Sentinel);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        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);
-        assertThrowsInstanceOf(function() { iter.next(); }, TypeError);
+        assertIteratorDone(iter, undefined);
 
     }
     Test7(instantiate(g));
 
     // That's probably enough.
 }
 TestNestedTry(function (g) { return g(); });
 TestNestedTry(function* (g) { return yield* g(); });