| Index: test/mjsunit/harmony/iteration-semantics.js
|
| diff --git a/test/mjsunit/harmony/iteration-semantics.js b/test/mjsunit/harmony/iteration-semantics.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6215522a41bd6c3eaee25c409bcdc1e14874a710
|
| --- /dev/null
|
| +++ b/test/mjsunit/harmony/iteration-semantics.js
|
| @@ -0,0 +1,327 @@
|
| +// 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
|
| +
|
| +// Test for-of semantics.
|
| +
|
| +"use strict";
|
| +
|
| +
|
| +// First, some helpers.
|
| +
|
| +function* values() {
|
| + for (var i = 0; i < arguments.length; i++) {
|
| + yield arguments[i];
|
| + }
|
| +}
|
| +
|
| +function integers_until(max) {
|
| + function next() {
|
| + var ret = { value: this.n, done: this.n == max };
|
| + this.n++;
|
| + return ret;
|
| + }
|
| + return { next: next, n: 0 }
|
| +}
|
| +
|
| +function results(results) {
|
| + var i = 0;
|
| + function next() {
|
| + return results[i++];
|
| + }
|
| + return { 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, iter) {
|
| + for (var x of iter) {
|
| + seed = cons(x, seed);
|
| + }
|
| + return seed;
|
| +}
|
| +
|
| +function* take(iter, n) {
|
| + if (n == 0) return;
|
| + for (let x of iter) {
|
| + yield x;
|
| + if (--n == 0) break;
|
| + }
|
| +}
|
| +
|
| +function nth(iter, n) {
|
| + for (let x of iter) {
|
| + if (n-- == 0) return x;
|
| + }
|
| + throw "unreachable";
|
| +}
|
| +
|
| +function* skip_every(iter, n) {
|
| + var i = 0;
|
| + for (let x of iter) {
|
| + if (++i % n == 0) continue;
|
| + yield x;
|
| + }
|
| +}
|
| +
|
| +function* iter_map(iter, f) {
|
| + for (var x of iter) {
|
| + yield f(x);
|
| + }
|
| +}
|
| +function nested_fold(cons, seed, iter) {
|
| + var visited = []
|
| + for (let x of iter) {
|
| + for (let y of x) {
|
| + seed = cons(y, seed);
|
| + }
|
| + }
|
| + return seed;
|
| +}
|
| +
|
| +function* unreachable(iter) {
|
| + for (let x of iter) {
|
| + 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(iter, n) {
|
| + function next() {
|
| + if (n-- == 0) delete this.next;
|
| + return iter.next();
|
| + }
|
| + return { next: next }
|
| +}
|
| +
|
| +function poison_next_after(iter, n) {
|
| + function next() {
|
| + return iter.next();
|
| + }
|
| + function next_getter() {
|
| + if (n-- < 0)
|
| + throw "poisoned";
|
| + return next;
|
| + }
|
| + var o = {};
|
| + Object.defineProperty(o, 'next', { get: next_getter });
|
| + return 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')])));
|
| +
|
| +// Null and undefined do not cause an error.
|
| +assertEquals(0, fold(sum, 0, unreachable(null)));
|
| +assertEquals(0, fold(sum, 0, unreachable(undefined)));
|
| +
|
| +// Other non-iterators do cause an error.
|
| +assertThrows('fold(sum, 0, unreachable({}))', TypeError);
|
| +assertThrows('fold(sum, 0, unreachable("foo"))', 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(iter) {
|
| + var n = 0;
|
| +outer:
|
| + while (true) {
|
| + n++;
|
| + for (var x of iter) continue outer;
|
| + break;
|
| + }
|
| + return n;
|
| +}
|
| +assertEquals(11, labelled_continue(integers_until(10)));
|
| +
|
| +function labelled_break(iter) {
|
| + var n = 0;
|
| +outer:
|
| + while (true) {
|
| + n++;
|
| + for (var x of iter) break outer;
|
| + }
|
| + return n;
|
| +}
|
| +assertEquals(1, labelled_break(integers_until(10)));
|
| +
|
| +// Test continue/break in catch.
|
| +function catch_control(iter, k) {
|
| + var n = 0;
|
| + for (var x of iter) {
|
| + 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(iter, k) {
|
| + var n = 0;
|
| + for (var x of iter) {
|
| + 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(x, n) {
|
| + return Proxy.create({
|
| + get: function(receiver, name) {
|
| + if (name == 'next' && n-- < 0) throw "unreachable";
|
| + return x[name];
|
| + },
|
| + // Needed for integers_until(10)'s this.n++.
|
| + set: function(receiver, name, val) {
|
| + return x[name] = val;
|
| + }
|
| + });
|
| +}
|
| +assertEquals(45, fold(sum, 0, poison_proxy_after(integers_until(10), 10)));
|
|
|