NameOpEmitter.cpp (16533B)
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/NameOpEmitter.h" 8 9 #include "frontend/AbstractScopePtr.h" 10 #include "frontend/BytecodeEmitter.h" 11 #include "frontend/ParserAtom.h" // ParserAtom 12 #include "frontend/SharedContext.h" 13 #include "frontend/TDZCheckCache.h" 14 #include "frontend/ValueUsage.h" 15 #include "js/Value.h" 16 #include "vm/Opcodes.h" 17 18 using namespace js; 19 using namespace js::frontend; 20 21 NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, TaggedParserAtomIndex name, 22 Kind kind) 23 : bce_(bce), kind_(kind), name_(name), loc_(bce_->lookupName(name_)) {} 24 25 NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, TaggedParserAtomIndex name, 26 const NameLocation& loc, Kind kind) 27 : bce_(bce), kind_(kind), name_(name), loc_(loc) {} 28 29 bool NameOpEmitter::emitGet() { 30 MOZ_ASSERT(state_ == State::Start); 31 32 bool needsImplicitThis = false; 33 if (isCall()) { 34 switch (loc_.kind()) { 35 case NameLocation::Kind::Dynamic: 36 if (bce_->needsImplicitThis()) { 37 needsImplicitThis = true; 38 break; 39 } 40 [[fallthrough]]; 41 case NameLocation::Kind::Global: 42 MOZ_ASSERT(bce_->outermostScope().hasNonSyntacticScopeOnChain() == 43 bce_->sc->hasNonSyntacticScope()); 44 needsImplicitThis = bce_->sc->hasNonSyntacticScope(); 45 break; 46 case NameLocation::Kind::Intrinsic: 47 case NameLocation::Kind::NamedLambdaCallee: 48 case NameLocation::Kind::Import: 49 case NameLocation::Kind::ArgumentSlot: 50 case NameLocation::Kind::FrameSlot: 51 case NameLocation::Kind::EnvironmentCoordinate: 52 case NameLocation::Kind::DebugEnvironmentCoordinate: 53 case NameLocation::Kind::DynamicAnnexBVar: 54 break; 55 } 56 } 57 58 switch (loc_.kind()) { 59 case NameLocation::Kind::Global: 60 MOZ_ASSERT(bce_->outermostScope().hasNonSyntacticScopeOnChain() == 61 bce_->sc->hasNonSyntacticScope()); 62 if (!bce_->sc->hasNonSyntacticScope()) { 63 MOZ_ASSERT(!needsImplicitThis); 64 65 // Some names on the global are not configurable and have fixed values 66 // which we can emit instead. 67 if (name_ == TaggedParserAtomIndex::WellKnown::undefined()) { 68 if (!bce_->emit1(JSOp::Undefined)) { 69 return false; 70 } 71 } else if (name_ == TaggedParserAtomIndex::WellKnown::NaN()) { 72 if (!bce_->emitDouble(JS::GenericNaN())) { 73 return false; 74 } 75 } else if (name_ == TaggedParserAtomIndex::WellKnown::Infinity()) { 76 if (!bce_->emitDouble(JS::Infinity())) { 77 return false; 78 } 79 } else { 80 if (!bce_->emitAtomOp(JSOp::GetGName, name_)) { 81 // [stack] VAL 82 return false; 83 } 84 } 85 break; 86 } 87 [[fallthrough]]; 88 case NameLocation::Kind::Dynamic: 89 if (needsImplicitThis) { 90 if (!bce_->emitAtomOp(JSOp::BindName, name_)) { 91 // [stack] ENV 92 return false; 93 } 94 if (!bce_->emit1(JSOp::Dup)) { 95 // [stack] ENV ENV 96 return false; 97 } 98 if (!bce_->emitAtomOp(JSOp::GetBoundName, name_)) { 99 // [stack] ENV V 100 return false; 101 } 102 } else { 103 if (!bce_->emitAtomOp(JSOp::GetName, name_)) { 104 // [stack] VAL 105 return false; 106 } 107 } 108 break; 109 case NameLocation::Kind::Intrinsic: 110 if (name_ == TaggedParserAtomIndex::WellKnown::undefined()) { 111 if (!bce_->emit1(JSOp::Undefined)) { 112 // [stack] Undefined 113 return false; 114 } 115 } else { 116 if (!bce_->emitAtomOp(JSOp::GetIntrinsic, name_)) { 117 // [stack] VAL 118 return false; 119 } 120 } 121 break; 122 case NameLocation::Kind::NamedLambdaCallee: 123 if (!bce_->emit1(JSOp::Callee)) { 124 // [stack] VAL 125 return false; 126 } 127 break; 128 case NameLocation::Kind::Import: 129 if (!bce_->emitAtomOp(JSOp::GetImport, name_)) { 130 // [stack] VAL 131 return false; 132 } 133 break; 134 case NameLocation::Kind::ArgumentSlot: 135 if (!bce_->emitArgOp(JSOp::GetArg, loc_.argumentSlot())) { 136 // [stack] VAL 137 return false; 138 } 139 break; 140 case NameLocation::Kind::FrameSlot: 141 if (!bce_->emitLocalOp(JSOp::GetLocal, loc_.frameSlot())) { 142 // [stack] VAL 143 return false; 144 } 145 if (loc_.isLexical()) { 146 if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::Yes)) { 147 // [stack] VAL 148 return false; 149 } 150 } 151 break; 152 case NameLocation::Kind::EnvironmentCoordinate: 153 case NameLocation::Kind::DebugEnvironmentCoordinate: 154 if (!bce_->emitEnvCoordOp( 155 loc_.kind() == NameLocation::Kind::EnvironmentCoordinate 156 ? JSOp::GetAliasedVar 157 : JSOp::GetAliasedDebugVar, 158 loc_.environmentCoordinate())) { 159 // [stack] VAL 160 return false; 161 } 162 if (loc_.isLexical()) { 163 if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::Yes)) { 164 // [stack] VAL 165 return false; 166 } 167 } 168 break; 169 case NameLocation::Kind::DynamicAnnexBVar: 170 MOZ_CRASH( 171 "Synthesized vars for Annex B.3.3 should only be used in " 172 "initialization"); 173 } 174 175 if (isCall()) { 176 switch (loc_.kind()) { 177 case NameLocation::Kind::Dynamic: 178 case NameLocation::Kind::Global: 179 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting); 180 if (needsImplicitThis) { 181 if (!bce_->emit1(JSOp::Swap)) { 182 // [stack] CALLEE ENV 183 return false; 184 } 185 if (!bce_->emit1(JSOp::ImplicitThis)) { 186 // [stack] CALLEE THIS 187 return false; 188 } 189 } else { 190 if (!bce_->emit1(JSOp::Undefined)) { 191 // [stack] CALLEE UNDEF 192 return false; 193 } 194 } 195 break; 196 case NameLocation::Kind::Intrinsic: 197 case NameLocation::Kind::NamedLambdaCallee: 198 case NameLocation::Kind::Import: 199 case NameLocation::Kind::ArgumentSlot: 200 case NameLocation::Kind::FrameSlot: 201 case NameLocation::Kind::EnvironmentCoordinate: 202 if (bce_->emitterMode == BytecodeEmitter::SelfHosting) { 203 if (!bce_->emitDebugCheckSelfHosted()) { 204 // [stack] CALLEE 205 return false; 206 } 207 } 208 if (!bce_->emit1(JSOp::Undefined)) { 209 // [stack] CALLEE UNDEF 210 return false; 211 } 212 break; 213 case NameLocation::Kind::DebugEnvironmentCoordinate: 214 MOZ_CRASH( 215 "DebugEnvironmentCoordinate should only be used to get the private " 216 "brand, and so should never call."); 217 break; 218 case NameLocation::Kind::DynamicAnnexBVar: 219 MOZ_CRASH( 220 "Synthesized vars for Annex B.3.3 should only be used in " 221 "initialization"); 222 } 223 } 224 225 #ifdef DEBUG 226 state_ = State::Get; 227 #endif 228 return true; 229 } 230 231 bool NameOpEmitter::prepareForRhs() { 232 MOZ_ASSERT(state_ == State::Start); 233 234 switch (loc_.kind()) { 235 case NameLocation::Kind::Dynamic: 236 case NameLocation::Kind::Import: 237 if (!bce_->makeAtomIndex(name_, ParserAtom::Atomize::Yes, &atomIndex_)) { 238 return false; 239 } 240 if (!bce_->emitAtomOp(JSOp::BindUnqualifiedName, atomIndex_)) { 241 // [stack] ENV 242 return false; 243 } 244 emittedBindOp_ = true; 245 break; 246 case NameLocation::Kind::DynamicAnnexBVar: 247 // Annex B vars always go on the nearest variable environment, even if 248 // lexical environments in between contain same-named bindings. 249 if (!bce_->emit1(JSOp::BindVar)) { 250 // [stack] ENV 251 return false; 252 } 253 emittedBindOp_ = true; 254 break; 255 case NameLocation::Kind::Global: 256 if (!bce_->makeAtomIndex(name_, ParserAtom::Atomize::Yes, &atomIndex_)) { 257 return false; 258 } 259 MOZ_ASSERT(bce_->outermostScope().hasNonSyntacticScopeOnChain() == 260 bce_->sc->hasNonSyntacticScope()); 261 262 if (loc_.isLexical() && isInitialize()) { 263 // InitGLexical always gets the global lexical scope. It doesn't 264 // need a BindUnqualifiedName/BindUnqualifiedGName. 265 MOZ_ASSERT(bce_->innermostScope().is<GlobalScope>()); 266 } else { 267 JSOp op; 268 if (bce_->sc->hasNonSyntacticScope()) { 269 op = JSOp::BindUnqualifiedName; 270 } else { 271 op = JSOp::BindUnqualifiedGName; 272 } 273 if (!bce_->emitAtomOp(op, atomIndex_)) { 274 // [stack] ENV 275 return false; 276 } 277 emittedBindOp_ = true; 278 } 279 break; 280 case NameLocation::Kind::Intrinsic: 281 case NameLocation::Kind::NamedLambdaCallee: 282 case NameLocation::Kind::ArgumentSlot: 283 case NameLocation::Kind::FrameSlot: 284 case NameLocation::Kind::DebugEnvironmentCoordinate: 285 case NameLocation::Kind::EnvironmentCoordinate: 286 break; 287 } 288 289 // For compound assignments, first get the LHS value, then emit 290 // the RHS and the op. 291 if (isCompoundAssignment() || isIncDec()) { 292 if (loc_.kind() == NameLocation::Kind::Dynamic) { 293 // For dynamic accesses we need to emit GetBoundName instead of 294 // GetName for correctness: looking up @@unscopables on the 295 // environment chain (due to 'with' environments) must only happen 296 // once. 297 // 298 // GetBoundName uses the environment already pushed on the stack 299 // from the earlier BindName. 300 if (!bce_->emit1(JSOp::Dup)) { 301 // [stack] ENV ENV 302 return false; 303 } 304 if (!bce_->emitAtomOp(JSOp::GetBoundName, atomIndex_)) { 305 // [stack] ENV V 306 return false; 307 } 308 } else { 309 if (!emitGet()) { 310 // [stack] ENV? V 311 return false; 312 } 313 } 314 } 315 316 #ifdef DEBUG 317 state_ = State::Rhs; 318 #endif 319 return true; 320 } 321 322 JSOp NameOpEmitter::strictifySetNameOp(JSOp op) const { 323 switch (op) { 324 case JSOp::SetName: 325 if (bce_->sc->strict()) { 326 op = JSOp::StrictSetName; 327 } 328 break; 329 case JSOp::SetGName: 330 if (bce_->sc->strict()) { 331 op = JSOp::StrictSetGName; 332 } 333 break; 334 default: 335 MOZ_CRASH("Invalid SetName op"); 336 } 337 return op; 338 } 339 340 #if defined(__clang__) && defined(XP_WIN) && \ 341 (defined(_M_X64) || defined(__x86_64__)) 342 // Work around a CPU bug. See bug 1524257. 343 __attribute__((__aligned__(32))) 344 #endif 345 bool NameOpEmitter::emitAssignment() { 346 MOZ_ASSERT(state_ == State::Rhs); 347 348 // [stack] # If emittedBindOp_ 349 // [stack] ENV V 350 // [stack] # else 351 // [stack] V 352 353 switch (loc_.kind()) { 354 case NameLocation::Kind::Dynamic: 355 case NameLocation::Kind::Import: 356 MOZ_ASSERT(emittedBindOp_); 357 if (!bce_->emitAtomOp(strictifySetNameOp(JSOp::SetName), atomIndex_)) { 358 return false; 359 } 360 break; 361 case NameLocation::Kind::DynamicAnnexBVar: 362 MOZ_ASSERT(emittedBindOp_); 363 if (!bce_->emitAtomOp(strictifySetNameOp(JSOp::SetName), name_)) { 364 return false; 365 } 366 break; 367 case NameLocation::Kind::Global: { 368 JSOp op; 369 if (emittedBindOp_) { 370 MOZ_ASSERT(bce_->outermostScope().hasNonSyntacticScopeOnChain() == 371 bce_->sc->hasNonSyntacticScope()); 372 if (bce_->sc->hasNonSyntacticScope()) { 373 op = strictifySetNameOp(JSOp::SetName); 374 } else { 375 op = strictifySetNameOp(JSOp::SetGName); 376 } 377 } else { 378 op = JSOp::InitGLexical; 379 } 380 if (!bce_->emitAtomOp(op, atomIndex_)) { 381 return false; 382 } 383 break; 384 } 385 case NameLocation::Kind::Intrinsic: 386 if (!bce_->emitAtomOp(JSOp::SetIntrinsic, name_)) { 387 return false; 388 } 389 break; 390 case NameLocation::Kind::NamedLambdaCallee: 391 // Assigning to the named lambda is a no-op in sloppy mode but 392 // throws in strict mode. 393 if (bce_->sc->strict()) { 394 if (!bce_->emitAtomOp(JSOp::ThrowSetConst, name_)) { 395 return false; 396 } 397 } 398 break; 399 case NameLocation::Kind::ArgumentSlot: 400 if (!bce_->emitArgOp(JSOp::SetArg, loc_.argumentSlot())) { 401 return false; 402 } 403 break; 404 case NameLocation::Kind::FrameSlot: { 405 JSOp op = JSOp::SetLocal; 406 // Lexicals, Synthetics and Private Methods have very similar handling 407 // around a variety of areas, including initialization. 408 if (loc_.isLexical() || loc_.isPrivateMethod() || loc_.isSynthetic()) { 409 if (isInitialize()) { 410 op = JSOp::InitLexical; 411 } else { 412 if (loc_.isConst()) { 413 op = JSOp::ThrowSetConst; 414 } 415 if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::No)) { 416 return false; 417 } 418 } 419 } 420 if (op == JSOp::ThrowSetConst) { 421 if (!bce_->emitAtomOp(op, name_)) { 422 return false; 423 } 424 } else { 425 if (!bce_->emitLocalOp(op, loc_.frameSlot())) { 426 return false; 427 } 428 } 429 if (op == JSOp::InitLexical) { 430 if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_, 431 DontCheckTDZ)) { 432 return false; 433 } 434 } 435 break; 436 } 437 case NameLocation::Kind::EnvironmentCoordinate: { 438 JSOp op = JSOp::SetAliasedVar; 439 // Lexicals, Synthetics and Private Methods have very similar handling 440 // around a variety of areas, including initialization. 441 if (loc_.isLexical() || loc_.isPrivateMethod() || loc_.isSynthetic()) { 442 if (isInitialize()) { 443 op = JSOp::InitAliasedLexical; 444 } else { 445 if (loc_.isConst()) { 446 op = JSOp::ThrowSetConst; 447 } 448 if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::No)) { 449 return false; 450 } 451 } 452 } else if (loc_.bindingKind() == BindingKind::NamedLambdaCallee) { 453 // Assigning to the named lambda is a no-op in sloppy mode and throws 454 // in strict mode. 455 if (!bce_->sc->strict()) { 456 break; 457 } 458 op = JSOp::ThrowSetConst; 459 } 460 if (op == JSOp::ThrowSetConst) { 461 if (!bce_->emitAtomOp(op, name_)) { 462 return false; 463 } 464 } else { 465 if (!bce_->emitEnvCoordOp(op, loc_.environmentCoordinate())) { 466 return false; 467 } 468 } 469 if (op == JSOp::InitAliasedLexical) { 470 if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_, 471 DontCheckTDZ)) { 472 return false; 473 } 474 } 475 break; 476 } 477 case NameLocation::Kind::DebugEnvironmentCoordinate: 478 MOZ_CRASH("Shouldn't be assigning to a private brand"); 479 break; 480 } 481 482 #ifdef DEBUG 483 state_ = State::Assignment; 484 #endif 485 return true; 486 } 487 488 bool NameOpEmitter::emitIncDec(ValueUsage valueUsage) { 489 MOZ_ASSERT(state_ == State::Start); 490 491 JSOp incOp = isInc() ? JSOp::Inc : JSOp::Dec; 492 if (!prepareForRhs()) { 493 // [stack] ENV? V 494 return false; 495 } 496 if (!bce_->emit1(JSOp::ToNumeric)) { 497 // [stack] ENV? N 498 return false; 499 } 500 if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { 501 if (!bce_->emit1(JSOp::Dup)) { 502 // [stack] ENV? N N 503 return false; 504 } 505 } 506 if (!bce_->emit1(incOp)) { 507 // [stack] ENV? N? N+1 508 return false; 509 } 510 if (isPostIncDec() && emittedBindOp() && 511 valueUsage == ValueUsage::WantValue) { 512 if (!bce_->emit2(JSOp::Pick, 2)) { 513 // [stack] N N+1 ENV 514 return false; 515 } 516 if (!bce_->emit1(JSOp::Swap)) { 517 // [stack] N ENV N+1 518 return false; 519 } 520 } 521 if (!emitAssignment()) { 522 // [stack] N? N+1 523 return false; 524 } 525 if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { 526 if (!bce_->emit1(JSOp::Pop)) { 527 // [stack] N 528 return false; 529 } 530 } 531 532 #ifdef DEBUG 533 state_ = State::IncDec; 534 #endif 535 return true; 536 }