Capture receiver in generator object
authormstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 25 Apr 2013 10:59:09 +0000 (10:59 +0000)
committermstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 25 Apr 2013 10:59:09 +0000 (10:59 +0000)
Previously there has been no reason to context-allocate the receiver, so
access to the receiver always goes through the stack.  This was failing
with generators, which assumed that forcing context allocation would
relieve the need of storing anything but the context and the function on
the stack.

This CL adds a slot in generator objects to capture the receiver, and
restores it when resuming a generator.

BUG=v8:2355
TEST=mjsunit/harmony/generators-iteration

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

Patch from Andy Wingo <wingo@igalia.com>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14434 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/arm/full-codegen-arm.cc
src/ia32/full-codegen-ia32.cc
src/objects-debug.cc
src/objects-inl.h
src/objects.h
src/runtime.cc
src/x64/full-codegen-x64.cc
test/mjsunit/harmony/generators-iteration.js

index 2a8b9ee..0ef4be0 100644 (file)
@@ -1998,20 +1998,25 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
   __ ldr(cp, FieldMemOperand(r1, JSGeneratorObject::kContextOffset));
   __ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset));
 
-  // Push holes for arguments to generator function.
+  // Load receiver and store as the first argument.
+  __ ldr(r2, FieldMemOperand(r1, JSGeneratorObject::kReceiverOffset));
+  __ push(r2);
+
+  // Push holes for the rest of the arguments to the generator function.
   __ ldr(r3, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
   __ ldr(r3,
          FieldMemOperand(r3, SharedFunctionInfo::kFormalParameterCountOffset));
   __ LoadRoot(r2, Heap::kTheHoleValueRootIndex);
-  Label push_argument_holes;
+  Label push_argument_holes, push_frame;
   __ bind(&push_argument_holes);
-  __ push(r2);
   __ sub(r3, r3, Operand(1), SetCC);
-  __ b(pl, &push_argument_holes);
+  __ b(mi, &push_frame);
+  __ push(r2);
+  __ jmp(&push_argument_holes);
 
   // Enter a new JavaScript frame, and initialize its slots as they were when
   // the generator was suspended.
-  Label push_frame, resume_frame;
+  Label resume_frame;
   __ bind(&push_frame);
   __ bl(&resume_frame);
   __ jmp(&done);
index 6b10c89..f71a76d 100644 (file)
@@ -1959,20 +1959,24 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
   __ mov(esi, FieldOperand(ebx, JSGeneratorObject::kContextOffset));
   __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset));
 
+  // Push receiver.
+  __ push(FieldOperand(ebx, JSGeneratorObject::kReceiverOffset));
+
   // Push holes for arguments to generator function.
   __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
   __ mov(edx,
          FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
   __ mov(ecx, isolate()->factory()->the_hole_value());
-  Label push_argument_holes;
+  Label push_argument_holes, push_frame;
   __ bind(&push_argument_holes);
-  __ push(ecx);
   __ sub(edx, Immediate(1));
-  __ j(not_carry, &push_argument_holes);
+  __ j(carry, &push_frame);
+  __ push(ecx);
+  __ jmp(&push_argument_holes);
 
   // Enter a new JavaScript frame, and initialize its slots as they were when
   // the generator was suspended.
-  Label push_frame, resume_frame;
+  Label resume_frame;
   __ bind(&push_frame);
   __ call(&resume_frame);
   __ jmp(&done);
index 32adf09..ee6df1d 100644 (file)
@@ -416,6 +416,7 @@ void JSGeneratorObject::JSGeneratorObjectVerify() {
   // initialized by the generator.  Hence these weak checks.
   VerifyObjectField(kFunctionOffset);
   VerifyObjectField(kContextOffset);
+  VerifyObjectField(kReceiverOffset);
   VerifyObjectField(kOperandStackOffset);
   VerifyObjectField(kContinuationOffset);
 }
index 822b310..147dc6b 100644 (file)
@@ -5054,6 +5054,7 @@ void Foreign::set_foreign_address(Address value) {
 
 ACCESSORS(JSGeneratorObject, function, JSFunction, kFunctionOffset)
 ACCESSORS(JSGeneratorObject, context, Context, kContextOffset)
+ACCESSORS(JSGeneratorObject, receiver, Object, kReceiverOffset)
 SMI_ACCESSORS(JSGeneratorObject, continuation, kContinuationOffset)
 ACCESSORS(JSGeneratorObject, operand_stack, FixedArray, kOperandStackOffset)
 
index 322841c..898bd52 100644 (file)
@@ -6320,6 +6320,9 @@ class JSGeneratorObject: public JSObject {
   // [context]: The context of the suspended computation.
   DECL_ACCESSORS(context, Context)
 
+  // [receiver]: The receiver of the suspended computation.
+  DECL_ACCESSORS(receiver, Object)
+
   // [continuation]: Offset into code of continuation.
   //
   // A positive offset indicates a suspended generator.  The special
@@ -6345,7 +6348,8 @@ class JSGeneratorObject: public JSObject {
   // Layout description.
   static const int kFunctionOffset = JSObject::kHeaderSize;
   static const int kContextOffset = kFunctionOffset + kPointerSize;
-  static const int kContinuationOffset = kContextOffset + kPointerSize;
+  static const int kReceiverOffset = kContextOffset + kPointerSize;
+  static const int kContinuationOffset = kReceiverOffset + kPointerSize;
   static const int kOperandStackOffset = kContinuationOffset + kPointerSize;
   static const int kSize = kOperandStackOffset + kPointerSize;
 
index a45caf4..ff630b8 100644 (file)
@@ -2413,6 +2413,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSGeneratorObject) {
   }
   generator->set_function(function);
   generator->set_context(Context::cast(frame->context()));
+  generator->set_receiver(frame->receiver());
   generator->set_continuation(0);
   generator->set_operand_stack(isolate->heap()->empty_fixed_array());
 
index 2f461a8..a20d468 100644 (file)
@@ -1983,21 +1983,25 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
   __ movq(rsi, FieldOperand(rbx, JSGeneratorObject::kContextOffset));
   __ movq(rdi, FieldOperand(rbx, JSGeneratorObject::kFunctionOffset));
 
+  // Push receiver.
+  __ push(FieldOperand(rbx, JSGeneratorObject::kReceiverOffset));
+
   // Push holes for arguments to generator function.
   __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
   __ movsxlq(rdx,
              FieldOperand(rdx,
                           SharedFunctionInfo::kFormalParameterCountOffset));
   __ LoadRoot(rcx, Heap::kTheHoleValueRootIndex);
-  Label push_argument_holes;
+  Label push_argument_holes, push_frame;
   __ bind(&push_argument_holes);
-  __ push(rcx);
   __ subq(rdx, Immediate(1));
-  __ j(not_carry, &push_argument_holes);
+  __ j(carry, &push_frame);
+  __ push(rcx);
+  __ jmp(&push_argument_holes);
 
   // Enter a new JavaScript frame, and initialize its slots as they were when
   // the generator was suspended.
-  Label push_frame, resume_frame;
+  Label resume_frame;
   __ bind(&push_frame);
   __ call(&resume_frame);
   __ jmp(&done);
index be795ea..5d41fc2 100644 (file)
@@ -211,32 +211,27 @@ TestGenerator(function* g16() { yield "baz"; gc(); yield "qux"; },
               ["baz", "qux", undefined]);
 
 // Receivers.
-function TestReceivers() {
-  TestGenerator(
-      function g17() {
-        function* g() { yield this.x; yield this.y; }
-        var o = { start: g, x: 1, y: 2 };
-        return o.start();
-      },
-      [1, 2, undefined],
-      "foo",
-      [1, 2, undefined]);
+TestGenerator(
+    function g17() {
+      function* g() { yield this.x; yield this.y; }
+      var o = { start: g, x: 1, y: 2 };
+      return o.start();
+    },
+    [1, 2, undefined],
+    "foo",
+    [1, 2, undefined]);
 
-  TestGenerator(
-      function g18() {
-        function* g() { yield this.x; yield this.y; }
-        var iter = new g;
-        iter.x = 1;
-        iter.y = 2;
-        return iter;
-      },
-      [1, 2, undefined],
-      "foo",
-      [1, 2, undefined]);
-}
-// TODO(wingo): Enable this test.  Currently accessing "this" doesn't work as
-// prior to generators, nothing needed to heap-allocate the receiver.
-// TestReceivers();
+TestGenerator(
+    function g18() {
+      function* g() { yield this.x; yield this.y; }
+      var iter = new g;
+      iter.x = 1;
+      iter.y = 2;
+      return iter;
+    },
+    [1, 2, undefined],
+    "foo",
+    [1, 2, undefined]);
 
 function TestRecursion() {
   function TestNextRecursion() {