Index: src/interpreter/bytecode-generator.cc |
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc |
index c28d4687d8a8cf5b742626739d8cbeb50ee9dc54..916a965855ccf0ad5d7182201b6c06cbcfecd853 100644 |
--- a/src/interpreter/bytecode-generator.cc |
+++ b/src/interpreter/bytecode-generator.cc |
@@ -199,7 +199,7 @@ class BytecodeGenerator::ControlScope::DeferredCommands final { |
ControlScope* execution_control() { return generator_->execution_control(); } |
private: |
- BytecodeGenerator* generator_; |
+ BytecodeGenerator* generator_ = nullptr; |
rmcilroy
2017/02/07 17:24:41
Why was this change made? The constructor should s
|
ZoneVector<Entry> deferred_; |
Register token_register_; |
Register result_register_; |
@@ -580,6 +580,217 @@ class BytecodeGenerator::GlobalDeclarationsBuilder final : public ZoneObject { |
bool has_constant_pool_entry_; |
}; |
+template <class T> |
+class TryBlockBuilder; |
+class BytecodeGenerator::SimpleTryCatchBuilder : private TryCatchBuilder { |
+ public: |
+ typedef ControlScopeForTryCatch TryControlScope; |
+ |
+ explicit SimpleTryCatchBuilder(BytecodeGenerator* generator, |
+ HandlerTable::CatchPrediction catch_prediction) |
+ : TryCatchBuilder(generator->builder(), catch_prediction), |
+ generator_(generator) {} |
+ |
+ Register context() const { |
+ DCHECK(context_.is_valid()); |
+ return context_; |
+ } |
+ |
+ private: |
+ friend class TryBlockBuilder<SimpleTryCatchBuilder>; |
+ friend class CatchBlockBuilder; |
+ |
+ void BeginTry(ControlScopeForTryCatch* scope) { |
+ DCHECK_NULL(control_scope_); |
+ control_scope_ = scope; |
+ |
+ // Preserve the context in a dedicated register, so that it can be restored |
+ // when the handler is entered by the stack-unwinding machinery. |
+ // TODO(mstarzinger): Be smarter about register allocation. |
+ DCHECK(!context_.is_valid()); |
+ context_ = generator_->register_allocator()->NewRegister(); |
+ builder()->MoveRegister(Register::current_context(), context_); |
+ |
+ TryCatchBuilder::BeginTry(context_); |
+ last_catch_prediction_ = generator_->catch_prediction_; |
+ if (catch_prediction() != HandlerTable::UNCAUGHT) { |
+ generator_->catch_prediction_ = catch_prediction(); |
+ } |
rmcilroy
2017/02/07 17:24:41
I'm not sure why we need to update the catch predi
|
+ } |
+ |
+ void EndTry() { |
+ DCHECK_NOT_NULL(control_scope_); |
+ generator_->catch_prediction_ = last_catch_prediction_; |
+ last_catch_prediction_ = HandlerTable::UNCAUGHT; |
+ |
+ TryCatchBuilder::EndTry(); |
+ control_scope_ = nullptr; |
+ } |
+ |
+ void BeginCatch() { |
+ DCHECK_NULL(control_scope_); |
+ DCHECK(context_.is_valid()); |
+ } |
+ |
+ void EndCatch() { TryCatchBuilder::EndCatch(); } |
+ |
+ BytecodeGenerator* generator_; |
+ Register context_; |
+ ControlScopeForTryCatch* control_scope_ = nullptr; |
+ HandlerTable::CatchPrediction last_catch_prediction_; |
+}; |
+ |
+class BytecodeGenerator::SimpleTryFinallyBuilder : public TryFinallyBuilder { |
rmcilroy
2017/02/07 17:24:41
I'm not sure on the naming, why is this SimpleTryF
|
+ public: |
+ typedef ControlScopeForTryFinally TryControlScope; |
+ |
+ explicit SimpleTryFinallyBuilder(BytecodeGenerator* generator) |
+ : TryFinallyBuilder(generator->builder(), generator->catch_prediction_), |
+ generator_(generator), |
+ token_(generator->register_allocator()->NewRegister()), |
+ result_(generator->register_allocator()->NewRegister()), |
+ commands_(generator, token_, result_) { |
+ // We keep a record of all paths that enter the finally-block to be able to |
+ // dispatch to the correct continuation point after the statements in the |
+ // finally-block have been evaluated. |
+ // |
+ // The try-finally construct can enter the finally-block in three ways: |
+ // 1. By exiting the try-block normally, falling through at the end. |
+ // 2. By exiting the try-block with a function-local control flow transfer |
+ // (i.e. through break/continue/return statements). |
+ // 3. By exiting the try-block with a thrown exception. |
+ // |
+ // The result register semantics depend on how the block was entered: |
+ // - ReturnStatement: It represents the return value being returned. |
+ // - ThrowStatement: It represents the exception being thrown. |
+ // - BreakStatement/ContinueStatement: Undefined and not used. |
+ // - Falling through into finally-block: Undefined and not used. |
+ } |
+ |
+ private: |
+ friend class TryBlockBuilder<SimpleTryFinallyBuilder>; |
+ friend class FinallyBlockBuilder; |
+ |
+ void BeginTry(ControlScopeForTryFinally* control_scope) { |
+ DCHECK_NULL(control_scope_); |
+ control_scope_ = control_scope; |
+ |
+ // Preserve the context in a dedicated register, so that it can be restored |
+ // when the handler is entered by the stack-unwinding machinery. |
+ // TODO(mstarzinger): Be smarter about register allocation. |
+ DCHECK(!context_or_message_.is_valid()); |
+ context_or_message_ = generator_->register_allocator()->NewRegister(); |
+ generator_->builder()->MoveRegister(Register::current_context(), |
+ context_or_message_); |
+ |
+ TryFinallyBuilder::BeginTry(context_or_message_); |
+ } |
+ |
+ void EndTry() { |
+ DCHECK_NOT_NULL(control_scope_); |
+ TryFinallyBuilder::EndTry(); |
+ control_scope_ = nullptr; |
+ |
+ // Record fall-through and exception cases. |
+ commands_.RecordFallThroughPath(); |
+ TryFinallyBuilder::LeaveTry(); |
+ TryFinallyBuilder::BeginHandler(); |
+ commands_.RecordHandlerReThrowPath(); |
+ } |
+ |
+ void BeginFinally() { |
+ TryFinallyBuilder::BeginFinally(); |
+ generator_->builder() |
+ ->LoadTheHole() |
+ .SetPendingMessage() |
+ .StoreAccumulatorInRegister(context_or_message_); |
+ } |
+ |
+ void EndFinally() { |
+ TryFinallyBuilder::EndFinally(); |
+ |
+ // Pending message object is restored on exit. |
+ generator_->builder() |
+ ->LoadAccumulatorWithRegister(context_or_message_) |
+ .SetPendingMessage(); |
+ |
+ // Dynamic dispatch after the finally-block. |
+ commands_.ApplyDeferredCommands(); |
+ } |
+ |
+ ControlScopeForTryFinally* control_scope_ = nullptr; |
+ BytecodeGenerator* generator_; |
+ Register token_; |
+ Register result_; |
+ ControlScope::DeferredCommands commands_; |
+ Register context_or_message_; |
+}; |
+ |
+template <class T> |
+class BytecodeGenerator::TryBlockBuilder { |
rmcilroy
2017/02/07 17:24:41
I don't think the scoped builders are necessary, I
|
+ public: |
+ typedef T SimpleControlBuilder; |
+ explicit TryBlockBuilder(SimpleControlBuilder& builder); |
+ ~TryBlockBuilder(); |
+ |
+ private: |
+ SimpleControlBuilder* builder_; |
+ typename SimpleControlBuilder::TryControlScope scope_; |
+}; |
+ |
+template <> |
+BytecodeGenerator::TryBlockBuilder<BytecodeGenerator::SimpleTryCatchBuilder>:: |
+ TryBlockBuilder(SimpleTryCatchBuilder& builder) |
+ : builder_(&builder), scope_(builder_->generator_, builder_) { |
+ builder_->BeginTry(&scope_); |
+} |
+ |
+template <> |
+BytecodeGenerator::TryBlockBuilder< |
+ BytecodeGenerator::SimpleTryCatchBuilder>::~TryBlockBuilder() { |
+ builder_->EndTry(); |
+} |
+ |
+template <> |
+BytecodeGenerator::TryBlockBuilder<BytecodeGenerator::SimpleTryFinallyBuilder>:: |
+ TryBlockBuilder(SimpleTryFinallyBuilder& builder) |
+ : builder_(&builder), |
+ scope_(builder_->generator_, builder_, &builder_->commands_) { |
+ builder_->BeginTry(&scope_); |
+} |
+ |
+template <> |
+BytecodeGenerator::TryBlockBuilder< |
+ BytecodeGenerator::SimpleTryFinallyBuilder>::~TryBlockBuilder() { |
+ builder_->EndTry(); |
+} |
+ |
+class BytecodeGenerator::CatchBlockBuilder { |
+ public: |
+ explicit CatchBlockBuilder(SimpleTryCatchBuilder& builder) |
+ : builder_(&builder) { |
+ builder_->BeginCatch(); |
+ } |
+ |
+ ~CatchBlockBuilder() { builder_->EndCatch(); } |
+ |
+ private: |
+ SimpleTryCatchBuilder* builder_; |
+}; |
+ |
+class BytecodeGenerator::FinallyBlockBuilder { |
+ public: |
+ explicit FinallyBlockBuilder(SimpleTryFinallyBuilder& builder) |
+ : builder_(&builder) { |
+ builder_->BeginFinally(); |
+ } |
+ |
+ ~FinallyBlockBuilder() { builder_->EndFinally(); } |
+ |
+ private: |
+ SimpleTryFinallyBuilder* builder_; |
+}; |
+ |
BytecodeGenerator::BytecodeGenerator(CompilationInfo* info) |
: zone_(info->zone()), |
builder_(new (zone()) BytecodeArrayBuilder( |
@@ -601,6 +812,7 @@ BytecodeGenerator::BytecodeGenerator(CompilationInfo* info) |
generator_resume_points_(info->literal()->yield_count(), info->zone()), |
generator_state_(), |
loop_depth_(0), |
+ catch_prediction_(HandlerTable::UNCAUGHT), |
home_object_symbol_(info->isolate()->factory()->home_object_symbol()), |
iterator_symbol_(info->isolate()->factory()->iterator_symbol()), |
prototype_string_(info->isolate()->factory()->prototype_string()), |
@@ -696,14 +908,6 @@ void BytecodeGenerator::GenerateBytecode(uintptr_t stack_limit) { |
GenerateBytecodeBody(); |
} |
- // In generator functions, we may not have visited every yield in the AST |
- // since we skip some obviously dead code. Hence the generated bytecode may |
- // contain jumps to unbound labels (resume points that will never be used). |
- // We bind these now. |
- for (auto& label : generator_resume_points_) { |
- if (!label.is_bound()) builder()->Bind(&label); |
- } |
- |
// Emit an implicit return instruction in case control flow can fall off the |
// end of the function without an explicit return being present on all paths. |
if (builder()->RequiresImplicitReturn()) { |
@@ -736,11 +940,234 @@ void BytecodeGenerator::GenerateBytecodeBody() { |
// Emit initializing assignments for module namespace imports (if any). |
VisitModuleNamespaceImports(); |
+ FunctionLiteral* literal = info()->literal(); |
+ |
// Perform a stack-check before the body. |
- builder()->StackCheck(info()->literal()->start_position()); |
+ builder()->StackCheck(literal->start_position()); |
+ |
+ // Build assignment to variable <function name> if function is a named |
+ // expression and the variable is used. |
+ if (literal->is_named_expression()) { |
+ Variable* function_var = scope()->function_var(); |
+ if (function_var != nullptr && !function_var->IsUnallocated()) { |
+ builder()->LoadAccumulatorWithRegister(Register::function_closure()); |
+ BuildVariableAssignment(function_var, Token::INIT, |
+ FeedbackVectorSlot::Invalid(), |
+ HoleCheckMode::kElided); |
+ } |
+ } |
+ if (IsAsyncFunction(literal->kind())) { |
+ return GenerateBytecodeBodyForAsyncFunction(literal); |
+ } |
+ if (IsGeneratorFunction(literal->kind())) { |
+ return GenerateBytecodeBodyForGenerator(literal); |
+ } |
+ if (IsModule(literal->kind())) { |
+ return GenerateBytecodeBodyForModule(literal); |
+ } |
+ |
+ Block* parameter_init_block = literal->parameter_init_block(); |
// Visit statements in the function body. |
- VisitStatements(info()->literal()->body()); |
+ if (parameter_init_block != nullptr) { |
+ VisitBlock(parameter_init_block); |
+ } |
+ VisitStatements(literal->body()); |
+} |
+ |
+void BytecodeGenerator::BuildAllocateAndStoreJSGeneratorObject( |
+ FunctionLiteral* literal, BuildJSGeneratorObject tag) { |
+ RegisterAllocationScope register_scope(this); |
+ RegisterList args = register_allocator()->NewRegisterList(2); |
+ builder()->MoveRegister(Register::function_closure(), args[0]); |
+ |
+ int yield_position = literal->position(); |
+ builder()->SetExpressionPosition(yield_position); |
+ |
+ if (IsArrowFunction(literal->kind())) { |
+ // Lexical `this` |
+ builder()->LoadUndefined().StoreAccumulatorInRegister(args[1]); |
+ } else { |
+ // Receiver parameter |
+ builder()->MoveRegister(builder()->Parameter(0), args[1]); |
+ } |
+ builder()->CallRuntime(Runtime::kCreateJSGeneratorObject, args); |
+ |
+ Variable* var_generator = scope()->generator_object_var(); |
+ DCHECK_NOT_NULL(var_generator); |
+ |
+ switch (tag) { |
+ case BuildJSGeneratorObject::kInitialYield: { |
+ Register generator = args[0]; |
+ builder()->StoreAccumulatorInRegister(generator); |
+ BuildVariableAssignment(var_generator, Token::INIT, |
+ FeedbackVectorSlot::Invalid(), |
+ HoleCheckMode::kElided); |
+ BuildYield(0, generator, generator); |
+ BuildYieldResumePoint(0, Yield::kOnExceptionThrow, generator, |
+ yield_position); |
+ break; |
+ } |
+ case BuildJSGeneratorObject::kNoInitialYield: { |
+ BuildVariableAssignment(var_generator, Token::INIT, |
+ FeedbackVectorSlot::Invalid(), |
+ HoleCheckMode::kElided); |
+ break; |
+ } |
+ } |
+} |
+ |
+void BytecodeGenerator::BuildAllocateAndStoreJSPromise() { |
+ Variable* var_promise = scope()->promise_var(); |
+ DCHECK_NOT_NULL(var_promise); |
+ |
+ RegisterAllocationScope register_scope(this); |
+ RegisterList args = register_allocator()->NewRegisterList(1); |
+ builder()->LoadUndefined().StoreAccumulatorInRegister(args[0]).CallJSRuntime( |
+ Context::ASYNC_FUNCTION_PROMISE_CREATE_INDEX, args); |
+ BuildVariableAssignment(var_promise, Token::INIT, |
+ FeedbackVectorSlot::Invalid(), |
+ HoleCheckMode::kElided); |
+} |
+ |
+void BytecodeGenerator::BindUnboundGeneratorResumePoints() { |
+ DCHECK(IsResumableFunction(info()->literal()->kind())); |
+ for (auto& label : generator_resume_points_) { |
+ if (!label.is_bound()) builder()->Bind(&label); |
+ } |
+} |
+ |
+void BytecodeGenerator::GenerateBytecodeBodyForGenerator( |
+ FunctionLiteral* literal) { |
+ DCHECK(IsGeneratorFunction(literal->kind())); |
+ |
+ // Desugar parameters before generator is allocated. |
+ Block* const parameter_init_block = literal->parameter_init_block(); |
+ if (parameter_init_block != nullptr) { |
+ // Initialize non-simple parameters before initial yield. |
+ VisitBlock(parameter_init_block); |
+ } |
+ |
+ { |
+ RegisterAllocationScope register_scope(this); |
+ SimpleTryFinallyBuilder try_control_builder(this); |
+ { // Try ... |
+ TryBlockBuilderForFinally try_builder(try_control_builder); |
+ BuildAllocateAndStoreJSGeneratorObject( |
+ literal, BuildJSGeneratorObject::kInitialYield); |
+ VisitStatements(literal->body()); |
+ } |
+ |
+ { // Finally ... |
+ FinallyBlockBuilder finally_builder(try_control_builder); |
+ Variable* var_generator = scope()->generator_object_var(); |
+ DCHECK_NOT_NULL(var_generator); |
+ |
+ RegisterAllocationScope register_scope(this); |
+ BuildVariableLoad(var_generator, FeedbackVectorSlot::Invalid(), |
+ HoleCheckMode::kElided); |
+ Register generator = register_allocator()->NewRegister(); |
+ builder()->StoreAccumulatorInRegister(generator).CallRuntime( |
+ Runtime::kInlineGeneratorClose, generator); |
+ } |
+ } |
+ |
+ // In generator functions, we may not have visited every yield in the AST |
+ // since we skip some obviously dead code. Hence the generated bytecode may |
+ // contain jumps to unbound labels (resume points that will never be used). |
+ // We bind these now. |
+ BindUnboundGeneratorResumePoints(); |
+ |
+ if (builder()->RequiresImplicitReturn()) { |
+ // Unreachable, but required for BytecodeArrayBuilder |
+ builder()->LoadUndefined().Return(); |
+ } |
+ DCHECK(!builder()->RequiresImplicitReturn()); |
+} |
+ |
+void BytecodeGenerator::GenerateBytecodeBodyForModule( |
+ FunctionLiteral* literal) { |
+ DCHECK(IsModule(literal->kind())); |
+ |
+ // Modules do not have non-simple parameters |
+ DCHECK_NULL(literal->parameter_init_block()); |
+ |
+ BuildAllocateAndStoreJSGeneratorObject(literal, |
+ BuildJSGeneratorObject::kInitialYield); |
+ VisitStatements(literal->body()); |
+ |
+ // In modules, we may not have visited every yield in the AST |
+ // since we skip some obviously dead code. Hence the generated bytecode may |
+ // contain jumps to unbound labels (resume points that will never be used). |
+ // We bind these now. |
+ BindUnboundGeneratorResumePoints(); |
+} |
+ |
+void BytecodeGenerator::GenerateBytecodeBodyForAsyncFunction( |
+ FunctionLiteral* literal) { |
+ DCHECK(IsAsyncFunction(literal->kind())); |
+ |
+ BuildAllocateAndStoreJSPromise(); |
+ |
+ { |
+ RegisterAllocationScope register_scope(this); |
+ SimpleTryCatchBuilder try_control_builder(this, HandlerTable::ASYNC_AWAIT); |
+ { |
+ TryBlockBuilderForCatch try_block(try_control_builder); |
+ |
+ Block* const parameter_init_block = literal->parameter_init_block(); |
+ if (parameter_init_block != nullptr) { |
+ // Initialize non-simple parameters if necessary, reject Promise in |
+ // case of exception |
+ RegisterAllocationScope register_scope(this); |
+ VisitBlock(parameter_init_block); |
+ } |
+ |
+ BuildAllocateAndStoreJSGeneratorObject( |
+ literal, BuildJSGeneratorObject::kNoInitialYield); |
+ |
+ // Finish generating function body |
+ VisitStatements(literal->body()); |
+ } |
+ |
+ { |
+ CatchBlockBuilder catch_block(try_control_builder); |
+ RegisterList args = register_allocator()->NewRegisterList(4); |
+ Register promise = args[1]; |
+ Register exception = args[2]; |
+ |
+ builder() |
+ ->StoreAccumulatorInRegister(exception) |
+ .LoadTheHole() |
+ .SetPendingMessage(); |
+ |
+ Variable* var_promise = scope()->promise_var(); |
+ DCHECK_NOT_NULL(var_promise); |
+ BuildVariableLoad(var_promise, FeedbackVectorSlot::Invalid(), |
+ HoleCheckMode::kElided); |
+ builder() |
+ ->StoreAccumulatorInRegister(promise) |
+ .LoadUndefined() |
+ .StoreAccumulatorInRegister(args[0]) |
+ .LoadFalse() |
+ .StoreAccumulatorInRegister(args[3]) |
+ .CallJSRuntime(Context::PROMISE_INTERNAL_REJECT_INDEX, args) |
+ .LoadAccumulatorWithRegister(promise) |
+ .Return(); |
+ } |
+ } |
+ |
+ // In async functions, we may not have visited every yield in the AST |
+ // since we skip some obviously dead code. Hence the generated bytecode may |
+ // contain jumps to unbound labels (resume points that will never be used). |
+ // We bind these now. |
+ BindUnboundGeneratorResumePoints(); |
+ |
+ if (builder()->RequiresImplicitReturn()) { |
+ // Unreachable, but required for BytecodeArrayBuilder |
+ builder()->LoadUndefined().Return(); |
+ } |
+ DCHECK(!builder()->RequiresImplicitReturn()); |
} |
void BytecodeGenerator::BuildIndexedJump(Register index, size_t start_index, |
@@ -1335,24 +1762,16 @@ void BytecodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { |
} |
void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { |
- TryCatchBuilder try_control_builder(builder(), stmt->catch_prediction()); |
- |
- // Preserve the context in a dedicated register, so that it can be restored |
- // when the handler is entered by the stack-unwinding machinery. |
- // TODO(mstarzinger): Be smarter about register allocation. |
- Register context = register_allocator()->NewRegister(); |
- builder()->MoveRegister(Register::current_context(), context); |
- |
- // Evaluate the try-block inside a control scope. This simulates a handler |
- // that is intercepting 'throw' control commands. |
- try_control_builder.BeginTry(context); |
+ SimpleTryCatchBuilder try_control_builder(this, stmt->catch_prediction()); |
{ |
- ControlScopeForTryCatch scope(this, &try_control_builder); |
+ TryBlockBuilderForCatch try_block(try_control_builder); |
Visit(stmt->try_block()); |
} |
- try_control_builder.EndTry(); |
+ // Evaluate the catch-block. |
// Create a catch scope that binds the exception. |
+ CatchBlockBuilder catch_block(try_control_builder); |
+ Register context = try_control_builder.context(); |
BuildNewLocalCatchContext(stmt->variable(), stmt->scope()); |
builder()->StoreAccumulatorInRegister(context); |
@@ -1363,72 +1782,18 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { |
// Load the catch context into the accumulator. |
builder()->LoadAccumulatorWithRegister(context); |
- |
- // Evaluate the catch-block. |
VisitInScope(stmt->catch_block(), stmt->scope()); |
- try_control_builder.EndCatch(); |
} |
void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
- TryFinallyBuilder try_control_builder(builder(), stmt->catch_prediction()); |
- |
- // We keep a record of all paths that enter the finally-block to be able to |
- // dispatch to the correct continuation point after the statements in the |
- // finally-block have been evaluated. |
- // |
- // The try-finally construct can enter the finally-block in three ways: |
- // 1. By exiting the try-block normally, falling through at the end. |
- // 2. By exiting the try-block with a function-local control flow transfer |
- // (i.e. through break/continue/return statements). |
- // 3. By exiting the try-block with a thrown exception. |
- // |
- // The result register semantics depend on how the block was entered: |
- // - ReturnStatement: It represents the return value being returned. |
- // - ThrowStatement: It represents the exception being thrown. |
- // - BreakStatement/ContinueStatement: Undefined and not used. |
- // - Falling through into finally-block: Undefined and not used. |
- Register token = register_allocator()->NewRegister(); |
- Register result = register_allocator()->NewRegister(); |
- ControlScope::DeferredCommands commands(this, token, result); |
- |
- // Preserve the context in a dedicated register, so that it can be restored |
- // when the handler is entered by the stack-unwinding machinery. |
- // TODO(mstarzinger): Be smarter about register allocation. |
- Register context = register_allocator()->NewRegister(); |
- builder()->MoveRegister(Register::current_context(), context); |
- |
- // Evaluate the try-block inside a control scope. This simulates a handler |
- // that is intercepting all control commands. |
- try_control_builder.BeginTry(context); |
+ SimpleTryFinallyBuilder try_control_builder(this); |
{ |
- ControlScopeForTryFinally scope(this, &try_control_builder, &commands); |
+ TryBlockBuilderForFinally try_block(try_control_builder); |
Visit(stmt->try_block()); |
} |
- try_control_builder.EndTry(); |
- |
- // Record fall-through and exception cases. |
- commands.RecordFallThroughPath(); |
- try_control_builder.LeaveTry(); |
- try_control_builder.BeginHandler(); |
- commands.RecordHandlerReThrowPath(); |
- |
- // Pending message object is saved on entry. |
- try_control_builder.BeginFinally(); |
- Register message = context; // Reuse register. |
- // Clear message object as we enter the finally block. |
- builder()->LoadTheHole().SetPendingMessage().StoreAccumulatorInRegister( |
- message); |
- |
- // Evaluate the finally-block. |
+ FinallyBlockBuilder finally_block(try_control_builder); |
Visit(stmt->finally_block()); |
- try_control_builder.EndFinally(); |
- |
- // Pending message object is restored on exit. |
- builder()->LoadAccumulatorWithRegister(message).SetPendingMessage(); |
- |
- // Dynamic dispatch after the finally-block. |
- commands.ApplyDeferredCommands(); |
} |
void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { |
@@ -2287,20 +2652,31 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { |
} |
} |
-void BytecodeGenerator::VisitYield(Yield* expr) { |
- builder()->SetExpressionPosition(expr); |
- Register value = VisitForRegisterValue(expr->expression()); |
- |
- Register generator = VisitForRegisterValue(expr->generator_object()); |
+void BytecodeGenerator::BuildYield(int yield_id, Register generator, |
+ Register value) { |
+ DCHECK_GE(yield_id, 0); |
+ DCHECK_LT(yield_id, generator_resume_points_.size()); |
+ DCHECK(generator.is_valid()); |
+ DCHECK(value.is_valid()); |
// Save context, registers, and state. Then return. |
builder() |
- ->LoadLiteral(Smi::FromInt(expr->yield_id())) |
+ ->LoadLiteral(Smi::FromInt(yield_id)) |
.SuspendGenerator(generator) |
.LoadAccumulatorWithRegister(value) |
.Return(); // Hard return (ignore any finally blocks). |
+} |
- builder()->Bind(&(generator_resume_points_[expr->yield_id()])); |
+void BytecodeGenerator::BuildYieldResumePoint(int yield_id, |
+ Yield::OnException on_exception, |
+ Register generator, |
+ int position) { |
+ DCHECK_GE(yield_id, 0); |
+ DCHECK_LT(yield_id, generator_resume_points_.size()); |
+ DCHECK(generator.is_valid()); |
+ |
+ DCHECK(!generator_resume_points_[yield_id].is_bound()); |
+ builder()->Bind(&(generator_resume_points_[yield_id])); |
// Upon resume, we continue here. |
{ |
@@ -2328,6 +2704,7 @@ void BytecodeGenerator::VisitYield(Yield* expr) { |
BytecodeLabel resume_with_return; |
BytecodeLabel resume_with_throw; |
+ // TODO(caitp): Don't generate `resume_with_return` block for non-generators |
builder() |
->LoadLiteral(Smi::FromInt(JSGeneratorObject::kNext)) |
.CompareOperation(Token::EQ_STRICT, resume_mode) |
@@ -2349,9 +2726,9 @@ void BytecodeGenerator::VisitYield(Yield* expr) { |
} |
builder()->Bind(&resume_with_throw); |
- builder()->SetExpressionPosition(expr); |
+ builder()->SetExpressionPosition(position); |
builder()->LoadAccumulatorWithRegister(input); |
- if (expr->rethrow_on_exception()) { |
+ if (on_exception == Yield::kOnExceptionRethrow) { |
builder()->ReThrow(); |
} else { |
builder()->Throw(); |
@@ -2362,6 +2739,17 @@ void BytecodeGenerator::VisitYield(Yield* expr) { |
} |
} |
+void BytecodeGenerator::VisitYield(Yield* expr) { |
+ Register generator = VisitForRegisterValue(expr->generator_object()); |
+ |
+ builder()->SetExpressionPosition(expr); |
+ Register value = VisitForRegisterValue(expr->expression()); |
+ |
+ BuildYield(expr->yield_id(), generator, value); |
+ BuildYieldResumePoint(expr->yield_id(), expr->on_exception(), generator, |
+ expr->position()); |
+} |
+ |
void BytecodeGenerator::VisitThrow(Throw* expr) { |
VisitForAccumulatorValue(expr->exception()); |
builder()->SetExpressionPosition(expr); |