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