Expression *value,
JSGeneratorObject::ResumeMode resume_mode) {
// The value stays in r0, and is ultimately read by the resumed generator, as
- // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. r1
- // will hold the generator object until the activation has been resumed.
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. Or it
+ // is read to throw the value when the resumed generator is already closed.
+ // r1 will hold the generator object until the activation has been resumed.
VisitForStackValue(generator);
VisitForAccumulatorValue(value);
__ pop(r1);
// Check generator state.
- Label wrong_state, done;
+ Label wrong_state, closed_state, done;
__ ldr(r3, FieldMemOperand(r1, JSGeneratorObject::kContinuationOffset));
- STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
- STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting < 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed == 0);
__ cmp(r3, Operand(Smi::FromInt(0)));
- __ b(le, &wrong_state);
+ __ b(eq, &closed_state);
+ __ b(lt, &wrong_state);
// Load suspended function and context.
__ ldr(cp, FieldMemOperand(r1, JSGeneratorObject::kContextOffset));
// 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);
Expression *value,
JSGeneratorObject::ResumeMode resume_mode) {
// The value stays in eax, and is ultimately read by the resumed generator, as
- // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. ebx
- // will hold the generator object until the activation has been resumed.
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. Or it
+ // is read to throw the value when the resumed generator is already closed.
+ // ebx will hold the generator object until the activation has been resumed.
VisitForStackValue(generator);
VisitForAccumulatorValue(value);
__ pop(ebx);
// Check generator state.
- Label wrong_state, done;
- STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
- STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+ 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(less_equal, &wrong_state);
+ __ j(equal, &closed_state);
+ __ j(less, &wrong_state);
// Load suspended function and context.
__ mov(esi, FieldOperand(ebx, JSGeneratorObject::kContextOffset));
// 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);
Expression *value,
JSGeneratorObject::ResumeMode resume_mode) {
// The value stays in a0, and is ultimately read by the resumed generator, as
- // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. a1
- // will hold the generator object until the activation has been resumed.
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. Or it
+ // is read to throw the value when the resumed generator is already closed.
+ // a1 will hold the generator object until the activation has been resumed.
VisitForStackValue(generator);
VisitForAccumulatorValue(value);
__ pop(a1);
// Check generator state.
- Label wrong_state, done;
+ Label wrong_state, closed_state, done;
__ lw(a3, FieldMemOperand(a1, JSGeneratorObject::kContinuationOffset));
- STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
- STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
- __ Branch(&wrong_state, le, a3, Operand(zero_reg));
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting < 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed == 0);
+ __ Branch(&closed_state, eq, a3, Operand(zero_reg));
+ __ Branch(&wrong_state, lt, a3, Operand(zero_reg));
// Load suspended function and context.
__ lw(cp, FieldMemOperand(a1, JSGeneratorObject::kContextOffset));
// 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(a2, Heap::kUndefinedValueRootIndex);
+ __ push(a2);
+ // Pop value from top-of-stack slot; box result into result register.
+ EmitCreateIteratorResult(true);
+ } else {
+ // Throw the provided value.
+ __ push(a0);
+ __ CallRuntime(Runtime::kThrow, 1);
+ }
+ __ jmp(&done);
+
// Throw error if we attempt to operate on a running generator.
__ bind(&wrong_state);
__ push(a1);
Expression *value,
JSGeneratorObject::ResumeMode resume_mode) {
// The value stays in rax, and is ultimately read by the resumed generator, as
- // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. rbx
- // will hold the generator object until the activation has been resumed.
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. Or it
+ // is read to throw the value when the resumed generator is already closed.
+ // rbx will hold the generator object until the activation has been resumed.
VisitForStackValue(generator);
VisitForAccumulatorValue(value);
__ pop(rbx);
// Check generator state.
- Label wrong_state, done;
- STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
- STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+ 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(less_equal, &wrong_state);
+ __ j(equal, &closed_state);
+ __ j(less, &wrong_state);
// Load suspended function and context.
__ movq(rsi, FieldOperand(rbx, JSGeneratorObject::kContextOffset));
// 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);
assertEquals({ value: value, done: done}, result);
}
+function assertIteratorIsClosed(iter) {
+ assertIteratorResult(undefined, true, iter.next());
+ assertDoesNotThrow(function() { iter.next(); });
+}
+
+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);
+}
+
function TestGeneratorResultPrototype() {
function* g() { yield 1; }
var iter = g();
// var v3 = iter.next();
assertIteratorResult(v1, v2, iter.next());
}
- assertThrows(function() { iter.next(); }, Error);
+ assertIteratorIsClosed(iter);
}
function testSend(thunk) {
var iter = thunk();
i == expected_values_for_send.length - 1,
iter.next(send_val));
}
- assertThrows(function() { iter.next(send_val); }, Error);
+ assertIteratorIsClosed(iter);
}
function testThrow(thunk) {
for (var i = 0; i < expected_values_for_next.length; i++) {
}
function Sentinel() {}
assertThrows(function () { iter.throw(new Sentinel); }, Sentinel);
- assertThrows(function () { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
}
assertIteratorResult(1, false, iter.next());
assertIteratorResult(2, false, iter.next());
assertIteratorResult(3, false, iter.next());
- assertIteratorResult(undefined, true, iter.next());
- assertThrows(function() { iter.next(); }, Error);
+ assertIteratorIsClosed(iter);
}
Test1(instantiate(g));
function Test2(iter) {
assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test2(instantiate(g));
function Test3(iter) {
assertIteratorResult(1, false, iter.next());
assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test3(instantiate(g));
var exn = new Sentinel;
assertIteratorResult(exn, false, iter.throw(exn));
assertIteratorResult(3, false, iter.next());
- assertIteratorResult(undefined, true, iter.next());
- assertThrows(function() { iter.next(); }, Error);
+ assertIteratorIsClosed(iter);
}
Test4(instantiate(g));
assertIteratorResult(exn, false, iter.throw(exn));
assertIteratorResult(3, false, iter.next());
assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
-
+ assertThrownIteratorIsClosed(iter);
}
Test5(instantiate(g));
var exn = new Sentinel;
assertIteratorResult(exn, false, iter.throw(exn));
assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test6(instantiate(g));
assertIteratorResult(1, false, iter.next());
assertIteratorResult(2, false, iter.next());
assertIteratorResult(3, false, iter.next());
- assertIteratorResult(undefined, true, iter.next());
- assertThrows(function() { iter.next(); }, Error);
+ assertIteratorIsClosed(iter);
}
Test7(instantiate(g));
}
assertIteratorResult(2, false, iter.next());
assertIteratorResult(3, false, iter.next());
assertIteratorResult(4, false, iter.next());
- assertIteratorResult(undefined, true, iter.next());
- assertThrows(function() { iter.next(); }, Error);
+ assertIteratorIsClosed(iter);
}
Test1(instantiate(g));
function Test2(iter) {
assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test2(instantiate(g));
function Test3(iter) {
assertIteratorResult(1, false, iter.next());
assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test3(instantiate(g));
assertIteratorResult(2, false, iter.next());
assertIteratorResult(3, false, iter.throw(new Sentinel));
assertThrows(function() { iter.next(); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
-
+ assertThrownIteratorIsClosed(iter);
}
Test4(instantiate(g));
assertIteratorResult(2, false, iter.next());
assertIteratorResult(3, false, iter.throw(new Sentinel));
assertThrows(function() { iter.throw(new Sentinel2); }, Sentinel2);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test5(instantiate(g));
assertIteratorResult(2, false, iter.next());
assertIteratorResult(3, false, iter.next());
assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test6(instantiate(g));
assertIteratorResult(3, false, iter.next());
assertIteratorResult(4, false, iter.next());
assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test7(instantiate(g));
assertIteratorResult(2, false, iter.next());
assertIteratorResult(3, false, iter.next());
assertIteratorResult(4, false, iter.next());
- assertIteratorResult(undefined, true, iter.next());
- assertThrows(function() { iter.next(); }, Error);
-
+ assertIteratorIsClosed(iter);
}
Test8(instantiate(g));
}
assertIteratorResult(3, false, iter.next());
assertIteratorResult(4, false, iter.next());
assertIteratorResult(5, false, iter.next());
- assertIteratorResult(undefined, true, iter.next());
- assertThrows(function() { iter.next(); }, Error);
+ assertIteratorIsClosed(iter);
}
Test1(instantiate(g));
function Test2(iter) {
assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test2(instantiate(g));
assertIteratorResult(1, false, iter.next());
assertIteratorResult(4, false, iter.throw(new Sentinel));
assertThrows(function() { iter.next(); }, Sentinel);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test3(instantiate(g));
assertIteratorResult(1, false, iter.next());
assertIteratorResult(4, false, iter.throw(new Sentinel));
assertThrows(function() { iter.throw(new Sentinel2); }, Sentinel2);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test4(instantiate(g));
assertIteratorResult(3, false, iter.next());
assertIteratorResult(4, false, iter.next());
assertIteratorResult(5, false, iter.next());
- assertIteratorResult(undefined, true, iter.next());
- assertThrows(function() { iter.next(); }, Error);
-
+ assertIteratorIsClosed(iter);
}
Test5(instantiate(g));
assertIteratorResult(exn, false, iter.throw(exn));
assertIteratorResult(4, false, iter.throw(new Sentinel2));
assertThrows(function() { iter.next(); }, Sentinel2);
- assertThrows(function() { iter.next(); }, Error);
+ assertThrownIteratorIsClosed(iter);
}
Test6(instantiate(g));
assertIteratorResult(3, false, iter.next());
assertIteratorResult(4, false, iter.throw(new Sentinel2));
assertThrows(function() { iter.next(); }, Sentinel2);
- assertThrows(function() { iter.next(); }, Error);
-
+ assertThrownIteratorIsClosed(iter);
}
Test7(instantiate(g));