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