Commit b29a78fb authored by wingo@igalia.com's avatar wingo@igalia.com

Baseline for-of implementation

Add full-codegen support for the ES6 for-of iteration statement.

R=mstarzinger@chromium.org, rossberg@chromium.org
TEST=mjsunit/harmony/iteration-semantics
BUG=v8:2214

Review URL: https://codereview.chromium.org/15288011

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15002 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 8afeaabe
......@@ -1081,9 +1081,8 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
ForIn loop_statement(this, stmt);
increment_loop_depth();
// Get the object to enumerate over. Both SpiderMonkey and JSC
// ignore null and undefined in contrast to the specification; see
// ECMA-262 section 12.6.4.
// Get the object to enumerate over. If the object is null or undefined, skip
// over the loop. See ECMA-262 version 5, section 12.6.4.
VisitForAccumulatorValue(stmt->enumerable());
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
__ cmp(r0, ip);
......@@ -1259,6 +1258,65 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
}
void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
Comment cmnt(masm_, "[ ForOfStatement");
SetStatementPosition(stmt);
Iteration loop_statement(this, stmt);
increment_loop_depth();
// var iterator = iterable[@@iterator]()
VisitForAccumulatorValue(stmt->assign_iterator());
// As with for-in, skip the loop if the iterator is null or undefined.
__ CompareRoot(r0, Heap::kUndefinedValueRootIndex);
__ b(eq, loop_statement.break_label());
__ CompareRoot(r0, Heap::kNullValueRootIndex);
__ b(eq, loop_statement.break_label());
// Convert the iterator to a JS object.
Label convert, done_convert;
__ JumpIfSmi(r0, &convert);
__ CompareObjectType(r0, r1, r1, FIRST_SPEC_OBJECT_TYPE);
__ b(ge, &done_convert);
__ bind(&convert);
__ push(r0);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ bind(&done_convert);
__ push(r0);
// Loop entry.
__ bind(loop_statement.continue_label());
// result = iterator.next()
VisitForEffect(stmt->next_result());
// if (result.done) break;
Label result_not_done;
VisitForControl(stmt->result_done(),
loop_statement.break_label(),
&result_not_done,
&result_not_done);
__ bind(&result_not_done);
// each = result.value
VisitForEffect(stmt->assign_each());
// Generate code for the body of the loop.
Visit(stmt->body());
// Check stack before looping.
PrepareForBailoutForId(stmt->BackEdgeId(), NO_REGISTERS);
EmitBackEdgeBookkeeping(stmt, loop_statement.continue_label());
__ jmp(loop_statement.continue_label());
// Exit and decrement the loop depth.
PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
__ bind(loop_statement.break_label());
decrement_loop_depth();
}
void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
bool pretenure) {
// Use the fast case closure allocation code that allocates in new
......
......@@ -891,25 +891,16 @@ class ForEachStatement: public IterationStatement {
Expression* each() const { return each_; }
Expression* subject() const { return subject_; }
virtual BailoutId ContinueId() const { return EntryId(); }
virtual BailoutId StackCheckId() const { return body_id_; }
BailoutId BodyId() const { return body_id_; }
BailoutId PrepareId() const { return prepare_id_; }
protected:
ForEachStatement(Isolate* isolate, ZoneStringList* labels)
: IterationStatement(isolate, labels),
each_(NULL),
subject_(NULL),
body_id_(GetNextId(isolate)),
prepare_id_(GetNextId(isolate)) {
subject_(NULL) {
}
private:
Expression* each_;
Expression* subject_;
const BailoutId body_id_;
const BailoutId prepare_id_;
};
......@@ -926,13 +917,22 @@ class ForInStatement: public ForEachStatement {
enum ForInType { FAST_FOR_IN, SLOW_FOR_IN };
ForInType for_in_type() const { return for_in_type_; }
BailoutId BodyId() const { return body_id_; }
BailoutId PrepareId() const { return prepare_id_; }
virtual BailoutId ContinueId() const { return EntryId(); }
virtual BailoutId StackCheckId() const { return body_id_; }
protected:
ForInStatement(Isolate* isolate, ZoneStringList* labels)
: ForEachStatement(isolate, labels),
for_in_type_(SLOW_FOR_IN) {
for_in_type_(SLOW_FOR_IN),
body_id_(GetNextId(isolate)),
prepare_id_(GetNextId(isolate)) {
}
ForInType for_in_type_;
const BailoutId body_id_;
const BailoutId prepare_id_;
};
......@@ -940,14 +940,64 @@ class ForOfStatement: public ForEachStatement {
public:
DECLARE_NODE_TYPE(ForOfStatement)
void Initialize(Expression* each,
Expression* subject,
Statement* body,
Expression* assign_iterator,
Expression* next_result,
Expression* result_done,
Expression* assign_each) {
ForEachStatement::Initialize(each, subject, body);
assign_iterator_ = assign_iterator;
next_result_ = next_result;
result_done_ = result_done;
assign_each_ = assign_each;
}
Expression* iterable() const {
return subject();
}
// var iterator = iterable;
Expression* assign_iterator() const {
return assign_iterator_;
}
// var result = iterator.next();
Expression* next_result() const {
return next_result_;
}
// result.done
Expression* result_done() const {
return result_done_;
}
// each = result.value
Expression* assign_each() const {
return assign_each_;
}
virtual BailoutId ContinueId() const { return EntryId(); }
virtual BailoutId StackCheckId() const { return BackEdgeId(); }
BailoutId BackEdgeId() const { return back_edge_id_; }
protected:
ForOfStatement(Isolate* isolate, ZoneStringList* labels)
: ForEachStatement(isolate, labels) {
: ForEachStatement(isolate, labels),
assign_iterator_(NULL),
next_result_(NULL),
result_done_(NULL),
assign_each_(NULL),
back_edge_id_(GetNextId(isolate)) {
}
Expression* assign_iterator_;
Expression* next_result_;
Expression* result_done_;
Expression* assign_each_;
const BailoutId back_edge_id_;
};
......
......@@ -164,8 +164,8 @@ void BreakableStatementChecker::VisitForInStatement(ForInStatement* stmt) {
void BreakableStatementChecker::VisitForOfStatement(ForOfStatement* stmt) {
// Mark for in statements breakable if the iterable expression is.
Visit(stmt->iterable());
// For-of is breakable because of the next() call.
is_breakable_ = true;
}
......@@ -1389,11 +1389,6 @@ void FullCodeGenerator::VisitForStatement(ForStatement* stmt) {
}
void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
// TODO(wingo): Implement.
}
void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
Comment cmnt(masm_, "[ TryCatchStatement");
SetStatementPosition(stmt);
......
......@@ -296,7 +296,8 @@ namespace internal {
V(send_string, "send") \
V(throw_string, "throw") \
V(done_string, "done") \
V(value_string, "value")
V(value_string, "value") \
V(next_string, "next")
// Forward declarations.
class GCTracer;
......
......@@ -1033,9 +1033,8 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
ForIn loop_statement(this, stmt);
increment_loop_depth();
// Get the object to enumerate over. Both SpiderMonkey and JSC
// ignore null and undefined in contrast to the specification; see
// ECMA-262 section 12.6.4.
// Get the object to enumerate over. If the object is null or undefined, skip
// over the loop. See ECMA-262 version 5, section 12.6.4.
VisitForAccumulatorValue(stmt->enumerable());
__ cmp(eax, isolate()->factory()->undefined_value());
__ j(equal, &exit);
......@@ -1198,6 +1197,64 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
}
void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
Comment cmnt(masm_, "[ ForOfStatement");
SetStatementPosition(stmt);
Iteration loop_statement(this, stmt);
increment_loop_depth();
// var iterator = iterable[@@iterator]()
VisitForAccumulatorValue(stmt->assign_iterator());
// As with for-in, skip the loop if the iterator is null or undefined.
__ CompareRoot(eax, Heap::kUndefinedValueRootIndex);
__ j(equal, loop_statement.break_label());
__ CompareRoot(eax, Heap::kNullValueRootIndex);
__ j(equal, loop_statement.break_label());
// Convert the iterator to a JS object.
Label convert, done_convert;
__ JumpIfSmi(eax, &convert);
__ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
__ j(above_equal, &done_convert);
__ bind(&convert);
__ push(eax);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ bind(&done_convert);
// Loop entry.
__ bind(loop_statement.continue_label());
// result = iterator.next()
VisitForEffect(stmt->next_result());
// if (result.done) break;
Label result_not_done;
VisitForControl(stmt->result_done(),
loop_statement.break_label(),
&result_not_done,
&result_not_done);
__ bind(&result_not_done);
// each = result.value
VisitForEffect(stmt->assign_each());
// Generate code for the body of the loop.
Visit(stmt->body());
// Check stack before looping.
PrepareForBailoutForId(stmt->BackEdgeId(), NO_REGISTERS);
EmitBackEdgeBookkeeping(stmt, loop_statement.continue_label());
__ jmp(loop_statement.continue_label());
// Exit and decrement the loop depth.
PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
__ bind(loop_statement.break_label());
decrement_loop_depth();
}
void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
bool pretenure) {
// Use the fast case closure allocation code that allocates in new
......
......@@ -2636,6 +2636,78 @@ bool Parser::CheckInOrOf(ForEachStatement::VisitMode* visit_mode) {
}
void Parser::InitializeForEachStatement(ForEachStatement* stmt,
Expression* each,
Expression* subject,
Statement* body) {
ForOfStatement* for_of = stmt->AsForOfStatement();
if (for_of != NULL) {
Factory* heap_factory = isolate()->factory();
Handle<String> iterator_str = heap_factory->InternalizeOneByteString(
STATIC_ASCII_VECTOR(".iterator"));
Handle<String> result_str = heap_factory->InternalizeOneByteString(
STATIC_ASCII_VECTOR(".result"));
Variable* iterator =
top_scope_->DeclarationScope()->NewTemporary(iterator_str);
Variable* result = top_scope_->DeclarationScope()->NewTemporary(result_str);
Expression* assign_iterator;
Expression* next_result;
Expression* result_done;
Expression* assign_each;
// var iterator = iterable;
{
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
assign_iterator = factory()->NewAssignment(
Token::ASSIGN, iterator_proxy, subject, RelocInfo::kNoPosition);
}
// var result = iterator.next();
{
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
Expression* next_literal =
factory()->NewLiteral(heap_factory->next_string());
Expression* next_property = factory()->NewProperty(
iterator_proxy, next_literal, RelocInfo::kNoPosition);
ZoneList<Expression*>* next_arguments =
new(zone()) ZoneList<Expression*>(0, zone());
Expression* next_call = factory()->NewCall(
next_property, next_arguments, RelocInfo::kNoPosition);
Expression* result_proxy = factory()->NewVariableProxy(result);
next_result = factory()->NewAssignment(
Token::ASSIGN, result_proxy, next_call, RelocInfo::kNoPosition);
}
// result.done
{
Expression* done_literal =
factory()->NewLiteral(heap_factory->done_string());
Expression* result_proxy = factory()->NewVariableProxy(result);
result_done = factory()->NewProperty(
result_proxy, done_literal, RelocInfo::kNoPosition);
}
// each = result.value
{
Expression* value_literal =
factory()->NewLiteral(heap_factory->value_string());
Expression* result_proxy = factory()->NewVariableProxy(result);
Expression* result_value = factory()->NewProperty(
result_proxy, value_literal, RelocInfo::kNoPosition);
assign_each = factory()->NewAssignment(
Token::ASSIGN, each, result_value, RelocInfo::kNoPosition);
}
for_of->Initialize(each, subject, body,
assign_iterator, next_result, result_done, assign_each);
} else {
stmt->Initialize(each, subject, body);
}
}
Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
// ForStatement ::
// 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
......@@ -2670,7 +2742,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
VariableProxy* each =
top_scope_->NewUnresolved(factory(), name, interface);
Statement* body = ParseStatement(NULL, CHECK_OK);
loop->Initialize(each, enumerable, body);
InitializeForEachStatement(loop, each, enumerable, body);
Block* result = factory()->NewBlock(NULL, 2, false);
result->AddStatement(variable_statement, zone());
result->AddStatement(loop, zone());
......@@ -2734,7 +2806,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
body_block->AddStatement(variable_statement, zone());
body_block->AddStatement(assignment_statement, zone());
body_block->AddStatement(body, zone());
loop->Initialize(temp_proxy, enumerable, body_block);
InitializeForEachStatement(loop, temp_proxy, enumerable, body_block);
top_scope_ = saved_scope;
for_scope->set_end_position(scanner().location().end_pos);
for_scope = for_scope->FinalizeBlockScope();
......@@ -2766,7 +2838,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
Expect(Token::RPAREN, CHECK_OK);
Statement* body = ParseStatement(NULL, CHECK_OK);
loop->Initialize(expression, enumerable, body);
InitializeForEachStatement(loop, expression, enumerable, body);
top_scope_ = saved_scope;
for_scope->set_end_position(scanner().location().end_pos);
for_scope = for_scope->FinalizeBlockScope();
......
......@@ -688,6 +688,12 @@ class Parser BASE_EMBEDDED {
// in the object literal boilerplate.
Handle<Object> GetBoilerplateValue(Expression* expression);
// Initialize the components of a for-in / for-of statement.
void InitializeForEachStatement(ForEachStatement* stmt,
Expression* each,
Expression* subject,
Statement* body);
ZoneList<Expression*>* ParseArguments(bool* ok);
FunctionLiteral* ParseFunctionLiteral(Handle<String> var_name,
bool name_is_reserved,
......
......@@ -1046,9 +1046,8 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
ForIn loop_statement(this, stmt);
increment_loop_depth();
// Get the object to enumerate over. Both SpiderMonkey and JSC
// ignore null and undefined in contrast to the specification; see
// ECMA-262 section 12.6.4.
// Get the object to enumerate over. If the object is null or undefined, skip
// over the loop. See ECMA-262 version 5, section 12.6.4.
VisitForAccumulatorValue(stmt->enumerable());
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
__ j(equal, &exit);
......@@ -1224,6 +1223,64 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
}
void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
Comment cmnt(masm_, "[ ForOfStatement");
SetStatementPosition(stmt);
Iteration loop_statement(this, stmt);
increment_loop_depth();
// var iterator = iterable[@@iterator]()
VisitForAccumulatorValue(stmt->assign_iterator());
// As with for-in, skip the loop if the iterator is null or undefined.
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
__ j(equal, loop_statement.break_label());
__ CompareRoot(rax, Heap::kNullValueRootIndex);
__ j(equal, loop_statement.break_label());
// Convert the iterator to a JS object.
Label convert, done_convert;
__ JumpIfSmi(rax, &convert);
__ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx);
__ j(above_equal, &done_convert);
__ bind(&convert);
__ push(rax);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ bind(&done_convert);
// Loop entry.
__ bind(loop_statement.continue_label());
// result = iterator.next()
VisitForEffect(stmt->next_result());
// if (result.done) break;
Label result_not_done;
VisitForControl(stmt->result_done(),
loop_statement.break_label(),
&result_not_done,
&result_not_done);
__ bind(&result_not_done);
// each = result.value
VisitForEffect(stmt->assign_each());
// Generate code for the body of the loop.
Visit(stmt->body());
// Check stack before looping.
PrepareForBailoutForId(stmt->BackEdgeId(), NO_REGISTERS);
EmitBackEdgeBookkeeping(stmt, loop_statement.continue_label());
__ jmp(loop_statement.continue_label());
// Exit and decrement the loop depth.
PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
__ bind(loop_statement.break_label());
decrement_loop_depth();
}
void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
bool pretenure) {
// Use the fast case closure allocation code that allocates in new
......
// 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)));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment