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:
parent
601e1cf0d5
commit
f88bca9576
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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") \
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user