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