Revision: 14666
Author:   [email protected]
Date:     Tue May 14 08:59:25 2013
Log:      Implement yield* (delegating yield)

Ideally this would have been implemented via desugaring at parse-time,
but yield* is an expression, and its desugaring includes statements like
while and try/catch.  We'd have to have BlockExpression in the AST to
support that, and it's not worth it for this feature.

So instead we implement all of the logic in
FullCodeGenerator::VisitYield.  Delegating yield AST nodes now have a
try handler index, for the try/catch.  Otherwise the implementation is
straightforward.

[email protected]
BUG=v8:2355
TEST=mjsunit/harmony/generators-iteration
http://code.google.com/p/v8/source/detail?r=14666

Modified:
 /branches/bleeding_edge/src/arm/full-codegen-arm.cc
 /branches/bleeding_edge/src/ast.h
 /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/x64/full-codegen-x64.cc
 /branches/bleeding_edge/test/mjsunit/harmony/generators-iteration.js

=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Wed May 8 08:02:08 2013 +++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Tue May 14 08:59:25 2013
@@ -1961,8 +1961,102 @@
       break;
     }

-    case Yield::DELEGATING:
-      UNIMPLEMENTED();
+    case Yield::DELEGATING: {
+      VisitForStackValue(expr->generator_object());
+
+      // Initial stack layout is as follows:
+      // [sp + 1 * kPointerSize] iter
+      // [sp + 0 * kPointerSize] g
+
+      Label l_catch, l_try, l_resume, l_send, l_call, l_loop;
+      // Initial send value is undefined.
+      __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+      __ b(&l_send);
+
+      // catch (e) { receiver = iter; f = iter.throw; arg = e; }
+      __ bind(&l_catch);
+      handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
+      __ ldr(r3, MemOperand(sp, 1 * kPointerSize));      // iter
+      __ push(r3);                                       // iter
+      __ push(r0);                                       // exception
+      __ ldr(r0, MemOperand(sp, 3 * kPointerSize));      // iter
+ __ push(r0); // push LoadIC state
+      __ LoadRoot(r2, Heap::kthrow_stringRootIndex);     // "throw"
+      Handle<Code> throw_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(throw_ic); // iter.throw in r0 + __ add(sp, sp, Operand(kPointerSize)); // drop LoadIC state
+      __ jmp(&l_call);
+
+      // try { received = yield result.value }
+      __ bind(&l_try);
+      __ pop(r0);                                        // result.value
+      __ PushTryHandler(StackHandler::CATCH, expr->index());
+      const int handler_size = StackHandlerConstants::kSize;
+      __ push(r0);                                       // result.value
+ __ ldr(r3, MemOperand(sp, (0 + 1) * kPointerSize + handler_size)); // g
+      __ push(r3);                                       // g
+      __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+      __ ldr(context_register(),
+             MemOperand(fp, StandardFrameConstants::kContextOffset));
+      __ CompareRoot(r0, Heap::kTheHoleValueRootIndex);
+      __ b(ne, &l_resume);
+      EmitReturnIteratorResult(false);
+      __ bind(&l_resume);                                // received in r0
+      __ PopTryHandler();
+
+      // receiver = iter; f = iter.send; arg = received;
+      __ bind(&l_send);
+      __ ldr(r3, MemOperand(sp, 1 * kPointerSize));      // iter
+      __ push(r3);                                       // iter
+      __ push(r0);                                       // received
+      __ ldr(r0, MemOperand(sp, 3 * kPointerSize));      // iter
+ __ push(r0); // push LoadIC state
+      __ LoadRoot(r2, Heap::ksend_stringRootIndex);      // "send"
+      Handle<Code> send_ic = isolate()->builtins()->LoadIC_Initialize();
+      CallIC(send_ic);                                   // iter.send in r0
+ __ add(sp, sp, Operand(kPointerSize)); // drop LoadIC state
+
+      // result = f.call(receiver, arg);
+      __ bind(&l_call);
+      Label l_call_runtime;
+      __ JumpIfSmi(r0, &l_call_runtime);
+      __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE);
+      __ b(ne, &l_call_runtime);
+      __ mov(r1, r0);
+      ParameterCount count(1);
+      __ InvokeFunction(r1, count, CALL_FUNCTION,
+                        NullCallWrapper(), CALL_AS_METHOD);
+      __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+      __ jmp(&l_loop);
+      __ bind(&l_call_runtime);
+      __ push(r0);
+      __ CallRuntime(Runtime::kCall, 3);
+
+      // val = result.value; if (!result.done) goto l_try;
+      __ bind(&l_loop);
+      // result.value
+      __ push(r0);                                       // save result
+      __ LoadRoot(r2, Heap::kvalue_stringRootIndex);     // "value"
+      Handle<Code> value_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(value_ic); // result.value in r0
+      __ pop(r1);                                        // result
+      __ push(r0);                                       // result.value
+      __ mov(r0, r1);                                    // result
+ __ push(r0); // push LoadIC state
+      __ LoadRoot(r2, Heap::kdone_stringRootIndex);      // "done"
+      Handle<Code> done_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(done_ic); // result.done in r0 + __ add(sp, sp, Operand(kPointerSize)); // drop LoadIC state
+      ToBooleanStub stub(r0);
+      __ CallStub(&stub);
+      __ cmp(r0, Operand(0));
+      __ b(eq, &l_try);
+
+      // result.value
+      __ pop(r0);                                        // result.value
+      context()->DropAndPlug(2, r0);                     // drop iter and g
+      break;
+    }
   }
 }

=======================================
--- /branches/bleeding_edge/src/ast.h   Wed May  8 08:02:08 2013
+++ /branches/bleeding_edge/src/ast.h   Tue May 14 08:59:25 2013
@@ -1991,6 +1991,18 @@
   Expression* expression() const { return expression_; }
   Kind yield_kind() const { return yield_kind_; }
   virtual int position() const { return pos_; }
+
+  // Delegating yield surrounds the "yield" in a "try/catch".  This index
+  // locates the catch handler in the handler table, and is equivalent to
+  // TryCatchStatement::index().
+  int index() const {
+    ASSERT(yield_kind() == DELEGATING);
+    return index_;
+  }
+  void set_index(int index) {
+    ASSERT(yield_kind() == DELEGATING);
+    index_ = index;
+  }

  protected:
   Yield(Isolate* isolate,
@@ -2002,12 +2014,14 @@
         generator_object_(generator_object),
         expression_(expression),
         yield_kind_(yield_kind),
+        index_(-1),
         pos_(pos) { }

  private:
   Expression* generator_object_;
   Expression* expression_;
   Kind yield_kind_;
+  int index_;
   int pos_;
 };

=======================================
--- /branches/bleeding_edge/src/heap.h  Fri May  3 03:36:16 2013
+++ /branches/bleeding_edge/src/heap.h  Tue May 14 08:59:25 2013
@@ -273,7 +273,11 @@
   V(minus_infinity_string, "-Infinity")                                  \
   V(hidden_stack_trace_string, "v8::hidden_stack_trace")                 \
   V(query_colon_string, "(?:)")                                          \
-  V(Generator_string, "Generator")
+  V(Generator_string, "Generator")                                       \
+  V(send_string, "send")                                                 \
+  V(throw_string, "throw")                                               \
+  V(done_string, "done")                                                 \
+  V(value_string, "value")

 // Forward declarations.
 class GCTracer;
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Wed May 8 08:02:08 2013 +++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Tue May 14 08:59:25 2013
@@ -1922,8 +1922,95 @@
       break;
     }

-    case Yield::DELEGATING:
-      UNIMPLEMENTED();
+    case Yield::DELEGATING: {
+      VisitForStackValue(expr->generator_object());
+
+      // Initial stack layout is as follows:
+      // [sp + 1 * kPointerSize] iter
+      // [sp + 0 * kPointerSize] g
+
+      Label l_catch, l_try, l_resume, l_send, l_call, l_loop;
+      // Initial send value is undefined.
+      __ mov(eax, isolate()->factory()->undefined_value());
+      __ jmp(&l_send);
+
+      // catch (e) { receiver = iter; f = iter.throw; arg = e; }
+      __ bind(&l_catch);
+      handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
+      __ push(Operand(esp, 1 * kPointerSize));           // iter
+      __ push(eax);                                      // exception
+      __ mov(edx, Operand(esp, 3 * kPointerSize));       // iter
+      __ mov(ecx, isolate()->factory()->throw_string());  // "throw"
+      Handle<Code> throw_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(throw_ic); // iter.throw in eax
+      __ jmp(&l_call);
+
+      // try { received = yield result.value }
+      __ bind(&l_try);
+      __ pop(eax);                                       // result.value
+      __ PushTryHandler(StackHandler::CATCH, expr->index());
+      const int handler_size = StackHandlerConstants::kSize;
+      __ push(eax);                                      // result.value
+      __ push(Operand(esp, (0 + 1) * kPointerSize + handler_size));  // g
+      __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+      __ mov(context_register(),
+             Operand(ebp, StandardFrameConstants::kContextOffset));
+      __ CompareRoot(eax, Heap::kTheHoleValueRootIndex);
+      __ j(not_equal, &l_resume);
+      EmitReturnIteratorResult(false);
+      __ bind(&l_resume);                                // received in eax
+      __ PopTryHandler();
+
+      // receiver = iter; f = iter.send; arg = received;
+      __ bind(&l_send);
+      __ push(Operand(esp, 1 * kPointerSize));           // iter
+      __ push(eax);                                      // received
+      __ mov(edx, Operand(esp, 3 * kPointerSize));       // iter
+      __ mov(ecx, isolate()->factory()->send_string());  // "send"
+      Handle<Code> send_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(send_ic); // iter.send in rax
+
+      // result = f.call(receiver, arg);
+      __ bind(&l_call);
+      Label l_call_runtime;
+      __ JumpIfSmi(eax, &l_call_runtime);
+      __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
+      __ j(not_equal, &l_call_runtime);
+      __ mov(edi, eax);
+      ParameterCount count(1);
+      __ InvokeFunction(edi, count, CALL_FUNCTION,
+                        NullCallWrapper(), CALL_AS_METHOD);
+      __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+      __ jmp(&l_loop);
+      __ bind(&l_call_runtime);
+      __ push(eax);
+      __ CallRuntime(Runtime::kCall, 3);
+
+      // val = result.value; if (!result.done) goto l_try;
+      __ bind(&l_loop);
+      // result.value
+      __ push(eax);                                      // save result
+      __ mov(edx, eax);                                  // result
+      __ mov(ecx, isolate()->factory()->value_string());  // "value"
+      Handle<Code> value_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(value_ic); // result.value in rax
+      __ pop(ebx);                                       // result
+      __ push(eax);                                      // result.value
+      __ mov(edx, ebx);                                  // result
+      __ mov(ecx, isolate()->factory()->done_string());  // "done"
+      Handle<Code> done_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(done_ic); // result.done in rax
+      ToBooleanStub stub(eax);
+      __ push(eax);
+      __ CallStub(&stub);
+      __ test(eax, eax);
+      __ j(zero, &l_try);
+
+      // result.value
+      __ pop(eax);                                       // result.value
+      context()->DropAndPlug(2, eax);                    // drop iter and g
+      break;
+    }
   }
 }

=======================================
--- /branches/bleeding_edge/src/parser.cc       Wed May  8 08:02:08 2013
+++ /branches/bleeding_edge/src/parser.cc       Tue May 14 08:59:25 2013
@@ -3113,7 +3113,12 @@
   Expression* generator_object = factory()->NewVariableProxy(
       current_function_state_->generator_object_variable());
   Expression* expression = ParseAssignmentExpression(false, CHECK_OK);
-  return factory()->NewYield(generator_object, expression, kind, position);
+  Yield* yield =
+      factory()->NewYield(generator_object, expression, kind, position);
+  if (kind == Yield::DELEGATING) {
+    yield->set_index(current_function_state_->NextHandlerIndex());
+  }
+  return yield;
 }


=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Wed May 8 08:02:08 2013 +++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Tue May 14 08:59:25 2013
@@ -1946,8 +1946,94 @@
       break;
     }

-    case Yield::DELEGATING:
-      UNIMPLEMENTED();
+    case Yield::DELEGATING: {
+      VisitForStackValue(expr->generator_object());
+
+      // Initial stack layout is as follows:
+      // [sp + 1 * kPointerSize] iter
+      // [sp + 0 * kPointerSize] g
+
+      Label l_catch, l_try, l_resume, l_send, l_call, l_loop;
+      // Initial send value is undefined.
+      __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+      __ jmp(&l_send);
+
+      // catch (e) { receiver = iter; f = iter.throw; arg = e; }
+      __ bind(&l_catch);
+      handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
+      __ push(Operand(rsp, 1 * kPointerSize));           // iter
+      __ push(rax);                                      // exception
+      __ movq(rax, Operand(rsp, 3 * kPointerSize));      // iter
+      __ LoadRoot(rcx, Heap::kthrow_stringRootIndex);    // "throw"
+      Handle<Code> throw_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(throw_ic); // iter.throw in rax
+      __ jmp(&l_call);
+
+      // try { received = yield result.value }
+      __ bind(&l_try);
+      __ pop(rax);                                       // result.value
+      __ PushTryHandler(StackHandler::CATCH, expr->index());
+      const int handler_size = StackHandlerConstants::kSize;
+      __ push(rax);                                      // result.value
+      __ push(Operand(rsp, (0 + 1) * kPointerSize + handler_size));  // g
+      __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+      __ movq(context_register(),
+              Operand(rbp, StandardFrameConstants::kContextOffset));
+      __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+      __ j(not_equal, &l_resume);
+      EmitReturnIteratorResult(false);
+      __ bind(&l_resume);                                // received in rax
+      __ PopTryHandler();
+
+      // receiver = iter; f = iter.send; arg = received;
+      __ bind(&l_send);
+      __ push(Operand(rsp, 1 * kPointerSize));           // iter
+      __ push(rax);                                      // received
+      __ movq(rax, Operand(rsp, 3 * kPointerSize));      // iter
+      __ LoadRoot(rcx, Heap::ksend_stringRootIndex);     // "send"
+      Handle<Code> send_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(send_ic); // iter.send in rax
+
+      // result = f.call(receiver, arg);
+      __ bind(&l_call);
+      Label l_call_runtime;
+      __ JumpIfSmi(rax, &l_call_runtime);
+      __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx);
+      __ j(not_equal, &l_call_runtime);
+      __ movq(rdi, rax);
+      ParameterCount count(1);
+      __ InvokeFunction(rdi, count, CALL_FUNCTION,
+                        NullCallWrapper(), CALL_AS_METHOD);
+      __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+      __ jmp(&l_loop);
+      __ bind(&l_call_runtime);
+      __ push(rax);
+      __ CallRuntime(Runtime::kCall, 3);
+
+      // val = result.value; if (!result.done) goto l_try;
+      __ bind(&l_loop);
+      // result.value
+      __ push(rax);                                      // save result
+      __ LoadRoot(rcx, Heap::kvalue_stringRootIndex);    // "value"
+      Handle<Code> value_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(value_ic); // result.value in rax
+      __ pop(rbx);                                       // result
+      __ push(rax);                                      // result.value
+      __ movq(rax, rbx);                                 // result
+      __ LoadRoot(rcx, Heap::kdone_stringRootIndex);     // "done"
+      Handle<Code> done_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(done_ic); // result.done in rax
+      ToBooleanStub stub(rax);
+      __ push(rax);
+      __ CallStub(&stub);
+      __ testq(rax, rax);
+      __ j(zero, &l_try);
+
+      // result.value
+      __ pop(rax);                                       // result.value
+      context()->DropAndPlug(2, rax);                    // drop iter and g
+      break;
+    }
   }
 }

=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/generators-iteration.js Wed May 8 01:08:23 2013 +++ /branches/bleeding_edge/test/mjsunit/harmony/generators-iteration.js Tue May 14 08:59:25 2013
@@ -320,6 +320,16 @@
     "foo",
     [2, "1foo3", 5, "4foo6", "foofoo"]);

+// Delegating yield
+TestGenerator(
+    function* g26() {
+      function* g() { var x = yield 1; yield 2; yield x; return 3; }
+      yield* g();
+    },
+    [1, 2, undefined, undefined],
+    "foo",
+    [1, 2, "foo", undefined]);
+
 function TestTryCatch() {
function* g() { yield 1; try { yield 2; } catch (e) { yield e; } yield 3; }
   function Sentinel() {}
@@ -359,6 +369,7 @@
   assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
   assertThrows(function() { iter.next(); }, Error);

+  // ***
   iter = g();
   assertIteratorResult(1, false, iter.next());
   assertIteratorResult(2, false, iter.next());
@@ -373,6 +384,15 @@
   assertIteratorResult(3, false, iter.next());
   assertIteratorResult(undefined, true, iter.next());
   assertThrows(function() { iter.next(); }, Error);
+
+  // same as *** above with delegate
+  iter = (function* () { yield* g(); })();
+  assertIteratorResult(1, false, iter.next());
+  assertIteratorResult(2, false, iter.next());
+  var exn = new Sentinel;
+  assertIteratorResult(exn, false, iter.throw(exn));
+  assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
+  assertThrows(function() { iter.next(); }, Error);
 }
 TestTryCatch();

@@ -406,6 +426,7 @@
   assertThrows(function() { iter.next(); }, Sentinel);
   assertThrows(function() { iter.next(); }, Error);

+  // ***
   iter = g();
   assertIteratorResult(1, false, iter.next());
   assertIteratorResult(2, false, iter.next());
@@ -435,6 +456,14 @@
   assertIteratorResult(4, false, iter.next());
   assertIteratorResult(undefined, true, iter.next());
   assertThrows(function() { iter.next(); }, Error);
+
+  // same as *** above with delegate
+  iter = (function* () { yield* g(); })();
+  assertIteratorResult(1, false, iter.next());
+  assertIteratorResult(2, false, iter.next());
+  assertIteratorResult(3, false, iter.throw(new Sentinel));
+  assertThrows(function() { iter.throw(new Sentinel2); }, Sentinel2);
+  assertThrows(function() { iter.next(); }, Error);
 }
 TestTryFinally();

@@ -489,6 +518,7 @@
   assertIteratorResult(undefined, true, iter.next());
   assertThrows(function() { iter.next(); }, Error);

+  // ***
   iter = g();
   assertIteratorResult(1, false, iter.next());
   assertIteratorResult(2, false, iter.next());
@@ -508,6 +538,16 @@
   assertThrows(function() { iter.next(); }, Sentinel2);
   assertThrows(function() { iter.next(); }, Error);

+  // same as *** above with delegate
+  iter = (function* () { yield* g(); })();
+  assertIteratorResult(1, false, iter.next());
+  assertIteratorResult(2, false, iter.next());
+  var exn = new Sentinel;
+  assertIteratorResult(exn, false, iter.throw(exn));
+  assertIteratorResult(4, false, iter.throw(new Sentinel2));
+  assertThrows(function() { iter.next(); }, Sentinel2);
+  assertThrows(function() { iter.next(); }, Error);
+
   // That's probably enough.
 }
 TestNestedTry();

--
--
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