CallOrNewEmitter.cpp (9783B)
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 #include "frontend/CallOrNewEmitter.h" 8 9 #include "frontend/BytecodeEmitter.h" 10 #include "frontend/NameOpEmitter.h" 11 #include "vm/ConstantCompareOperand.h" 12 #include "vm/Opcodes.h" 13 14 using namespace js; 15 using namespace js::frontend; 16 17 CallOrNewEmitter::CallOrNewEmitter(BytecodeEmitter* bce, JSOp op, 18 ArgumentsKind argumentsKind, 19 ValueUsage valueUsage) 20 : bce_(bce), op_(op), argumentsKind_(argumentsKind) { 21 if (op_ == JSOp::Call && valueUsage == ValueUsage::IgnoreValue) { 22 op_ = JSOp::CallIgnoresRv; 23 } 24 25 MOZ_ASSERT(isCall() || isNew() || isSuperCall()); 26 } 27 28 bool CallOrNewEmitter::emitNameCallee(TaggedParserAtomIndex name) { 29 MOZ_ASSERT(state_ == State::Start); 30 31 // [stack] 32 33 NameOpEmitter noe( 34 bce_, name, 35 isCall() ? NameOpEmitter::Kind::Call : NameOpEmitter::Kind::Get); 36 if (!noe.emitGet()) { 37 // [stack] # if isCall() 38 // [stack] CALLEE THIS 39 // [stack] # if isNew() or isSuperCall() 40 // [stack] CALLEE 41 return false; 42 } 43 44 state_ = State::NameCallee; 45 return true; 46 } 47 48 [[nodiscard]] PropOpEmitter& CallOrNewEmitter::prepareForPropCallee( 49 bool isSuperProp) { 50 MOZ_ASSERT(state_ == State::Start); 51 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting); 52 53 // [stack] 54 55 poe_.emplace(bce_, 56 isCall() ? PropOpEmitter::Kind::Call : PropOpEmitter::Kind::Get, 57 isSuperProp ? PropOpEmitter::ObjKind::Super 58 : PropOpEmitter::ObjKind::Other); 59 60 state_ = State::PropCallee; 61 return *poe_; 62 } 63 64 [[nodiscard]] ElemOpEmitter& CallOrNewEmitter::prepareForElemCallee( 65 bool isSuperElem) { 66 MOZ_ASSERT(state_ == State::Start); 67 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting); 68 69 // [stack] 70 71 eoe_.emplace(bce_, 72 isCall() ? ElemOpEmitter::Kind::Call : ElemOpEmitter::Kind::Get, 73 isSuperElem ? ElemOpEmitter::ObjKind::Super 74 : ElemOpEmitter::ObjKind::Other); 75 76 state_ = State::ElemCallee; 77 return *eoe_; 78 } 79 80 PrivateOpEmitter& CallOrNewEmitter::prepareForPrivateCallee( 81 TaggedParserAtomIndex privateName) { 82 MOZ_ASSERT(state_ == State::Start); 83 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting); 84 85 // [stack] 86 87 xoe_.emplace( 88 bce_, 89 isCall() ? PrivateOpEmitter::Kind::Call : PrivateOpEmitter::Kind::Get, 90 privateName); 91 state_ = State::PrivateCallee; 92 return *xoe_; 93 } 94 95 bool CallOrNewEmitter::prepareForFunctionCallee() { 96 MOZ_ASSERT(state_ == State::Start); 97 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting); 98 99 // [stack] 100 101 state_ = State::FunctionCallee; 102 return true; 103 } 104 105 bool CallOrNewEmitter::emitSuperCallee() { 106 MOZ_ASSERT(state_ == State::Start); 107 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting); 108 109 // [stack] 110 111 if (!bce_->emitThisEnvironmentCallee()) { 112 // [stack] CALLEE 113 return false; 114 } 115 if (!bce_->emit1(JSOp::SuperFun)) { 116 // [stack] SUPER_FUN 117 return false; 118 } 119 if (!bce_->emit1(JSOp::IsConstructing)) { 120 // [stack] SUPER_FUN IS_CONSTRUCTING 121 return false; 122 } 123 124 state_ = State::SuperCallee; 125 return true; 126 } 127 128 bool CallOrNewEmitter::prepareForOtherCallee() { 129 MOZ_ASSERT(state_ == State::Start); 130 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting); 131 132 // [stack] 133 134 state_ = State::OtherCallee; 135 return true; 136 } 137 138 bool CallOrNewEmitter::emitThis() { 139 MOZ_ASSERT(state_ == State::NameCallee || state_ == State::PropCallee || 140 state_ == State::ElemCallee || state_ == State::PrivateCallee || 141 state_ == State::FunctionCallee || state_ == State::SuperCallee || 142 state_ == State::OtherCallee); 143 144 // [stack] # if isCall() 145 // [stack] CALLEE THIS? 146 // [stack] # if isNew() or isSuperCall() 147 // [stack] CALLEE 148 149 bool needsThis = false; 150 switch (state_) { 151 case State::NameCallee: 152 if (!isCall()) { 153 needsThis = true; 154 } 155 break; 156 case State::PropCallee: 157 poe_.reset(); 158 if (!isCall()) { 159 needsThis = true; 160 } 161 break; 162 case State::ElemCallee: 163 eoe_.reset(); 164 if (!isCall()) { 165 needsThis = true; 166 } 167 break; 168 case State::PrivateCallee: 169 xoe_.reset(); 170 if (!isCall()) { 171 needsThis = true; 172 } 173 break; 174 case State::FunctionCallee: 175 needsThis = true; 176 break; 177 case State::SuperCallee: 178 break; 179 case State::OtherCallee: 180 needsThis = true; 181 break; 182 default:; 183 } 184 if (needsThis) { 185 if (isNew() || isSuperCall()) { 186 if (!bce_->emit1(JSOp::IsConstructing)) { 187 // [stack] CALLEE IS_CONSTRUCTING 188 return false; 189 } 190 } else { 191 if (!bce_->emit1(JSOp::Undefined)) { 192 // [stack] CALLEE THIS 193 return false; 194 } 195 } 196 } 197 198 // [stack] CALLEE THIS 199 200 state_ = State::This; 201 return true; 202 } 203 204 bool CallOrNewEmitter::prepareForNonSpreadArguments() { 205 MOZ_ASSERT(state_ == State::This); 206 MOZ_ASSERT(!isSpread()); 207 208 // [stack] CALLEE THIS 209 210 state_ = State::Arguments; 211 return true; 212 } 213 214 // See the usage in the comment at the top of the class. 215 bool CallOrNewEmitter::wantSpreadOperand() { 216 MOZ_ASSERT(state_ == State::This); 217 MOZ_ASSERT(isSpread()); 218 219 // [stack] CALLEE THIS 220 221 state_ = State::WantSpreadOperand; 222 return isSingleSpread() || isPassthroughRest(); 223 } 224 225 bool CallOrNewEmitter::prepareForSpreadArguments() { 226 MOZ_ASSERT(state_ == State::WantSpreadOperand); 227 MOZ_ASSERT(isSpread()); 228 MOZ_ASSERT(!isSingleSpread() && !isPassthroughRest()); 229 230 // [stack] CALLEE THIS 231 232 state_ = State::Arguments; 233 return true; 234 } 235 236 bool CallOrNewEmitter::emitSpreadArgumentsTest() { 237 // Caller should check wantSpreadOperand before this. 238 MOZ_ASSERT(state_ == State::WantSpreadOperand); 239 MOZ_ASSERT(isSpread()); 240 MOZ_ASSERT(isSingleSpread() || isPassthroughRest()); 241 242 // [stack] CALLEE THIS ARG0 243 244 if (isSingleSpread()) { 245 // Emit a preparation code to optimize the spread call: 246 // 247 // g(...args); 248 // 249 // If the spread operand is a packed array, skip the spread 250 // operation and pass it directly to spread call operation. 251 // See the comment in OptimizeSpreadCall in Interpreter.cpp 252 // for the optimizable conditions. 253 // [stack] CALLEE THIS ARG0 254 255 ifNotOptimizable_.emplace(bce_); 256 if (!bce_->emit1(JSOp::Dup)) { 257 // [stack] CALLEE THIS ARG0 ARG0 258 return false; 259 } 260 if (!bce_->emit1(JSOp::OptimizeSpreadCall)) { 261 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF 262 return false; 263 } 264 265 if (!bce_->emit1(JSOp::Dup)) { 266 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF ARRAY_OR_UNDEF 267 return false; 268 } 269 270 ConstantCompareOperand operand( 271 ConstantCompareOperand::EncodedType::Undefined); 272 if (!bce_->emitUint16Operand(JSOp::StrictConstantEq, operand.rawValue())) { 273 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF EQ 274 return false; 275 } 276 277 if (!ifNotOptimizable_->emitThenElse()) { 278 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF 279 return false; 280 } 281 if (!bce_->emit1(JSOp::Pop)) { 282 // [stack] CALLEE THIS ARG0 283 return false; 284 } 285 } 286 287 state_ = State::SpreadArgumentsTest; 288 return true; 289 } 290 291 bool CallOrNewEmitter::wantSpreadIteration() { 292 MOZ_ASSERT(state_ == State::SpreadArgumentsTest); 293 MOZ_ASSERT(isSpread()); 294 295 state_ = State::SpreadIteration; 296 return !isPassthroughRest(); 297 } 298 299 bool CallOrNewEmitter::emitSpreadArgumentsTestEnd() { 300 MOZ_ASSERT(state_ == State::SpreadIteration); 301 MOZ_ASSERT(isSpread()); 302 303 if (isSingleSpread()) { 304 if (!ifNotOptimizable_->emitElse()) { 305 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF 306 return false; 307 } 308 if (!bce_->emit1(JSOp::Swap)) { 309 // [stack] CALLEE THIS ARRAY_OR_UNDEF ARG0 310 return false; 311 } 312 if (!bce_->emit1(JSOp::Pop)) { 313 // [stack] CALLEE THIS ARRAY_OR_UNDEF 314 return false; 315 } 316 317 if (!ifNotOptimizable_->emitEnd()) { 318 // [stack] CALLEE THIS ARR 319 return false; 320 } 321 322 ifNotOptimizable_.reset(); 323 } 324 325 state_ = State::Arguments; 326 return true; 327 } 328 329 bool CallOrNewEmitter::emitEnd(uint32_t argc, uint32_t beginPos) { 330 MOZ_ASSERT(state_ == State::Arguments); 331 332 // [stack] # if isCall() 333 // [stack] CALLEE THIS ARG0 ... ARGN 334 // [stack] # if isNew() or isSuperCall() 335 // [stack] CALLEE IS_CONSTRUCTING ARG0 ... ARGN NEW.TARGET? 336 337 if (!bce_->updateSourceCoordNotes(beginPos)) { 338 return false; 339 } 340 if (!bce_->markSimpleBreakpoint()) { 341 return false; 342 } 343 if (!isSpread()) { 344 if (!bce_->emitCall(op_, argc)) { 345 // [stack] RVAL 346 return false; 347 } 348 } else { 349 if (!bce_->emit1(op_)) { 350 // [stack] RVAL 351 return false; 352 } 353 } 354 355 if (isEval()) { 356 uint32_t lineNum = bce_->errorReporter().lineAt(beginPos); 357 if (!bce_->emitUint32Operand(JSOp::Lineno, lineNum)) { 358 // [stack] RVAL 359 return false; 360 } 361 } 362 363 state_ = State::End; 364 return true; 365 }