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}
This commit is contained in:
neis 2016-05-10 05:07:06 -07:00 committed by Commit bot
parent 61f5fbbb19
commit 3cc7315eda
14 changed files with 347 additions and 200 deletions

View File

@ -91,6 +91,7 @@ namespace internal {
V(kGraphBuildingFailed, "Optimized graph construction failed") \
V(kHeapNumberMapRegisterClobbered, "HeapNumberMap register clobbered") \
V(kHydrogenFilter, "Optimization disabled by filter") \
V(kIllegalBytecode, "Illegal bytecode") \
V(kImportDeclaration, "Import declaration") \
V(kIndexIsNegative, "Index is negative") \
V(kIndexIsTooLarge, "Index is too large") \

View File

@ -735,13 +735,6 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
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.
const int kMaxOptCount =
FLAG_deopt_every_n_times == 0 ? FLAG_max_opt_count : 1000;
@ -1250,6 +1243,12 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
Zone zone(info->isolate()->allocator());
CompilationInfo unoptimized(info->parse_info(), info->closure());
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
// of interpreter activations of the given function. The reasons are:
// 1) The debugger assumes each function is either full-code or bytecode.
@ -1260,6 +1259,7 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
HasInterpreterActivations(info->isolate(), *shared)) {
return false;
}
// If the current code has reloc info for serialization, also include
// reloc info for serialization for the new code, so that deopt support
// can be added without losing IC state.

View File

@ -1367,11 +1367,45 @@ void BytecodeGraphBuilder::VisitForInStep() {
}
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() {
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() {
@ -1385,8 +1419,8 @@ void BytecodeGraphBuilder::VisitExtraWide() {
}
void BytecodeGraphBuilder::VisitIllegal() {
// Never present in valid bytecode.
UNREACHABLE();
NewNode(javascript()->CallRuntime(Runtime::kAbort),
jsgraph()->Constant(kIllegalBytecode));
}
void BytecodeGraphBuilder::SwitchToMergeEnvironment(int current_offset) {

View File

@ -48,6 +48,11 @@ Node* JSGraph::OptimizedOutConstant() {
HeapConstant(factory()->optimized_out()));
}
Node* JSGraph::StaleRegisterConstant() {
return CACHED(kStaleRegisterConstant,
HeapConstant(factory()->stale_register()));
}
Node* JSGraph::UndefinedConstant() {
return CACHED(kUndefinedConstant, HeapConstant(factory()->undefined_value()));
}

View File

@ -45,6 +45,7 @@ class JSGraph : public ZoneObject {
Node* EmptyFixedArrayConstant();
Node* HeapNumberMapConstant();
Node* OptimizedOutConstant();
Node* StaleRegisterConstant();
Node* UndefinedConstant();
Node* TheHoleConstant();
Node* TrueConstant();
@ -148,6 +149,7 @@ class JSGraph : public ZoneObject {
kEmptyFixedArrayConstant,
kHeapNumberMapConstant,
kOptimizedOutConstant,
kStaleRegisterConstant,
kUndefinedConstant,
kTheHoleConstant,
kTrueConstant,

View File

@ -136,6 +136,7 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
// not to call into arbitrary JavaScript, not to throw, and not to deoptimize
// are blacklisted here and can be called without a FrameState.
switch (function) {
case Runtime::kAbort:
case Runtime::kAllocateInTargetSpace:
case Runtime::kCreateIterResultObject:
case Runtime::kDefineDataPropertyInLiteral:
@ -144,6 +145,11 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
case Runtime::kFinalizeClassDefinition: // TODO(conradw): Is it safe?
case Runtime::kForInDone:
case Runtime::kForInStep:
case Runtime::kGeneratorSetContext:
case Runtime::kGeneratorGetContinuation:
case Runtime::kGeneratorSetContinuation:
case Runtime::kGeneratorLoadRegister:
case Runtime::kGeneratorStoreRegister:
case Runtime::kGetSuperConstructor:
case Runtime::kIsFunction:
case Runtime::kNewClosure:

View File

@ -3088,7 +3088,7 @@ bool LinearScanAllocator::TryReuseSpillForPhi(TopLevelLiveRange* range) {
? range->TopLevel()->GetSpillRange()
: data()->AssignSpillRangeToLiveRange(range->TopLevel());
bool merged = first_op_spill->TryMerge(spill_range);
CHECK(merged);
if (!merged) return false;
Spill(range);
return true;
} else if (pos->pos() > range->Start().NextStart()) {
@ -3097,7 +3097,7 @@ bool LinearScanAllocator::TryReuseSpillForPhi(TopLevelLiveRange* range) {
? range->TopLevel()->GetSpillRange()
: data()->AssignSpillRangeToLiveRange(range->TopLevel());
bool merged = first_op_spill->TryMerge(spill_range);
CHECK(merged);
if (!merged) return false;
SpillBetween(range, range->Start(), pos->pos());
DCHECK(UnhandledIsSorted());
return true;

View File

@ -158,6 +158,11 @@ HCompilationJob::Status HCompilationJob::CreateGraphImpl() {
return AbortOptimization(kTooManyParametersLocals);
}
if (IsGeneratorFunction(info()->shared_info()->kind())) {
// Crankshaft does not support generators.
return AbortOptimization(kGenerator);
}
if (FLAG_trace_hydrogen) {
isolate()->GetHTracer()->TraceCompilation(info());
}

View File

@ -726,6 +726,8 @@ Node* InterpreterAssembler::ExportRegisterFile(Node* array) {
var_index.Bind(Int32Constant(0));
// 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);
Goto(&loop);
Bind(&loop);

View File

@ -7295,7 +7295,6 @@ class JSGeneratorObject: public JSObject {
DECLARE_CAST(JSGeneratorObject)
// Dispatched behavior.
DECLARE_PRINTER(JSGeneratorObject)
DECLARE_VERIFIER(JSGeneratorObject)
// Magic sentinel values for the continuation.

View File

@ -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) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
@ -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) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);

View File

@ -231,9 +231,13 @@ namespace internal {
F(GeneratorGetFunction, 1, 1) \
F(GeneratorGetReceiver, 1, 1) \
F(GeneratorGetInput, 1, 1) \
F(GeneratorSetContext, 1, 1) \
F(GeneratorGetContinuation, 1, 1) \
F(GeneratorSetContinuation, 2, 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
#define FOR_EACH_INTRINSIC_I18N(F) \

View File

@ -192,7 +192,8 @@ Type::bitset BitsetType::Lub(i::Map* map) {
map == heap->no_interceptor_result_sentinel_map() ||
map == heap->termination_exception_map() ||
map == heap->arguments_marker_map() ||
map == heap->optimized_out_map());
map == heap->optimized_out_map() ||
map == heap->stale_register_map());
return kInternal & kTaggedPointer;
}
case HEAP_NUMBER_TYPE:

View File

@ -3,6 +3,32 @@
// found in the LICENSE file.
// 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
@ -11,19 +37,19 @@
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();
assertEquals({value: 1, done: false}, x.next());
assertEquals({value: undefined, done: true}, x.throw("caught"));
assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: undefined, done: true}, Throw(x, "caught"));
}
{
let x = g();
assertEquals({value: 1, done: false}, x.next());
assertEquals({value: undefined, done: true}, x.next());
assertThrowsEquals(() => x.throw("not caught"), "not caught");
assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: undefined, done: true}, Next(x));
assertThrowsEquals(() => Throw(x, "not caught"), "not caught");
}
}
@ -33,19 +59,19 @@
{
let x = g();
assertEquals({value: 43, done: false}, x.next());
assertEquals({value: 42, done: true}, x.next());
assertEquals({value: 43, done: false}, Next(x));
assertEquals({value: 42, done: true}, Next(x));
}
}
{ // return that doesn't close
let x;
let g = function*() { try {return 42} finally {x.throw(666)} };
let g = function*() { try {return 42} finally {Throw(x, 666)} };
{
x = g();
assertThrows(() => x.next(), TypeError); // still executing
assertThrows(() => Next(x), TypeError); // still executing
}
}
@ -56,42 +82,42 @@
{ // "return" closes at suspendedStart
let x = g();
assertEquals({value: 666, done: true}, x.return(666));
assertEquals({value: undefined, done: true}, x.next(42));
assertThrowsEquals(() => x.throw(43), 43);
assertEquals({value: 42, done: true}, x.return(42));
assertEquals({value: 666, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, Next(x, 42));
assertThrowsEquals(() => Throw(x, 43), 43);
assertEquals({value: 42, done: true}, Return(x, 42));
}
{ // "throw" closes at suspendedStart
let x = g();
assertThrowsEquals(() => x.throw(666), 666);
assertEquals({value: undefined, done: true}, x.next(42));
assertEquals({value: 43, done: true}, x.return(43));
assertThrowsEquals(() => x.throw(44), 44);
assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: undefined, done: true}, Next(x, 42));
assertEquals({value: 43, done: true}, Return(x, 43));
assertThrowsEquals(() => Throw(x, 44), 44);
}
{ // "next" closes at suspendedYield
let x = g();
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 13, done: true}, x.next(666));
assertEquals({value: undefined, done: true}, x.next(666));
assertThrowsEquals(() => x.throw(666), 666);
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 13, done: true}, Next(x, 666));
assertEquals({value: undefined, done: true}, Next(x, 666));
assertThrowsEquals(() => Throw(x, 666), 666);
}
{ // "return" closes at suspendedYield
let x = g();
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 13, done: true}, x.return(666));
assertEquals({value: undefined, done: true}, x.next(666));
assertEquals({value: 666, done: true}, x.return(666));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 13, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, Next(x, 666));
assertEquals({value: 666, done: true}, Return(x, 666));
}
{ // "throw" closes at suspendedYield
let x = g();
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 13, done: true}, x.throw(666));
assertThrowsEquals(() => x.throw(666), 666);
assertEquals({value: undefined, done: true}, x.next(666));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 13, done: true}, Throw(x, 666));
assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: undefined, done: true}, Next(x, 666));
}
}
@ -102,45 +128,45 @@
{ // "return" closes at suspendedStart
let x = g();
assertEquals({value: 666, done: true}, x.return(666));
assertEquals({value: undefined, done: true}, x.next(42));
assertThrowsEquals(() => x.throw(43), 43);
assertEquals({value: 42, done: true}, x.return(42));
assertEquals({value: 666, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, Next(x, 42));
assertThrowsEquals(() => Throw(x, 43), 43);
assertEquals({value: 42, done: true}, Return(x, 42));
}
{ // "throw" closes at suspendedStart
let x = g();
assertThrowsEquals(() => x.throw(666), 666);
assertEquals({value: undefined, done: true}, x.next(42));
assertEquals({value: 43, done: true}, x.return(43));
assertThrowsEquals(() => x.throw(44), 44);
assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: undefined, done: true}, Next(x, 42));
assertEquals({value: 43, done: true}, Return(x, 43));
assertThrowsEquals(() => Throw(x, 44), 44);
}
{ // "next" closes at suspendedYield
let x = g();
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: undefined, done: true}, x.next(666));
assertEquals({value: undefined, done: true}, x.next(666));
assertThrowsEquals(() => x.throw(666), 666);
assertEquals({value: 42, done: true}, x.return(42));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: undefined, done: true}, Next(x, 666));
assertEquals({value: undefined, done: true}, Next(x, 666));
assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: 42, done: true}, Return(x, 42));
}
{ // "return" closes at suspendedYield
let x = g();
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 666, done: true}, x.return(666));
assertEquals({value: undefined, done: true}, x.next(666));
assertThrowsEquals(() => x.throw(44), 44);
assertEquals({value: 42, done: true}, x.return(42));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 666, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, Next(x, 666));
assertThrowsEquals(() => Throw(x, 44), 44);
assertEquals({value: 42, done: true}, Return(x, 42));
}
{ // "throw" closes at suspendedYield
let x = g();
assertEquals({value: 42, done: false}, x.next());
assertThrowsEquals(() => x.throw(666), 666);
assertEquals({value: undefined, done: true}, x.next(666));
assertThrowsEquals(() => x.throw(666), 666);
assertEquals({value: 42, done: true}, x.return(42));
assertEquals({value: 42, done: false}, Next(x));
assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: undefined, done: true}, Next(x, 666));
assertThrowsEquals(() => Throw(x, 666), 666);
assertEquals({value: 42, done: true}, Return(x, 42));
}
}
@ -151,17 +177,17 @@
{
let x = g();
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 43, done: false}, x.return(666));
assertEquals({value: 13, done: true}, x.next());
assertEquals({value: 666, done: true}, x.return(666));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, Return(x, 666));
assertEquals({value: 13, done: true}, Next(x));
assertEquals({value: 666, done: true}, Return(x, 666));
}
{
let x = g();
assertEquals({value: 666, done: true}, x.return(666));
assertEquals({value: undefined, done: true}, x.next());
assertEquals({value: 666, done: true}, x.return(666));
assertEquals({value: 666, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, Next(x));
assertEquals({value: 666, done: true}, Return(x, 666));
}
}
@ -172,17 +198,17 @@
{
let x = g();
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 43, done: false}, x.return(666));
assertEquals({value: 666, done: true}, x.next());
assertEquals({value: 5, done: true}, x.return(5));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, Return(x, 666));
assertEquals({value: 666, done: true}, Next(x));
assertEquals({value: 5, done: true}, Return(x, 5));
}
{
let x = g();
assertEquals({value: 666, done: true}, x.return(666));
assertEquals({value: undefined, done: true}, x.next());
assertEquals({value: 666, done: true}, x.return(666));
assertEquals({value: 666, done: true}, Return(x, 666));
assertEquals({value: undefined, done: true}, Next(x));
assertEquals({value: 666, done: true}, Return(x, 666));
}
}
@ -194,29 +220,29 @@
{
let x = g();
assertEquals({value: 1, done: false}, x.next());
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 43, done: false}, x.next(666));
assertEquals({value: 13, done: false}, x.next());
assertEquals({value: undefined, done: true}, x.next());
assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, Next(x, 666));
assertEquals({value: 13, done: false}, Next(x));
assertEquals({value: undefined, done: true}, Next(x));
}
{
let x = g();
assertEquals({value: 1, done: false}, x.next());
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 43, done: false}, x.return(666));
assertEquals({value: 13, done: false}, x.next());
assertEquals({value: undefined, done: true}, x.next());
assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, Return(x, 666));
assertEquals({value: 13, done: false}, Next(x));
assertEquals({value: undefined, done: true}, Next(x));
}
{
let x = g();
assertEquals({value: 1, done: false}, x.next());
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 43, done: false}, x.throw(666));
assertEquals({value: 13, done: false}, x.next());
assertEquals({value: undefined, done: true}, x.next());
assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, Throw(x, 666));
assertEquals({value: 13, done: false}, Next(x));
assertEquals({value: undefined, done: true}, Next(x));
}
}
@ -228,28 +254,28 @@
{
let x = g();
assertEquals({value: 1, done: false}, x.next());
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 43, done: false}, x.next(666));
assertEquals({value: undefined, done: false}, x.next());
assertEquals({value: undefined, done: true}, x.next());
assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, Next(x, 666));
assertEquals({value: undefined, done: false}, Next(x));
assertEquals({value: undefined, done: true}, Next(x));
}
{
let x = g();
assertEquals({value: 1, done: false}, x.next());
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 43, done: false}, x.return(44));
assertEquals({value: 44, done: false}, x.next());
assertEquals({value: undefined, done: true}, x.next());
assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, Return(x, 44));
assertEquals({value: 44, done: false}, Next(x));
assertEquals({value: undefined, done: true}, Next(x));
}
{
let x = g();
assertEquals({value: 1, done: false}, x.next());
assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 43, done: false}, x.throw(666));
assertThrowsEquals(() => x.next(), 666);
assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 42, done: false}, Next(x));
assertEquals({value: 43, done: false}, Throw(x, 666));
assertThrowsEquals(() => Next(x), 666);
}
}
@ -267,9 +293,9 @@
{
let x = g();
assertEquals({value: 1, done: false}, x.next());
assertEquals({value: 2, done: false}, x.next());
assertEquals({value: 42, done: true}, x.return(42));
assertEquals({value: 1, done: false}, Next(x));
assertEquals({value: 2, done: false}, Next(x));
assertEquals({value: 42, done: true}, Return(x, 42));
}
}
@ -280,143 +306,144 @@
{
function* foo() { }
let g = foo();
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
function* foo() { return new.target }
let g = foo();
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
function* foo() { throw 666; return 42}
let g = foo();
assertThrowsEquals(() => g.next(), 666);
assertEquals({value: undefined, done: true}, g.next());
assertThrowsEquals(() => Next(g), 666);
assertEquals({value: undefined, done: true}, Next(g));
}
{
function* foo(a) { return a; }
let g = foo(42);
assertEquals({value: 42, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 42, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
function* foo(a) { a.iwashere = true; return a; }
let x = {};
let g = foo(x);
assertEquals({value: {iwashere: true}, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: {iwashere: true}, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
let a = 42;
function* foo() { return a; }
let g = foo();
assertEquals({value: 42, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 42, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
let a = 40;
function* foo(b) { return a + b; }
let g = foo(2);
assertEquals({value: 42, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 42, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
let a = 40;
function* foo(b) { a--; b++; return a + b; }
let g = foo(2);
assertEquals({value: 42, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 42, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
let g;
function* foo() { g.next() }
function* foo() { Next(g) }
g = foo();
assertThrows(() => g.next(), TypeError);
assertEquals({value: undefined, done: true}, g.next());
assertThrows(() => Next(g), TypeError);
assertEquals({value: undefined, done: true}, Next(g));
}
{
function* foo() { yield 2; yield 3; yield 4 }
g = foo();
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 4, done: false}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
function* foo() { yield 2; if (true) { yield 3 }; yield 4 }
g = foo();
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 4, done: false}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
function* foo() { yield 2; if (true) { yield 3; yield 4 } }
g = foo();
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 4, done: false}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
function* foo() { yield 2; if (false) { yield 3 }; yield 4 }
g = foo();
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 4, done: false}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 4, done: false}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
function* foo() { yield 2; while (true) { yield 3 }; yield 4 }
g = foo();
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
}
{
function* foo() { yield 2; (yield 3) + 42; yield 4 }
g = foo();
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 4, done: false}, g.next());
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, Next(g));
}
{
function* foo() { yield 2; (do {yield 3}) + 42; yield 4 }
g = foo();
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 4, done: false}, g.next());
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, Next(g));
}
{
function* foo() { yield 2; return (yield 3) + 42; yield 4 }
g = foo();
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 42, done: true}, g.next(0));
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 42, done: true}, Next(g, 0));
assertEquals({value: undefined, done: true}, Next(g));
}
{
@ -435,19 +462,19 @@
return 5;
}
g = foo();
assertEquals({value: 42, done: false}, g.next());
assertEquals({value: 'a', done: false}, g.next());
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 1, done: false}, g.next());
assertEquals({value: 0, done: false}, g.next());
assertEquals({value: 'b', done: false}, g.next());
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 1, done: false}, g.next());
assertEquals({value: 0, done: false}, g.next());
assertEquals({value: 42, done: false}, g.next());
assertEquals({value: 5, done: true}, g.next());
assertEquals({value: 42, done: false}, Next(g));
assertEquals({value: 'a', done: false}, Next(g));
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 1, done: false}, Next(g));
assertEquals({value: 0, done: false}, Next(g));
assertEquals({value: 'b', done: false}, Next(g));
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 1, done: false}, Next(g));
assertEquals({value: 0, done: false}, Next(g));
assertEquals({value: 42, done: false}, Next(g));
assertEquals({value: 5, done: true}, Next(g));
}
{
@ -458,12 +485,12 @@
{ let c = 5; yield 2; yield a; yield b; yield c; }
}
g = foo();
assertEquals({value: 1, done: false}, g.next());
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 3, done: false}, g.next());
assertEquals({value: 4, done: false}, g.next());
assertEquals({value: 5, done: false}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 1, done: false}, Next(g));
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 3, done: false}, Next(g));
assertEquals({value: 4, done: false}, Next(g));
assertEquals({value: 5, done: false}, Next(g));
assertEquals({value: undefined, done: true}, Next(g));
}
{
@ -571,9 +598,9 @@
}
g = foo();
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 @@
}
}
g = foo();
assertEquals({value: 0, done: false}, g.next());
assertEquals({value: 10, done: false}, g.next());
assertEquals({value: 10, done: false}, g.next());
assertEquals({value: 1, done: false}, g.next());
assertEquals({value: 11, done: false}, g.next());
assertEquals({value: 11, done: false}, g.next());
assertEquals({value: 2, done: false}, g.next());
assertEquals({value: 12, done: false}, g.next());
assertEquals({value: 12, done: false}, g.next());
assertEquals({value: undefined, done: true}, g.next());
assertEquals({value: 0, done: false}, Next(g));
assertEquals({value: 10, done: false}, Next(g));
assertEquals({value: 10, done: false}, Next(g));
assertEquals({value: 1, done: false}, Next(g));
assertEquals({value: 11, done: false}, Next(g));
assertEquals({value: 11, done: false}, Next(g));
assertEquals({value: 2, done: false}, Next(g));
assertEquals({value: 12, done: false}, Next(g));
assertEquals({value: 12, done: false}, Next(g));
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));
}