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.


Reply via email to