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_CallOrNewEmitter_h
8 #define frontend_CallOrNewEmitter_h
9 
10 #include "mozilla/Attributes.h"
11 #include "mozilla/Maybe.h"
12 
13 #include <stdint.h>
14 
15 #include "frontend/ElemOpEmitter.h"
16 #include "frontend/IfEmitter.h"
17 #include "frontend/PropOpEmitter.h"
18 #include "frontend/ValueUsage.h"
19 #include "js/TypeDecls.h"
20 #include "vm/BytecodeUtil.h"
21 #include "vm/Opcodes.h"
22 
23 namespace js {
24 namespace frontend {
25 
26 struct BytecodeEmitter;
27 
28 class MOZ_RAII AutoEmittingRunOnceLambda {
29   BytecodeEmitter* bce_;
30 
31  public:
32   explicit AutoEmittingRunOnceLambda(BytecodeEmitter* bce);
33   ~AutoEmittingRunOnceLambda();
34 };
35 
36 // Class for emitting bytecode for call or new expression.
37 //
38 // Usage: (check for the return value is omitted for simplicity)
39 //
40 //   `print(arg);`
41 //     CallOrNewEmitter cone(this, JSOp::Call,
42 //                           CallOrNewEmitter::ArgumentsKind::Other,
43 //                           ValueUsage::WantValue);
44 //     cone.emitNameCallee(print);
45 //     cone.emitThis();
46 //     cone.prepareForNonSpreadArguments();
47 //     emit(arg);
48 //     cone.emitEnd(1, Some(offset_of_callee));
49 //
50 //   `callee.prop(arg1, arg2);`
51 //     CallOrNewEmitter cone(this, JSOp::Call,
52 //                           CallOrNewEmitter::ArgumentsKind::Other,
53 //                           ValueUsage::WantValue);
54 //     PropOpEmitter& poe = cone.prepareForPropCallee(false);
55 //     ... emit `callee.prop` with `poe` here...
56 //     cone.emitThis();
57 //     cone.prepareForNonSpreadArguments();
58 //     emit(arg1);
59 //     emit(arg2);
60 //     cone.emitEnd(2, Some(offset_of_callee));
61 //
62 //   `callee[key](arg);`
63 //     CallOrNewEmitter cone(this, JSOp::Call,
64 //                           CallOrNewEmitter::ArgumentsKind::Other,
65 //                           ValueUsage::WantValue);
66 //     ElemOpEmitter& eoe = cone.prepareForElemCallee(false);
67 //     ... emit `callee[key]` with `eoe` here...
68 //     cone.emitThis();
69 //     cone.prepareForNonSpreadArguments();
70 //     emit(arg);
71 //     cone.emitEnd(1, Some(offset_of_callee));
72 //
73 //   `(function() { ... })(arg);`
74 //     CallOrNewEmitter cone(this, JSOp::Call,
75 //                           CallOrNewEmitter::ArgumentsKind::Other,
76 //                           ValueUsage::WantValue);
77 //     cone.prepareForFunctionCallee();
78 //     emit(function);
79 //     cone.emitThis();
80 //     cone.prepareForNonSpreadArguments();
81 //     emit(arg);
82 //     cone.emitEnd(1, Some(offset_of_callee));
83 //
84 //   `super(arg);`
85 //     CallOrNewEmitter cone(this, JSOp::Call,
86 //                           CallOrNewEmitter::ArgumentsKind::Other,
87 //                           ValueUsage::WantValue);
88 //     cone.emitSuperCallee();
89 //     cone.emitThis();
90 //     cone.prepareForNonSpreadArguments();
91 //     emit(arg);
92 //     cone.emitEnd(1, Some(offset_of_callee));
93 //
94 //   `(some_other_expression)(arg);`
95 //     CallOrNewEmitter cone(this, JSOp::Call,
96 //                           CallOrNewEmitter::ArgumentsKind::Other,
97 //                           ValueUsage::WantValue);
98 //     cone.prepareForOtherCallee();
99 //     emit(some_other_expression);
100 //     cone.emitThis();
101 //     cone.prepareForNonSpreadArguments();
102 //     emit(arg);
103 //     cone.emitEnd(1, Some(offset_of_callee));
104 //
105 //   `print(...arg);`
106 //     CallOrNewEmitter cone(this, JSOp::SpreadCall,
107 //                           CallOrNewEmitter::ArgumentsKind::Other,
108 //                           ValueUsage::WantValue);
109 //     cone.emitNameCallee(print);
110 //     cone.emitThis();
111 //     if (cone.wantSpreadOperand()) {
112 //       emit(arg)
113 //     }
114 //     cone.emitSpreadArgumentsTest();
115 //     emit([...arg]);
116 //     cone.emitEnd(1, Some(offset_of_callee));
117 //
118 //   `print(...rest);`
119 //   where `rest` is rest parameter
120 //     CallOrNewEmitter cone(this, JSOp::SpreadCall,
121 //                           CallOrNewEmitter::ArgumentsKind::SingleSpreadRest,
122 //                           ValueUsage::WantValue);
123 //     cone.emitNameCallee(print);
124 //     cone.emitThis();
125 //     if (cone.wantSpreadOperand()) {
126 //       emit(arg)
127 //     }
128 //     cone.emitSpreadArgumentsTest();
129 //     emit([...arg]);
130 //     cone.emitEnd(1, Some(offset_of_callee));
131 //
132 //   `new f(arg);`
133 //     CallOrNewEmitter cone(this, JSOp::New,
134 //                           CallOrNewEmitter::ArgumentsKind::Other,
135 //                           ValueUsage::WantValue);
136 //     cone.emitNameCallee(f);
137 //     cone.emitThis();
138 //     cone.prepareForNonSpreadArguments();
139 //     emit(arg);
140 //     cone.emitEnd(1, Some(offset_of_callee));
141 //
142 class MOZ_STACK_CLASS CallOrNewEmitter {
143  public:
144   enum class ArgumentsKind {
145     Other,
146 
147     // Specify this for the following case:
148     //
149     //   function f(...rest) {
150     //     g(...rest);
151     //   }
152     //
153     // This enables optimization to avoid allocating an intermediate array
154     // for spread operation.
155     //
156     // wantSpreadOperand() returns true when this is specified.
157     SingleSpreadRest
158   };
159 
160  private:
161   BytecodeEmitter* bce_;
162 
163   // The opcode for the call or new.
164   JSOp op_;
165 
166   // Whether the call is a spread call with single rest parameter or not.
167   // See the comment in emitSpreadArgumentsTest for more details.
168   ArgumentsKind argumentsKind_;
169 
170   // The branch for spread call optimization.
171   mozilla::Maybe<InternalIfEmitter> ifNotOptimizable_;
172 
173   mozilla::Maybe<AutoEmittingRunOnceLambda> autoEmittingRunOnceLambda_;
174 
175   mozilla::Maybe<PropOpEmitter> poe_;
176   mozilla::Maybe<ElemOpEmitter> eoe_;
177 
178   // The state of this emitter.
179   //
180   // +-------+   emitNameCallee           +------------+
181   // | Start |-+------------------------->| NameCallee |------+
182   // +-------+ |                          +------------+      |
183   //           |                                              |
184   //           | prepareForPropCallee     +------------+      v
185   //           +------------------------->| PropCallee |----->+
186   //           |                          +------------+      |
187   //           |                                              |
188   //           | prepareForElemCallee     +------------+      v
189   //           +------------------------->| ElemCallee |----->+
190   //           |                          +------------+      |
191   //           |                                              |
192   //           | prepareForFunctionCallee +----------------+  v
193   //           +------------------------->| FunctionCallee |->+
194   //           |                          +----------------+  |
195   //           |                                              |
196   //           | emitSuperCallee          +-------------+     v
197   //           +------------------------->| SuperCallee |---->+
198   //           |                          +-------------+     |
199   //           |                                              |
200   //           | prepareForOtherCallee    +-------------+     v
201   //           +------------------------->| OtherCallee |---->+
202   //                                      +-------------+     |
203   //                                                          |
204   // +--------------------------------------------------------+
205   // |
206   // | emitThis +------+
207   // +--------->| This |-+
208   //            +------+ |
209   //                     |
210   // +-------------------+
211   // |
212   // | [!isSpread]
213   // |   prepareForNonSpreadArguments    +-----------+ emitEnd +-----+
214   // +------------------------------->+->| Arguments |-------->| End |
215   // |                                ^  +-----------+         +-----+
216   // |                                |
217   // |                                +----------------------------------+
218   // |                                                                   |
219   // | [isSpread]                                                        |
220   // |   wantSpreadOperand +-------------------+ emitSpreadArgumentsTest |
221   // +-------------------->| WantSpreadOperand |-------------------------+
222   //                       +-------------------+
223   enum class State {
224     // The initial state.
225     Start,
226 
227     // After calling emitNameCallee.
228     NameCallee,
229 
230     // After calling prepareForPropCallee.
231     PropCallee,
232 
233     // After calling prepareForElemCallee.
234     ElemCallee,
235 
236     // After calling prepareForFunctionCallee.
237     FunctionCallee,
238 
239     // After calling emitSuperCallee.
240     SuperCallee,
241 
242     // After calling prepareForOtherCallee.
243     OtherCallee,
244 
245     // After calling emitThis.
246     This,
247 
248     // After calling wantSpreadOperand.
249     WantSpreadOperand,
250 
251     // After calling prepareForNonSpreadArguments.
252     Arguments,
253 
254     // After calling emitEnd.
255     End
256   };
257   State state_ = State::Start;
258 
259  public:
260   CallOrNewEmitter(BytecodeEmitter* bce, JSOp op, ArgumentsKind argumentsKind,
261                    ValueUsage valueUsage);
262 
263  private:
isCall()264   MOZ_MUST_USE bool isCall() const {
265     return op_ == JSOp::Call || op_ == JSOp::CallIgnoresRv ||
266            op_ == JSOp::SpreadCall || isEval() || isFunApply() || isFunCall();
267   }
268 
isNew()269   MOZ_MUST_USE bool isNew() const {
270     return op_ == JSOp::New || op_ == JSOp::SpreadNew;
271   }
272 
isSuperCall()273   MOZ_MUST_USE bool isSuperCall() const {
274     return op_ == JSOp::SuperCall || op_ == JSOp::SpreadSuperCall;
275   }
276 
isEval()277   MOZ_MUST_USE bool isEval() const {
278     return op_ == JSOp::Eval || op_ == JSOp::StrictEval ||
279            op_ == JSOp::SpreadEval || op_ == JSOp::StrictSpreadEval;
280   }
281 
isFunApply()282   MOZ_MUST_USE bool isFunApply() const { return op_ == JSOp::FunApply; }
283 
isFunCall()284   MOZ_MUST_USE bool isFunCall() const { return op_ == JSOp::FunCall; }
285 
isSpread()286   MOZ_MUST_USE bool isSpread() const { return JOF_OPTYPE(op_) == JOF_BYTE; }
287 
isSingleSpreadRest()288   MOZ_MUST_USE bool isSingleSpreadRest() const {
289     return argumentsKind_ == ArgumentsKind::SingleSpreadRest;
290   }
291 
292  public:
293   MOZ_MUST_USE bool emitNameCallee(Handle<JSAtom*> name);
294   MOZ_MUST_USE PropOpEmitter& prepareForPropCallee(bool isSuperProp);
295   MOZ_MUST_USE ElemOpEmitter& prepareForElemCallee(bool isSuperElem);
296   MOZ_MUST_USE bool prepareForFunctionCallee();
297   MOZ_MUST_USE bool emitSuperCallee();
298   MOZ_MUST_USE bool prepareForOtherCallee();
299 
300   MOZ_MUST_USE bool emitThis();
301 
302   // Used by BytecodeEmitter::emitPipeline to reuse CallOrNewEmitter instance
303   // across multiple chained calls.
304   void reset();
305 
306   MOZ_MUST_USE bool prepareForNonSpreadArguments();
307 
308   // See the usage in the comment at the top of the class.
309   MOZ_MUST_USE bool wantSpreadOperand();
310   MOZ_MUST_USE bool emitSpreadArgumentsTest();
311 
312   // Parameters are the offset in the source code for each character below:
313   //
314   //   callee(arg);
315   //   ^
316   //   |
317   //   beginPos
318   //
319   // Can be Nothing() if not available.
320   MOZ_MUST_USE bool emitEnd(uint32_t argc,
321                             const mozilla::Maybe<uint32_t>& beginPos);
322 };
323 
324 } /* namespace frontend */
325 } /* namespace js */
326 
327 #endif /* frontend_CallOrNewEmitter_h */
328