BytecodeControlStructures.cpp (12747B)
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/BytecodeControlStructures.h" 8 9 #include "frontend/BytecodeEmitter.h" // BytecodeEmitter 10 #include "frontend/EmitterScope.h" // EmitterScope 11 #include "frontend/ForOfLoopControl.h" // ForOfLoopControl 12 #include "frontend/SwitchEmitter.h" // SwitchEmitter 13 #include "vm/Opcodes.h" // JSOp 14 15 using namespace js; 16 using namespace js::frontend; 17 18 using mozilla::Maybe; 19 20 NestableControl::NestableControl(BytecodeEmitter* bce, StatementKind kind) 21 : Nestable<NestableControl>(&bce->innermostNestableControl), 22 kind_(kind), 23 emitterScope_(bce->innermostEmitterScopeNoCheck()) {} 24 25 BreakableControl::BreakableControl(BytecodeEmitter* bce, StatementKind kind) 26 : NestableControl(bce, kind) { 27 MOZ_ASSERT(is<BreakableControl>()); 28 } 29 30 bool BreakableControl::patchBreaks(BytecodeEmitter* bce) { 31 return bce->emitJumpTargetAndPatch(breaks); 32 } 33 34 LabelControl::LabelControl(BytecodeEmitter* bce, TaggedParserAtomIndex label, 35 BytecodeOffset startOffset) 36 : BreakableControl(bce, StatementKind::Label), 37 label_(label), 38 startOffset_(startOffset) {} 39 40 LoopControl::LoopControl(BytecodeEmitter* bce, StatementKind loopKind) 41 : BreakableControl(bce, loopKind), tdzCache_(bce) { 42 MOZ_ASSERT(is<LoopControl>()); 43 44 LoopControl* enclosingLoop = findNearest<LoopControl>(enclosing()); 45 46 stackDepth_ = bce->bytecodeSection().stackDepth(); 47 loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1; 48 } 49 50 bool LoopControl::emitContinueTarget(BytecodeEmitter* bce) { 51 // Note: this is always called after emitting the loop body so we must have 52 // emitted all 'continues' by now. 53 return bce->emitJumpTargetAndPatch(continues); 54 } 55 56 bool LoopControl::emitLoopHead(BytecodeEmitter* bce, 57 const Maybe<uint32_t>& nextPos) { 58 // Insert a Nop if needed to ensure the script does not start with a 59 // JSOp::LoopHead. This avoids JIT issues with prologue code + try notes 60 // or OSR. See bug 1602390 and bug 1602681. 61 if (bce->bytecodeSection().offset().toUint32() == 0) { 62 if (!bce->emit1(JSOp::Nop)) { 63 return false; 64 } 65 } 66 67 if (nextPos) { 68 if (!bce->updateSourceCoordNotes(*nextPos)) { 69 return false; 70 } 71 } 72 73 MOZ_ASSERT(loopDepth_ > 0); 74 75 head_ = {bce->bytecodeSection().offset()}; 76 77 BytecodeOffset off; 78 if (!bce->emitJumpTargetOp(JSOp::LoopHead, &off)) { 79 return false; 80 } 81 SetLoopHeadDepthHint(bce->bytecodeSection().code(off), loopDepth_); 82 83 return true; 84 } 85 86 bool LoopControl::emitLoopEnd(BytecodeEmitter* bce, JSOp op, 87 TryNoteKind tryNoteKind) { 88 JumpList jump; 89 if (!bce->emitJumpNoFallthrough(op, &jump)) { 90 return false; 91 } 92 bce->patchJumpsToTarget(jump, head_); 93 94 // Create a fallthrough for closing iterators, and as a target for break 95 // statements. 96 JumpTarget breakTarget; 97 if (!bce->emitJumpTarget(&breakTarget)) { 98 return false; 99 } 100 if (!patchBreaks(bce)) { 101 return false; 102 } 103 if (!bce->addTryNote(tryNoteKind, bce->bytecodeSection().stackDepth(), 104 headOffset(), breakTarget.offset)) { 105 return false; 106 } 107 return true; 108 } 109 110 TryFinallyControl::TryFinallyControl(BytecodeEmitter* bce, StatementKind kind) 111 : NestableControl(bce, kind) { 112 MOZ_ASSERT(is<TryFinallyControl>()); 113 } 114 115 bool TryFinallyControl::allocateContinuation(NestableControl* target, 116 NonLocalExitKind kind, 117 uint32_t* idx) { 118 for (uint32_t i = 0; i < continuations_.length(); i++) { 119 if (continuations_[i].target_ == target && 120 continuations_[i].kind_ == kind) { 121 *idx = i + SpecialContinuations::Count; 122 return true; 123 } 124 } 125 *idx = continuations_.length() + SpecialContinuations::Count; 126 return continuations_.emplaceBack(target, kind); 127 } 128 129 bool TryFinallyControl::emitContinuations(BytecodeEmitter* bce) { 130 SwitchEmitter::TableGenerator tableGen(bce); 131 for (uint32_t i = 0; i < continuations_.length(); i++) { 132 if (!tableGen.addNumber(i + SpecialContinuations::Count)) { 133 return false; 134 } 135 } 136 tableGen.finish(continuations_.length()); 137 MOZ_RELEASE_ASSERT(tableGen.isValid()); 138 139 InternalSwitchEmitter se(bce); 140 if (!se.validateCaseCount(continuations_.length())) { 141 return false; 142 } 143 if (!se.emitTable(tableGen)) { 144 return false; 145 } 146 147 // Continuation index 0 is special-cased to be the fallthrough block. 148 // Non-default switch cases are numbered 1-N. 149 uint32_t caseIdx = SpecialContinuations::Count; 150 for (TryFinallyContinuation& continuation : continuations_) { 151 if (!se.emitCaseBody(caseIdx++, tableGen)) { 152 return false; 153 } 154 // Resume the non-local control flow that was intercepted by 155 // this finally. 156 NonLocalExitControl nle(bce, continuation.kind_); 157 if (!nle.emitNonLocalJump(continuation.target_, this)) { 158 return false; 159 } 160 } 161 162 // The only unhandled case is the fallthrough case, which is handled 163 // by the switch default. 164 if (!se.emitDefaultBody()) { 165 return false; 166 } 167 if (!se.emitEnd()) { 168 return false; 169 } 170 return true; 171 } 172 173 NonLocalExitControl::NonLocalExitControl(BytecodeEmitter* bce, 174 NonLocalExitKind kind) 175 : bce_(bce), 176 savedScopeNoteIndex_(bce->bytecodeSection().scopeNoteList().length()), 177 savedDepth_(bce->bytecodeSection().stackDepth()), 178 openScopeNoteIndex_(bce->innermostEmitterScope()->noteIndex()), 179 kind_(kind) {} 180 181 NonLocalExitControl::~NonLocalExitControl() { 182 for (uint32_t n = savedScopeNoteIndex_; 183 n < bce_->bytecodeSection().scopeNoteList().length(); n++) { 184 bce_->bytecodeSection().scopeNoteList().recordEnd( 185 n, bce_->bytecodeSection().offset()); 186 } 187 bce_->bytecodeSection().setStackDepth(savedDepth_); 188 } 189 190 bool NonLocalExitControl::emitReturn(BytecodeOffset setRvalOffset) { 191 MOZ_ASSERT(kind_ == NonLocalExitKind::Return); 192 setRvalOffset_ = setRvalOffset; 193 return emitNonLocalJump(nullptr); 194 } 195 196 bool NonLocalExitControl::leaveScope(EmitterScope* es) { 197 if (!es->leave(bce_, /* nonLocal = */ true)) { 198 return false; 199 } 200 201 // As we pop each scope due to the non-local jump, emit notes that 202 // record the extent of the enclosing scope. These notes will have 203 // their ends recorded in ~NonLocalExitControl(). 204 GCThingIndex enclosingScopeIndex = ScopeNote::NoScopeIndex; 205 if (es->enclosingInFrame()) { 206 enclosingScopeIndex = es->enclosingInFrame()->index(); 207 } 208 if (!bce_->bytecodeSection().scopeNoteList().append( 209 enclosingScopeIndex, bce_->bytecodeSection().offset(), 210 openScopeNoteIndex_)) { 211 return false; 212 } 213 openScopeNoteIndex_ = bce_->bytecodeSection().scopeNoteList().length() - 1; 214 215 return true; 216 } 217 218 /* 219 * Emit additional bytecode(s) for non-local jumps. 220 */ 221 bool NonLocalExitControl::emitNonLocalJump(NestableControl* target, 222 NestableControl* startingAfter) { 223 NestableControl* startingControl = startingAfter 224 ? startingAfter->enclosing() 225 : bce_->innermostNestableControl; 226 EmitterScope* es = startingAfter ? startingAfter->emitterScope() 227 : bce_->innermostEmitterScope(); 228 229 int npops = 0; 230 231 AutoCheckUnstableEmitterScope cues(bce_); 232 233 // We emit IteratorClose bytecode inline. 'continue' statements do 234 // not call IteratorClose for the loop they are continuing. 235 bool emitIteratorCloseAtTarget = kind_ != NonLocalExitKind::Continue; 236 237 auto flushPops = [&npops](BytecodeEmitter* bce) { 238 if (npops && !bce->emitPopN(npops)) { 239 return false; 240 } 241 npops = 0; 242 return true; 243 }; 244 245 // If we are closing multiple for-of loops, the resulting 246 // TryNoteKind::ForOfIterClose trynotes must be appropriately nested. Each 247 // TryNoteKind::ForOfIterClose starts when we close the corresponding for-of 248 // iterator, and continues until the actual jump. 249 Vector<BytecodeOffset, 4> forOfIterCloseScopeStarts(bce_->fc); 250 251 // If we have to execute a finally block, then we will jump there now and 252 // continue the non-local jump from the end of the finally block. 253 bool jumpingToFinally = false; 254 255 // Walk the nestable control stack and patch jumps. 256 for (NestableControl* control = startingControl; 257 control != target && !jumpingToFinally; control = control->enclosing()) { 258 // Walk the scope stack and leave the scopes we entered. Leaving a scope 259 // may emit administrative ops like JSOp::PopLexicalEnv but never anything 260 // that manipulates the stack. 261 for (; es != control->emitterScope(); es = es->enclosingInFrame()) { 262 if (!leaveScope(es)) { 263 return false; 264 } 265 } 266 267 switch (control->kind()) { 268 case StatementKind::Finally: { 269 TryFinallyControl& finallyControl = control->as<TryFinallyControl>(); 270 if (finallyControl.emittingSubroutine()) { 271 /* 272 * There's a [resume-index-or-exception, exception-stack, throwing] 273 * triple on the stack that we need to pop. If the script is not a 274 * noScriptRval script, we also need to pop the cached rval. 275 */ 276 if (bce_->sc->noScriptRval()) { 277 npops += 3; 278 } else { 279 npops += 4; 280 } 281 } else { 282 jumpingToFinally = true; 283 284 if (!flushPops(bce_)) { 285 return false; 286 } 287 uint32_t idx; 288 if (!finallyControl.allocateContinuation(target, kind_, &idx)) { 289 return false; 290 } 291 if (!bce_->emitJumpToFinally(&finallyControl.finallyJumps_, idx)) { 292 return false; 293 } 294 } 295 break; 296 } 297 298 case StatementKind::ForOfLoop: { 299 if (!flushPops(bce_)) { 300 return false; 301 } 302 BytecodeOffset tryNoteStart; 303 ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>(); 304 if (!loopinfo.emitPrepareForNonLocalJumpFromScope( 305 bce_, *es, 306 /* isTarget = */ false, &tryNoteStart)) { 307 // [stack] ... 308 return false; 309 } 310 if (!forOfIterCloseScopeStarts.append(tryNoteStart)) { 311 return false; 312 } 313 break; 314 } 315 316 case StatementKind::ForInLoop: 317 if (!flushPops(bce_)) { 318 return false; 319 } 320 321 // The iterator and the current value are on the stack. 322 if (!bce_->emit1(JSOp::EndIter)) { 323 // [stack] ... 324 return false; 325 } 326 break; 327 328 default: 329 break; 330 } 331 } 332 333 if (!flushPops(bce_)) { 334 return false; 335 } 336 337 if (!jumpingToFinally) { 338 if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) { 339 BytecodeOffset tryNoteStart; 340 ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>(); 341 if (!loopinfo.emitPrepareForNonLocalJumpFromScope(bce_, *es, 342 /* isTarget = */ true, 343 &tryNoteStart)) { 344 // [stack] ... UNDEF UNDEF UNDEF 345 return false; 346 } 347 if (!forOfIterCloseScopeStarts.append(tryNoteStart)) { 348 return false; 349 } 350 } 351 352 EmitterScope* targetEmitterScope = 353 target ? target->emitterScope() : bce_->varEmitterScope; 354 for (; es != targetEmitterScope; es = es->enclosingInFrame()) { 355 if (!leaveScope(es)) { 356 return false; 357 } 358 } 359 switch (kind_) { 360 case NonLocalExitKind::Continue: { 361 LoopControl* loop = &target->as<LoopControl>(); 362 if (!bce_->emitJump(JSOp::Goto, &loop->continues)) { 363 return false; 364 } 365 break; 366 } 367 case NonLocalExitKind::Break: { 368 BreakableControl* breakable = &target->as<BreakableControl>(); 369 if (!bce_->emitJump(JSOp::Goto, &breakable->breaks)) { 370 return false; 371 } 372 break; 373 } 374 case NonLocalExitKind::Return: 375 MOZ_ASSERT(!target); 376 if (!bce_->finishReturn(setRvalOffset_)) { 377 return false; 378 } 379 break; 380 } 381 } 382 383 // Close TryNoteKind::ForOfIterClose trynotes. 384 BytecodeOffset end = bce_->bytecodeSection().offset(); 385 for (BytecodeOffset start : forOfIterCloseScopeStarts) { 386 if (!bce_->addTryNote(TryNoteKind::ForOfIterClose, 0, start, end)) { 387 return false; 388 } 389 } 390 391 return true; 392 }