163b5d705e
This reverts commit bf4cc9ee15
.
Reason for revert: Breaks windows with msvc and linux with gcc
https://build.chromium.org/p/client.v8/builders/V8%20Win64%20-%20msvc/builds/841
https://build.chromium.org/p/client.v8/builders/V8%20Linux%20gcc%204.8/builds/17265
Original change's description:
> [esnext] load `iterator.next` only once at beginning of iteration
>
> https://github.com/tc39/ecma262/pull/988 gained concensus during the
> september 2017 TC39 meetings. This moves the load of the "next" method
> to the very beginning of the iteration protocol, rather than during
> each iteration step.
>
> This impacts:
>
> - yield*
> - for-of loops
> - spread arguments
> - array spreads
>
> In the v8 implementation, this also affects async iteration versions of
> these things (the sole exception being the Async-From-Sync iterator,
> which requires a few more changes to work with this, likely done in a
> followup patch).
>
> This change introduces a new AST node, ResolvedProperty, which can be used
> as a callee by Call nodes to produce the same bytecode as Property calls,
> without observably re-loading the property. This is used in several
> AST-desugarings involving the iteration protocol.
>
> BUG=v8:6861, v8:5699
> R=rmcilroy@chromium.org, neis@chromium.org, adamk@chromium.org
>
> Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
> Change-Id: Ib81106a0182687fc5efea0bc32302ad06376773b
> Reviewed-on: https://chromium-review.googlesource.com/687997
> Commit-Queue: Caitlin Potter <caitp@igalia.com>
> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
> Reviewed-by: Adam Klein <adamk@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#50452}
TBR=rmcilroy@chromium.org,adamk@chromium.org,neis@chromium.org,caitp@igalia.com,caitp@chromium.org
Change-Id: I1797c0d596dfd6850d6f0f505f591a7a990dd1f1
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: v8:6861, v8:5699
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Reviewed-on: https://chromium-review.googlesource.com/857616
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50454}
360 lines
10 KiB
JavaScript
360 lines
10 KiB
JavaScript
// Copyright 2013 the V8 project authors. All rights reserved.
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following
|
|
// disclaimer in the documentation and/or other materials provided
|
|
// with the distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived
|
|
// from this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
// Test for-of semantics.
|
|
|
|
"use strict";
|
|
|
|
|
|
// First, some helpers.
|
|
|
|
function* values() {
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
yield arguments[i];
|
|
}
|
|
}
|
|
|
|
function wrap_iterator(iterator) {
|
|
var iterable = {};
|
|
iterable[Symbol.iterator] = function() { return iterator; };
|
|
return iterable;
|
|
}
|
|
|
|
function integers_until(max) {
|
|
function next() {
|
|
var ret = { value: this.n, done: this.n == max };
|
|
this.n++;
|
|
return ret;
|
|
}
|
|
return wrap_iterator({ next: next, n: 0 });
|
|
}
|
|
|
|
function results(results) {
|
|
var i = 0;
|
|
function next() {
|
|
return results[i++];
|
|
}
|
|
return wrap_iterator({ next: next });
|
|
}
|
|
|
|
function* integers_from(n) {
|
|
while (1) yield n++;
|
|
}
|
|
|
|
// A destructive append.
|
|
function append(x, tail) {
|
|
tail[tail.length] = x;
|
|
return tail;
|
|
}
|
|
|
|
function sum(x, tail) {
|
|
return x + tail;
|
|
}
|
|
|
|
function fold(cons, seed, iterable) {
|
|
for (var x of iterable) {
|
|
seed = cons(x, seed);
|
|
}
|
|
return seed;
|
|
}
|
|
|
|
function* take(iterable, n) {
|
|
if (n == 0) return;
|
|
for (let x of iterable) {
|
|
yield x;
|
|
if (--n == 0) break;
|
|
}
|
|
}
|
|
|
|
function nth(iterable, n) {
|
|
for (let x of iterable) {
|
|
if (n-- == 0) return x;
|
|
}
|
|
throw "unreachable";
|
|
}
|
|
|
|
function* skip_every(iterable, n) {
|
|
var i = 0;
|
|
for (let x of iterable) {
|
|
if (++i % n == 0) continue;
|
|
yield x;
|
|
}
|
|
}
|
|
|
|
function* iter_map(iterable, f) {
|
|
for (var x of iterable) {
|
|
yield f(x);
|
|
}
|
|
}
|
|
function nested_fold(cons, seed, iterable) {
|
|
var visited = []
|
|
for (let x of iterable) {
|
|
for (let y of x) {
|
|
seed = cons(y, seed);
|
|
}
|
|
}
|
|
return seed;
|
|
}
|
|
|
|
function* unreachable(iterable) {
|
|
for (let x of iterable) {
|
|
throw "not reached";
|
|
}
|
|
}
|
|
|
|
function one_time_getter(o, prop, val) {
|
|
function set_never() { throw "unreachable"; }
|
|
var gotten = false;
|
|
function get_once() {
|
|
if (gotten) throw "got twice";
|
|
gotten = true;
|
|
return val;
|
|
}
|
|
Object.defineProperty(o, prop, {get: get_once, set: set_never})
|
|
return o;
|
|
}
|
|
|
|
function never_getter(o, prop) {
|
|
function never() { throw "unreachable"; }
|
|
Object.defineProperty(o, prop, {get: never, set: never})
|
|
return o;
|
|
}
|
|
|
|
function remove_next_after(iterable, n) {
|
|
var iterator = iterable[Symbol.iterator]();
|
|
function next() {
|
|
if (n-- == 0) delete this.next;
|
|
return iterator.next();
|
|
}
|
|
return wrap_iterator({ next: next });
|
|
}
|
|
|
|
function poison_next_after(iterable, n) {
|
|
var iterator = iterable[Symbol.iterator]();
|
|
function next() {
|
|
return iterator.next();
|
|
}
|
|
function next_getter() {
|
|
if (n-- < 0)
|
|
throw "poisoned";
|
|
return next;
|
|
}
|
|
var o = {};
|
|
Object.defineProperty(o, 'next', { get: next_getter });
|
|
return wrap_iterator(o);
|
|
}
|
|
|
|
// Now, the tests.
|
|
|
|
// Non-generator iterators.
|
|
assertEquals(45, fold(sum, 0, integers_until(10)));
|
|
// Generator iterators.
|
|
assertEquals([1, 2, 3], fold(append, [], values(1, 2, 3)));
|
|
// Break.
|
|
assertEquals(45, fold(sum, 0, take(integers_from(0), 10)));
|
|
// Continue.
|
|
assertEquals(90, fold(sum, 0, take(skip_every(integers_from(0), 2), 10)));
|
|
// Return.
|
|
assertEquals(10, nth(integers_from(0), 10));
|
|
// Nested for-of.
|
|
assertEquals([0, 0, 1, 0, 1, 2, 0, 1, 2, 3],
|
|
nested_fold(append,
|
|
[],
|
|
iter_map(integers_until(5), integers_until)));
|
|
// Result objects with sparse fields.
|
|
assertEquals([undefined, 1, 2, 3],
|
|
fold(append, [],
|
|
results([{ done: false },
|
|
{ value: 1, done: false },
|
|
// A missing "done" is the same as undefined, which
|
|
// is false.
|
|
{ value: 2 },
|
|
// Not done.
|
|
{ value: 3, done: 0 },
|
|
// Done.
|
|
{ value: 4, done: 42 }])));
|
|
// Results that are not objects.
|
|
assertThrows(function() {
|
|
assertEquals([undefined, undefined, undefined],
|
|
fold(append, [],
|
|
results([10, "foo", /qux/, { value: 37, done: true }])));
|
|
}, TypeError);
|
|
// Getters (shudder).
|
|
assertEquals([1, 2],
|
|
fold(append, [],
|
|
results([one_time_getter({ value: 1 }, 'done', false),
|
|
one_time_getter({ done: false }, 'value', 2),
|
|
{ value: 37, done: true },
|
|
never_getter(never_getter({}, 'done'), 'value')])));
|
|
|
|
// Unlike the case with for-in, null and undefined cause an error.
|
|
assertThrows('fold(sum, 0, unreachable(null))', TypeError);
|
|
assertThrows('fold(sum, 0, unreachable(undefined))', TypeError);
|
|
|
|
// Other non-iterators do cause an error.
|
|
assertThrows('fold(sum, 0, unreachable({}))', TypeError);
|
|
assertThrows('fold(sum, 0, unreachable(false))', TypeError);
|
|
assertThrows('fold(sum, 0, unreachable(37))', TypeError);
|
|
|
|
// "next" is looked up each time.
|
|
assertThrows('fold(sum, 0, remove_next_after(integers_until(10), 5))',
|
|
TypeError);
|
|
// It is not called at any other time.
|
|
assertEquals(45,
|
|
fold(sum, 0, remove_next_after(integers_until(10), 10)));
|
|
// It is not looked up too many times.
|
|
assertEquals(45,
|
|
fold(sum, 0, poison_next_after(integers_until(10), 10)));
|
|
|
|
function labelled_continue(iterable) {
|
|
var n = 0;
|
|
outer:
|
|
while (true) {
|
|
n++;
|
|
for (var x of iterable) continue outer;
|
|
break;
|
|
}
|
|
return n;
|
|
}
|
|
assertEquals(11, labelled_continue(integers_until(10)));
|
|
|
|
function labelled_break(iterable) {
|
|
var n = 0;
|
|
outer:
|
|
while (true) {
|
|
n++;
|
|
for (var x of iterable) break outer;
|
|
}
|
|
return n;
|
|
}
|
|
assertEquals(1, labelled_break(integers_until(10)));
|
|
|
|
// Test continue/break in catch.
|
|
function catch_control(iterable, k) {
|
|
var n = 0;
|
|
for (var x of iterable) {
|
|
try {
|
|
return k(x);
|
|
} catch (e) {
|
|
if (e == "continue") continue;
|
|
else if (e == "break") break;
|
|
else throw e;
|
|
}
|
|
} while (false);
|
|
return false;
|
|
}
|
|
assertEquals(false,
|
|
catch_control(integers_until(10),
|
|
function() { throw "break" }));
|
|
assertEquals(false,
|
|
catch_control(integers_until(10),
|
|
function() { throw "continue" }));
|
|
assertEquals(5,
|
|
catch_control(integers_until(10),
|
|
function(x) {
|
|
if (x == 5) return x;
|
|
throw "continue";
|
|
}));
|
|
|
|
// Test continue/break in try.
|
|
function try_control(iterable, k) {
|
|
var n = 0;
|
|
for (var x of iterable) {
|
|
try {
|
|
var e = k(x);
|
|
if (e == "continue") continue;
|
|
else if (e == "break") break;
|
|
return e;
|
|
} catch (e) {
|
|
throw e;
|
|
}
|
|
} while (false);
|
|
return false;
|
|
}
|
|
assertEquals(false,
|
|
try_control(integers_until(10),
|
|
function() { return "break" }));
|
|
assertEquals(false,
|
|
try_control(integers_until(10),
|
|
function() { return "continue" }));
|
|
assertEquals(5,
|
|
try_control(integers_until(10),
|
|
function(x) { return (x == 5) ? x : "continue" }));
|
|
|
|
// TODO(neis,cbruni): Enable once the corresponding traps work again.
|
|
// Proxy results, with getters.
|
|
// function transparent_proxy(x) {
|
|
// return new Proxy({}, {
|
|
// get: function(receiver, name) { return x[name]; }
|
|
// });
|
|
// }
|
|
// assertEquals([1, 2],
|
|
// fold(append, [],
|
|
// results([one_time_getter({ value: 1 }, 'done', false),
|
|
// one_time_getter({ done: false }, 'value', 2),
|
|
// { value: 37, done: true },
|
|
// never_getter(never_getter({}, 'done'), 'value')]
|
|
// .map(transparent_proxy))));
|
|
|
|
// Proxy iterators.
|
|
// function poison_proxy_after(iterable, n) {
|
|
// var iterator = iterable[Symbol.iterator]();
|
|
// return wrap_iterator(new Proxy({}, {
|
|
// get: function(receiver, name) {
|
|
// if (name == 'next' && n-- < 0) throw "unreachable";
|
|
// return iterator[name];
|
|
// },
|
|
// // Needed for integers_until(10)'s this.n++.
|
|
// set: function(receiver, name, val) {
|
|
// return iterator[name] = val;
|
|
// }
|
|
// }));
|
|
// }
|
|
// assertEquals(45, fold(sum, 0, poison_proxy_after(integers_until(10), 10)));
|
|
|
|
|
|
function test_iterator_result_object_non_object(value, descr) {
|
|
var arr = [];
|
|
var ex;
|
|
var message = 'Iterator result ' + (descr || value) + ' is not an object';
|
|
try {
|
|
fold(append, arr,
|
|
results([{value: 1}, {}, value, {value: 2}, {done: true}]));
|
|
} catch (e) {
|
|
ex = e;
|
|
}
|
|
assertInstanceof(ex, TypeError);
|
|
assertEquals(message, ex.message);
|
|
assertArrayEquals([1, undefined], arr);
|
|
}
|
|
test_iterator_result_object_non_object(null);
|
|
test_iterator_result_object_non_object(undefined);
|
|
test_iterator_result_object_non_object(42);
|
|
test_iterator_result_object_non_object('abc');
|
|
test_iterator_result_object_non_object(false);
|
|
test_iterator_result_object_non_object(Symbol('x'), 'Symbol(x)');
|