Commit 3cc7315e authored by neis's avatar neis Committed by Commit bot

Allow Turbofan optimization of Ignition generators, first version.

In the bytecode graphbuilder, translate the two generator-specific
bytecodes as a couple of runtime calls for now.

BUG=v8:4907
LOG=n

Review-Url: https://codereview.chromium.org/1957393004
Cr-Commit-Position: refs/heads/master@{#36134}
parent 61f5fbbb
...@@ -91,6 +91,7 @@ namespace internal { ...@@ -91,6 +91,7 @@ namespace internal {
V(kGraphBuildingFailed, "Optimized graph construction failed") \ V(kGraphBuildingFailed, "Optimized graph construction failed") \
V(kHeapNumberMapRegisterClobbered, "HeapNumberMap register clobbered") \ V(kHeapNumberMapRegisterClobbered, "HeapNumberMap register clobbered") \
V(kHydrogenFilter, "Optimization disabled by filter") \ V(kHydrogenFilter, "Optimization disabled by filter") \
V(kIllegalBytecode, "Illegal bytecode") \
V(kImportDeclaration, "Import declaration") \ V(kImportDeclaration, "Import declaration") \
V(kIndexIsNegative, "Index is negative") \ V(kIndexIsNegative, "Index is negative") \
V(kIndexIsTooLarge, "Index is too large") \ V(kIndexIsTooLarge, "Index is too large") \
......
...@@ -735,13 +735,6 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function, ...@@ -735,13 +735,6 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
return MaybeHandle<Code>(); return MaybeHandle<Code>();
} }
// Do not use Crankshaft/TurboFan on a generator function.
// TODO(neis): Eventually enable for Turbofan.
if (IsGeneratorFunction(info->shared_info()->kind())) {
info->AbortOptimization(kGenerator);
return MaybeHandle<Code>();
}
// Limit the number of times we try to optimize functions. // Limit the number of times we try to optimize functions.
const int kMaxOptCount = const int kMaxOptCount =
FLAG_deopt_every_n_times == 0 ? FLAG_max_opt_count : 1000; FLAG_deopt_every_n_times == 0 ? FLAG_max_opt_count : 1000;
...@@ -1250,6 +1243,12 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) { ...@@ -1250,6 +1243,12 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
Zone zone(info->isolate()->allocator()); Zone zone(info->isolate()->allocator());
CompilationInfo unoptimized(info->parse_info(), info->closure()); CompilationInfo unoptimized(info->parse_info(), info->closure());
unoptimized.EnableDeoptimizationSupport(); unoptimized.EnableDeoptimizationSupport();
// TODO(4280): For now we do not switch generators to baseline code because
// there might be suspended activations stored in generator objects on the
// heap. We could eventually go directly to TurboFan in this case.
if (shared->is_generator()) return false;
// TODO(4280): For now we disable switching to baseline code in the presence // TODO(4280): For now we disable switching to baseline code in the presence
// of interpreter activations of the given function. The reasons are: // of interpreter activations of the given function. The reasons are:
// 1) The debugger assumes each function is either full-code or bytecode. // 1) The debugger assumes each function is either full-code or bytecode.
...@@ -1260,6 +1259,7 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) { ...@@ -1260,6 +1259,7 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
HasInterpreterActivations(info->isolate(), *shared)) { HasInterpreterActivations(info->isolate(), *shared)) {
return false; return false;
} }
// If the current code has reloc info for serialization, also include // If the current code has reloc info for serialization, also include
// reloc info for serialization for the new code, so that deopt support // reloc info for serialization for the new code, so that deopt support
// can be added without losing IC state. // can be added without losing IC state.
......
...@@ -1367,11 +1367,45 @@ void BytecodeGraphBuilder::VisitForInStep() { ...@@ -1367,11 +1367,45 @@ void BytecodeGraphBuilder::VisitForInStep() {
} }
void BytecodeGraphBuilder::VisitSuspendGenerator() { void BytecodeGraphBuilder::VisitSuspendGenerator() {
UNIMPLEMENTED(); Node* state = environment()->LookupAccumulator();
Node* generator = environment()->LookupRegister(
bytecode_iterator().GetRegisterOperand(0));
for (int i = 0; i < environment()->register_count(); ++i) {
Node* value = environment()->LookupRegister(interpreter::Register(i));
NewNode(javascript()->CallRuntime(Runtime::kGeneratorStoreRegister),
generator, jsgraph()->Constant(i), value);
}
NewNode(javascript()->CallRuntime(Runtime::kGeneratorSetContext), generator);
NewNode(javascript()->CallRuntime(Runtime::kGeneratorSetContinuation),
generator, state);
} }
void BytecodeGraphBuilder::VisitResumeGenerator() { void BytecodeGraphBuilder::VisitResumeGenerator() {
UNIMPLEMENTED(); FrameStateBeforeAndAfter states(this);
Node* generator = environment()->LookupRegister(
bytecode_iterator().GetRegisterOperand(0));
Node* state = NewNode(javascript()->CallRuntime(
Runtime::kGeneratorGetContinuation), generator);
// Bijection between registers and array indices must match that used in
// InterpreterAssembler::ExportRegisterFile.
for (int i = 0; i < environment()->register_count(); ++i) {
Node* value = NewNode(
javascript()->CallRuntime(Runtime::kGeneratorLoadRegister),
generator, jsgraph()->Constant(i));
environment()->BindRegister(interpreter::Register(i), value);
NewNode(javascript()->CallRuntime(Runtime::kGeneratorStoreRegister),
generator, jsgraph()->Constant(i), jsgraph()->StaleRegisterConstant());
}
NewNode(javascript()->CallRuntime(Runtime::kGeneratorSetContinuation),
generator, jsgraph()->Constant(JSGeneratorObject::kGeneratorExecuting));
environment()->BindAccumulator(state, &states);
} }
void BytecodeGraphBuilder::VisitWide() { void BytecodeGraphBuilder::VisitWide() {
...@@ -1385,8 +1419,8 @@ void BytecodeGraphBuilder::VisitExtraWide() { ...@@ -1385,8 +1419,8 @@ void BytecodeGraphBuilder::VisitExtraWide() {
} }
void BytecodeGraphBuilder::VisitIllegal() { void BytecodeGraphBuilder::VisitIllegal() {
// Never present in valid bytecode. NewNode(javascript()->CallRuntime(Runtime::kAbort),
UNREACHABLE(); jsgraph()->Constant(kIllegalBytecode));
} }
void BytecodeGraphBuilder::SwitchToMergeEnvironment(int current_offset) { void BytecodeGraphBuilder::SwitchToMergeEnvironment(int current_offset) {
......
...@@ -48,6 +48,11 @@ Node* JSGraph::OptimizedOutConstant() { ...@@ -48,6 +48,11 @@ Node* JSGraph::OptimizedOutConstant() {
HeapConstant(factory()->optimized_out())); HeapConstant(factory()->optimized_out()));
} }
Node* JSGraph::StaleRegisterConstant() {
return CACHED(kStaleRegisterConstant,
HeapConstant(factory()->stale_register()));
}
Node* JSGraph::UndefinedConstant() { Node* JSGraph::UndefinedConstant() {
return CACHED(kUndefinedConstant, HeapConstant(factory()->undefined_value())); return CACHED(kUndefinedConstant, HeapConstant(factory()->undefined_value()));
} }
......
...@@ -45,6 +45,7 @@ class JSGraph : public ZoneObject { ...@@ -45,6 +45,7 @@ class JSGraph : public ZoneObject {
Node* EmptyFixedArrayConstant(); Node* EmptyFixedArrayConstant();
Node* HeapNumberMapConstant(); Node* HeapNumberMapConstant();
Node* OptimizedOutConstant(); Node* OptimizedOutConstant();
Node* StaleRegisterConstant();
Node* UndefinedConstant(); Node* UndefinedConstant();
Node* TheHoleConstant(); Node* TheHoleConstant();
Node* TrueConstant(); Node* TrueConstant();
...@@ -148,6 +149,7 @@ class JSGraph : public ZoneObject { ...@@ -148,6 +149,7 @@ class JSGraph : public ZoneObject {
kEmptyFixedArrayConstant, kEmptyFixedArrayConstant,
kHeapNumberMapConstant, kHeapNumberMapConstant,
kOptimizedOutConstant, kOptimizedOutConstant,
kStaleRegisterConstant,
kUndefinedConstant, kUndefinedConstant,
kTheHoleConstant, kTheHoleConstant,
kTrueConstant, kTrueConstant,
......
...@@ -136,6 +136,7 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) { ...@@ -136,6 +136,7 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
// not to call into arbitrary JavaScript, not to throw, and not to deoptimize // not to call into arbitrary JavaScript, not to throw, and not to deoptimize
// are blacklisted here and can be called without a FrameState. // are blacklisted here and can be called without a FrameState.
switch (function) { switch (function) {
case Runtime::kAbort:
case Runtime::kAllocateInTargetSpace: case Runtime::kAllocateInTargetSpace:
case Runtime::kCreateIterResultObject: case Runtime::kCreateIterResultObject:
case Runtime::kDefineDataPropertyInLiteral: case Runtime::kDefineDataPropertyInLiteral:
...@@ -144,6 +145,11 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) { ...@@ -144,6 +145,11 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
case Runtime::kFinalizeClassDefinition: // TODO(conradw): Is it safe? case Runtime::kFinalizeClassDefinition: // TODO(conradw): Is it safe?
case Runtime::kForInDone: case Runtime::kForInDone:
case Runtime::kForInStep: case Runtime::kForInStep:
case Runtime::kGeneratorSetContext:
case Runtime::kGeneratorGetContinuation:
case Runtime::kGeneratorSetContinuation:
case Runtime::kGeneratorLoadRegister:
case Runtime::kGeneratorStoreRegister:
case Runtime::kGetSuperConstructor: case Runtime::kGetSuperConstructor:
case Runtime::kIsFunction: case Runtime::kIsFunction:
case Runtime::kNewClosure: case Runtime::kNewClosure:
......
...@@ -3088,7 +3088,7 @@ bool LinearScanAllocator::TryReuseSpillForPhi(TopLevelLiveRange* range) { ...@@ -3088,7 +3088,7 @@ bool LinearScanAllocator::TryReuseSpillForPhi(TopLevelLiveRange* range) {
? range->TopLevel()->GetSpillRange() ? range->TopLevel()->GetSpillRange()
: data()->AssignSpillRangeToLiveRange(range->TopLevel()); : data()->AssignSpillRangeToLiveRange(range->TopLevel());
bool merged = first_op_spill->TryMerge(spill_range); bool merged = first_op_spill->TryMerge(spill_range);
CHECK(merged); if (!merged) return false;
Spill(range); Spill(range);
return true; return true;
} else if (pos->pos() > range->Start().NextStart()) { } else if (pos->pos() > range->Start().NextStart()) {
...@@ -3097,7 +3097,7 @@ bool LinearScanAllocator::TryReuseSpillForPhi(TopLevelLiveRange* range) { ...@@ -3097,7 +3097,7 @@ bool LinearScanAllocator::TryReuseSpillForPhi(TopLevelLiveRange* range) {
? range->TopLevel()->GetSpillRange() ? range->TopLevel()->GetSpillRange()
: data()->AssignSpillRangeToLiveRange(range->TopLevel()); : data()->AssignSpillRangeToLiveRange(range->TopLevel());
bool merged = first_op_spill->TryMerge(spill_range); bool merged = first_op_spill->TryMerge(spill_range);
CHECK(merged); if (!merged) return false;
SpillBetween(range, range->Start(), pos->pos()); SpillBetween(range, range->Start(), pos->pos());
DCHECK(UnhandledIsSorted()); DCHECK(UnhandledIsSorted());
return true; return true;
......
...@@ -158,6 +158,11 @@ HCompilationJob::Status HCompilationJob::CreateGraphImpl() { ...@@ -158,6 +158,11 @@ HCompilationJob::Status HCompilationJob::CreateGraphImpl() {
return AbortOptimization(kTooManyParametersLocals); return AbortOptimization(kTooManyParametersLocals);
} }
if (IsGeneratorFunction(info()->shared_info()->kind())) {
// Crankshaft does not support generators.
return AbortOptimization(kGenerator);
}
if (FLAG_trace_hydrogen) { if (FLAG_trace_hydrogen) {
isolate()->GetHTracer()->TraceCompilation(info()); isolate()->GetHTracer()->TraceCompilation(info());
} }
......
...@@ -726,6 +726,8 @@ Node* InterpreterAssembler::ExportRegisterFile(Node* array) { ...@@ -726,6 +726,8 @@ Node* InterpreterAssembler::ExportRegisterFile(Node* array) {
var_index.Bind(Int32Constant(0)); var_index.Bind(Int32Constant(0));
// Iterate over register file and write values into array. // Iterate over register file and write values into array.
// The mapping of register to array index must match that used in
// BytecodeGraphBuilder::VisitResumeGenerator.
Label loop(this, &var_index), done_loop(this); Label loop(this, &var_index), done_loop(this);
Goto(&loop); Goto(&loop);
Bind(&loop); Bind(&loop);
......
...@@ -7295,7 +7295,6 @@ class JSGeneratorObject: public JSObject { ...@@ -7295,7 +7295,6 @@ class JSGeneratorObject: public JSObject {
DECLARE_CAST(JSGeneratorObject) DECLARE_CAST(JSGeneratorObject)
// Dispatched behavior. // Dispatched behavior.
DECLARE_PRINTER(JSGeneratorObject)
DECLARE_VERIFIER(JSGeneratorObject) DECLARE_VERIFIER(JSGeneratorObject)
// Magic sentinel values for the continuation. // Magic sentinel values for the continuation.
......
...@@ -129,7 +129,16 @@ RUNTIME_FUNCTION(Runtime_GeneratorGetResumeMode) { ...@@ -129,7 +129,16 @@ RUNTIME_FUNCTION(Runtime_GeneratorGetResumeMode) {
} }
// Returns generator continuation as a PC offset, or the magic -1 or 0 values. RUNTIME_FUNCTION(Runtime_GeneratorSetContext) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
generator->set_context(isolate->context());
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_GeneratorGetContinuation) { RUNTIME_FUNCTION(Runtime_GeneratorGetContinuation) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
...@@ -139,6 +148,45 @@ RUNTIME_FUNCTION(Runtime_GeneratorGetContinuation) { ...@@ -139,6 +148,45 @@ RUNTIME_FUNCTION(Runtime_GeneratorGetContinuation) {
} }
RUNTIME_FUNCTION(Runtime_GeneratorSetContinuation) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
CONVERT_SMI_ARG_CHECKED(continuation, 1);
generator->set_continuation(continuation);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_GeneratorLoadRegister) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
CONVERT_SMI_ARG_CHECKED(index, 1);
DCHECK(FLAG_ignition && FLAG_ignition_generators);
DCHECK(generator->function()->shared()->HasBytecodeArray());
return generator->operand_stack()->get(index);
}
RUNTIME_FUNCTION(Runtime_GeneratorStoreRegister) {
HandleScope scope(isolate);
DCHECK(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
CONVERT_SMI_ARG_CHECKED(index, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
DCHECK(FLAG_ignition && FLAG_ignition_generators);
DCHECK(generator->function()->shared()->HasBytecodeArray());
generator->operand_stack()->set(index, *value);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_GeneratorGetSourcePosition) { RUNTIME_FUNCTION(Runtime_GeneratorGetSourcePosition) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
......
...@@ -231,9 +231,13 @@ namespace internal { ...@@ -231,9 +231,13 @@ namespace internal {
F(GeneratorGetFunction, 1, 1) \ F(GeneratorGetFunction, 1, 1) \
F(GeneratorGetReceiver, 1, 1) \ F(GeneratorGetReceiver, 1, 1) \
F(GeneratorGetInput, 1, 1) \ F(GeneratorGetInput, 1, 1) \
F(GeneratorSetContext, 1, 1) \
F(GeneratorGetContinuation, 1, 1) \ F(GeneratorGetContinuation, 1, 1) \
F(GeneratorSetContinuation, 2, 1) \
F(GeneratorGetSourcePosition, 1, 1) \ F(GeneratorGetSourcePosition, 1, 1) \
F(GeneratorGetResumeMode, 1, 1) F(GeneratorGetResumeMode, 1, 1) \
F(GeneratorLoadRegister, 2, 1) \
F(GeneratorStoreRegister, 3, 1)
#ifdef V8_I18N_SUPPORT #ifdef V8_I18N_SUPPORT
#define FOR_EACH_INTRINSIC_I18N(F) \ #define FOR_EACH_INTRINSIC_I18N(F) \
......
...@@ -192,7 +192,8 @@ Type::bitset BitsetType::Lub(i::Map* map) { ...@@ -192,7 +192,8 @@ Type::bitset BitsetType::Lub(i::Map* map) {
map == heap->no_interceptor_result_sentinel_map() || map == heap->no_interceptor_result_sentinel_map() ||
map == heap->termination_exception_map() || map == heap->termination_exception_map() ||
map == heap->arguments_marker_map() || map == heap->arguments_marker_map() ||
map == heap->optimized_out_map()); map == heap->optimized_out_map() ||
map == heap->stale_register_map());
return kInternal & kTaggedPointer; return kInternal & kTaggedPointer;
} }
case HEAP_NUMBER_TYPE: case HEAP_NUMBER_TYPE:
......
...@@ -3,6 +3,32 @@ ...@@ -3,6 +3,32 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --ignition-generators --harmony-do-expressions // Flags: --ignition-generators --harmony-do-expressions
// Flags: --allow-natives-syntax
function MaybeOptimizeOrDeoptimize(f) {
let x = Math.random(); // --random-seed makes this deterministic
if (x <= 0.33) {
%OptimizeFunctionOnNextCall(f);
} else if (x <= 0.66) {
%DeoptimizeFunction(f);
}
}
function Next(generator, ...args) {
MaybeOptimizeOrDeoptimize(%GeneratorGetFunction(generator));
return generator.next(...args);
}
function Return(generator, ...args) {
MaybeOptimizeOrDeoptimize(%GeneratorGetFunction(generator));
return generator.return(...args);
}
function Throw(generator, ...args) {
MaybeOptimizeOrDeoptimize(%GeneratorGetFunction(generator));
return generator.throw(...args);
}
{ // yield in try-catch { // yield in try-catch
...@@ -11,19 +37,19 @@ ...@@ -11,19 +37,19 @@
try {yield 1} catch (error) {assertEquals("caught", error)} try {yield 1} catch (error) {assertEquals("caught", error)}
}; };
assertThrowsEquals(() => g().throw("not caught"), "not caught"); assertThrowsEquals(() => Throw(g(), "not caught"), "not caught");
{ {
let x = g(); let x = g();
assertEquals({value: 1, done: false}, x.next()); assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: undefined, done: true}, x.throw("caught")); assertEquals({value: undefined, done: true}, Throw(x, "caught"));
} }
{ {
let x = g(); let x = g();
assertEquals({value: 1, done: false}, x.next()); assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: undefined, done: true}, x.next()); assertEquals({value: undefined, done: true}, Next(x));
assertThrowsEquals(() => x.throw("not caught"), "not caught"); assertThrowsEquals(() => Throw(x, "not caught"), "not caught");
} }
} }
...@@ -33,19 +59,19 @@ ...@@ -33,19 +59,19 @@
{ {
let x = g(); let x = g();
assertEquals({value: 43, done: false}, x.next()); assertEquals({value: 43, done: false}, Next(x));
assertEquals({value: 42, done: true}, x.next()); assertEquals({value: 42, done: true}, Next(x));
} }
} }
{ // return that doesn't close { // return that doesn't close
let x; let x;
let g = function*() { try {return 42} finally {x.throw(666)} }; let g = function*() { try {return 42} finally {Throw(x, 666)} };
{ {
x = g(); x = g();
assertThrows(() => x.next(), TypeError); // still executing assertThrows(() => Next(x), TypeError); // still executing
} }
} }
...@@ -56,42 +82,42 @@ ...@@ -56,42 +82,42 @@
{ // "return" closes at suspendedStart { // "return" closes at suspendedStart
let x = g(); let x = g();
assertEquals({value: 666, done: true}, x.return(666)); assertEquals({value: 666, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, x.next(42)); assertEquals({value: undefined, done: true}, Next(x, 42));
assertThrowsEquals(() => x.throw(43), 43); assertThrowsEquals(() => Throw(x, 43), 43);
assertEquals({value: 42, done: true}, x.return(42)); assertEquals({value: 42, done: true}, Return(x, 42));
} }
{ // "throw" closes at suspendedStart { // "throw" closes at suspendedStart
let x = g(); let x = g();
assertThrowsEquals(() => x.throw(666), 666); assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: undefined, done: true}, x.next(42)); assertEquals({value: undefined, done: true}, Next(x, 42));
assertEquals({value: 43, done: true}, x.return(43)); assertEquals({value: 43, done: true}, Return(x, 43));
assertThrowsEquals(() => x.throw(44), 44); assertThrowsEquals(() => Throw(x, 44), 44);
} }
{ // "next" closes at suspendedYield { // "next" closes at suspendedYield
let x = g(); let x = g();
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 13, done: true}, x.next(666)); assertEquals({value: 13, done: true}, Next(x, 666));
assertEquals({value: undefined, done: true}, x.next(666)); assertEquals({value: undefined, done: true}, Next(x, 666));
assertThrowsEquals(() => x.throw(666), 666); assertThrowsEquals(() => Throw(x, 666), 666);
} }
{ // "return" closes at suspendedYield { // "return" closes at suspendedYield
let x = g(); let x = g();
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 13, done: true}, x.return(666)); assertEquals({value: 13, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, x.next(666)); assertEquals({value: undefined, done: true}, Next(x, 666));
assertEquals({value: 666, done: true}, x.return(666)); assertEquals({value: 666, done: true}, Return(x, 666));
} }
{ // "throw" closes at suspendedYield { // "throw" closes at suspendedYield
let x = g(); let x = g();
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 13, done: true}, x.throw(666)); assertEquals({value: 13, done: true}, Throw(x, 666));
assertThrowsEquals(() => x.throw(666), 666); assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: undefined, done: true}, x.next(666)); assertEquals({value: undefined, done: true}, Next(x, 666));
} }
} }
...@@ -102,45 +128,45 @@ ...@@ -102,45 +128,45 @@
{ // "return" closes at suspendedStart { // "return" closes at suspendedStart
let x = g(); let x = g();
assertEquals({value: 666, done: true}, x.return(666)); assertEquals({value: 666, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, x.next(42)); assertEquals({value: undefined, done: true}, Next(x, 42));
assertThrowsEquals(() => x.throw(43), 43); assertThrowsEquals(() => Throw(x, 43), 43);
assertEquals({value: 42, done: true}, x.return(42)); assertEquals({value: 42, done: true}, Return(x, 42));
} }
{ // "throw" closes at suspendedStart { // "throw" closes at suspendedStart
let x = g(); let x = g();
assertThrowsEquals(() => x.throw(666), 666); assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: undefined, done: true}, x.next(42)); assertEquals({value: undefined, done: true}, Next(x, 42));
assertEquals({value: 43, done: true}, x.return(43)); assertEquals({value: 43, done: true}, Return(x, 43));
assertThrowsEquals(() => x.throw(44), 44); assertThrowsEquals(() => Throw(x, 44), 44);
} }
{ // "next" closes at suspendedYield { // "next" closes at suspendedYield
let x = g(); let x = g();
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: undefined, done: true}, x.next(666)); assertEquals({value: undefined, done: true}, Next(x, 666));
assertEquals({value: undefined, done: true}, x.next(666)); assertEquals({value: undefined, done: true}, Next(x, 666));
assertThrowsEquals(() => x.throw(666), 666); assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: 42, done: true}, x.return(42)); assertEquals({value: 42, done: true}, Return(x, 42));
} }
{ // "return" closes at suspendedYield { // "return" closes at suspendedYield
let x = g(); let x = g();
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 666, done: true}, x.return(666)); assertEquals({value: 666, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, x.next(666)); assertEquals({value: undefined, done: true}, Next(x, 666));
assertThrowsEquals(() => x.throw(44), 44); assertThrowsEquals(() => Throw(x, 44), 44);
assertEquals({value: 42, done: true}, x.return(42)); assertEquals({value: 42, done: true}, Return(x, 42));
} }
{ // "throw" closes at suspendedYield { // "throw" closes at suspendedYield
let x = g(); let x = g();
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertThrowsEquals(() => x.throw(666), 666); assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: undefined, done: true}, x.next(666)); assertEquals({value: undefined, done: true}, Next(x, 666));
assertThrowsEquals(() => x.throw(666), 666); assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: 42, done: true}, x.return(42)); assertEquals({value: 42, done: true}, Return(x, 42));
} }
} }
...@@ -151,17 +177,17 @@ ...@@ -151,17 +177,17 @@
{ {
let x = g(); let x = g();
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, x.return(666)); assertEquals({value: 43, done: false}, Return(x, 666));
assertEquals({value: 13, done: true}, x.next()); assertEquals({value: 13, done: true}, Next(x));
assertEquals({value: 666, done: true}, x.return(666)); assertEquals({value: 666, done: true}, Return(x, 666));
} }
{ {
let x = g(); let x = g();
assertEquals({value: 666, done: true}, x.return(666)); assertEquals({value: 666, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, x.next()); assertEquals({value: undefined, done: true}, Next(x));
assertEquals({value: 666, done: true}, x.return(666)); assertEquals({value: 666, done: true}, Return(x, 666));
} }
} }
...@@ -172,17 +198,17 @@ ...@@ -172,17 +198,17 @@
{ {
let x = g(); let x = g();
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, x.return(666)); assertEquals({value: 43, done: false}, Return(x, 666));
assertEquals({value: 666, done: true}, x.next()); assertEquals({value: 666, done: true}, Next(x));
assertEquals({value: 5, done: true}, x.return(5)); assertEquals({value: 5, done: true}, Return(x, 5));
} }
{ {
let x = g(); let x = g();
assertEquals({value: 666, done: true}, x.return(666)); assertEquals({value: 666, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, x.next()); assertEquals({value: undefined, done: true}, Next(x));
assertEquals({value: 666, done: true}, x.return(666)); assertEquals({value: 666, done: true}, Return(x, 666));
} }
} }
...@@ -194,29 +220,29 @@ ...@@ -194,29 +220,29 @@
{ {
let x = g(); let x = g();
assertEquals({value: 1, done: false}, x.next()); assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, x.next(666)); assertEquals({value: 43, done: false}, Next(x, 666));
assertEquals({value: 13, done: false}, x.next()); assertEquals({value: 13, done: false}, Next(x));
assertEquals({value: undefined, done: true}, x.next()); assertEquals({value: undefined, done: true}, Next(x));
} }
{ {
let x = g(); let x = g();
assertEquals({value: 1, done: false}, x.next()); assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, x.return(666)); assertEquals({value: 43, done: false}, Return(x, 666));
assertEquals({value: 13, done: false}, x.next()); assertEquals({value: 13, done: false}, Next(x));
assertEquals({value: undefined, done: true}, x.next()); assertEquals({value: undefined, done: true}, Next(x));
} }
{ {
let x = g(); let x = g();
assertEquals({value: 1, done: false}, x.next()); assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, x.throw(666)); assertEquals({value: 43, done: false}, Throw(x, 666));
assertEquals({value: 13, done: false}, x.next()); assertEquals({value: 13, done: false}, Next(x));
assertEquals({value: undefined, done: true}, x.next()); assertEquals({value: undefined, done: true}, Next(x));
} }
} }
...@@ -228,28 +254,28 @@ ...@@ -228,28 +254,28 @@
{ {
let x = g(); let x = g();
assertEquals({value: 1, done: false}, x.next()); assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, x.next(666)); assertEquals({value: 43, done: false}, Next(x, 666));
assertEquals({value: undefined, done: false}, x.next()); assertEquals({value: undefined, done: false}, Next(x));
assertEquals({value: undefined, done: true}, x.next()); assertEquals({value: undefined, done: true}, Next(x));
} }
{ {
let x = g(); let x = g();
assertEquals({value: 1, done: false}, x.next()); assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, x.return(44)); assertEquals({value: 43, done: false}, Return(x, 44));
assertEquals({value: 44, done: false}, x.next()); assertEquals({value: 44, done: false}, Next(x));
assertEquals({value: undefined, done: true}, x.next()); assertEquals({value: undefined, done: true}, Next(x));
} }
{ {
let x = g(); let x = g();
assertEquals({value: 1, done: false}, x.next()); assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, x.throw(666)); assertEquals({value: 43, done: false}, Throw(x, 666));
assertThrowsEquals(() => x.next(), 666); assertThrowsEquals(() => Next(x), 666);
} }
} }
...@@ -267,9 +293,9 @@ ...@@ -267,9 +293,9 @@
{ {
let x = g(); let x = g();
assertEquals({value: 1, done: false}, x.next()); assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 2, done: false}, x.next()); assertEquals({value: 2, done: false}, Next(x));
assertEquals({value: 42, done: true}, x.return(42)); assertEquals({value: 42, done: true}, Return(x, 42));
} }
} }
...@@ -280,143 +306,144 @@ ...@@ -280,143 +306,144 @@
{ {
function* foo() { } function* foo() { }
let g = foo(); let g = foo();
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
function* foo() { return new.target } function* foo() { return new.target }
let g = foo(); let g = foo();
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
function* foo() { throw 666; return 42} function* foo() { throw 666; return 42}
let g = foo(); let g = foo();
assertThrowsEquals(() => g.next(), 666); assertThrowsEquals(() => Next(g), 666);
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
function* foo(a) { return a; } function* foo(a) { return a; }
let g = foo(42); let g = foo(42);
assertEquals({value: 42, done: true}, g.next()); assertEquals({value: 42, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
function* foo(a) { a.iwashere = true; return a; } function* foo(a) { a.iwashere = true; return a; }
let x = {}; let x = {};
let g = foo(x); let g = foo(x);
assertEquals({value: {iwashere: true}, done: true}, g.next()); assertEquals({value: {iwashere: true}, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
let a = 42; let a = 42;
function* foo() { return a; } function* foo() { return a; }
let g = foo(); let g = foo();
assertEquals({value: 42, done: true}, g.next()); assertEquals({value: 42, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
let a = 40; let a = 40;
function* foo(b) { return a + b; } function* foo(b) { return a + b; }
let g = foo(2); let g = foo(2);
assertEquals({value: 42, done: true}, g.next()); assertEquals({value: 42, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
let a = 40; let a = 40;
function* foo(b) { a--; b++; return a + b; } function* foo(b) { a--; b++; return a + b; }
let g = foo(2); let g = foo(2);
assertEquals({value: 42, done: true}, g.next()); assertEquals({value: 42, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
let g; let g;
function* foo() { g.next() } function* foo() { Next(g) }
g = foo(); g = foo();
assertThrows(() => g.next(), TypeError); assertThrows(() => Next(g), TypeError);
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
function* foo() { yield 2; yield 3; yield 4 } function* foo() { yield 2; yield 3; yield 4 }
g = foo(); g = foo();
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, g.next()); assertEquals({value: 4, done: false}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
function* foo() { yield 2; if (true) { yield 3 }; yield 4 } function* foo() { yield 2; if (true) { yield 3 }; yield 4 }
g = foo(); g = foo();
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, g.next()); assertEquals({value: 4, done: false}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
function* foo() { yield 2; if (true) { yield 3; yield 4 } } function* foo() { yield 2; if (true) { yield 3; yield 4 } }
g = foo(); g = foo();
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, g.next()); assertEquals({value: 4, done: false}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
function* foo() { yield 2; if (false) { yield 3 }; yield 4 } function* foo() { yield 2; if (false) { yield 3 }; yield 4 }
g = foo(); g = foo();
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 4, done: false}, g.next()); assertEquals({value: 4, done: false}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
function* foo() { yield 2; while (true) { yield 3 }; yield 4 } function* foo() { yield 2; while (true) { yield 3 }; yield 4 }
g = foo(); g = foo();
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
} }
{ {
function* foo() { yield 2; (yield 3) + 42; yield 4 } function* foo() { yield 2; (yield 3) + 42; yield 4 }
g = foo(); g = foo();
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, g.next()); assertEquals({value: 4, done: false}, Next(g));
} }
{ {
function* foo() { yield 2; (do {yield 3}) + 42; yield 4 } function* foo() { yield 2; (do {yield 3}) + 42; yield 4 }
g = foo(); g = foo();
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, g.next()); assertEquals({value: 4, done: false}, Next(g));
} }
{ {
function* foo() { yield 2; return (yield 3) + 42; yield 4 } function* foo() { yield 2; return (yield 3) + 42; yield 4 }
g = foo(); g = foo();
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 42, done: true}, g.next(0)); assertEquals({value: 42, done: true}, Next(g, 0));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
...@@ -435,19 +462,19 @@ ...@@ -435,19 +462,19 @@
return 5; return 5;
} }
g = foo(); g = foo();
assertEquals({value: 42, done: false}, g.next()); assertEquals({value: 42, done: false}, Next(g));
assertEquals({value: 'a', done: false}, g.next()); assertEquals({value: 'a', done: false}, Next(g));
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 1, done: false}, g.next()); assertEquals({value: 1, done: false}, Next(g));
assertEquals({value: 0, done: false}, g.next()); assertEquals({value: 0, done: false}, Next(g));
assertEquals({value: 'b', done: false}, g.next()); assertEquals({value: 'b', done: false}, Next(g));
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 1, done: false}, g.next()); assertEquals({value: 1, done: false}, Next(g));
assertEquals({value: 0, done: false}, g.next()); assertEquals({value: 0, done: false}, Next(g));
assertEquals({value: 42, done: false}, g.next()); assertEquals({value: 42, done: false}, Next(g));
assertEquals({value: 5, done: true}, g.next()); assertEquals({value: 5, done: true}, Next(g));
} }
{ {
...@@ -458,12 +485,12 @@ ...@@ -458,12 +485,12 @@
{ let c = 5; yield 2; yield a; yield b; yield c; } { let c = 5; yield 2; yield a; yield b; yield c; }
} }
g = foo(); g = foo();
assertEquals({value: 1, done: false}, g.next()); assertEquals({value: 1, done: false}, Next(g));
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, g.next()); assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, g.next()); assertEquals({value: 4, done: false}, Next(g));
assertEquals({value: 5, done: false}, g.next()); assertEquals({value: 5, done: false}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
...@@ -571,9 +598,9 @@ ...@@ -571,9 +598,9 @@
} }
g = foo(); g = foo();
for (let i = 0; i < 100; ++i) { for (let i = 0; i < 100; ++i) {
assertEquals({value: 42, done: false}, g.next()); assertEquals({value: 42, done: false}, i%25 === 0 ? Next(g) : g.next());
} }
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
} }
{ {
...@@ -587,14 +614,27 @@ ...@@ -587,14 +614,27 @@
} }
} }
g = foo(); g = foo();
assertEquals({value: 0, done: false}, g.next()); assertEquals({value: 0, done: false}, Next(g));
assertEquals({value: 10, done: false}, g.next()); assertEquals({value: 10, done: false}, Next(g));
assertEquals({value: 10, done: false}, g.next()); assertEquals({value: 10, done: false}, Next(g));
assertEquals({value: 1, done: false}, g.next()); assertEquals({value: 1, done: false}, Next(g));
assertEquals({value: 11, done: false}, g.next()); assertEquals({value: 11, done: false}, Next(g));
assertEquals({value: 11, done: false}, g.next()); assertEquals({value: 11, done: false}, Next(g));
assertEquals({value: 2, done: false}, g.next()); assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 12, done: false}, g.next()); assertEquals({value: 12, done: false}, Next(g));
assertEquals({value: 12, done: false}, g.next()); assertEquals({value: 12, done: false}, Next(g));
assertEquals({value: undefined, done: true}, g.next()); assertEquals({value: undefined, done: true}, Next(g));
}
{
let foo = function*() {
while (true) {
if (true || false) yield 42;
continue;
}
}
g = foo();
assertEquals({value: 42, done: false}, Next(g));
assertEquals({value: 42, done: false}, Next(g));
assertEquals({value: 42, done: false}, Next(g));
} }
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