TryEmitter.cpp (9101B)
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/TryEmitter.h" 8 9 #include "mozilla/Assertions.h" // MOZ_ASSERT 10 11 #include "frontend/BytecodeEmitter.h" // BytecodeEmitter 12 #include "frontend/IfEmitter.h" // BytecodeEmitter 13 #include "frontend/SharedContext.h" // StatementKind 14 #include "vm/Opcodes.h" // JSOp 15 16 using namespace js; 17 using namespace js::frontend; 18 19 using mozilla::Maybe; 20 21 TryEmitter::TryEmitter(BytecodeEmitter* bce, Kind kind, ControlKind controlKind) 22 : bce_(bce), 23 kind_(kind), 24 controlKind_(controlKind), 25 depth_(0), 26 tryOpOffset_(0) 27 #ifdef DEBUG 28 , 29 state_(State::Start) 30 #endif 31 { 32 MOZ_ASSERT_IF(controlKind_ == ControlKind::Disposal, 33 kind_ == Kind::TryFinally); 34 } 35 36 #ifdef DEBUG 37 bool TryEmitter::hasControlInfo() { return controlInfo_.get() != nullptr; } 38 #endif 39 40 bool TryEmitter::emitTry() { 41 MOZ_ASSERT(state_ == State::Start); 42 43 if (requiresControlInfo()) { 44 controlInfo_ = bce_->fc->getAllocator()->make_unique<TryFinallyControl>( 45 bce_, hasFinally() ? StatementKind::Finally : StatementKind::Try); 46 if (!controlInfo_) { 47 return false; 48 } 49 } 50 51 // Since an exception can be thrown at any place inside the try block, 52 // we need to restore the stack and the scope chain before we transfer 53 // the control to the exception handler. 54 // 55 // For that we store in a try note associated with the catch or 56 // finally block the stack depth upon the try entry. The interpreter 57 // uses this depth to properly unwind the stack and the scope chain. 58 depth_ = bce_->bytecodeSection().stackDepth(); 59 60 tryOpOffset_ = bce_->bytecodeSection().offset(); 61 if (!bce_->emit1(JSOp::Try)) { 62 return false; 63 } 64 65 #ifdef DEBUG 66 state_ = State::Try; 67 #endif 68 return true; 69 } 70 71 bool TryEmitter::emitJumpToFinallyWithFallthrough() { 72 uint32_t stackDepthForNextBlock = bce_->bytecodeSection().stackDepth(); 73 74 // The fallthrough continuation is special-cased with index 0. 75 uint32_t idx = TryFinallyControl::SpecialContinuations::Fallthrough; 76 if (!bce_->emitJumpToFinally(&controlInfo_->finallyJumps_, idx)) { 77 return false; 78 } 79 80 // Reset the stack depth for the following catch or finally block. 81 bce_->bytecodeSection().setStackDepth(stackDepthForNextBlock); 82 return true; 83 } 84 85 bool TryEmitter::emitTryEnd() { 86 MOZ_ASSERT(state_ == State::Try); 87 MOZ_ASSERT(depth_ == bce_->bytecodeSection().stackDepth()); 88 89 if (hasFinally() && controlInfo_) { 90 if (!emitJumpToFinallyWithFallthrough()) { 91 return false; 92 } 93 } else { 94 // Emit jump over catch 95 if (!bce_->emitJump(JSOp::Goto, &catchAndFinallyJump_)) { 96 return false; 97 } 98 } 99 100 if (!bce_->emitJumpTarget(&tryEnd_)) { 101 return false; 102 } 103 104 return true; 105 } 106 107 bool TryEmitter::emitCatch(ExceptionStack stack) { 108 MOZ_ASSERT(state_ == State::Try); 109 if (!emitTryEnd()) { 110 return false; 111 } 112 113 MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == depth_); 114 115 if (shouldUpdateRval()) { 116 // Clear the frame's return value that might have been set by the 117 // try block: 118 // 119 // eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1 120 if (!bce_->emit1(JSOp::Undefined)) { 121 return false; 122 } 123 if (!bce_->emit1(JSOp::SetRval)) { 124 return false; 125 } 126 } 127 128 if (stack == ExceptionStack::No) { 129 if (!bce_->emit1(JSOp::Exception)) { 130 return false; 131 } 132 } else { 133 if (!bce_->emit1(JSOp::ExceptionAndStack)) { 134 return false; 135 } 136 } 137 138 #ifdef DEBUG 139 state_ = State::Catch; 140 #endif 141 return true; 142 } 143 144 bool TryEmitter::emitCatchEnd() { 145 MOZ_ASSERT(state_ == State::Catch); 146 147 if (!controlInfo_) { 148 return true; 149 } 150 151 // Jump to <finally>, if required. 152 if (hasFinally()) { 153 if (!emitJumpToFinallyWithFallthrough()) { 154 return false; 155 } 156 } 157 158 return true; 159 } 160 161 bool TryEmitter::emitFinally( 162 const Maybe<uint32_t>& finallyPos /* = Nothing() */) { 163 // If we are using controlInfo_ (i.e., emitting a syntactic try 164 // blocks), we must have specified up front if there will be a finally 165 // close. For internal non-syntactic try blocks, like those emitted for 166 // yield* and IteratorClose inside for-of loops, we can emitFinally even 167 // without specifying up front, since the internal non-syntactic try 168 // blocks emit no JSOp::Goto. 169 if (!controlInfo_) { 170 if (kind_ == Kind::TryCatch) { 171 kind_ = Kind::TryCatchFinally; 172 } 173 } else { 174 MOZ_ASSERT(hasFinally()); 175 } 176 177 if (!hasCatch()) { 178 MOZ_ASSERT(state_ == State::Try); 179 if (!emitTryEnd()) { 180 return false; 181 } 182 } else { 183 MOZ_ASSERT(state_ == State::Catch); 184 if (!emitCatchEnd()) { 185 return false; 186 } 187 } 188 189 MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == depth_); 190 191 // Upon entry to the finally, there are three additional values on the stack: 192 // a boolean value to indicate whether we're throwing an exception, the 193 // exception stack (if we're throwing) or null, and either that exception (if 194 // we're throwing) or a resume index to which we will return (if we're not 195 // throwing). 196 bce_->bytecodeSection().setStackDepth(depth_ + 3); 197 198 if (!bce_->emitJumpTarget(&finallyStart_)) { 199 return false; 200 } 201 202 if (controlInfo_) { 203 // Fix up the jumps to the finally code. 204 bce_->patchJumpsToTarget(controlInfo_->finallyJumps_, finallyStart_); 205 206 // Indicate that we're emitting a subroutine body. 207 controlInfo_->setEmittingSubroutine(); 208 } 209 if (finallyPos) { 210 if (!bce_->updateSourceCoordNotes(finallyPos.value())) { 211 return false; 212 } 213 } 214 if (!bce_->emit1(JSOp::Finally)) { 215 return false; 216 } 217 218 if (shouldUpdateRval()) { 219 if (!bce_->emit1(JSOp::GetRval)) { 220 return false; 221 } 222 223 // Clear the frame's return value to make break/continue return 224 // correct value even if there's no other statement before them: 225 // 226 // eval("x: try { 1 } finally { break x; }"); // undefined, not 1 227 if (!bce_->emit1(JSOp::Undefined)) { 228 return false; 229 } 230 if (!bce_->emit1(JSOp::SetRval)) { 231 return false; 232 } 233 } 234 235 #ifdef DEBUG 236 state_ = State::Finally; 237 #endif 238 return true; 239 } 240 241 bool TryEmitter::emitFinallyEnd() { 242 MOZ_ASSERT(state_ == State::Finally); 243 244 if (shouldUpdateRval()) { 245 if (!bce_->emit1(JSOp::SetRval)) { 246 return false; 247 } 248 } 249 250 // [stack] RESUME_INDEX_OR_EXCEPTION, EXCEPTION_STACK, THROWING 251 252 InternalIfEmitter ifThrowing(bce_); 253 if (!ifThrowing.emitThenElse()) { 254 // [stack] RESUME_INDEX_OR_EXCEPTION, EXCEPTION_STACK 255 return false; 256 } 257 258 if (!bce_->emit1(JSOp::ThrowWithStack)) { 259 // [stack] 260 return false; 261 } 262 263 if (!ifThrowing.emitElse()) { 264 // [stack] RESUME_INDEX_OR_EXCEPTION, EXCEPTION_STACK 265 return false; 266 } 267 268 if (!bce_->emit1(JSOp::Pop)) { 269 // [stack] RESUME_INDEX_OR_EXCEPTION 270 return false; 271 } 272 273 if (controlInfo_ && !controlInfo_->continuations_.empty()) { 274 if (!controlInfo_->emitContinuations(bce_)) { 275 // [stack] 276 return false; 277 } 278 } else { 279 // If there are no non-local jumps, then the only possible jump target 280 // is the code immediately following this finally block. Instead of 281 // emitting a tableswitch, we can simply pop the continuation index 282 // and fall through. 283 if (!bce_->emit1(JSOp::Pop)) { 284 // [stack] 285 return false; 286 } 287 } 288 289 if (!ifThrowing.emitEnd()) { 290 return false; 291 } 292 293 bce_->hasTryFinally = true; 294 return true; 295 } 296 297 bool TryEmitter::emitEnd() { 298 if (!hasFinally()) { 299 MOZ_ASSERT(state_ == State::Catch); 300 if (!emitCatchEnd()) { 301 return false; 302 } 303 } else { 304 MOZ_ASSERT(state_ == State::Finally); 305 if (!emitFinallyEnd()) { 306 return false; 307 } 308 } 309 310 MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == depth_); 311 312 if (catchAndFinallyJump_.offset.valid()) { 313 // Fix up the end-of-try/catch jumps to come here. 314 if (!bce_->emitJumpTargetAndPatch(catchAndFinallyJump_)) { 315 return false; 316 } 317 } 318 319 // Add the try note last, to let post-order give us the right ordering 320 // (first to last for a given nesting level, inner to outer by level). 321 if (hasCatch()) { 322 if (!bce_->addTryNote(TryNoteKind::Catch, depth_, offsetAfterTryOp(), 323 tryEnd_.offset)) { 324 return false; 325 } 326 } 327 328 // If we've got a finally, mark try+catch region with additional 329 // trynote to catch exceptions (re)thrown from a catch block or 330 // for the try{}finally{} case. 331 if (hasFinally()) { 332 if (!bce_->addTryNote(TryNoteKind::Finally, depth_, offsetAfterTryOp(), 333 finallyStart_.offset)) { 334 return false; 335 } 336 } 337 338 #ifdef DEBUG 339 state_ = State::End; 340 #endif 341 return true; 342 } 343 344 bool TryEmitter::shouldUpdateRval() const { 345 return requiresControlInfo() && !bce_->sc->noScriptRval(); 346 }