Revision: 25297
Author: [email protected]
Date: Wed Nov 12 14:28:53 2014 UTC
Log: Leaving a generator via an exception causes it to close
[email protected]
BUG=v8:3096
LOG=Y
Review URL: https://codereview.chromium.org/717123002
https://code.google.com/p/v8/source/detail?r=25297
Added:
/branches/bleeding_edge/test/mjsunit/es6/generators-states.js
Modified:
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/arm64/full-codegen-arm64.cc
/branches/bleeding_edge/src/generator.js
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/messages.js
/branches/bleeding_edge/src/runtime/runtime-generator.cc
/branches/bleeding_edge/src/runtime/runtime.h
/branches/bleeding_edge/src/x64/full-codegen-x64.cc
/branches/bleeding_edge/test/mjsunit/es6/generators-iteration.js
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/generators-states.js Wed Nov
12 14:28:53 2014 UTC
@@ -0,0 +1,67 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Test generator states.
+
+function Foo() {}
+function Bar() {}
+
+function assertIteratorResult(value, done, result) {
+ assertEquals({ value: value, done: done}, result);
+}
+
+function assertIteratorIsClosed(iter) {
+ assertIteratorResult(undefined, true, iter.next());
+ // Next and throw on a closed iterator.
+ assertDoesNotThrow(function() { iter.next(); });
+ assertThrows(function() { iter.throw(new Bar); }, Bar);
+}
+
+var iter;
+function* nextGenerator() { yield iter.next(); }
+function* throwGenerator() { yield iter.throw(new Bar); }
+
+// Throw on a suspendedStart iterator.
+iter = nextGenerator();
+assertThrows(function() { iter.throw(new Foo) }, Foo)
+assertThrows(function() { iter.throw(new Foo) }, Foo)
+assertIteratorIsClosed(iter);
+
+// The same.
+iter = throwGenerator();
+assertThrows(function() { iter.throw(new Foo) }, Foo)
+assertThrows(function() { iter.throw(new Foo) }, Foo)
+assertIteratorIsClosed(iter);
+
+// Next on an executing iterator raises a TypeError.
+iter = nextGenerator();
+assertThrows(function() { iter.next() }, TypeError)
+assertIteratorIsClosed(iter);
+
+// Throw on an executing iterator raises a TypeError.
+iter = throwGenerator();
+assertThrows(function() { iter.next() }, TypeError)
+assertIteratorIsClosed(iter);
+
+// Next on an executing iterator doesn't change the state of the
+// generator.
+iter = (function* () {
+ try {
+ iter.next();
+ yield 1;
+ } catch (e) {
+ try {
+ // This next() should raise the same exception, because the first
+ // next() left the iter in the executing state.
+ iter.next();
+ yield 2;
+ } catch (e) {
+ yield 3;
+ }
+ }
+ yield 4;
+})();
+assertIteratorResult(3, false, iter.next());
+assertIteratorResult(4, false, iter.next());
+assertIteratorIsClosed(iter);
=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Wed Nov 12 11:34:09
2014 UTC
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Wed Nov 12 14:28:53
2014 UTC
@@ -2195,15 +2195,6 @@
VisitForAccumulatorValue(value);
__ pop(r1);
- // Check generator state.
- Label wrong_state, closed_state, done;
- __ ldr(r3, FieldMemOperand(r1, JSGeneratorObject::kContinuationOffset));
- STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting < 0);
- STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed == 0);
- __ cmp(r3, Operand(Smi::FromInt(0)));
- __ b(eq, &closed_state);
- __ b(lt, &wrong_state);
-
// Load suspended function and context.
__ ldr(cp, FieldMemOperand(r1, JSGeneratorObject::kContextOffset));
__ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset));
@@ -2226,7 +2217,7 @@
// Enter a new JavaScript frame, and initialize its slots as they were
when
// the generator was suspended.
- Label resume_frame;
+ Label resume_frame, done;
__ bind(&push_frame);
__ bl(&resume_frame);
__ jmp(&done);
@@ -2286,26 +2277,6 @@
// Not reached: the runtime call returns elsewhere.
__ stop("not-reached");
- // Reach here when generator is closed.
- __ bind(&closed_state);
- if (resume_mode == JSGeneratorObject::NEXT) {
- // Return completed iterator result when generator is closed.
- __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
- __ push(r2);
- // Pop value from top-of-stack slot; box result into result register.
- EmitCreateIteratorResult(true);
- } else {
- // Throw the provided value.
- __ push(r0);
- __ CallRuntime(Runtime::kThrow, 1);
- }
- __ jmp(&done);
-
- // Throw error if we attempt to operate on a running generator.
- __ bind(&wrong_state);
- __ push(r1);
- __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
-
__ bind(&done);
context()->Plug(result_register());
}
=======================================
--- /branches/bleeding_edge/src/arm64/full-codegen-arm64.cc Wed Nov 12
11:34:09 2014 UTC
+++ /branches/bleeding_edge/src/arm64/full-codegen-arm64.cc Wed Nov 12
14:28:53 2014 UTC
@@ -4894,7 +4894,6 @@
Expression *value,
JSGeneratorObject::ResumeMode resume_mode) {
ASM_LOCATION("FullCodeGenerator::EmitGeneratorResume");
- Register value_reg = x0;
Register generator_object = x1;
Register the_hole = x2;
Register operand_stack_size = w3;
@@ -4908,15 +4907,6 @@
VisitForAccumulatorValue(value);
__ Pop(generator_object);
- // Check generator state.
- Label wrong_state, closed_state, done;
- __ Ldr(x10, FieldMemOperand(generator_object,
- JSGeneratorObject::kContinuationOffset));
- STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting < 0);
- STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed == 0);
- __ CompareAndBranch(x10, Smi::FromInt(0), eq, &closed_state);
- __ CompareAndBranch(x10, Smi::FromInt(0), lt, &wrong_state);
-
// Load suspended function and context.
__ Ldr(cp, FieldMemOperand(generator_object,
JSGeneratorObject::kContextOffset));
@@ -4942,7 +4932,7 @@
// Enter a new JavaScript frame, and initialize its slots as they were
when
// the generator was suspended.
- Label resume_frame;
+ Label resume_frame, done;
__ Bl(&resume_frame);
__ B(&done);
@@ -4987,26 +4977,6 @@
// Not reached: the runtime call returns elsewhere.
__ Unreachable();
- // Reach here when generator is closed.
- __ Bind(&closed_state);
- if (resume_mode == JSGeneratorObject::NEXT) {
- // Return completed iterator result when generator is closed.
- __ LoadRoot(x10, Heap::kUndefinedValueRootIndex);
- __ Push(x10);
- // Pop value from top-of-stack slot; box result into result register.
- EmitCreateIteratorResult(true);
- } else {
- // Throw the provided value.
- __ Push(value_reg);
- __ CallRuntime(Runtime::kThrow, 1);
- }
- __ B(&done);
-
- // Throw error if we attempt to operate on a running generator.
- __ Bind(&wrong_state);
- __ Push(generator_object);
- __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
-
__ Bind(&done);
context()->Plug(result_register());
}
=======================================
--- /branches/bleeding_edge/src/generator.js Wed Nov 12 10:06:19 2014 UTC
+++ /branches/bleeding_edge/src/generator.js Wed Nov 12 14:28:53 2014 UTC
@@ -20,8 +20,23 @@
['[Generator].prototype.next', this]);
}
- if (DEBUG_IS_ACTIVE) %DebugPrepareStepInIfStepping(this);
- return %_GeneratorNext(this, value);
+ var continuation = %GeneratorGetContinuation(this);
+ if (continuation > 0) {
+ // Generator is suspended.
+ if (DEBUG_IS_ACTIVE) %DebugPrepareStepInIfStepping(this);
+ try {
+ return %_GeneratorNext(this, value);
+ } catch (e) {
+ %GeneratorClose(this);
+ throw e;
+ }
+ } else if (continuation == 0) {
+ // Generator is already closed.
+ return { value: void 0, done: true };
+ } else {
+ // Generator is running.
+ throw MakeTypeError('generator_running', []);
+ }
}
function GeneratorObjectThrow(exn) {
@@ -30,7 +45,22 @@
['[Generator].prototype.throw', this]);
}
- return %_GeneratorThrow(this, exn);
+ var continuation = %GeneratorGetContinuation(this);
+ if (continuation > 0) {
+ // Generator is suspended.
+ try {
+ return %_GeneratorThrow(this, exn);
+ } catch (e) {
+ %GeneratorClose(this);
+ throw e;
+ }
+ } else if (continuation == 0) {
+ // Generator is already closed.
+ throw exn;
+ } else {
+ // Generator is running.
+ throw MakeTypeError('generator_running', []);
+ }
}
function GeneratorObjectIterator() {
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Wed Nov 12
11:34:09 2014 UTC
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Wed Nov 12
14:28:53 2014 UTC
@@ -2125,15 +2125,6 @@
VisitForAccumulatorValue(value);
__ pop(ebx);
- // Check generator state.
- Label wrong_state, closed_state, done;
- STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting < 0);
- STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed == 0);
- __ cmp(FieldOperand(ebx, JSGeneratorObject::kContinuationOffset),
- Immediate(Smi::FromInt(0)));
- __ j(equal, &closed_state);
- __ j(less, &wrong_state);
-
// Load suspended function and context.
__ mov(esi, FieldOperand(ebx, JSGeneratorObject::kContextOffset));
__ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset));
@@ -2155,7 +2146,7 @@
// Enter a new JavaScript frame, and initialize its slots as they were
when
// the generator was suspended.
- Label resume_frame;
+ Label resume_frame, done;
__ bind(&push_frame);
__ call(&resume_frame);
__ jmp(&done);
@@ -2202,25 +2193,6 @@
// Not reached: the runtime call returns elsewhere.
__ Abort(kGeneratorFailedToResume);
- // Reach here when generator is closed.
- __ bind(&closed_state);
- if (resume_mode == JSGeneratorObject::NEXT) {
- // Return completed iterator result when generator is closed.
- __ push(Immediate(isolate()->factory()->undefined_value()));
- // Pop value from top-of-stack slot; box result into result register.
- EmitCreateIteratorResult(true);
- } else {
- // Throw the provided value.
- __ push(eax);
- __ CallRuntime(Runtime::kThrow, 1);
- }
- __ jmp(&done);
-
- // Throw error if we attempt to operate on a running generator.
- __ bind(&wrong_state);
- __ push(ebx);
- __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
-
__ bind(&done);
context()->Plug(result_register());
}
=======================================
--- /branches/bleeding_edge/src/messages.js Wed Nov 12 10:06:19 2014 UTC
+++ /branches/bleeding_edge/src/messages.js Wed Nov 12 14:28:53 2014 UTC
@@ -9,9 +9,8 @@
cyclic_proto: ["Cyclic __proto__ value"],
code_gen_from_strings: ["%0"],
constructor_special_method: ["Class constructor may not be an
accessor"],
+ // TypeError
generator_running: ["Generator is already running"],
- generator_finished: ["Generator has already finished"],
- // TypeError
unexpected_token: ["Unexpected token ", "%0"],
unexpected_token_number: ["Unexpected number"],
unexpected_token_string: ["Unexpected string"],
=======================================
--- /branches/bleeding_edge/src/runtime/runtime-generator.cc Mon Oct 20
12:07:45 2014 UTC
+++ /branches/bleeding_edge/src/runtime/runtime-generator.cc Wed Nov 12
14:28:53 2014 UTC
@@ -135,16 +135,14 @@
}
-RUNTIME_FUNCTION(Runtime_ThrowGeneratorStateError) {
+RUNTIME_FUNCTION(Runtime_GeneratorClose) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
- int continuation = generator->continuation();
- const char* message = continuation == JSGeneratorObject::kGeneratorClosed
- ? "generator_finished"
- : "generator_running";
- Vector<Handle<Object> > argv = HandleVector<Object>(NULL, 0);
- THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewError(message, argv));
+
+ generator->set_continuation(JSGeneratorObject::kGeneratorClosed);
+
+ return isolate->heap()->undefined_value();
}
=======================================
--- /branches/bleeding_edge/src/runtime/runtime.h Wed Nov 12 11:34:09 2014
UTC
+++ /branches/bleeding_edge/src/runtime/runtime.h Wed Nov 12 14:28:53 2014
UTC
@@ -477,7 +477,7 @@
F(CreateJSGeneratorObject, 0, 1) \
F(SuspendJSGeneratorObject, 1, 1) \
F(ResumeJSGeneratorObject, 3, 1) \
- F(ThrowGeneratorStateError, 1, 1) \
+ F(GeneratorClose, 1, 1) \
\
/* Arrays */ \
F(ArrayConstructor, -1, 1) \
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Wed Nov 12 11:34:09
2014 UTC
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Wed Nov 12 14:28:53
2014 UTC
@@ -2157,15 +2157,6 @@
VisitForAccumulatorValue(value);
__ Pop(rbx);
- // Check generator state.
- Label wrong_state, closed_state, done;
- STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting < 0);
- STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed == 0);
- __ SmiCompare(FieldOperand(rbx, JSGeneratorObject::kContinuationOffset),
- Smi::FromInt(0));
- __ j(equal, &closed_state);
- __ j(less, &wrong_state);
-
// Load suspended function and context.
__ movp(rsi, FieldOperand(rbx, JSGeneratorObject::kContextOffset));
__ movp(rdi, FieldOperand(rbx, JSGeneratorObject::kFunctionOffset));
@@ -2187,7 +2178,7 @@
// Enter a new JavaScript frame, and initialize its slots as they were
when
// the generator was suspended.
- Label resume_frame;
+ Label resume_frame, done;
__ bind(&push_frame);
__ call(&resume_frame);
__ jmp(&done);
@@ -2234,25 +2225,6 @@
// Not reached: the runtime call returns elsewhere.
__ Abort(kGeneratorFailedToResume);
- // Reach here when generator is closed.
- __ bind(&closed_state);
- if (resume_mode == JSGeneratorObject::NEXT) {
- // Return completed iterator result when generator is closed.
- __ PushRoot(Heap::kUndefinedValueRootIndex);
- // Pop value from top-of-stack slot; box result into result register.
- EmitCreateIteratorResult(true);
- } else {
- // Throw the provided value.
- __ Push(rax);
- __ CallRuntime(Runtime::kThrow, 1);
- }
- __ jmp(&done);
-
- // Throw error if we attempt to operate on a running generator.
- __ bind(&wrong_state);
- __ Push(rbx);
- __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
-
__ bind(&done);
context()->Plug(result_register());
}
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/generators-iteration.js Tue
Sep 16 12:30:39 2014 UTC
+++ /branches/bleeding_edge/test/mjsunit/es6/generators-iteration.js Wed
Nov 12 14:28:53 2014 UTC
@@ -41,10 +41,7 @@
}
function assertThrownIteratorIsClosed(iter) {
- // TODO(yusukesuzuki): Since status of a thrown generator is "executing",
- // following tests are failed.
- // https://code.google.com/p/v8/issues/detail?id=3096
- // assertIteratorIsClosed(iter);
+ assertIteratorIsClosed(iter);
}
function TestGeneratorResultPrototype() {
--
--
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/d/optout.