| Index: src/interpreter/bytecode-generator.cc
|
| diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
|
| index 5f640854f751764938498f4fe2b33a00fc4c9543..2ac0e3b42856dff035789362522e801fd4504043 100644
|
| --- a/src/interpreter/bytecode-generator.cc
|
| +++ b/src/interpreter/bytecode-generator.cc
|
| @@ -88,9 +88,13 @@ class BytecodeGenerator::ControlScope BASE_EMBEDDED {
|
|
|
| void Break(Statement* stmt) { PerformCommand(CMD_BREAK, stmt); }
|
| void Continue(Statement* stmt) { PerformCommand(CMD_CONTINUE, stmt); }
|
| + void ReturnAccumulator() { PerformCommand(CMD_RETURN, nullptr); }
|
| + void ReThrowAccumulator() { PerformCommand(CMD_RETHROW, nullptr); }
|
| +
|
| + class DeferredCommands;
|
|
|
| protected:
|
| - enum Command { CMD_BREAK, CMD_CONTINUE };
|
| + enum Command { CMD_BREAK, CMD_CONTINUE, CMD_RETURN, CMD_RETHROW };
|
| void PerformCommand(Command command, Statement* statement);
|
| virtual bool Execute(Command command, Statement* statement) = 0;
|
|
|
| @@ -105,6 +109,112 @@ class BytecodeGenerator::ControlScope BASE_EMBEDDED {
|
| };
|
|
|
|
|
| +// Helper class for a try-finally control scope. It can record intercepted
|
| +// control-flow commands that cause entry into a finally-block, and re-apply
|
| +// them after again leaving that block. Special tokens are used to identify
|
| +// paths going through the finally-block to dispatch after leaving the block.
|
| +class BytecodeGenerator::ControlScope::DeferredCommands final {
|
| + public:
|
| + DeferredCommands(BytecodeGenerator* generator, Register token_register,
|
| + Register result_register)
|
| + : generator_(generator),
|
| + deferred_(generator->zone()),
|
| + token_register_(token_register),
|
| + result_register_(result_register) {}
|
| +
|
| + // One recorded control-flow command.
|
| + struct Entry {
|
| + Command command; // The command type being applied on this path.
|
| + Statement* statement; // The target statement for the command or {nullptr}.
|
| + int token; // A token identifying this particular path.
|
| + };
|
| +
|
| + // Records a control-flow command while entering the finally-block. This also
|
| + // generates a new dispatch token that identifies one particular path. This
|
| + // expects the result to be in the accumulator.
|
| + void RecordCommand(Command command, Statement* statement) {
|
| + int token = static_cast<int>(deferred_.size());
|
| + deferred_.push_back({command, statement, token});
|
| +
|
| + builder()->StoreAccumulatorInRegister(result_register_);
|
| + builder()->LoadLiteral(Smi::FromInt(token));
|
| + builder()->StoreAccumulatorInRegister(token_register_);
|
| + }
|
| +
|
| + // Records the dispatch token to be used to identify the re-throw path when
|
| + // the finally-block has been entered through the exception handler. This
|
| + // expects the exception to be in the accumulator.
|
| + void RecordHandlerReThrowPath() {
|
| + // The accumulator contains the exception object.
|
| + RecordCommand(CMD_RETHROW, nullptr);
|
| + }
|
| +
|
| + // Records the dispatch token to be used to identify the implicit fall-through
|
| + // path at the end of a try-block into the corresponding finally-block.
|
| + void RecordFallThroughPath() {
|
| + builder()->LoadLiteral(Smi::FromInt(-1));
|
| + builder()->StoreAccumulatorInRegister(token_register_);
|
| + }
|
| +
|
| + // Applies all recorded control-flow commands after the finally-block again.
|
| + // This generates a dynamic dispatch on the token from the entry point.
|
| + void ApplyDeferredCommands() {
|
| + // The fall-through path is covered by the default case, hence +1 here.
|
| + SwitchBuilder dispatch(builder(), static_cast<int>(deferred_.size() + 1));
|
| + for (size_t i = 0; i < deferred_.size(); ++i) {
|
| + Entry& entry = deferred_[i];
|
| + builder()->LoadLiteral(Smi::FromInt(entry.token));
|
| + builder()->CompareOperation(Token::EQ_STRICT, token_register_,
|
| + Strength::WEAK);
|
| + dispatch.Case(static_cast<int>(i));
|
| + }
|
| + dispatch.DefaultAt(static_cast<int>(deferred_.size()));
|
| + for (size_t i = 0; i < deferred_.size(); ++i) {
|
| + Entry& entry = deferred_[i];
|
| + dispatch.SetCaseTarget(static_cast<int>(i));
|
| + builder()->LoadAccumulatorWithRegister(result_register_);
|
| + execution_control()->PerformCommand(entry.command, entry.statement);
|
| + }
|
| + dispatch.SetCaseTarget(static_cast<int>(deferred_.size()));
|
| + }
|
| +
|
| + BytecodeArrayBuilder* builder() { return generator_->builder(); }
|
| + ControlScope* execution_control() { return generator_->execution_control(); }
|
| +
|
| + private:
|
| + BytecodeGenerator* generator_;
|
| + ZoneVector<Entry> deferred_;
|
| + Register token_register_;
|
| + Register result_register_;
|
| +};
|
| +
|
| +
|
| +// Scoped class for dealing with control flow reaching the function level.
|
| +class BytecodeGenerator::ControlScopeForTopLevel final
|
| + : public BytecodeGenerator::ControlScope {
|
| + public:
|
| + explicit ControlScopeForTopLevel(BytecodeGenerator* generator)
|
| + : ControlScope(generator) {}
|
| +
|
| + protected:
|
| + bool Execute(Command command, Statement* statement) override {
|
| + switch (command) {
|
| + case CMD_BREAK:
|
| + case CMD_CONTINUE:
|
| + break;
|
| + case CMD_RETURN:
|
| + generator()->builder()->Return();
|
| + return true;
|
| + case CMD_RETHROW:
|
| + // TODO(mstarzinger): Should be a ReThrow instead.
|
| + generator()->builder()->Throw();
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +};
|
| +
|
| +
|
| // Scoped class for enabling break inside blocks and switch blocks.
|
| class BytecodeGenerator::ControlScopeForBreakable final
|
| : public BytecodeGenerator::ControlScope {
|
| @@ -124,6 +234,8 @@ class BytecodeGenerator::ControlScopeForBreakable final
|
| control_builder_->Break();
|
| return true;
|
| case CMD_CONTINUE:
|
| + case CMD_RETURN:
|
| + case CMD_RETHROW:
|
| break;
|
| }
|
| return false;
|
| @@ -157,6 +269,9 @@ class BytecodeGenerator::ControlScopeForIteration final
|
| case CMD_CONTINUE:
|
| loop_builder_->Continue();
|
| return true;
|
| + case CMD_RETURN:
|
| + case CMD_RETHROW:
|
| + break;
|
| }
|
| return false;
|
| }
|
| @@ -167,6 +282,66 @@ class BytecodeGenerator::ControlScopeForIteration final
|
| };
|
|
|
|
|
| +// Scoped class for enabling 'throw' in try-catch constructs.
|
| +class BytecodeGenerator::ControlScopeForTryCatch final
|
| + : public BytecodeGenerator::ControlScope {
|
| + public:
|
| + ControlScopeForTryCatch(BytecodeGenerator* generator,
|
| + TryCatchBuilder* try_catch_builder)
|
| + : ControlScope(generator), try_catch_builder_(try_catch_builder) {}
|
| +
|
| + protected:
|
| + bool Execute(Command command, Statement* statement) override {
|
| + switch (command) {
|
| + case CMD_BREAK:
|
| + case CMD_CONTINUE:
|
| + case CMD_RETURN:
|
| + break;
|
| + case CMD_RETHROW:
|
| + // TODO(mstarzinger): Test and implement this!
|
| + USE(try_catch_builder_);
|
| + UNIMPLEMENTED();
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + private:
|
| + TryCatchBuilder* try_catch_builder_;
|
| +};
|
| +
|
| +
|
| +// Scoped class for enabling control flow through try-finally constructs.
|
| +class BytecodeGenerator::ControlScopeForTryFinally final
|
| + : public BytecodeGenerator::ControlScope {
|
| + public:
|
| + ControlScopeForTryFinally(BytecodeGenerator* generator,
|
| + TryFinallyBuilder* try_finally_builder,
|
| + DeferredCommands* commands)
|
| + : ControlScope(generator),
|
| + try_finally_builder_(try_finally_builder),
|
| + commands_(commands) {}
|
| +
|
| + protected:
|
| + bool Execute(Command command, Statement* statement) override {
|
| + switch (command) {
|
| + case CMD_BREAK:
|
| + case CMD_CONTINUE:
|
| + case CMD_RETURN:
|
| + case CMD_RETHROW:
|
| + commands_->RecordCommand(command, statement);
|
| + try_finally_builder_->LeaveTry();
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + private:
|
| + TryFinallyBuilder* try_finally_builder_;
|
| + DeferredCommands* commands_;
|
| +};
|
| +
|
| +
|
| void BytecodeGenerator::ControlScope::PerformCommand(Command command,
|
| Statement* statement) {
|
| ControlScope* current = this;
|
| @@ -365,6 +540,9 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
|
| // Initialize the incoming context.
|
| ContextScope incoming_context(this, scope(), false);
|
|
|
| + // Initialize control scope.
|
| + ControlScopeForTopLevel control(this);
|
| +
|
| builder()->set_parameter_count(info->num_parameters_including_this());
|
| builder()->set_locals_count(scope()->num_stack_slots());
|
| builder()->set_context_count(scope()->MaxNestedContextChainLength());
|
| @@ -661,7 +839,7 @@ void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
|
|
|
| void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
|
| VisitForAccumulatorValue(stmt->expression());
|
| - builder()->Return();
|
| + execution_control()->ReturnAccumulator();
|
| }
|
|
|
|
|
| @@ -909,8 +1087,10 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
| // Evaluate the try-block inside a control scope. This simulates a handler
|
| // that is intercepting 'throw' control commands.
|
| try_control_builder.BeginTry(context);
|
| - // TODO(mstarzinger): Control scope is missing!
|
| - Visit(stmt->try_block());
|
| + {
|
| + ControlScopeForTryCatch scope(this, &try_control_builder);
|
| + Visit(stmt->try_block());
|
| + }
|
| try_control_builder.EndTry();
|
|
|
| // Clear message object as we enter the catch block.
|
| @@ -928,6 +1108,25 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
| void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
| TryFinallyBuilder try_control_builder(builder());
|
|
|
| + // 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.
|
| @@ -936,16 +1135,29 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
| // Evaluate the try-block inside a control scope. This simulates a handler
|
| // that is intercepting all control commands.
|
| try_control_builder.BeginTry(context);
|
| - // TODO(mstarzinger): Control scope is missing!
|
| - Visit(stmt->try_block());
|
| + {
|
| + ControlScopeForTryFinally scope(this, &try_control_builder, &commands);
|
| + 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();
|
| +
|
| + try_control_builder.BeginFinally();
|
| +
|
| // Clear message object as we enter the finally block.
|
| // TODO(mstarzinger): Implement this!
|
|
|
| // Evaluate the finally-block.
|
| Visit(stmt->finally_block());
|
| try_control_builder.EndFinally();
|
| +
|
| + // Dynamic dispatch after the finally-block.
|
| + commands.ApplyDeferredCommands();
|
| }
|
|
|
|
|
|
|