Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(151)

Unified Diff: src/interpreter/bytecode-generator.cc

Issue 2654423004: [async-functions] support await expressions in finally statements (Closed)
Patch Set: I'd like to build with -Wunused-variables locally, but how!? Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/interpreter/bytecode-generator.h ('k') | src/parsing/parser.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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(&regular_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();
« no previous file with comments | « src/interpreter/bytecode-generator.h ('k') | src/parsing/parser.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698