Commit 3cd73ebc authored by wingo@igalia.com's avatar wingo@igalia.com

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
R=mstarzinger@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14563 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 24649209
......@@ -1939,11 +1939,12 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
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();
}
__ bind(&resume);
context()->Plug(result_register());
......@@ -1955,18 +1956,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
__ 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;
}
......@@ -2074,6 +2064,55 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
}
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) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
......
......@@ -1387,6 +1387,40 @@ void Genesis::InitializeExperimentalGlobal() {
*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);
}
}
......
......@@ -180,6 +180,7 @@ enum BindingFlags {
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 @@ class Context: public FixedArray {
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.
......
......@@ -410,6 +410,11 @@ class FullCodeGenerator: public AstVisitor {
// 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.
......
......@@ -1900,11 +1900,12 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
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();
}
__ bind(&resume);
context()->Plug(result_register());
......@@ -1916,18 +1917,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
__ 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;
}
......@@ -2033,6 +2023,54 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
}
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) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
......
......@@ -6435,6 +6435,17 @@ class JSGeneratorObject: public JSObject {
// 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);
};
......
......@@ -1924,11 +1924,12 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
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();
}
__ bind(&resume);
context()->Plug(result_register());
......@@ -1940,18 +1941,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
__ 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;
}
......@@ -2058,6 +2048,56 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
}
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) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
......
......@@ -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 @@ function TestGenerator(g, expected_values_for_next,
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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment