Index: src/interpreter/bytecode-generator.cc |
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc |
index 0dabea2ed31db0db4b2b42282162ce19e958f4ab..b1b8517b1917c3611991b9a95f69ffa70d394df6 100644 |
--- a/src/interpreter/bytecode-generator.cc |
+++ b/src/interpreter/bytecode-generator.cc |
@@ -565,8 +565,10 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) |
execution_context_(nullptr), |
execution_result_(nullptr), |
register_allocator_(nullptr), |
+ generator_resume_points_(0, zone), |
try_catch_nesting_level_(0), |
- try_finally_nesting_level_(0) { |
+ try_finally_nesting_level_(0), |
+ generator_yields_seen_(0) { |
InitializeAstVisitor(isolate); |
} |
@@ -586,6 +588,10 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { |
// Initialize control scope. |
ControlScopeForTopLevel control(this); |
+ if (IsGeneratorFunction(info->literal()->kind())) { |
+ VisitGeneratorPrologue(); |
+ } |
+ |
// Build function context only if there are context allocated variables. |
if (scope()->NeedsContext()) { |
// Push a new inner context scope for the function. |
@@ -634,6 +640,40 @@ void BytecodeGenerator::MakeBytecodeBody() { |
VisitStatements(info()->literal()->body()); |
} |
+void BytecodeGenerator::VisitGeneratorPrologue() { |
+ generator_resume_points_.clear(); |
+ generator_resume_points_.resize(info()->literal()->yield_count()); |
+ |
+ BytecodeLabel regular_call; |
+ builder() |
+ ->LoadAccumulatorWithRegister(Register::new_target()) |
+ .JumpIfUndefined(®ular_call); |
+ |
+ // This is a resume call. Restore registers and perform state dispatch. |
+ // (The current context has already been restored by the trampoline.) |
+ { |
+ RegisterAllocationScope register_scope(this); |
+ Register state = register_allocator()->NewRegister(); |
+ builder() |
+ ->CallRuntime(Runtime::kResumeIgnitionGenerator, Register::new_target(), |
+ 1) |
+ .StoreAccumulatorInRegister(state); |
+ |
+ // TODO(neis): Optimize this by using a proper jump table. |
+ for (size_t i = 0; i < generator_resume_points_.size(); ++i) { |
+ builder() |
+ ->LoadLiteral(Smi::FromInt(static_cast<int>(i))) |
+ .CompareOperation(Token::Value::EQ_STRICT, state) |
+ .JumpIfTrue(&(generator_resume_points_[i])); |
+ } |
+ builder()->Illegal(); // Should never get here. |
+ } |
+ |
+ builder()->Bind(®ular_call); |
+ // This is a regular call. Fall through to the ordinary function prologue, |
+ // after which we will run into the generator object creation and the initial |
+ // yield (both inserted by the parser). |
+} |
void BytecodeGenerator::VisitBlock(Block* stmt) { |
// Visit declarations and statements. |
@@ -2229,16 +2269,86 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { |
execution_result()->SetResultInAccumulator(); |
} |
+void BytecodeGenerator::VisitYield(Yield* expr) { |
+ int id = generator_yields_seen_++; |
+ |
+ builder()->SetExpressionPosition(expr); |
+ Register value = VisitForRegisterValue(expr->expression()); |
+ |
+ register_allocator()->PrepareForConsecutiveAllocations(2); |
+ Register generator = register_allocator()->NextConsecutiveRegister(); |
+ Register state = register_allocator()->NextConsecutiveRegister(); |
+ |
+ // Save context, registers, and state. Then return. |
+ VisitForRegisterValue(expr->generator_object(), generator); |
+ builder() |
+ ->LoadLiteral(Smi::FromInt(id)) |
+ .StoreAccumulatorInRegister(state) |
+ .CallRuntime(Runtime::kSuspendIgnitionGenerator, generator, 2) |
+ .LoadAccumulatorWithRegister(value) |
+ .Return(); // Hard return (ignore any finally blocks). |
+ |
+ builder()->Bind(&(generator_resume_points_[id])); |
+ // Upon resume, we continue here. |
+ |
+ { |
+ RegisterAllocationScope register_scope(this); |
+ |
+ Register input = register_allocator()->NewRegister(); |
+ builder() |
+ ->CallRuntime(Runtime::kGeneratorGetInput, generator, 1) |
+ .StoreAccumulatorInRegister(input); |
+ |
+ Register resume_mode = register_allocator()->NewRegister(); |
+ builder() |
+ ->CallRuntime(Runtime::kGeneratorGetResumeMode, generator, 1) |
+ .StoreAccumulatorInRegister(resume_mode); |
+ |
+ // Now dispatch on resume mode. |
+ |
+ BytecodeLabel resume_with_next; |
+ BytecodeLabel resume_with_return; |
+ BytecodeLabel resume_with_throw; |
-void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); } |
+ builder() |
+ ->LoadLiteral(Smi::FromInt(JSGeneratorObject::kNext)) |
+ .CompareOperation(Token::EQ_STRICT, resume_mode) |
+ .JumpIfTrue(&resume_with_next) |
+ .LoadLiteral(Smi::FromInt(JSGeneratorObject::kThrow)) |
+ .CompareOperation(Token::EQ_STRICT, resume_mode) |
+ .JumpIfTrue(&resume_with_throw) |
+ .Jump(&resume_with_return); |
Jarin
2016/04/18 13:55:06
Out of curiosity, do you even need the Jump?
neis
2016/04/18 14:07:52
Shhh :)
|
+ |
+ builder()->Bind(&resume_with_return); |
+ { |
+ register_allocator()->PrepareForConsecutiveAllocations(2); |
+ Register value = register_allocator()->NextConsecutiveRegister(); |
+ Register done = register_allocator()->NextConsecutiveRegister(); |
+ builder() |
+ ->MoveRegister(input, value) |
+ .LoadTrue() |
+ .StoreAccumulatorInRegister(done) |
+ .CallRuntime(Runtime::kCreateIterResultObject, value, 2); |
+ execution_control()->ReturnAccumulator(); |
+ } |
+ builder()->Bind(&resume_with_throw); |
+ builder() |
+ ->LoadAccumulatorWithRegister(input) |
+ .Throw(); |
+ |
+ builder()->Bind(&resume_with_next); |
+ builder()->LoadAccumulatorWithRegister(input); |
+ } |
+ execution_result()->SetResultInAccumulator(); |
+} |
void BytecodeGenerator::VisitThrow(Throw* expr) { |
VisitForAccumulatorValue(expr->exception()); |
builder()->SetExpressionPosition(expr); |
builder()->Throw(); |
- // Throw statments are modeled as expression instead of statments. These are |
- // converted from assignment statements in Rewriter::ReWrite pass. An |
+ // Throw statements are modeled as expressions instead of statements. These |
+ // are converted from assignment statements in Rewriter::ReWrite pass. An |
// assignment statement expects a value in the accumulator. This is a hack to |
// avoid DCHECK fails assert accumulator has been set. |
execution_result()->SetResultInAccumulator(); |