Index: src/interpreter/bytecode-generator.cc |
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc |
index cef7d2753596d410f5b34ea0d54502f4520716dd..b7195dd73c682f4889dbb2994779f45d7e68dedb 100644 |
--- a/src/interpreter/bytecode-generator.cc |
+++ b/src/interpreter/bytecode-generator.cc |
@@ -601,6 +601,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()), |
@@ -680,9 +681,13 @@ void BytecodeGenerator::GenerateBytecode(uintptr_t stack_limit) { |
ControlScopeForTopLevel control(this); |
RegisterAllocationScope register_scope(this); |
- |
- if (IsResumableFunction(info()->literal()->kind())) { |
+ if (IsResumableFunction(info()->literal()->kind()) || |
+ scope()->is_module_scope()) { |
+ generator_object_ = register_allocator()->NewRegister(); |
generator_state_ = register_allocator()->NewRegister(); |
+ if (IsAsyncFunction(info()->literal()->kind())) { |
+ promise_ = register_allocator()->NewRegister(); |
+ } |
VisitGeneratorPrologue(); |
} |
@@ -714,6 +719,12 @@ void BytecodeGenerator::GenerateBytecode(uintptr_t stack_limit) { |
} |
void BytecodeGenerator::GenerateBytecodeBody() { |
+ const bool kIsModule = scope()->is_module_scope(); |
+ const bool kIsGeneratorFunction = |
+ IsGeneratorFunction(info()->literal()->kind()); |
+ const bool kIsResumableFunction = |
+ IsResumableFunction(info()->literal()->kind()); |
+ |
// Build the arguments object if it is used. |
VisitArgumentsObject(scope()->arguments()); |
@@ -739,7 +750,123 @@ void BytecodeGenerator::GenerateBytecodeBody() { |
// Perform a stack-check before the body. |
builder()->StackCheck(info()->literal()->start_position()); |
+ // Build assignment to variable <function name> if function is a named |
+ // expression and the variable is used. |
+ if (info()->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); |
+ } |
+ } |
+ |
+ Block* parameter_init_block = info()->literal()->parameter_init_block(); |
+ |
+ if (kIsResumableFunction || kIsModule) { |
+ if (kIsResumableFunction) { |
+ // No need to store `undefined` in the generator register for Modules, |
+ // as there is no finally block. |
+ builder()->LoadUndefined().StoreAccumulatorInRegister(generator_object_); |
+ } |
+ |
+ auto try_block = [=]() { |
+ // Try ... |
+ if (parameter_init_block != nullptr) { |
+ // Initialize non-simple parameters before initial yield. |
+ VisitBlock(parameter_init_block); |
+ } |
+ // Allocate generator object |
+ RegisterAllocationScope register_scope(this); |
+ RegisterList args = register_allocator()->NewRegisterList(2); |
+ builder()->MoveRegister(Register::function_closure(), args[0]); |
+ |
+ int yield_position = info()->literal()->position(); |
+ builder()->SetExpressionPosition(yield_position); |
+ |
+ if (IsArrowFunction(info()->literal()->kind())) { |
+ builder()->LoadUndefined().StoreAccumulatorInRegister(args[1]); |
+ } else { |
+ builder()->MoveRegister(builder()->Receiver(), args[1]); |
+ } |
+ builder() |
+ ->CallRuntime(Runtime::kCreateJSGeneratorObject, args) |
+ .StoreAccumulatorInRegister(generator_object_); |
+ |
+ if (kIsGeneratorFunction || kIsModule) { |
+ BuildYield(0, Yield::kOnExceptionThrow, generator_object_, |
+ yield_position); |
+ } |
+ VisitStatements(info()->literal()->body()); |
+ |
+ if (builder()->RequiresImplicitReturn()) { |
+ if (IsGeneratorFunction(info()->literal()->kind())) { |
+ // Add implicit `return { value: undefined, done: true }` |
+ builder() |
+ ->LoadUndefined() |
+ .StoreAccumulatorInRegister(args[0]) |
+ .LoadTrue() |
+ .StoreAccumulatorInRegister(args[1]) |
+ .CallRuntime(Runtime::kInlineCreateIterResultObject, args); |
+ execution_control()->ReturnAccumulator(); |
+ } |
+ } |
+ }; |
+ |
+ auto catch_block = [=](Register context) { |
+ // Catch ... |
+ if (IsAsyncFunction(info()->literal()->kind())) { |
+ RegisterAllocationScope register_scope(this); |
+ RegisterList args = register_allocator()->NewRegisterList(4); |
+ Register exception = args[2]; |
+ |
+ builder() |
+ ->StoreAccumulatorInRegister(exception) |
+ .LoadTheHole() |
+ .SetPendingMessage() |
+ .LoadUndefined() |
+ .StoreAccumulatorInRegister(args[0]) |
+ .MoveRegister(promise_, args[1]) |
+ .LoadFalse() |
+ .StoreAccumulatorInRegister(args[3]) |
+ .CallJSRuntime(Context::PROMISE_INTERNAL_REJECT_INDEX, args) |
+ .LoadAccumulatorWithRegister(promise_) |
+ .Return(); // No need to close the generator for async functions |
+ } |
+ }; |
+ |
+ auto finally_block = [=]() { |
+ // Finally ... |
+ if (kIsGeneratorFunction) { |
+ BytecodeLabel if_generatorundefined; |
+ builder()->LoadAccumulatorWithRegister(generator_object_); |
+ builder()->JumpIfUndefined(&if_generatorundefined); |
+ builder()->CallRuntime(Runtime::kInlineGeneratorClose, |
+ generator_object_); |
+ builder()->Bind(&if_generatorundefined); |
+ } |
+ }; |
+ |
+ const bool kNeedsTryCatch = IsAsyncFunction(info()->literal()->kind()); |
+ if (kNeedsTryCatch) { |
+ // Currently, only async functions need a special behaviour for top-level |
+ // exceptions |
+ BuildTryCatchFinally(HandlerTable::ASYNC_AWAIT, try_block, catch_block, |
+ finally_block); |
+ } else if (kIsGeneratorFunction) { |
+ BuildTryFinally(try_block, finally_block); |
+ } else { |
+ DCHECK(kIsModule); |
+ try_block(); |
+ } |
+ return; |
+ } |
+ |
// Visit statements in the function body. |
+ if (parameter_init_block != nullptr) { |
+ VisitBlock(parameter_init_block); |
+ } |
VisitStatements(info()->literal()->body()); |
} |
@@ -819,6 +946,18 @@ void BytecodeGenerator::VisitGeneratorPrologue() { |
->Bind(®ular_call) |
.LoadLiteral(Smi::FromInt(JSGeneratorObject::kGeneratorExecuting)) |
.StoreAccumulatorInRegister(generator_state_); |
+ |
+ if (IsAsyncFunction(info()->literal()->kind())) { |
+ // Create initial async function promise |
+ RegisterAllocationScope register_scope(this); |
+ RegisterList args = register_allocator()->NewRegisterList(1); |
+ builder() |
+ ->LoadUndefined() |
+ .StoreAccumulatorInRegister(args[0]) |
+ .CallJSRuntime(Context::ASYNC_FUNCTION_PROMISE_CREATE_INDEX, args) |
+ .StoreAccumulatorInRegister(promise_); |
+ } |
+ |
// This is a regular call. Fall through to the ordinary function prologue, |
// after which we will run into the generator object creation and other extra |
// code inserted by the parser. |
@@ -1334,8 +1473,11 @@ void BytecodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { |
loop_builder.EndLoop(); |
} |
-void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { |
- TryCatchBuilder try_control_builder(builder(), stmt->catch_prediction()); |
+void BytecodeGenerator::BuildTryCatch( |
+ HandlerTable::CatchPrediction catch_prediction, |
+ std::function<void()> build_try, |
+ std::function<void(Register)> build_catch) { |
+ TryCatchBuilder try_control_builder(builder(), 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. |
@@ -1348,29 +1490,43 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { |
try_control_builder.BeginTry(context); |
{ |
ControlScopeForTryCatch scope(this, &try_control_builder); |
- Visit(stmt->try_block()); |
+ if (catch_prediction == HandlerTable::UNCAUGHT) { |
+ catch_prediction = catch_prediction_; |
+ } |
+ ScopedAssignment<HandlerTable::CatchPrediction> prediction( |
+ catch_prediction_, catch_prediction); |
+ build_try(); |
} |
try_control_builder.EndTry(); |
- // Create a catch scope that binds the exception. |
- BuildNewLocalCatchContext(stmt->variable(), stmt->scope()); |
- builder()->StoreAccumulatorInRegister(context); |
+ // Evaluate the catch-block. |
+ build_catch(context); |
+ try_control_builder.EndCatch(); |
+} |
- // If requested, clear message object as we enter the catch block. |
- if (stmt->clear_pending_message()) { |
- builder()->LoadTheHole().SetPendingMessage(); |
- } |
+void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { |
+ BuildTryCatch(stmt->catch_prediction(), [=]() { Visit(stmt->try_block()); }, |
+ [=](Register context) { |
+ // Create a catch scope that binds the exception. |
+ BuildNewLocalCatchContext(stmt->variable(), stmt->scope()); |
+ builder()->StoreAccumulatorInRegister(context); |
- // Load the catch context into the accumulator. |
- builder()->LoadAccumulatorWithRegister(context); |
+ // If requested, clear message object as we enter the catch |
+ // block. |
+ if (stmt->clear_pending_message()) { |
+ builder()->LoadTheHole().SetPendingMessage(); |
+ } |
- // Evaluate the catch-block. |
- VisitInScope(stmt->catch_block(), stmt->scope()); |
- try_control_builder.EndCatch(); |
+ // Load the catch context into the accumulator. |
+ builder()->LoadAccumulatorWithRegister(context); |
+ |
+ VisitInScope(stmt->catch_block(), stmt->scope()); |
+ }); |
} |
-void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
- TryFinallyBuilder try_control_builder(builder(), stmt->catch_prediction()); |
+void BytecodeGenerator::BuildTryFinally(std::function<void()> build_try, |
+ std::function<void()> build_finally) { |
+ TryFinallyBuilder try_control_builder(builder(), 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 |
@@ -1402,7 +1558,7 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
try_control_builder.BeginTry(context); |
{ |
ControlScopeForTryFinally scope(this, &try_control_builder, &commands); |
- Visit(stmt->try_block()); |
+ build_try(); |
} |
try_control_builder.EndTry(); |
@@ -1421,7 +1577,7 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
message); |
// Evaluate the finally-block. |
- Visit(stmt->finally_block()); |
+ build_finally(); |
try_control_builder.EndFinally(); |
// Pending message object is restored on exit. |
@@ -1431,6 +1587,20 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
commands.ApplyDeferredCommands(); |
} |
+void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
+ BuildTryFinally([=]() { Visit(stmt->try_block()); }, |
+ [=]() { Visit(stmt->finally_block()); }); |
+} |
+ |
+void BytecodeGenerator::BuildTryCatchFinally( |
+ HandlerTable::CatchPrediction catch_prediction, |
+ std::function<void()> build_try, std::function<void(Register)> build_catch, |
+ std::function<void()> build_finally) { |
+ BuildTryFinally( |
+ [=]() { BuildTryCatch(catch_prediction, build_try, build_catch); }, |
+ build_finally); |
+} |
+ |
void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { |
builder()->SetStatementPosition(stmt); |
builder()->Debugger(); |
@@ -1991,16 +2161,34 @@ void BytecodeGenerator::BuildVariableLoadForAccumulatorValue( |
} |
void BytecodeGenerator::BuildReturn() { |
- if (FLAG_trace) { |
- RegisterAllocationScope register_scope(this); |
- Register result = register_allocator()->NewRegister(); |
- // Runtime returns {result} value, preserving accumulator. |
- builder()->StoreAccumulatorInRegister(result).CallRuntime( |
- Runtime::kTraceExit, result); |
+ if (IsAsyncFunction(info()->literal()->kind())) { |
+ RegisterList args = register_allocator()->NewRegisterList(3); |
+ Register receiver = args[0]; |
+ Register promise = args[1]; |
+ Register return_value = args[2]; |
+ |
+ builder() |
+ ->StoreAccumulatorInRegister(return_value) |
+ .LoadUndefined() |
+ .StoreAccumulatorInRegister(receiver) |
+ .MoveRegister(promise_, promise) |
+ .CallJSRuntime(Context::PROMISE_RESOLVE_INDEX, args) |
+ .LoadAccumulatorWithRegister(promise); |
} |
+ |
+ BuildTraceExit(); |
builder()->Return(); |
} |
+void BytecodeGenerator::BuildTraceExit() { |
+ if (!FLAG_trace) return; |
+ RegisterAllocationScope register_scope(this); |
+ Register result = register_allocator()->NewRegister(); |
+ // Runtime returns {result} value, preserving accumulator. |
+ builder()->StoreAccumulatorInRegister(result).CallRuntime(Runtime::kTraceExit, |
+ result); |
+} |
+ |
void BytecodeGenerator::BuildReThrow() { builder()->ReThrow(); } |
void BytecodeGenerator::BuildAbort(BailoutReason bailout_reason) { |
@@ -2287,20 +2475,16 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { |
} |
} |
-void BytecodeGenerator::VisitYield(Yield* expr) { |
- builder()->SetExpressionPosition(expr); |
- Register value = VisitForRegisterValue(expr->expression()); |
+void BytecodeGenerator::BuildYieldResumePoint(int yield_id, |
+ Yield::OnException on_exception, |
+ int position) { |
+ DCHECK_GE(yield_id, 0); |
+ DCHECK_LT(yield_id, generator_resume_points_.size()); |
- Register generator = VisitForRegisterValue(expr->generator_object()); |
- |
- // Save context, registers, and state. Then return. |
- builder() |
- ->LoadLiteral(Smi::FromInt(expr->yield_id())) |
- .SuspendGenerator(generator) |
- .LoadAccumulatorWithRegister(value) |
- .Return(); // Hard return (ignore any finally blocks). |
+ Register generator = generator_object_; |
+ DCHECK(generator.is_valid()); |
- builder()->Bind(&(generator_resume_points_[expr->yield_id()])); |
+ builder()->Bind(&(generator_resume_points_[yield_id])); |
// Upon resume, we continue here. |
{ |
@@ -2349,9 +2533,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 +2546,72 @@ void BytecodeGenerator::VisitYield(Yield* expr) { |
} |
} |
+void BytecodeGenerator::BuildYield(int yield_id, |
+ Yield::OnException on_exception, |
+ Register value, int position) { |
+ DCHECK_GE(yield_id, 0); |
+ DCHECK_LT(yield_id, generator_resume_points_.size()); |
+ |
+ Register generator = generator_object_; |
+ DCHECK(generator.is_valid()); |
+ |
+ // Save context, registers, and state. Then return. |
+ builder() |
+ ->LoadLiteral(Smi::FromInt(yield_id)) |
+ .SuspendGenerator(generator) |
+ .LoadAccumulatorWithRegister(value) |
+ .Return(); // Hard return (ignore any finally blocks). |
+ |
+ BuildYieldResumePoint(yield_id, on_exception, position); |
+} |
+ |
+void BytecodeGenerator::BuildAwait(int yield_id, |
+ Yield::OnException on_exception, |
+ Register value, int position) { |
+ DCHECK_GE(yield_id, 0); |
+ DCHECK_LT(yield_id, generator_resume_points_.size()); |
+ |
+ Register generator = generator_object_; |
+ DCHECK(generator.is_valid()); |
+ |
+ int context_index = Context::ASYNC_FUNCTION_AWAIT_CAUGHT_INDEX; |
+ if (catch_prediction_ == HandlerTable::ASYNC_AWAIT) { |
+ context_index = Context::ASYNC_FUNCTION_AWAIT_UNCAUGHT_INDEX; |
+ } |
+ |
+ { |
+ RegisterAllocationScope register_scope(this); |
+ RegisterList args = register_allocator()->NewRegisterList(4); |
+ builder()->SetStatementPosition(position); |
+ builder() |
+ ->LoadUndefined() |
+ .StoreAccumulatorInRegister(args[0]) |
+ .MoveRegister(generator, args[1]) |
+ .MoveRegister(value, args[2]) |
+ .MoveRegister(promise_, args[3]) |
+ .CallJSRuntime(context_index, args) |
+ .LoadLiteral(Smi::FromInt(yield_id)) |
+ .SuspendGenerator(generator) |
+ .LoadAccumulatorWithRegister(promise_) |
+ .Return(); // Hard return (ignore any finally blocks) |
+ } |
+ |
+ BuildYieldResumePoint(yield_id, on_exception, kNoSourcePosition); |
+} |
+ |
+void BytecodeGenerator::VisitYield(Yield* expr) { |
+ if (expr->yield_type() == Yield::kAwait) { |
+ builder()->SetExpressionPosition(kNoSourcePosition); |
+ builder()->SetStatementPosition(expr->expression()->position()); |
+ Register value = VisitForRegisterValue(expr->expression()); |
+ BuildAwait(expr->yield_id(), expr->on_exception(), value, expr->position()); |
+ } else { |
+ builder()->SetExpressionPosition(expr); |
+ Register value = VisitForRegisterValue(expr->expression()); |
+ BuildYield(expr->yield_id(), expr->on_exception(), value, expr->position()); |
+ } |
+} |
+ |
void BytecodeGenerator::VisitThrow(Throw* expr) { |
VisitForAccumulatorValue(expr->exception()); |
builder()->SetExpressionPosition(expr); |
@@ -2934,6 +3184,20 @@ void BytecodeGenerator::VisitEmptyParentheses(EmptyParentheses* expr) { |
UNREACHABLE(); |
} |
+void BytecodeGenerator::VisitInternalVariable(InternalVariable* expr) { |
+ switch (expr->type()) { |
+ case InternalVariable::kGeneratorObject: |
+ DCHECK(IsGeneratorFunction(info()->literal()->kind())); |
+ builder()->LoadAccumulatorWithRegister(generator_object_); |
+ return; |
+ |
+ default: |
+ break; |
+ } |
+ |
+ UNREACHABLE(); |
+} |
+ |
void BytecodeGenerator::VisitGetIterator(GetIterator* expr) { |
FeedbackVectorSlot load_slot = expr->IteratorPropertyFeedbackSlot(); |
FeedbackVectorSlot call_slot = expr->IteratorCallFeedbackSlot(); |