Revision: 15002
Author: [email protected]
Date: Fri Jun 7 04:12:21 2013
Log: Baseline for-of implementation
Add full-codegen support for the ES6 for-of iteration statement.
[email protected], [email protected]
TEST=mjsunit/harmony/iteration-semantics
BUG=v8:2214
Review URL: https://codereview.chromium.org/15288011
http://code.google.com/p/v8/source/detail?r=15002
Added:
/branches/bleeding_edge/test/mjsunit/harmony/iteration-semantics.js
Modified:
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/ast.h
/branches/bleeding_edge/src/full-codegen.cc
/branches/bleeding_edge/src/heap.h
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/parser.cc
/branches/bleeding_edge/src/parser.h
/branches/bleeding_edge/src/x64/full-codegen-x64.cc
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/harmony/iteration-semantics.js Fri
Jun 7 04:12:21 2013
@@ -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)));
=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Tue Jun 4 03:30:05
2013
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Fri Jun 7 04:12:21
2013
@@ -1081,9 +1081,8 @@
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);
@@ -1257,6 +1256,65 @@
__ bind(&exit);
decrement_loop_depth();
}
+
+
+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,
=======================================
--- /branches/bleeding_edge/src/ast.h Thu Jun 6 07:38:26 2013
+++ /branches/bleeding_edge/src/ast.h Fri Jun 7 04:12:21 2013
@@ -890,26 +890,17 @@
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_;
};
@@ -925,29 +916,88 @@
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
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_;
};
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_;
};
=======================================
--- /branches/bleeding_edge/src/full-codegen.cc Thu Jun 6 07:38:26 2013
+++ /branches/bleeding_edge/src/full-codegen.cc Fri Jun 7 04:12:21 2013
@@ -164,8 +164,8 @@
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;
}
@@ -1387,11 +1387,6 @@
__ bind(loop_statement.break_label());
decrement_loop_depth();
}
-
-
-void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
- // TODO(wingo): Implement.
-}
void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
=======================================
--- /branches/bleeding_edge/src/heap.h Fri Jun 7 03:52:11 2013
+++ /branches/bleeding_edge/src/heap.h Fri Jun 7 04:12:21 2013
@@ -296,7 +296,8 @@
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;
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Tue Jun 4
03:30:05 2013
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Fri Jun 7
04:12:21 2013
@@ -1033,9 +1033,8 @@
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);
@@ -1196,6 +1195,64 @@
__ bind(&exit);
decrement_loop_depth();
}
+
+
+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,
=======================================
--- /branches/bleeding_edge/src/parser.cc Thu Jun 6 07:38:26 2013
+++ /branches/bleeding_edge/src/parser.cc Fri Jun 7 04:12:21 2013
@@ -2634,6 +2634,78 @@
}
return false;
}
+
+
+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) {
@@ -2670,7 +2742,7 @@
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 @@
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 @@
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();
=======================================
--- /branches/bleeding_edge/src/parser.h Thu Jun 6 07:38:26 2013
+++ /branches/bleeding_edge/src/parser.h Fri Jun 7 04:12:21 2013
@@ -688,6 +688,12 @@
// 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,
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Tue Jun 4 03:30:05
2013
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Fri Jun 7 04:12:21
2013
@@ -1046,9 +1046,8 @@
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);
@@ -1222,6 +1221,64 @@
__ bind(&exit);
decrement_loop_depth();
}
+
+
+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,
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.