Generator object "next" method takes optional send value

Update the generators implementation to make "next" also do the job of
what was previously called "send" by taking an optional argument.
Remove send, and do a bunch of renamings.

R=rossberg@chromium.org
BUG=v8:2355, v8:2715

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15028 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
wingo@igalia.com 2013-06-10 09:26:18 +00:00
parent 601e1cf0d5
commit f88bca9576
13 changed files with 43 additions and 55 deletions

View File

@ -2029,10 +2029,10 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
// [sp + 1 * kPointerSize] iter
// [sp + 0 * kPointerSize] g
Label l_catch, l_try, l_resume, l_send, l_call, l_loop;
Label l_catch, l_try, l_resume, l_next, l_call, l_loop;
// Initial send value is undefined.
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
__ b(&l_send);
__ b(&l_next);
// catch (e) { receiver = iter; f = iter.throw; arg = e; goto l_call; }
__ bind(&l_catch);
@ -2063,15 +2063,15 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
__ bind(&l_resume); // received in r0
__ PopTryHandler();
// receiver = iter; f = iter.send; arg = received;
__ bind(&l_send);
// receiver = iter; f = iter.next; arg = received;
__ bind(&l_next);
__ ldr(r3, MemOperand(sp, 1 * kPointerSize)); // iter
__ push(r3); // iter
__ push(r0); // received
__ mov(r0, r3); // iter
__ LoadRoot(r2, Heap::ksend_stringRootIndex); // "send"
Handle<Code> send_ic = isolate()->builtins()->LoadIC_Initialize();
CallIC(send_ic); // iter.send in r0
__ LoadRoot(r2, Heap::knext_stringRootIndex); // "next"
Handle<Code> next_ic = isolate()->builtins()->LoadIC_Initialize();
CallIC(next_ic); // iter.next in r0
// result = f.call(receiver, arg);
__ bind(&l_call);
@ -2174,7 +2174,7 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
// If we are sending a value and there is no operand stack, we can jump back
// in directly.
if (resume_mode == JSGeneratorObject::SEND) {
if (resume_mode == JSGeneratorObject::NEXT) {
Label slow_resume;
__ cmp(r3, Operand(0));
__ b(ne, &slow_resume);

View File

@ -926,10 +926,10 @@ void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
}
void FullCodeGenerator::EmitGeneratorSend(CallRuntime* expr) {
void FullCodeGenerator::EmitGeneratorNext(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 2);
EmitGeneratorResume(args->at(0), args->at(1), JSGeneratorObject::SEND);
EmitGeneratorResume(args->at(0), args->at(1), JSGeneratorObject::NEXT);
}

View File

@ -34,26 +34,16 @@
// ----------------------------------------------------------------------------
// TODO(wingo): Give link to specification. For now, the following diagram is
// the spec:
// http://wiki.ecmascript.org/lib/exe/fetch.php?cache=cache&media=harmony:es6_generator_object_model_3-29-13.png
// Generator functions and objects are specified by ES6, sections 15.19.3 and
// 15.19.4.
function GeneratorObjectNext() {
function GeneratorObjectNext(value) {
if (!IS_GENERATOR(this)) {
throw MakeTypeError('incompatible_method_receiver',
['[Generator].prototype.next', this]);
}
return %_GeneratorSend(this, void 0);
}
function GeneratorObjectSend(value) {
if (!IS_GENERATOR(this)) {
throw MakeTypeError('incompatible_method_receiver',
['[Generator].prototype.send', this]);
}
return %_GeneratorSend(this, value);
return %_GeneratorNext(this, value);
}
function GeneratorObjectThrow(exn) {
@ -71,7 +61,6 @@ function SetUpGenerators() {
InstallFunctions(GeneratorObjectPrototype,
DONT_ENUM | DONT_DELETE | READ_ONLY,
["next", GeneratorObjectNext,
"send", GeneratorObjectSend,
"throw", GeneratorObjectThrow]);
%SetProperty(GeneratorObjectPrototype, "constructor",
GeneratorFunctionPrototype, DONT_ENUM | DONT_DELETE | READ_ONLY);

View File

@ -293,7 +293,6 @@ namespace internal {
V(hidden_stack_trace_string, "v8::hidden_stack_trace") \
V(query_colon_string, "(?:)") \
V(Generator_string, "Generator") \
V(send_string, "send") \
V(throw_string, "throw") \
V(done_string, "done") \
V(value_string, "value") \

View File

@ -10917,8 +10917,8 @@ void HOptimizedGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
// Support for generators.
void HOptimizedGraphBuilder::GenerateGeneratorSend(CallRuntime* call) {
return Bailout("inlined runtime function: GeneratorSend");
void HOptimizedGraphBuilder::GenerateGeneratorNext(CallRuntime* call) {
return Bailout("inlined runtime function: GeneratorNext");
}

View File

@ -1988,10 +1988,10 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
// [sp + 1 * kPointerSize] iter
// [sp + 0 * kPointerSize] g
Label l_catch, l_try, l_resume, l_send, l_call, l_loop;
Label l_catch, l_try, l_resume, l_next, l_call, l_loop;
// Initial send value is undefined.
__ mov(eax, isolate()->factory()->undefined_value());
__ jmp(&l_send);
__ jmp(&l_next);
// catch (e) { receiver = iter; f = iter.throw; arg = e; goto l_call; }
__ bind(&l_catch);
@ -2020,14 +2020,14 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
__ bind(&l_resume); // received in eax
__ PopTryHandler();
// receiver = iter; f = iter.send; arg = received;
__ bind(&l_send);
// receiver = iter; f = iter.next; arg = received;
__ bind(&l_next);
__ mov(edx, Operand(esp, 1 * kPointerSize)); // iter
__ push(edx); // iter
__ push(eax); // received
__ mov(ecx, isolate()->factory()->send_string()); // "send"
Handle<Code> send_ic = isolate()->builtins()->LoadIC_Initialize();
CallIC(send_ic); // iter.send in eax
__ mov(ecx, isolate()->factory()->next_string()); // "next"
Handle<Code> next_ic = isolate()->builtins()->LoadIC_Initialize();
CallIC(next_ic); // iter.next in eax
// result = f.call(receiver, arg);
__ bind(&l_call);
@ -2129,7 +2129,7 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
// If we are sending a value and there is no operand stack, we can jump back
// in directly.
if (resume_mode == JSGeneratorObject::SEND) {
if (resume_mode == JSGeneratorObject::NEXT) {
Label slow_resume;
__ cmp(edx, Immediate(0));
__ j(not_zero, &slow_resume);

View File

@ -6529,7 +6529,7 @@ class JSGeneratorObject: public JSObject {
static const int kSize = kStackHandlerIndexOffset + kPointerSize;
// Resume mode, for use by runtime functions.
enum ResumeMode { SEND, THROW };
enum ResumeMode { NEXT, THROW };
// Yielding from a generator returns an object with the following inobject
// properties. See Context::generator_result_map() for the map.

View File

@ -2641,9 +2641,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SuspendJSGeneratorObject) {
// called if the suspended activation had operands on the stack, stack handlers
// needing rewinding, or if the resume should throw an exception. The fast path
// is handled directly in FullCodeGenerator::EmitGeneratorResume(), which is
// inlined into GeneratorNext, GeneratorSend, and GeneratorThrow.
// EmitGeneratorResumeResume is called in any case, as it needs to reconstruct
// the stack frame and make space for arguments and operands.
// inlined into GeneratorNext and GeneratorThrow. EmitGeneratorResumeResume is
// called in any case, as it needs to reconstruct the stack frame and make space
// for arguments and operands.
RUNTIME_FUNCTION(MaybeObject*, Runtime_ResumeJSGeneratorObject) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 3);
@ -2676,7 +2676,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ResumeJSGeneratorObject) {
JSGeneratorObject::ResumeMode resume_mode =
static_cast<JSGeneratorObject::ResumeMode>(resume_mode_int);
switch (resume_mode) {
case JSGeneratorObject::SEND:
case JSGeneratorObject::NEXT:
return value;
case JSGeneratorObject::THROW:
return isolate->Throw(value);

View File

@ -572,7 +572,7 @@ namespace internal {
F(HasCachedArrayIndex, 1, 1) \
F(GetCachedArrayIndex, 1, 1) \
F(FastAsciiArrayJoin, 2, 1) \
F(GeneratorSend, 2, 1) \
F(GeneratorNext, 2, 1) \
F(GeneratorThrow, 2, 1)

View File

@ -2012,10 +2012,10 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
// [sp + 1 * kPointerSize] iter
// [sp + 0 * kPointerSize] g
Label l_catch, l_try, l_resume, l_send, l_call, l_loop;
Label l_catch, l_try, l_resume, l_next, l_call, l_loop;
// Initial send value is undefined.
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
__ jmp(&l_send);
__ jmp(&l_next);
// catch (e) { receiver = iter; f = iter.throw; arg = e; goto l_call; }
__ bind(&l_catch);
@ -2045,15 +2045,15 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
__ bind(&l_resume); // received in rax
__ PopTryHandler();
// receiver = iter; f = iter.send; arg = received;
__ bind(&l_send);
// receiver = iter; f = iter.next; arg = received;
__ bind(&l_next);
__ movq(rcx, Operand(rsp, 1 * kPointerSize)); // iter
__ push(rcx); // iter
__ push(rax); // received
__ movq(rax, rcx); // iter
__ LoadRoot(rcx, Heap::ksend_stringRootIndex); // "send"
Handle<Code> send_ic = isolate()->builtins()->LoadIC_Initialize();
CallIC(send_ic); // iter.send in rax
__ LoadRoot(rcx, Heap::knext_stringRootIndex); // "next"
Handle<Code> next_ic = isolate()->builtins()->LoadIC_Initialize();
CallIC(next_ic); // iter.next in rax
// result = f.call(receiver, arg);
__ bind(&l_call);
@ -2155,7 +2155,7 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
// If we are sending a value and there is no operand stack, we can jump back
// in directly.
if (resume_mode == JSGeneratorObject::SEND) {
if (resume_mode == JSGeneratorObject::NEXT) {
Label slow_resume;
__ cmpq(rdx, Immediate(0));
__ j(not_zero, &slow_resume);

View File

@ -64,9 +64,9 @@ function TestGenerator(g, expected_values_for_next,
for (var i = 0; i < expected_values_for_send.length; i++) {
assertIteratorResult(expected_values_for_send[i],
i == expected_values_for_send.length - 1,
iter.send(send_val));
iter.next(send_val));
}
assertThrows(function() { iter.send(send_val); }, Error);
assertThrows(function() { iter.next(send_val); }, Error);
}
function testThrow(thunk) {
for (var i = 0; i < expected_values_for_next.length; i++) {
@ -572,7 +572,7 @@ function TestRecursion() {
return iter.next();
}
function TestSendRecursion() {
function* g() { yield iter.send(42); }
function* g() { yield iter.next(42); }
var iter = g();
return iter.next();
}

View File

@ -79,7 +79,7 @@ function TestGeneratorObjectMethods() {
function TestNonGenerator(non_generator) {
assertThrows(function() { iter.next.call(non_generator); }, TypeError);
assertThrows(function() { iter.send.call(non_generator, 1); }, TypeError);
assertThrows(function() { iter.next.call(non_generator, 1); }, TypeError);
assertThrows(function() { iter.throw.call(non_generator, 1); }, TypeError);
assertThrows(function() { iter.close.call(non_generator); }, TypeError);
}

View File

@ -84,7 +84,7 @@ function TestGeneratorObjectPrototype() {
assertSame(GeneratorObjectPrototype,
Object.getPrototypeOf((function*(){yield 1}).prototype));
var expected_property_names = ["next", "send", "throw", "constructor"];
var expected_property_names = ["next", "throw", "constructor"];
var found_property_names =
Object.getOwnPropertyNames(GeneratorObjectPrototype);