Revision: 14563
Author: [email protected]
Date: Tue May 7 01:46:42 2013
Log: Generators return boxed values
Generators now box their return values in object literals of the form
{ value: VAL, done: DONE }
where DONE is false for yield expressions, and true for return
statements.
BUG=v8:2355
TEST=mjsunit/harmony/generators-iteration
[email protected]
Review URL: https://codereview.chromium.org/13870007
http://code.google.com/p/v8/source/detail?r=14563
Modified:
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/bootstrapper.cc
/branches/bleeding_edge/src/contexts.h
/branches/bleeding_edge/src/full-codegen.h
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/objects.h
/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 Thu Apr 25 03:59:09
2013
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Tue May 7 01:46:42
2013
@@ -1939,11 +1939,12 @@
Label resume;
__ CompareRoot(result_register(), Heap::kTheHoleValueRootIndex);
__ b(ne, &resume);
- __ pop(result_register());
if (expr->yield_kind() == Yield::SUSPEND) {
- // TODO(wingo): Box into { value: VALUE, done: false }.
+ EmitReturnIteratorResult(false);
+ } else {
+ __ pop(result_register());
+ EmitReturnSequence();
}
- EmitReturnSequence();
__ bind(&resume);
context()->Plug(result_register());
@@ -1955,18 +1956,7 @@
__ mov(r1,
Operand(Smi::FromInt(JSGeneratorObject::kGeneratorClosed)));
__ str(r1, FieldMemOperand(result_register(),
JSGeneratorObject::kContinuationOffset));
- __ pop(result_register());
- // TODO(wingo): Box into { value: VALUE, done: true }.
-
- // Exit all nested statements.
- NestedStatement* current = nesting_stack_;
- int stack_depth = 0;
- int context_length = 0;
- while (current != NULL) {
- current = current->Exit(&stack_depth, &context_length);
- }
- __ Drop(stack_depth);
- EmitReturnSequence();
+ EmitReturnIteratorResult(true);
break;
}
@@ -2072,6 +2062,55 @@
__ bind(&done);
context()->Plug(result_register());
}
+
+
+void FullCodeGenerator::EmitReturnIteratorResult(bool done) {
+ Label gc_required;
+ Label allocated;
+
+ Handle<Map> map(isolate()->native_context()->generator_result_map());
+
+ __ Allocate(map->instance_size(), r0, r2, r3, &gc_required, TAG_OBJECT);
+
+ __ bind(&allocated);
+ __ mov(r1, Operand(map));
+ __ pop(r2);
+ __ mov(r3, Operand(isolate()->factory()->ToBoolean(done)));
+ __ mov(r4, Operand(isolate()->factory()->empty_fixed_array()));
+ ASSERT_EQ(map->instance_size(), 5 * kPointerSize);
+ __ str(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
+ __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset));
+ __ str(r2,
+ FieldMemOperand(r0,
JSGeneratorObject::kResultValuePropertyOffset));
+ __ str(r3,
+ FieldMemOperand(r0,
JSGeneratorObject::kResultDonePropertyOffset));
+
+ // Only the value field needs a write barrier, as the other values are
in the
+ // root set.
+ __ RecordWriteField(r0, JSGeneratorObject::kResultValuePropertyOffset,
+ r2, r3, kLRHasBeenSaved, kDontSaveFPRegs);
+
+ if (done) {
+ // Exit all nested statements.
+ NestedStatement* current = nesting_stack_;
+ int stack_depth = 0;
+ int context_length = 0;
+ while (current != NULL) {
+ current = current->Exit(&stack_depth, &context_length);
+ }
+ __ Drop(stack_depth);
+ }
+
+ EmitReturnSequence();
+
+ __ bind(&gc_required);
+ __ Push(Smi::FromInt(map->instance_size()));
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ ldr(context_register(),
+ MemOperand(fp, StandardFrameConstants::kContextOffset));
+ __ jmp(&allocated);
+}
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Mon May 6 09:19:27 2013
+++ /branches/bleeding_edge/src/bootstrapper.cc Tue May 7 01:46:42 2013
@@ -1387,6 +1387,40 @@
*generator_object_prototype);
native_context()->set_generator_object_prototype_map(
*generator_object_prototype_map);
+
+ // Create a map for generator result objects.
+ ASSERT(object_map->inobject_properties() == 0);
+ STATIC_ASSERT(JSGeneratorObject::kResultPropertyCount == 2);
+ Handle<Map> generator_result_map = factory()->CopyMap(object_map,
+ JSGeneratorObject::kResultPropertyCount);
+ ASSERT(generator_result_map->inobject_properties() ==
+ JSGeneratorObject::kResultPropertyCount);
+
+ Handle<DescriptorArray> descriptors = factory()->NewDescriptorArray(0,
+ JSGeneratorObject::kResultPropertyCount);
+ DescriptorArray::WhitenessWitness witness(*descriptors);
+ generator_result_map->set_instance_descriptors(*descriptors);
+
+ Handle<String> value_string = factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("value"));
+ FieldDescriptor value_descr(*value_string,
+
JSGeneratorObject::kResultValuePropertyIndex,
+ NONE,
+ Representation::Tagged());
+ generator_result_map->AppendDescriptor(&value_descr, witness);
+
+ Handle<String> done_string = factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("done"));
+ FieldDescriptor done_descr(*done_string,
+ JSGeneratorObject::kResultDonePropertyIndex,
+ NONE,
+ Representation::Tagged());
+ generator_result_map->AppendDescriptor(&done_descr, witness);
+
+ generator_result_map->set_unused_property_fields(0);
+ ASSERT_EQ(JSGeneratorObject::kResultSize,
+ generator_result_map->instance_size());
+ native_context()->set_generator_result_map(*generator_result_map);
}
}
=======================================
--- /branches/bleeding_edge/src/contexts.h Thu May 2 04:36:48 2013
+++ /branches/bleeding_edge/src/contexts.h Tue May 7 01:46:42 2013
@@ -180,6 +180,7 @@
strict_mode_generator_function_map) \
V(GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, Map, \
generator_object_prototype_map) \
+ V(GENERATOR_RESULT_MAP_INDEX, Map, generator_result_map) \
V(RANDOM_SEED_INDEX, ByteArray, random_seed)
// JSFunctions are pairs (context, function code), sometimes also called
@@ -323,6 +324,7 @@
GENERATOR_FUNCTION_MAP_INDEX,
STRICT_MODE_GENERATOR_FUNCTION_MAP_INDEX,
GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX,
+ GENERATOR_RESULT_MAP_INDEX,
RANDOM_SEED_INDEX,
// Properties from here are treated as weak references by the full GC.
=======================================
--- /branches/bleeding_edge/src/full-codegen.h Wed Apr 24 06:00:16 2013
+++ /branches/bleeding_edge/src/full-codegen.h Tue May 7 01:46:42 2013
@@ -410,6 +410,11 @@
// this has to be a separate pass _before_ populating or executing any
module.
void AllocateModules(ZoneList<Declaration*>* declarations);
+ // Generator code to return a fresh iterator result object. The "value"
+ // property is set to a value popped from the stack, and "done" is set
+ // according to the argument.
+ void EmitReturnIteratorResult(bool done);
+
// Try to perform a comparison as a fast inlined literal compare if
// the operands allow it. Returns true if the compare operations
// has been matched and all code generated; false otherwise.
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Thu Apr 25
03:59:09 2013
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Tue May 7
01:46:42 2013
@@ -1900,11 +1900,12 @@
Label resume;
__ CompareRoot(result_register(), Heap::kTheHoleValueRootIndex);
__ j(not_equal, &resume);
- __ pop(result_register());
if (expr->yield_kind() == Yield::SUSPEND) {
- // TODO(wingo): Box into { value: VALUE, done: false }.
+ EmitReturnIteratorResult(false);
+ } else {
+ __ pop(result_register());
+ EmitReturnSequence();
}
- EmitReturnSequence();
__ bind(&resume);
context()->Plug(result_register());
@@ -1916,18 +1917,7 @@
__ mov(FieldOperand(result_register(),
JSGeneratorObject::kContinuationOffset),
Immediate(Smi::FromInt(JSGeneratorObject::kGeneratorClosed)));
- __ pop(result_register());
- // TODO(wingo): Box into { value: VALUE, done: true }.
-
- // Exit all nested statements.
- NestedStatement* current = nesting_stack_;
- int stack_depth = 0;
- int context_length = 0;
- while (current != NULL) {
- current = current->Exit(&stack_depth, &context_length);
- }
- __ Drop(stack_depth);
- EmitReturnSequence();
+ EmitReturnIteratorResult(true);
break;
}
@@ -2031,6 +2021,54 @@
__ bind(&done);
context()->Plug(result_register());
}
+
+
+void FullCodeGenerator::EmitReturnIteratorResult(bool done) {
+ Label gc_required;
+ Label allocated;
+
+ Handle<Map> map(isolate()->native_context()->generator_result_map());
+
+ __ Allocate(map->instance_size(), eax, ecx, edx, &gc_required,
TAG_OBJECT);
+
+ __ bind(&allocated);
+ __ mov(ebx, map);
+ __ pop(ecx);
+ __ mov(edx, isolate()->factory()->ToBoolean(done));
+ ASSERT_EQ(map->instance_size(), 5 * kPointerSize);
+ __ mov(FieldOperand(eax, HeapObject::kMapOffset), ebx);
+ __ mov(FieldOperand(eax, JSObject::kPropertiesOffset),
+ isolate()->factory()->empty_fixed_array());
+ __ mov(FieldOperand(eax, JSObject::kElementsOffset),
+ isolate()->factory()->empty_fixed_array());
+ __ mov(FieldOperand(eax, JSGeneratorObject::kResultValuePropertyOffset),
ecx);
+ __ mov(FieldOperand(eax, JSGeneratorObject::kResultDonePropertyOffset),
edx);
+
+ // Only the value field needs a write barrier, as the other values are
in the
+ // root set.
+ __ RecordWriteField(eax, JSGeneratorObject::kResultValuePropertyOffset,
+ ecx, edx, kDontSaveFPRegs);
+
+ if (done) {
+ // Exit all nested statements.
+ NestedStatement* current = nesting_stack_;
+ int stack_depth = 0;
+ int context_length = 0;
+ while (current != NULL) {
+ current = current->Exit(&stack_depth, &context_length);
+ }
+ __ Drop(stack_depth);
+ }
+
+ EmitReturnSequence();
+
+ __ bind(&gc_required);
+ __ Push(Smi::FromInt(map->instance_size()));
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ mov(context_register(),
+ Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ jmp(&allocated);
+}
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
=======================================
--- /branches/bleeding_edge/src/objects.h Thu May 2 08:42:22 2013
+++ /branches/bleeding_edge/src/objects.h Tue May 7 01:46:42 2013
@@ -6435,6 +6435,17 @@
// Resume mode, for use by runtime functions.
enum ResumeMode { SEND, THROW };
+ // Yielding from a generator returns an object with the following
inobject
+ // properties. See Context::generator_result_map() for the map.
+ static const int kResultValuePropertyIndex = 0;
+ static const int kResultDonePropertyIndex = 1;
+ static const int kResultPropertyCount = 2;
+
+ static const int kResultValuePropertyOffset = JSObject::kHeaderSize;
+ static const int kResultDonePropertyOffset =
+ kResultValuePropertyOffset + kPointerSize;
+ static const int kResultSize = kResultDonePropertyOffset + kPointerSize;
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSGeneratorObject);
};
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Thu Apr 25 03:59:09
2013
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Tue May 7 01:46:42
2013
@@ -1924,11 +1924,12 @@
Label resume;
__ CompareRoot(result_register(), Heap::kTheHoleValueRootIndex);
__ j(not_equal, &resume);
- __ pop(result_register());
if (expr->yield_kind() == Yield::SUSPEND) {
- // TODO(wingo): Box into { value: VALUE, done: false }.
+ EmitReturnIteratorResult(false);
+ } else {
+ __ pop(result_register());
+ EmitReturnSequence();
}
- EmitReturnSequence();
__ bind(&resume);
context()->Plug(result_register());
@@ -1940,18 +1941,7 @@
__ Move(FieldOperand(result_register(),
JSGeneratorObject::kContinuationOffset),
Smi::FromInt(JSGeneratorObject::kGeneratorClosed));
- __ pop(result_register());
- // TODO(wingo): Box into { value: VALUE, done: true }.
-
- // Exit all nested statements.
- NestedStatement* current = nesting_stack_;
- int stack_depth = 0;
- int context_length = 0;
- while (current != NULL) {
- current = current->Exit(&stack_depth, &context_length);
- }
- __ Drop(stack_depth);
- EmitReturnSequence();
+ EmitReturnIteratorResult(true);
break;
}
@@ -2056,6 +2046,56 @@
__ bind(&done);
context()->Plug(result_register());
}
+
+
+void FullCodeGenerator::EmitReturnIteratorResult(bool done) {
+ Label gc_required;
+ Label allocated;
+
+ Handle<Map> map(isolate()->native_context()->generator_result_map());
+
+ __ Allocate(map->instance_size(), rax, rcx, rdx, &gc_required,
TAG_OBJECT);
+
+ __ bind(&allocated);
+ __ Move(rbx, map);
+ __ pop(rcx);
+ __ Move(rdx, isolate()->factory()->ToBoolean(done));
+ ASSERT_EQ(map->instance_size(), 5 * kPointerSize);
+ __ movq(FieldOperand(rax, HeapObject::kMapOffset), rbx);
+ __ Move(FieldOperand(rax, JSObject::kPropertiesOffset),
+ isolate()->factory()->empty_fixed_array());
+ __ Move(FieldOperand(rax, JSObject::kElementsOffset),
+ isolate()->factory()->empty_fixed_array());
+ __ movq(FieldOperand(rax, JSGeneratorObject::kResultValuePropertyOffset),
+ rcx);
+ __ movq(FieldOperand(rax, JSGeneratorObject::kResultDonePropertyOffset),
+ rdx);
+
+ // Only the value field needs a write barrier, as the other values are
in the
+ // root set.
+ __ RecordWriteField(rax, JSGeneratorObject::kResultValuePropertyOffset,
+ rcx, rdx, kDontSaveFPRegs);
+
+ if (done) {
+ // Exit all nested statements.
+ NestedStatement* current = nesting_stack_;
+ int stack_depth = 0;
+ int context_length = 0;
+ while (current != NULL) {
+ current = current->Exit(&stack_depth, &context_length);
+ }
+ __ Drop(stack_depth);
+ }
+
+ EmitReturnSequence();
+
+ __ bind(&gc_required);
+ __ Push(Smi::FromInt(map->instance_size()));
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ movq(context_register(),
+ Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ jmp(&allocated);
+}
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/generators-iteration.js
Fri Apr 26 05:09:32 2013
+++ /branches/bleeding_edge/test/mjsunit/harmony/generators-iteration.js
Tue May 7 01:46:42 2013
@@ -31,19 +31,36 @@
var GeneratorFunction = (function*(){yield 1;}).__proto__.constructor;
+function TestGeneratorResultPrototype() {
+ function* g() { yield 1; }
+ var iter = g();
+ var result = iter.next();
+
+ assertSame(Object.prototype, Object.getPrototypeOf(result));
+ property_names = Object.getOwnPropertyNames(result);
+ property_names.sort();
+ assertEquals(["done", "value"], property_names);
+ assertEquals({ value: 1, done: false }, result);
+}
+TestGeneratorResultPrototype()
+
function TestGenerator(g, expected_values_for_next,
send_val, expected_values_for_send) {
function testNext(thunk) {
var iter = thunk();
for (var i = 0; i < expected_values_for_next.length; i++) {
- assertEquals(expected_values_for_next[i], iter.next());
+ assertEquals({ value: expected_values_for_next[i],
+ done: i == expected_values_for_next.length - 1 },
+ iter.next());
}
assertThrows(function() { iter.next(); }, Error);
}
function testSend(thunk) {
var iter = thunk();
for (var i = 0; i < expected_values_for_send.length; i++) {
- assertEquals(expected_values_for_send[i], iter.send(send_val));
+ assertEquals({ value: expected_values_for_send[i],
+ done: i == expected_values_for_send.length - 1 },
+ iter.send(send_val));
}
assertThrows(function() { iter.send(send_val); }, Error);
}
@@ -51,7 +68,9 @@
for (var i = 0; i < expected_values_for_next.length; i++) {
var iter = thunk();
for (var j = 0; j < i; j++) {
- assertEquals(expected_values_for_next[j], iter.next());
+ assertEquals({ value: expected_values_for_next[j],
+ done: j == expected_values_for_next.length - 1 },
+ iter.next());
}
function Sentinel() {}
assertThrows(function () { iter.throw(new Sentinel); }, Sentinel);
--
--
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.