1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef frontend_IfEmitter_h
8 #define frontend_IfEmitter_h
9 
10 #include "mozilla/Attributes.h"
11 #include "mozilla/Maybe.h"
12 
13 #include <stdint.h>
14 
15 #include "frontend/JumpList.h"
16 #include "frontend/SourceNotes.h"
17 #include "frontend/TDZCheckCache.h"
18 
19 namespace js {
20 namespace frontend {
21 
22 struct BytecodeEmitter;
23 
24 class MOZ_STACK_CLASS BranchEmitterBase {
25  protected:
26   BytecodeEmitter* bce_;
27 
28   // Jump around the then clause, to the beginning of the else clause.
29   JumpList jumpAroundThen_;
30 
31   // Jump around the else clause, to the end of the entire branch.
32   JumpList jumpsAroundElse_;
33 
34   // The stack depth before emitting the then block.
35   // Used for restoring stack depth before emitting the else block.
36   // Also used for assertion to make sure then and else blocks pushed the
37   // same number of values.
38   int32_t thenDepth_ = 0;
39 
40   // Whether the then-clause, the else-clause, or else-if condition may
41   // contain declaration or access to lexical variables, which means they
42   // should have their own TDZCheckCache.  Basically TDZCheckCache should be
43   // created for each basic block, which then-clause, else-clause, and
44   // else-if condition are, but for internally used branches which are
45   // known not to touch lexical variables we can skip creating TDZCheckCache
46   // for them.
47   //
48   // See the comment for TDZCheckCache class for more details.
49   enum class Kind {
50     // For syntactic branches (if, if-else, and conditional expression),
51     // which basically may contain declaration or accesses to lexical
52     // variables inside then-clause, else-clause, and else-if condition.
53     MayContainLexicalAccessInBranch,
54 
55     // For internally used branches which don't touch lexical variables
56     // inside then-clause, else-clause, nor else-if condition.
57     NoLexicalAccessInBranch
58   };
59   Kind kind_;
60 
61   mozilla::Maybe<TDZCheckCache> tdzCache_;
62 
63 #ifdef DEBUG
64   // The number of values pushed in the then and else blocks.
65   int32_t pushed_ = 0;
66   bool calculatedPushed_ = false;
67 #endif
68 
69  protected:
70   BranchEmitterBase(BytecodeEmitter* bce, Kind kind);
71 
72   MOZ_MUST_USE bool emitThenInternal();
73   void calculateOrCheckPushed();
74   MOZ_MUST_USE bool emitElseInternal();
75   MOZ_MUST_USE bool emitEndInternal();
76 
77  public:
78 #ifdef DEBUG
79   // Returns the number of values pushed onto the value stack inside
80   // `then_block` and `else_block`.
81   // Can be used in assertion after emitting if-then-else.
pushed()82   int32_t pushed() const { return pushed_; }
83 
84   // Returns the number of values popped onto the value stack inside
85   // `then_block` and `else_block`.
86   // Can be used in assertion after emitting if-then-else.
popped()87   int32_t popped() const { return -pushed_; }
88 #endif
89 };
90 
91 // Class for emitting bytecode for blocks like if-then-else.
92 //
93 // This class can be used to emit single if-then-else block, or cascading
94 // else-if blocks.
95 //
96 // Usage: (check for the return value is omitted for simplicity)
97 //
98 //   `if (cond) then_block`
99 //     IfEmitter ifThen(this);
100 //     ifThen.emitIf(Some(offset_of_if));
101 //     emit(cond);
102 //     ifThen.emitThen();
103 //     emit(then_block);
104 //     ifThen.emitEnd();
105 //
106 //   `if (cond) then_block else else_block`
107 //     IfEmitter ifThenElse(this);
108 //     ifThen.emitIf(Some(offset_of_if));
109 //     emit(cond);
110 //     ifThenElse.emitThenElse();
111 //     emit(then_block);
112 //     ifThenElse.emitElse();
113 //     emit(else_block);
114 //     ifThenElse.emitEnd();
115 //
116 //   `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
117 //     IfEmitter ifThenElse(this);
118 //     ifThen.emitIf(Some(offset_of_if));
119 //     emit(c1);
120 //     ifThenElse.emitThenElse();
121 //     emit(b1);
122 //     ifThenElse.emitElseIf(Some(offset_of_if));
123 //     emit(c2);
124 //     ifThenElse.emitThenElse();
125 //     emit(b2);
126 //     ifThenElse.emitElseIf(Some(offset_of_if));
127 //     emit(c3);
128 //     ifThenElse.emitThenElse();
129 //     emit(b3);
130 //     ifThenElse.emitElse();
131 //     emit(b4);
132 //     ifThenElse.emitEnd();
133 //
134 class MOZ_STACK_CLASS IfEmitter : public BranchEmitterBase {
135  protected:
136 #ifdef DEBUG
137   // The state of this emitter.
138   //
139   // +-------+ emitIf +----+
140   // | Start |------->| If |-+
141   // +-------+        +----+ |
142   //                         |
143   //    +--------------------+
144   //    |
145   //    v emitThen +------+                               emitEnd +-----+
146   // +->+--------->| Then |---------------------------->+-------->| End |
147   // ^  |          +------+                             ^         +-----+
148   // |  |                                               |
149   // |  |                                               |
150   // |  |                                               |
151   // |  | emitThenElse +----------+   emitElse +------+ |
152   // |  +------------->| ThenElse |-+--------->| Else |-+
153   // |                 +----------+ |          +------+
154   // |                              |
155   // |                              | emitElseIf +--------+
156   // |                              +----------->| ElseIf |-+
157   // |                                           +--------+ |
158   // |                                                      |
159   // +------------------------------------------------------+
160   enum class State {
161     // The initial state.
162     Start,
163 
164     // After calling emitIf.
165     If,
166 
167     // After calling emitThen.
168     Then,
169 
170     // After calling emitThenElse.
171     ThenElse,
172 
173     // After calling emitElse.
174     Else,
175 
176     // After calling emitElseIf.
177     ElseIf,
178 
179     // After calling emitEnd.
180     End
181   };
182   State state_ = State::Start;
183 #endif
184 
185  protected:
186   // For InternalIfEmitter.
187   IfEmitter(BytecodeEmitter* bce, Kind kind);
188 
189  public:
190   explicit IfEmitter(BytecodeEmitter* bce);
191 
192   // `ifPos` is the offset in the source code for the character below:
193   //
194   //   if ( cond ) { ... } else if ( cond2 ) { ... }
195   //   ^                        ^
196   //   |                        |
197   //   |                        ifPos for emitElseIf
198   //   |
199   //   ifPos for emitIf
200   //
201   // Can be Nothing() if not available.
202   MOZ_MUST_USE bool emitIf(const mozilla::Maybe<uint32_t>& ifPos);
203 
204   MOZ_MUST_USE bool emitThen();
205   MOZ_MUST_USE bool emitThenElse();
206 
207   MOZ_MUST_USE bool emitElseIf(const mozilla::Maybe<uint32_t>& ifPos);
208   MOZ_MUST_USE bool emitElse();
209 
210   MOZ_MUST_USE bool emitEnd();
211 };
212 
213 // Class for emitting bytecode for blocks like if-then-else which doesn't touch
214 // lexical variables.
215 //
216 // See the comments above NoLexicalAccessInBranch for more details when to use
217 // this instead of IfEmitter.
218 // Compared to IfEmitter, this class doesn't have emitIf method, given that
219 // it doesn't have syntactic `if`, and also the `cond` value can be already
220 // on the stack.
221 //
222 // Usage: (check for the return value is omitted for simplicity)
223 //
224 //   `if (cond) then_block else else_block` (effectively)
225 //     emit(cond);
226 //     InternalIfEmitter ifThenElse(this);
227 //     ifThenElse.emitThenElse();
228 //     emit(then_block);
229 //     ifThenElse.emitElse();
230 //     emit(else_block);
231 //     ifThenElse.emitEnd();
232 //
233 class MOZ_STACK_CLASS InternalIfEmitter : public IfEmitter {
234  public:
235   explicit InternalIfEmitter(BytecodeEmitter* bce);
236 };
237 
238 // Class for emitting bytecode for conditional expression.
239 //
240 // Usage: (check for the return value is omitted for simplicity)
241 //
242 //   `cond ? then_expr : else_expr`
243 //     CondEmitter condElse(this);
244 //     condElse.emitCond();
245 //     emit(cond);
246 //     condElse.emitThenElse();
247 //     emit(then_expr);
248 //     condElse.emitElse();
249 //     emit(else_expr);
250 //     condElse.emitEnd();
251 //
252 class MOZ_STACK_CLASS CondEmitter : public BranchEmitterBase {
253 #ifdef DEBUG
254   // The state of this emitter.
255   //
256   // +-------+ emitCond +------+ emitThenElse +----------+
257   // | Start |--------->| Cond |------------->| ThenElse |-+
258   // +-------+          +------+              +----------+ |
259   //                                                       |
260   //                                     +-----------------+
261   //                                     |
262   //                                     | emitElse +------+ emitEnd +-----+
263   //                                     +--------->| Else |-------->| End |
264   //                                                +------+         +-----+
265   enum class State {
266     // The initial state.
267     Start,
268 
269     // After calling emitCond.
270     Cond,
271 
272     // After calling emitThenElse.
273     ThenElse,
274 
275     // After calling emitElse.
276     Else,
277 
278     // After calling emitEnd.
279     End
280   };
281   State state_ = State::Start;
282 #endif
283 
284  public:
285   explicit CondEmitter(BytecodeEmitter* bce);
286 
287   MOZ_MUST_USE bool emitCond();
288   MOZ_MUST_USE bool emitThenElse();
289   MOZ_MUST_USE bool emitElse();
290   MOZ_MUST_USE bool emitEnd();
291 };
292 
293 } /* namespace frontend */
294 } /* namespace js */
295 
296 #endif /* frontend_IfEmitter_h */
297