// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_ #define V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_ #include "src/interpreter/bytecode-array-builder.h" #include "src/ast/ast-source-ranges.h" #include "src/interpreter/block-coverage-builder.h" #include "src/interpreter/bytecode-label.h" #include "src/zone/zone-containers.h" namespace v8 { namespace internal { namespace interpreter { class V8_EXPORT_PRIVATE ControlFlowBuilder { public: explicit ControlFlowBuilder(BytecodeArrayBuilder* builder) : builder_(builder) {} virtual ~ControlFlowBuilder() = default; protected: BytecodeArrayBuilder* builder() const { return builder_; } private: BytecodeArrayBuilder* builder_; DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder); }; class V8_EXPORT_PRIVATE BreakableControlFlowBuilder : public ControlFlowBuilder { public: BreakableControlFlowBuilder(BytecodeArrayBuilder* builder, BlockCoverageBuilder* block_coverage_builder, AstNode* node) : ControlFlowBuilder(builder), break_labels_(builder->zone()), node_(node), block_coverage_builder_(block_coverage_builder) {} ~BreakableControlFlowBuilder() override; // This method is called when visiting break statements in the AST. // Inserts a jump to an unbound label that is patched when the corresponding // BindBreakTarget is called. void Break() { EmitJump(&break_labels_); } void BreakIfTrue(BytecodeArrayBuilder::ToBooleanMode mode) { EmitJumpIfTrue(mode, &break_labels_); } void BreakIfFalse(BytecodeArrayBuilder::ToBooleanMode mode) { EmitJumpIfFalse(mode, &break_labels_); } void BreakIfUndefined() { EmitJumpIfUndefined(&break_labels_); } void BreakIfNull() { EmitJumpIfNull(&break_labels_); } BytecodeLabels* break_labels() { return &break_labels_; } protected: void EmitJump(BytecodeLabels* labels); void EmitJumpIfTrue(BytecodeArrayBuilder::ToBooleanMode mode, BytecodeLabels* labels); void EmitJumpIfFalse(BytecodeArrayBuilder::ToBooleanMode mode, BytecodeLabels* labels); void EmitJumpIfUndefined(BytecodeLabels* labels); void EmitJumpIfNull(BytecodeLabels* labels); // Called from the destructor to update sites that emit jumps for break. void BindBreakTarget(); // Unbound labels that identify jumps for break statements in the code. BytecodeLabels break_labels_; // A continuation counter (for block coverage) is needed e.g. when // encountering a break statement. AstNode* node_; BlockCoverageBuilder* block_coverage_builder_; }; // Class to track control flow for block statements (which can break in JS). class V8_EXPORT_PRIVATE BlockBuilder final : public BreakableControlFlowBuilder { public: BlockBuilder(BytecodeArrayBuilder* builder, BlockCoverageBuilder* block_coverage_builder, BreakableStatement* statement) : BreakableControlFlowBuilder(builder, block_coverage_builder, statement) {} }; // A class to help with co-ordinating break and continue statements with // their loop. class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder { public: LoopBuilder(BytecodeArrayBuilder* builder, BlockCoverageBuilder* block_coverage_builder, AstNode* node) : BreakableControlFlowBuilder(builder, block_coverage_builder, node), continue_labels_(builder->zone()) { if (block_coverage_builder_ != nullptr) { block_coverage_body_slot_ = block_coverage_builder_->AllocateBlockCoverageSlot( node, SourceRangeKind::kBody); } } ~LoopBuilder() override; void LoopHeader(); void LoopBody(); void JumpToHeader(int loop_depth); void BindContinueTarget(); // This method is called when visiting continue statements in the AST. // Inserts a jump to an unbound label that is patched when BindContinueTarget // is called. void Continue() { EmitJump(&continue_labels_); } void ContinueIfUndefined() { EmitJumpIfUndefined(&continue_labels_); } void ContinueIfNull() { EmitJumpIfNull(&continue_labels_); } private: BytecodeLabel loop_header_; // Unbound labels that identify jumps for continue statements in the code and // jumps from checking the loop condition to the header for do-while loops. BytecodeLabels continue_labels_; int block_coverage_body_slot_; }; // A class to help with co-ordinating break statements with their switch. class V8_EXPORT_PRIVATE SwitchBuilder final : public BreakableControlFlowBuilder { public: SwitchBuilder(BytecodeArrayBuilder* builder, BlockCoverageBuilder* block_coverage_builder, SwitchStatement* statement, int number_of_cases) : BreakableControlFlowBuilder(builder, block_coverage_builder, statement), case_sites_(builder->zone()) { case_sites_.resize(number_of_cases); } ~SwitchBuilder() override; // NOLINT (modernize-use-equals-default) // This method should be called by the SwitchBuilder owner when the case // statement with |index| is emitted to update the case jump site. void SetCaseTarget(int index, CaseClause* clause); // This method is called when visiting case comparison operation for |index|. // Inserts a JumpIfTrue with ToBooleanMode |mode| to a unbound label that is // patched when the corresponding SetCaseTarget is called. void Case(BytecodeArrayBuilder::ToBooleanMode mode, int index) { builder()->JumpIfTrue(mode, &case_sites_.at(index)); } // This method is called when all cases comparisons have been emitted if there // is a default case statement. Inserts a Jump to a unbound label that is // patched when the corresponding SetCaseTarget is called. void DefaultAt(int index) { builder()->Jump(&case_sites_.at(index)); } private: // Unbound labels that identify jumps for case statements in the code. ZoneVector case_sites_; }; // A class to help with co-ordinating control flow in try-catch statements. class V8_EXPORT_PRIVATE TryCatchBuilder final : public ControlFlowBuilder { public: TryCatchBuilder(BytecodeArrayBuilder* builder, BlockCoverageBuilder* block_coverage_builder, TryCatchStatement* statement, HandlerTable::CatchPrediction catch_prediction) : ControlFlowBuilder(builder), handler_id_(builder->NewHandlerEntry()), catch_prediction_(catch_prediction), block_coverage_builder_(block_coverage_builder), statement_(statement) {} ~TryCatchBuilder() override; void BeginTry(Register context); void EndTry(); void EndCatch(); private: int handler_id_; HandlerTable::CatchPrediction catch_prediction_; BytecodeLabel handler_; BytecodeLabel exit_; BlockCoverageBuilder* block_coverage_builder_; TryCatchStatement* statement_; }; // A class to help with co-ordinating control flow in try-finally statements. class V8_EXPORT_PRIVATE TryFinallyBuilder final : public ControlFlowBuilder { public: TryFinallyBuilder(BytecodeArrayBuilder* builder, BlockCoverageBuilder* block_coverage_builder, TryFinallyStatement* statement, HandlerTable::CatchPrediction catch_prediction) : ControlFlowBuilder(builder), handler_id_(builder->NewHandlerEntry()), catch_prediction_(catch_prediction), finalization_sites_(builder->zone()), block_coverage_builder_(block_coverage_builder), statement_(statement) {} ~TryFinallyBuilder() override; void BeginTry(Register context); void LeaveTry(); void EndTry(); void BeginHandler(); void BeginFinally(); void EndFinally(); private: int handler_id_; HandlerTable::CatchPrediction catch_prediction_; BytecodeLabel handler_; // Unbound labels that identify jumps to the finally block in the code. BytecodeLabels finalization_sites_; BlockCoverageBuilder* block_coverage_builder_; TryFinallyStatement* statement_; }; class V8_EXPORT_PRIVATE ConditionalControlFlowBuilder final : public ControlFlowBuilder { public: ConditionalControlFlowBuilder(BytecodeArrayBuilder* builder, BlockCoverageBuilder* block_coverage_builder, AstNode* node) : ControlFlowBuilder(builder), end_labels_(builder->zone()), then_labels_(builder->zone()), else_labels_(builder->zone()), node_(node), block_coverage_builder_(block_coverage_builder) { DCHECK(node->IsIfStatement() || node->IsConditional()); if (block_coverage_builder != nullptr) { block_coverage_then_slot_ = block_coverage_builder->AllocateBlockCoverageSlot( node, SourceRangeKind::kThen); block_coverage_else_slot_ = block_coverage_builder->AllocateBlockCoverageSlot( node, SourceRangeKind::kElse); } } ~ConditionalControlFlowBuilder() override; BytecodeLabels* then_labels() { return &then_labels_; } BytecodeLabels* else_labels() { return &else_labels_; } void Then(); void Else(); void JumpToEnd(); private: BytecodeLabels end_labels_; BytecodeLabels then_labels_; BytecodeLabels else_labels_; AstNode* node_; int block_coverage_then_slot_; int block_coverage_else_slot_; BlockCoverageBuilder* block_coverage_builder_; }; } // namespace interpreter } // namespace internal } // namespace v8 #endif // V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_