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:
parent
61f5fbbb19
commit
3cc7315eda
@ -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") \
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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()));
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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) \
|
||||
|
@ -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:
|
||||
|
@ -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));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user