| Index: src/interpreter/bytecode-generator.cc
|
| diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
|
| index f543e8c6e5fd7f3414bd93ad9adcfdefffecd417..753e9685d923a291f48548b5bfe20ea19362ce96 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,125 @@ 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, Yield::kNormal,
|
| + 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
|
| + HandlerTable::CatchPrediction catch_prediction =
|
| + HandlerTable::ASYNC_AWAIT;
|
| + BuildTryCatchFinally(HandlerTable::UNCAUGHT, catch_prediction, try_block,
|
| + catch_block, finally_block);
|
| + } else if (kIsGeneratorFunction) {
|
| + BuildTryFinally(HandlerTable::UNCAUGHT, 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 +948,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 +1475,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 +1492,41 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
| try_control_builder.BeginTry(context);
|
| {
|
| ControlScopeForTryCatch scope(this, &try_control_builder);
|
| - Visit(stmt->try_block());
|
| + 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(
|
| + HandlerTable::CatchPrediction catch_prediction,
|
| + 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,22 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
| commands.ApplyDeferredCommands();
|
| }
|
|
|
| +void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
| + BuildTryFinally(stmt->catch_prediction(), [=]() { Visit(stmt->try_block()); },
|
| + [=]() { Visit(stmt->finally_block()); });
|
| +}
|
| +
|
| +void BytecodeGenerator::BuildTryCatchFinally(
|
| + HandlerTable::CatchPrediction outer_catch_prediction,
|
| + HandlerTable::CatchPrediction inner_catch_prediction,
|
| + std::function<void()> build_try, std::function<void(Register)> build_catch,
|
| + std::function<void()> build_finally) {
|
| + BuildTryFinally(
|
| + outer_catch_prediction,
|
| + [=]() { BuildTryCatch(inner_catch_prediction, build_try, build_catch); },
|
| + build_finally);
|
| +}
|
| +
|
| void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
|
| builder()->SetStatementPosition(stmt);
|
| builder()->Debugger();
|
| @@ -1989,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) {
|
| @@ -2285,20 +2475,46 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
|
| }
|
| }
|
|
|
| -void BytecodeGenerator::VisitYield(Yield* expr) {
|
| - builder()->SetExpressionPosition(expr);
|
| - Register value = VisitForRegisterValue(expr->expression());
|
| +void BytecodeGenerator::BuildYield(int yield_id,
|
| + Yield::OnException on_exception,
|
| + Yield::YieldType yield_type, Register value,
|
| + int position) {
|
| + DCHECK_GE(yield_id, 0);
|
| + DCHECK_LT(yield_id, generator_resume_points_.size());
|
|
|
| - Register generator = VisitForRegisterValue(expr->generator_object());
|
| + Register generator = generator_object_;
|
| + DCHECK(generator.is_valid());
|
|
|
| - // 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).
|
| + if (IsAsyncFunction(info()->literal()->kind()) &&
|
| + yield_type == Yield::kAwait) {
|
| + RegisterAllocationScope register_scope(this);
|
| + RegisterList args = register_allocator()->NewRegisterList(4);
|
| +
|
| + int context_index = Context::ASYNC_FUNCTION_AWAIT_CAUGHT_INDEX;
|
| + if (catch_prediction_ == HandlerTable::ASYNC_AWAIT)
|
| + context_index = Context::ASYNC_FUNCTION_AWAIT_UNCAUGHT_INDEX;
|
| +
|
| + 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)
|
| + } else {
|
| + // Save context, registers, and state. Then return.
|
| + builder()
|
| + ->LoadLiteral(Smi::FromInt(yield_id))
|
| + .SuspendGenerator(generator)
|
| + .LoadAccumulatorWithRegister(value)
|
| + .Return(); // Hard return (ignore any finally blocks).
|
| + }
|
|
|
| - builder()->Bind(&(generator_resume_points_[expr->yield_id()]));
|
| + builder()->Bind(&(generator_resume_points_[yield_id]));
|
| // Upon resume, we continue here.
|
|
|
| {
|
| @@ -2347,9 +2563,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();
|
| @@ -2360,6 +2576,14 @@ void BytecodeGenerator::VisitYield(Yield* expr) {
|
| }
|
| }
|
|
|
| +void BytecodeGenerator::VisitYield(Yield* expr) {
|
| + builder()->SetExpressionPosition(expr);
|
| + Register value = VisitForRegisterValue(expr->expression());
|
| +
|
| + BuildYield(expr->yield_id(), expr->on_exception(), expr->yield_type(), value,
|
| + expr->position());
|
| +}
|
| +
|
| void BytecodeGenerator::VisitThrow(Throw* expr) {
|
| VisitForAccumulatorValue(expr->exception());
|
| builder()->SetExpressionPosition(expr);
|
| @@ -2932,6 +3156,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();
|
|
|