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