Bug 924040 - Update yield* to use @@iterator protocol. r=jwalden
authorAndy Wingo <wingo@igalia.com>
Thu, 17 Oct 2013 12:36:04 +0200
changeset 164933 60f90ee1eb33c28e3f62f8616619971191668e7e
parent 164932 ceb4bd44eb3497c808cdc552be7e2acfafe81a15
child 164934 4d45af314d05377a5da7550b8cd25ae9163a0c98
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs924040
milestone27.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 924040 - Update yield* to use @@iterator protocol. r=jwalden
js/src/frontend/BytecodeEmitter.cpp
js/src/tests/ecma_6/Generators/delegating-yield-1.js
js/src/tests/ecma_6/Generators/delegating-yield-11.js
js/src/tests/ecma_6/Generators/delegating-yield-12.js
js/src/tests/ecma_6/Generators/delegating-yield-5.js
js/src/tests/ecma_6/Generators/delegating-yield-6.js
js/src/tests/ecma_6/Generators/delegating-yield-7.js
js/src/tests/ecma_6/Generators/shell.js
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5104,18 +5104,31 @@ EmitReturn(ExclusiveContext *cx, Bytecod
 }
 
 static bool
 EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter)
 {
     JS_ASSERT(bce->sc->isFunctionBox());
     JS_ASSERT(bce->sc->asFunctionBox()->isStarGenerator());
 
-    if (!EmitTree(cx, bce, iter))                                // ITER
-        return false;
+    if (!EmitTree(cx, bce, iter))                                // ITERABLE
+        return false;
+
+    // Convert iterable to iterator.
+    if (Emit1(cx, bce, JSOP_DUP) < 0)                            // ITERABLE ITERABLE
+        return false;
+    if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // ITERABLE @@ITERATOR
+        return false;
+    if (Emit1(cx, bce, JSOP_SWAP) < 0)                           // @@ITERATOR ITERABLE
+        return false;
+    if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
+        return false;
+    if (EmitCall(cx, bce, JSOP_CALL, 0) < 0)                     // ITER
+        return false;
+    CheckTypeSet(cx, bce, JSOP_CALL);
 
     int depth = bce->stackDepth;
     JS_ASSERT(depth >= 1);
 
     // Initial send value is undefined.
     if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)                      // ITER RECEIVED
         return false;
     ptrdiff_t initialSend = -1;
--- a/js/src/tests/ecma_6/Generators/delegating-yield-1.js
+++ b/js/src/tests/ecma_6/Generators/delegating-yield-1.js
@@ -5,26 +5,30 @@
 
 // Test that yield* re-yields received results without re-boxing.
 
 function results(results) {
     var i = 0;
     function next() {
         return results[i++];
     }
-    return { next: next }
+    var iter = { next: next }
+    var ret = {};
+    ret[std_iterator] = function () { return iter; }
+    return ret;
 }
 
 function* yield_results(expected) {
     return yield* results(expected);
 }
 
-function collect_results(iter) {
+function collect_results(iterable) {
     var ret = [];
     var result;
+    var iter = iterable[std_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.
--- a/js/src/tests/ecma_6/Generators/delegating-yield-11.js
+++ b/js/src/tests/ecma_6/Generators/delegating-yield-11.js
@@ -3,16 +3,17 @@
 function Iter() {
     function next() {
         if (arguments.length != 1)
             throw Error;
         return { value: 42, done: true }
     }
 
     this.next = next;
+    this[std_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")
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Generators/delegating-yield-12.js
@@ -0,0 +1,49 @@
+// yield* calls @@iterator on the iterable to produce the iterator.
+
+var log = '';
+
+function IteratorWrapper(iterator) {
+    return {
+        next: function (val) {
+            log += 'n';
+            return iterator.next(val);
+        },
+
+        throw: function (exn) {
+            log += 't';
+            return iterator.throw(exn);
+        }
+    };
+}
+
+function IterableWrapper(iterable) {
+    var ret = {};
+
+    ret[std_iterator] = function () {
+        log += 'i';
+        return IteratorWrapper(iterable[std_iterator]());
+    }
+
+    return ret;
+}
+
+function* delegate(iter) { return yield* iter; }
+
+var iter = delegate(IterableWrapper([1, 2, 3]));
+assertIteratorResult(1, false, iter.next());
+assertIteratorResult(2, false, iter.next());
+assertIteratorResult(3, false, iter.next());
+assertIteratorResult(undefined, true, iter.next());
+
+assertEq(log, 'innnn');
+
+iter = delegate([1, 2, 3]);
+assertIteratorResult(1, false, iter.next());
+assertIteratorResult(2, false, iter.next());
+assertIteratorResult(3, false, iter.next());
+assertIteratorResult(undefined, true, iter.next());
+
+assertEq(log, 'innnn');
+
+if (typeof reportCompare == "function")
+    reportCompare(true, true);
--- a/js/src/tests/ecma_6/Generators/delegating-yield-5.js
+++ b/js/src/tests/ecma_6/Generators/delegating-yield-5.js
@@ -1,26 +1,30 @@
 // Test that a deep yield* chain re-yields received results without
 // re-boxing.
 
 function results(results) {
     var i = 0;
     function next() {
         return results[i++];
     }
-    return { next: next }
+    var iter = { next: next };
+    var ret = {};
+    ret[std_iterator] = function () { return iter; }
+    return ret;
 }
 
 function* yield_results(expected, n) {
     return yield* n ? yield_results(expected, n - 1) : results(expected);
 }
 
-function collect_results(iter) {
+function collect_results(iterable) {
     var ret = [];
     var result;
+    var iter = iterable[std_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.
--- a/js/src/tests/ecma_6/Generators/delegating-yield-6.js
+++ b/js/src/tests/ecma_6/Generators/delegating-yield-6.js
@@ -10,40 +10,47 @@ function collect_results(iter) {
         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[std_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, "ddddddv");
+assertEq(log, "indndndndndndv");
 
 // Outer's dead, man.  Outer's dead.
 assertThrowsInstanceOf(outer.next.bind(outer), TypeError);
 
 // No more checking the iterator.
-assertEq(log, "ddddddv");
+assertEq(log, "indndndndndndv");
 
 if (typeof reportCompare == "function")
     reportCompare(true, true);
--- a/js/src/tests/ecma_6/Generators/delegating-yield-7.js
+++ b/js/src/tests/ecma_6/Generators/delegating-yield-7.js
@@ -1,16 +1,21 @@
 // The iteratee of yield* can be a proxy.
 
 function results(results) {
     var i = 0;
+    function iterator() {
+        return this;
+    }
     function next() {
         return results[i++];
     }
-    return { next: next }
+    var ret = { next: next }
+    ret[std_iterator] = iterator;
+    return ret;
 }
 
 function* yield_results(expected) {
     return yield* Proxy(results(expected), {});
 }
 
 function collect_results(iter) {
     var ret = [];
--- a/js/src/tests/ecma_6/Generators/shell.js
+++ b/js/src/tests/ecma_6/Generators/shell.js
@@ -1,11 +1,22 @@
 /* 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 std_iterator = (function() {
+    try {
+        for (var _ of new Proxy({}, { get: function(_, name) { throw name; } }))
+            break;
+    } catch (name) {
+        return name;
+    }
+    throw 'wat';
+})();
+
 function assertFalse(a) { assertEq(a, false) }
 function assertTrue(a) { assertEq(a, true) }
 function assertNotEq(found, not_expected) { assertFalse(found === expected) }
 function assertIteratorResult(value, done, result) {
-    assertDeepEq(result, { value: value, done: done });
+    assertDeepEq(result.value, value);
+    assertEq(result.done, done);
 }