| // 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-generator.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()), | 
 |         end_labels_(builder->zone()) { | 
 |     if (block_coverage_builder_ != nullptr) { | 
 |       block_coverage_body_slot_ = | 
 |           block_coverage_builder_->AllocateBlockCoverageSlot( | 
 |               node, SourceRangeKind::kBody); | 
 |     } | 
 |     source_position_ = node ? node->position() : kNoSourcePosition; | 
 |   } | 
 |   ~LoopBuilder() override; | 
 |  | 
 |   void LoopHeader(); | 
 |   void LoopBody(); | 
 |   void JumpToHeader(int loop_depth, LoopBuilder* const parent_loop); | 
 |   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: | 
 |   // Emit a Jump to our parent_loop_'s end label which could be a JumpLoop or, | 
 |   // iff they are a nested inner loop with the same loop header bytecode offset | 
 |   // as their parent's, a Jump to its parent's end label. | 
 |   void JumpToLoopEnd() { EmitJump(&end_labels_); } | 
 |   void BindLoopEnd(); | 
 |  | 
 |   BytecodeLoopHeader 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_; | 
 |  | 
 |   // Unbound labels that identify jumps for nested inner loops which share the | 
 |   // same header offset as this loop. Said inner loops will Jump to our end | 
 |   // label, which could be a JumpLoop or, iff we are a nested inner loop too, a | 
 |   // Jump to our parent's end label. | 
 |   BytecodeLabels end_labels_; | 
 |  | 
 |   int block_coverage_body_slot_; | 
 |  | 
 |   int source_position_; | 
 | }; | 
 |  | 
 | // 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<BytecodeLabel> 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 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_ |