1 // Copyright 2015 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_ 6 #define V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_ 7 8 #include "src/interpreter/bytecode-array-builder.h" 9 10 #include "src/ast/ast-source-ranges.h" 11 #include "src/interpreter/block-coverage-builder.h" 12 #include "src/interpreter/bytecode-label.h" 13 #include "src/zone/zone-containers.h" 14 15 namespace v8 { 16 namespace internal { 17 namespace interpreter { 18 19 class V8_EXPORT_PRIVATE ControlFlowBuilder BASE_EMBEDDED { 20 public: ControlFlowBuilder(BytecodeArrayBuilder * builder)21 explicit ControlFlowBuilder(BytecodeArrayBuilder* builder) 22 : builder_(builder) {} ~ControlFlowBuilder()23 virtual ~ControlFlowBuilder() {} 24 25 protected: builder()26 BytecodeArrayBuilder* builder() const { return builder_; } 27 28 private: 29 BytecodeArrayBuilder* builder_; 30 31 DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder); 32 }; 33 34 class V8_EXPORT_PRIVATE BreakableControlFlowBuilder 35 : public ControlFlowBuilder { 36 public: BreakableControlFlowBuilder(BytecodeArrayBuilder * builder,BlockCoverageBuilder * block_coverage_builder,AstNode * node)37 BreakableControlFlowBuilder(BytecodeArrayBuilder* builder, 38 BlockCoverageBuilder* block_coverage_builder, 39 AstNode* node) 40 : ControlFlowBuilder(builder), 41 break_labels_(builder->zone()), 42 node_(node), 43 block_coverage_builder_(block_coverage_builder) {} 44 virtual ~BreakableControlFlowBuilder(); 45 46 // This method is called when visiting break statements in the AST. 47 // Inserts a jump to an unbound label that is patched when the corresponding 48 // BindBreakTarget is called. Break()49 void Break() { EmitJump(&break_labels_); } BreakIfTrue(BytecodeArrayBuilder::ToBooleanMode mode)50 void BreakIfTrue(BytecodeArrayBuilder::ToBooleanMode mode) { 51 EmitJumpIfTrue(mode, &break_labels_); 52 } BreakIfFalse(BytecodeArrayBuilder::ToBooleanMode mode)53 void BreakIfFalse(BytecodeArrayBuilder::ToBooleanMode mode) { 54 EmitJumpIfFalse(mode, &break_labels_); 55 } BreakIfUndefined()56 void BreakIfUndefined() { EmitJumpIfUndefined(&break_labels_); } BreakIfNull()57 void BreakIfNull() { EmitJumpIfNull(&break_labels_); } 58 break_labels()59 BytecodeLabels* break_labels() { return &break_labels_; } 60 set_needs_continuation_counter()61 void set_needs_continuation_counter() { needs_continuation_counter_ = true; } needs_continuation_counter()62 bool needs_continuation_counter() const { 63 return needs_continuation_counter_; 64 } 65 66 protected: 67 void EmitJump(BytecodeLabels* labels); 68 void EmitJumpIfTrue(BytecodeArrayBuilder::ToBooleanMode mode, 69 BytecodeLabels* labels); 70 void EmitJumpIfFalse(BytecodeArrayBuilder::ToBooleanMode mode, 71 BytecodeLabels* labels); 72 void EmitJumpIfUndefined(BytecodeLabels* labels); 73 void EmitJumpIfNull(BytecodeLabels* labels); 74 75 // Called from the destructor to update sites that emit jumps for break. 76 void BindBreakTarget(); 77 78 // Unbound labels that identify jumps for break statements in the code. 79 BytecodeLabels break_labels_; 80 81 // A continuation counter (for block coverage) is needed e.g. when 82 // encountering a break statement. 83 AstNode* node_; 84 bool needs_continuation_counter_ = false; 85 BlockCoverageBuilder* block_coverage_builder_; 86 }; 87 88 89 // Class to track control flow for block statements (which can break in JS). 90 class V8_EXPORT_PRIVATE BlockBuilder final 91 : public BreakableControlFlowBuilder { 92 public: BlockBuilder(BytecodeArrayBuilder * builder,BlockCoverageBuilder * block_coverage_builder,BreakableStatement * statement)93 BlockBuilder(BytecodeArrayBuilder* builder, 94 BlockCoverageBuilder* block_coverage_builder, 95 BreakableStatement* statement) 96 : BreakableControlFlowBuilder(builder, block_coverage_builder, 97 statement) {} 98 }; 99 100 101 // A class to help with co-ordinating break and continue statements with 102 // their loop. 103 class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder { 104 public: LoopBuilder(BytecodeArrayBuilder * builder,BlockCoverageBuilder * block_coverage_builder,AstNode * node)105 LoopBuilder(BytecodeArrayBuilder* builder, 106 BlockCoverageBuilder* block_coverage_builder, AstNode* node) 107 : BreakableControlFlowBuilder(builder, block_coverage_builder, node), 108 continue_labels_(builder->zone()) { 109 if (block_coverage_builder_ != nullptr) { 110 set_needs_continuation_counter(); 111 block_coverage_body_slot_ = 112 block_coverage_builder_->AllocateBlockCoverageSlot( 113 node, SourceRangeKind::kBody); 114 } 115 } 116 ~LoopBuilder(); 117 118 void LoopHeader(); 119 void LoopBody(); 120 void JumpToHeader(int loop_depth); 121 void BindContinueTarget(); 122 123 // This method is called when visiting continue statements in the AST. 124 // Inserts a jump to an unbound label that is patched when BindContinueTarget 125 // is called. Continue()126 void Continue() { EmitJump(&continue_labels_); } ContinueIfUndefined()127 void ContinueIfUndefined() { EmitJumpIfUndefined(&continue_labels_); } ContinueIfNull()128 void ContinueIfNull() { EmitJumpIfNull(&continue_labels_); } 129 130 private: 131 BytecodeLabel loop_header_; 132 133 // Unbound labels that identify jumps for continue statements in the code and 134 // jumps from checking the loop condition to the header for do-while loops. 135 BytecodeLabels continue_labels_; 136 137 int block_coverage_body_slot_; 138 }; 139 140 141 // A class to help with co-ordinating break statements with their switch. 142 class V8_EXPORT_PRIVATE SwitchBuilder final 143 : public BreakableControlFlowBuilder { 144 public: SwitchBuilder(BytecodeArrayBuilder * builder,BlockCoverageBuilder * block_coverage_builder,SwitchStatement * statement,int number_of_cases)145 SwitchBuilder(BytecodeArrayBuilder* builder, 146 BlockCoverageBuilder* block_coverage_builder, 147 SwitchStatement* statement, int number_of_cases) 148 : BreakableControlFlowBuilder(builder, block_coverage_builder, statement), 149 case_sites_(builder->zone()) { 150 case_sites_.resize(number_of_cases); 151 } 152 ~SwitchBuilder(); 153 154 // This method should be called by the SwitchBuilder owner when the case 155 // statement with |index| is emitted to update the case jump site. 156 void SetCaseTarget(int index, CaseClause* clause); 157 158 // This method is called when visiting case comparison operation for |index|. 159 // Inserts a JumpIfTrue with ToBooleanMode |mode| to a unbound label that is 160 // patched when the corresponding SetCaseTarget is called. Case(BytecodeArrayBuilder::ToBooleanMode mode,int index)161 void Case(BytecodeArrayBuilder::ToBooleanMode mode, int index) { 162 builder()->JumpIfTrue(mode, &case_sites_.at(index)); 163 } 164 165 // This method is called when all cases comparisons have been emitted if there 166 // is a default case statement. Inserts a Jump to a unbound label that is 167 // patched when the corresponding SetCaseTarget is called. DefaultAt(int index)168 void DefaultAt(int index) { builder()->Jump(&case_sites_.at(index)); } 169 170 private: 171 // Unbound labels that identify jumps for case statements in the code. 172 ZoneVector<BytecodeLabel> case_sites_; 173 }; 174 175 176 // A class to help with co-ordinating control flow in try-catch statements. 177 class V8_EXPORT_PRIVATE TryCatchBuilder final : public ControlFlowBuilder { 178 public: TryCatchBuilder(BytecodeArrayBuilder * builder,BlockCoverageBuilder * block_coverage_builder,TryCatchStatement * statement,HandlerTable::CatchPrediction catch_prediction)179 TryCatchBuilder(BytecodeArrayBuilder* builder, 180 BlockCoverageBuilder* block_coverage_builder, 181 TryCatchStatement* statement, 182 HandlerTable::CatchPrediction catch_prediction) 183 : ControlFlowBuilder(builder), 184 handler_id_(builder->NewHandlerEntry()), 185 catch_prediction_(catch_prediction), 186 block_coverage_builder_(block_coverage_builder), 187 statement_(statement) {} 188 189 ~TryCatchBuilder(); 190 191 void BeginTry(Register context); 192 void EndTry(); 193 void EndCatch(); 194 195 private: 196 int handler_id_; 197 HandlerTable::CatchPrediction catch_prediction_; 198 BytecodeLabel handler_; 199 BytecodeLabel exit_; 200 201 BlockCoverageBuilder* block_coverage_builder_; 202 TryCatchStatement* statement_; 203 }; 204 205 206 // A class to help with co-ordinating control flow in try-finally statements. 207 class V8_EXPORT_PRIVATE TryFinallyBuilder final : public ControlFlowBuilder { 208 public: TryFinallyBuilder(BytecodeArrayBuilder * builder,BlockCoverageBuilder * block_coverage_builder,TryFinallyStatement * statement,HandlerTable::CatchPrediction catch_prediction)209 TryFinallyBuilder(BytecodeArrayBuilder* builder, 210 BlockCoverageBuilder* block_coverage_builder, 211 TryFinallyStatement* statement, 212 HandlerTable::CatchPrediction catch_prediction) 213 : ControlFlowBuilder(builder), 214 handler_id_(builder->NewHandlerEntry()), 215 catch_prediction_(catch_prediction), 216 finalization_sites_(builder->zone()), 217 block_coverage_builder_(block_coverage_builder), 218 statement_(statement) {} 219 220 ~TryFinallyBuilder(); 221 222 void BeginTry(Register context); 223 void LeaveTry(); 224 void EndTry(); 225 void BeginHandler(); 226 void BeginFinally(); 227 void EndFinally(); 228 229 private: 230 int handler_id_; 231 HandlerTable::CatchPrediction catch_prediction_; 232 BytecodeLabel handler_; 233 234 // Unbound labels that identify jumps to the finally block in the code. 235 BytecodeLabels finalization_sites_; 236 237 BlockCoverageBuilder* block_coverage_builder_; 238 TryFinallyStatement* statement_; 239 }; 240 241 class V8_EXPORT_PRIVATE ConditionalControlFlowBuilder final 242 : public ControlFlowBuilder { 243 public: ConditionalControlFlowBuilder(BytecodeArrayBuilder * builder,BlockCoverageBuilder * block_coverage_builder,AstNode * node)244 ConditionalControlFlowBuilder(BytecodeArrayBuilder* builder, 245 BlockCoverageBuilder* block_coverage_builder, 246 AstNode* node) 247 : ControlFlowBuilder(builder), 248 end_labels_(builder->zone()), 249 then_labels_(builder->zone()), 250 else_labels_(builder->zone()), 251 node_(node), 252 block_coverage_builder_(block_coverage_builder) { 253 DCHECK(node->IsIfStatement() || node->IsConditional()); 254 if (block_coverage_builder != nullptr) { 255 block_coverage_then_slot_ = 256 block_coverage_builder->AllocateBlockCoverageSlot( 257 node, SourceRangeKind::kThen); 258 block_coverage_else_slot_ = 259 block_coverage_builder->AllocateBlockCoverageSlot( 260 node, SourceRangeKind::kElse); 261 } 262 } 263 ~ConditionalControlFlowBuilder(); 264 then_labels()265 BytecodeLabels* then_labels() { return &then_labels_; } else_labels()266 BytecodeLabels* else_labels() { return &else_labels_; } 267 268 void Then(); 269 void Else(); 270 271 void JumpToEnd(); 272 273 private: 274 BytecodeLabels end_labels_; 275 BytecodeLabels then_labels_; 276 BytecodeLabels else_labels_; 277 278 AstNode* node_; 279 int block_coverage_then_slot_; 280 int block_coverage_else_slot_; 281 BlockCoverageBuilder* block_coverage_builder_; 282 }; 283 284 } // namespace interpreter 285 } // namespace internal 286 } // namespace v8 287 288 #endif // V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_ 289