a76fe0a2cf
R=rossberg@chromium.org BUG=v8:2355 LOG=Y Review URL: https://codereview.chromium.org/573963003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23974 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
337 lines
9.5 KiB
JavaScript
337 lines
9.5 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.
|
|
|
|
// Flags: --harmony-scoping --harmony-proxies
|
|
|
|
// 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.
|
|
assertEquals([undefined, undefined, undefined],
|
|
fold(append, [],
|
|
results([10, "foo", /qux/, { value: 37, done: true }])));
|
|
// 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" }));
|
|
|
|
// Proxy results, with getters.
|
|
function transparent_proxy(x) {
|
|
return Proxy.create({
|
|
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(Proxy.create({
|
|
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)));
|