UsingEmitter.cpp (35852B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "frontend/UsingEmitter.h" 6 7 #include "frontend/BytecodeEmitter.h" 8 #include "frontend/EmitterScope.h" 9 #include "frontend/IfEmitter.h" 10 #include "frontend/TryEmitter.h" 11 #include "frontend/WhileEmitter.h" 12 13 using namespace js; 14 using namespace js::frontend; 15 16 UsingEmitter::UsingEmitter(BytecodeEmitter* bce) : bce_(bce) {} 17 18 bool UsingEmitter::emitTakeDisposeCapability() { 19 if (!bce_->emit1(JSOp::TakeDisposeCapability)) { 20 // [stack] RESOURCES-OR-UNDEF 21 return false; 22 } 23 24 if (!bce_->emit1(JSOp::IsNullOrUndefined)) { 25 // [stack] RESOURCES-OR-UNDEF IS-UNDEF 26 return false; 27 } 28 29 InternalIfEmitter ifUndefined(bce_); 30 31 if (!ifUndefined.emitThenElse()) { 32 // [stack] UNDEFINED 33 return false; 34 } 35 36 if (!bce_->emit1(JSOp::Zero)) { 37 // [stack] UNDEFINED 0 38 return false; 39 } 40 41 if (!ifUndefined.emitElse()) { 42 // [stack] RESOURCES 43 return false; 44 } 45 46 if (!bce_->emit1(JSOp::Dup)) { 47 // [stack] RESOURCES RESOURCES 48 return false; 49 } 50 51 if (!bce_->emitAtomOp(JSOp::GetProp, 52 TaggedParserAtomIndex::WellKnown::length())) { 53 // [stack] RESOURCES COUNT 54 return false; 55 } 56 57 if (!ifUndefined.emitEnd()) { 58 // [stack] RESOURCES COUNT 59 return false; 60 } 61 62 return true; 63 } 64 65 bool UsingEmitter::emitThrowIfException() { 66 // [stack] EXC THROWING 67 68 InternalIfEmitter ifThrow(bce_); 69 70 if (!ifThrow.emitThenElse()) { 71 // [stack] EXC 72 return false; 73 } 74 75 if (!bce_->emit1(JSOp::Throw)) { 76 // [stack] 77 return false; 78 } 79 80 if (!ifThrow.emitElse()) { 81 // [stack] EXC 82 return false; 83 } 84 85 if (!bce_->emit1(JSOp::Pop)) { 86 // [stack] 87 return false; 88 } 89 90 if (!ifThrow.emitEnd()) { 91 // [stack] 92 return false; 93 } 94 95 return true; 96 } 97 98 bool DisposalEmitter::emitResourcePropertyAccess(TaggedParserAtomIndex prop, 99 unsigned resourcesFromTop) { 100 // [stack] # if resourcesFromTop == 1 101 // [stack] RESOURCES INDEX 102 // [stack] # if resourcesFromTop > 1 103 // [stack] RESOURCES INDEX ... (resourcesFromTop - 1 values) 104 MOZ_ASSERT(resourcesFromTop >= 1); 105 106 if (!bce_->emitDupAt(resourcesFromTop, 2)) { 107 // [stack] RESOURCES INDEX ... RESOURCES INDEX 108 return false; 109 } 110 111 if (!bce_->emit1(JSOp::GetElem)) { 112 // [stack] RESOURCES INDEX ... RESOURCE 113 return false; 114 } 115 116 if (!bce_->emitAtomOp(JSOp::GetProp, prop)) { 117 // [stack] RESOURCES INDEX ... VALUE 118 return false; 119 } 120 121 return true; 122 } 123 124 // Explicit Resource Management Proposal 125 // DisposeResources ( disposeCapability, completion ) 126 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-disposeresources 127 // Steps 1-2. 128 bool DisposalEmitter::prepareForDisposeCapability() { 129 MOZ_ASSERT(state_ == State::Start); 130 131 // [stack] THROWING EXC 132 133 if (hasAsyncDisposables_) { 134 // Step 1. Let needsAwait be false. 135 if (!bce_->emit1(JSOp::False)) { 136 // [stack] THROWING EXC NEEDS-AWAIT 137 return false; 138 } 139 140 // Step 2. Let hasAwaited be false. 141 if (!bce_->emit1(JSOp::False)) { 142 // [stack] THROWING EXC NEEDS-AWAIT HAS-AWAITED 143 return false; 144 } 145 146 if (!bce_->emitPickN(3)) { 147 // [stack] EXC NEEDS-AWAIT HAS-AWAITED THROWING 148 return false; 149 } 150 151 if (!bce_->emitPickN(3)) { 152 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC 153 return false; 154 } 155 } 156 157 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC 158 159 #ifdef DEBUG 160 state_ = State::DisposeCapability; 161 #endif 162 return true; 163 } 164 165 // Explicit Resource Management Proposal 166 // DisposeResources ( disposeCapability, completion ) 167 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-disposeresources 168 // Steps 3-5, 7. 169 // 170 // This implementation of DisposeResources is designed for using and await using 171 // syntax as well as for disposals in DisposableStack and AsyncDisposableStack, 172 // it covers the complete algorithm as defined in the spec for both sync and 173 // async disposals as necessary in bytecode. 174 bool DisposalEmitter::emitEnd(EmitterScope& es) { 175 MOZ_ASSERT(state_ == State::DisposeCapability); 176 177 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC RESOURCES COUNT 178 179 // For the purpose of readbility some values are omitted from 180 // the stack comments and are assumed to be present, 181 // we mention the values in the comments as and when they are being 182 // operated upon. 183 184 // [stack] ... RESOURCES COUNT 185 186 // We do the iteration in reverse order as per spec, 187 // there can be the case when count is 0 and hence index 188 // below becomes -1 but the loop condition will ensure 189 // no code is executed in that case. 190 if (!bce_->emit1(JSOp::Dec)) { 191 // [stack] ... RESOURCES INDEX 192 return false; 193 } 194 195 InternalWhileEmitter wh(bce_); 196 197 // Step 3. For each element resource of 198 // disposeCapability.[[DisposableResourceStack]], in reverse list order, do 199 if (!wh.emitCond()) { 200 // [stack] ... RESOURCES INDEX 201 return false; 202 } 203 204 if (!bce_->emit1(JSOp::Dup)) { 205 // [stack] ... RESOURCES INDEX INDEX 206 return false; 207 } 208 209 if (!bce_->emit1(JSOp::Zero)) { 210 // [stack] ... RESOURCES INDEX INDEX 0 211 return false; 212 } 213 214 if (!bce_->emit1(JSOp::Ge)) { 215 // [stack] ... RESOURCES INDEX BOOL 216 return false; 217 } 218 219 if (!wh.emitBody()) { 220 // [stack] ... RESOURCES INDEX 221 return false; 222 } 223 224 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC RESOURCES INDEX 225 226 if (hasAsyncDisposables_) { 227 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX 228 229 // Step 3.b. Let hint be resource.[[Hint]]. 230 // (reordered) 231 // Step 3.d. If hint is sync-dispose and needsAwait is true and hasAwaited 232 // is false, then 233 if (!emitResourcePropertyAccess(TaggedParserAtomIndex::WellKnown::hint())) { 234 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX HINT 235 return false; 236 } 237 238 // [stack] NEEDS-AWAIT HAS-AWAITED ... HINT 239 240 static_assert(uint8_t(UsingHint::Sync) == 0, "Sync hint must be 0"); 241 static_assert(uint8_t(UsingHint::Async) == 1, "Async hint must be 1"); 242 if (!bce_->emit1(JSOp::Not)) { 243 // [stack] NEEDS-AWAIT HAS-AWAITED ... IS-SYNC 244 return false; 245 } 246 247 if (!bce_->emitDupAt(6, 2)) { 248 // [stack] NEEDS-AWAIT HAS-AWAITED ... IS-SYNC NEEDS-AWAIT HAS-AWAITED 249 return false; 250 } 251 252 // [stack] ... IS-SYNC NEEDS-AWAIT HAS-AWAITED 253 254 if (!bce_->emit1(JSOp::Not)) { 255 // [stack] ... IS-SYNC NEEDS-AWAIT (!HAS-AWAITED) 256 return false; 257 } 258 259 // The use of BitAnd is a simple optimisation to avoid having 260 // jumps if we were to implement this using && operator. The value 261 // IS-SYNC is integer 0 or 1 (see static_assert above) and 262 // NEEDS-AWAIT and HAS-AWAITED are boolean values. thus 263 // the result of the operation is either 0 or 1 which is 264 // truthy value that can be consumed by the IfEmitter. 265 if (!bce_->emit1(JSOp::BitAnd)) { 266 // [stack] ... IS-SYNC (NEEDS-AWAIT & !HAS-AWAITED) 267 return false; 268 } 269 270 if (!bce_->emit1(JSOp::BitAnd)) { 271 // [stack] ... (IS-SYNC & NEEDS-AWAIT & !HAS-AWAITED) 272 return false; 273 } 274 275 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX AWAIT-NEEDED 276 277 InternalIfEmitter ifNeedsSyncDisposeUndefinedAwaited(bce_); 278 279 if (!ifNeedsSyncDisposeUndefinedAwaited.emitThen()) { 280 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX 281 return false; 282 } 283 284 // Step 3.d.i. Perform ! Await(undefined). 285 if (!bce_->emit1(JSOp::Undefined)) { 286 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX UNDEF 287 return false; 288 } 289 290 if (!bce_->emitAwaitInScope(es)) { 291 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX RESOLVED 292 return false; 293 } 294 295 // Step 3.d.ii. Set needsAwait to false. 296 if (!bce_->emitPickN(6)) { 297 // [stack] HAS-AWAITED THROWING EXC RESOURCES INDEX RESOLVED NEEDS-AWAIT 298 return false; 299 } 300 301 if (!bce_->emitPopN(2)) { 302 // [stack] HAS-AWAITED THROWING EXC RESOURCES INDEX 303 return false; 304 } 305 306 if (!bce_->emit1(JSOp::False)) { 307 // [stack] HAS-AWAITED THROWING EXC RESOURCES INDEX NEEDS-AWAIT 308 return false; 309 } 310 311 if (!bce_->emitUnpickN(5)) { 312 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX 313 return false; 314 } 315 316 if (!ifNeedsSyncDisposeUndefinedAwaited.emitEnd()) { 317 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX 318 return false; 319 } 320 } 321 322 // [stack] ... RESOURCES INDEX 323 324 // Step 3.c. Let method be resource.[[DisposeMethod]]. 325 // (reordered) 326 // Step 3.e. If method is not undefined, then 327 if (!emitResourcePropertyAccess(TaggedParserAtomIndex::WellKnown::method())) { 328 // [stack] ... RESOURCES INDEX METHOD 329 return false; 330 } 331 332 if (!bce_->emit1(JSOp::IsNullOrUndefined)) { 333 // [stack] ... RESOURCES INDEX METHOD IS-UNDEF 334 return false; 335 } 336 337 InternalIfEmitter ifMethodNotUndefined(bce_); 338 339 if (!ifMethodNotUndefined.emitThenElse(IfEmitter::ConditionKind::Negative)) { 340 // [stack] ... RESOURCES INDEX METHOD 341 return false; 342 } 343 344 if (!bce_->emit1(JSOp::Pop)) { 345 // [stack] ... RESOURCES INDEX 346 return false; 347 } 348 349 TryEmitter tryCall(bce_, TryEmitter::Kind::TryCatch, 350 TryEmitter::ControlKind::NonSyntactic); 351 352 if (!tryCall.emitTry()) { 353 // [stack] ... RESOURCES INDEX 354 return false; 355 } 356 357 // Step 3.c. Let method be resource.[[DisposeMethod]]. 358 // (reordered) 359 if (!emitResourcePropertyAccess(TaggedParserAtomIndex::WellKnown::method())) { 360 // [stack] ... RESOURCES INDEX METHOD 361 return false; 362 } 363 364 // Step 3.a. Let value be resource.[[ResourceValue]]. 365 // (reordered) 366 if (!emitResourcePropertyAccess(TaggedParserAtomIndex::WellKnown::value(), 367 2)) { 368 // [stack] ... RESOURCES INDEX METHOD VALUE 369 return false; 370 } 371 372 // Step 3.e.i. Let result be Completion(Call(method, value)). 373 if (!bce_->emitCall(JSOp::Call, 0)) { 374 // [stack] ... RESOURCES INDEX RESULT 375 return false; 376 } 377 378 if (hasAsyncDisposables_) { 379 // Step 3.e.ii. If result is a normal completion and hint is async-dispose, 380 // then 381 if (!emitResourcePropertyAccess(TaggedParserAtomIndex::WellKnown::hint(), 382 2)) { 383 // [stack] ... RESOURCES INDEX RESULT HINT 384 return false; 385 } 386 387 // Hint value is either 0 or 1, which can be consumed by the IfEmitter, 388 // see static_assert above. 389 // [stack] ... RESOURCES INDEX RESULT IS-ASYNC 390 391 InternalIfEmitter ifAsyncDispose(bce_); 392 393 if (!ifAsyncDispose.emitThen()) { 394 // [stack] ... RESOURCES INDEX RESULT 395 return false; 396 } 397 398 // [stack] NEEDS-AWAIT THROWING EXC RESOURCES INDEX RESULT HAS-AWAITED 399 400 // Step 3.e.ii.2. Set hasAwaited to true. (reordered) 401 if (!bce_->emitPickN(5)) { 402 // [stack] NEEDS-AWAIT THROWING EXC RESOURCES INDEX RESULT HAS-AWAITED 403 return false; 404 } 405 406 if (!bce_->emit1(JSOp::Pop)) { 407 // [stack] NEEDS-AWAIT THROWING EXC RESOURCES INDEX RESULT 408 return false; 409 } 410 411 if (!bce_->emit1(JSOp::True)) { 412 // [stack] NEEDS-AWAIT THROWING EXC RESOURCES INDEX RESULT HAS-AWAITED 413 return false; 414 } 415 416 if (!bce_->emitUnpickN(5)) { 417 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX RESULT 418 return false; 419 } 420 421 // Step 3.e.ii.1. Set result to Completion(Await(result.[[Value]])). 422 if (!bce_->emitAwaitInScope(es)) { 423 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX RESOLVED 424 return false; 425 } 426 427 if (!ifAsyncDispose.emitEnd()) { 428 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX RESULT 429 return false; 430 } 431 } 432 433 // [stack] ... THROWING EXC RESOURCES INDEX RESULT 434 435 if (!bce_->emit1(JSOp::Pop)) { 436 // [stack] ... THROWING EXC RESOURCES INDEX 437 return false; 438 } 439 440 // Step 3.e.iii. If result is a throw completion, then 441 if (!tryCall.emitCatch()) { 442 // [stack] ... THROWING EXC RESOURCES INDEX EXC2 443 return false; 444 } 445 446 if (!bce_->emitPickN(3)) { 447 // [stack] ... THROWING RESOURCES INDEX EXC2 EXC 448 return false; 449 } 450 451 if (bce_->sc->isSuspendableContext() && 452 bce_->sc->asSuspendableContext()->isGenerator()) { 453 // [stack] ... THROWING RESOURCES INDEX EXC2 EXC 454 455 // Generator closure is implemented by throwing a magic value 456 // thus when we have a throw completion we must check whether 457 // the pending exception is a generator closing exception and overwrite 458 // it with the normal exception here or else we will end up exposing 459 // the magic value to user program. 460 if (!bce_->emit1(JSOp::IsGenClosing)) { 461 // [stack] ... THROWING RESOURCES INDEX EXC2 EXC GEN-CLOSING 462 return false; 463 } 464 465 if (!bce_->emit1(JSOp::Not)) { 466 // [stack] ... THROWING RESOURCES INDEX EXC2 EXC !GEN-CLOSING 467 return false; 468 } 469 470 if (!bce_->emitPickN(5)) { 471 // [stack] ... RESOURCES INDEX EXC2 EXC (!GEN-CLOSING) THROWING 472 return false; 473 } 474 475 if (!bce_->emit1(JSOp::BitAnd)) { 476 // [stack] ... RESOURCES INDEX EXC2 EXC (!GEN-CLOSING & THROWING) 477 return false; 478 } 479 } else { 480 if (!bce_->emitPickN(4)) { 481 // [stack] ... RESOURCES INDEX EXC2 EXC THROWING 482 return false; 483 } 484 } 485 486 // [stack] NEEDS-AWAIT? HAS-AWAITED? RESOURCES INDEX EXC2 EXC THROWING 487 488 InternalIfEmitter ifException(bce_); 489 490 // Step 3.e.iii.1. If completion is a throw completion, then 491 if (!ifException.emitThenElse()) { 492 // [stack] NEEDS-AWAIT? HAS-AWAITED? RESOURCES INDEX EXC2 EXC 493 return false; 494 } 495 496 // Step 3.e.iii.1.a-f 497 if (!bce_->emit1(JSOp::CreateSuppressedError)) { 498 // [stack] NEEDS-AWAIT? HAS-AWAITED? RESOURCES INDEX SUPPRESSED 499 return false; 500 } 501 502 if (!bce_->emitUnpickN(2)) { 503 // [stack] NEEDS-AWAIT? HAS-AWAITED? SUPPRESSED RESOURCED INDEX 504 return false; 505 } 506 507 if (!bce_->emit1(JSOp::True)) { 508 // [stack] NEEDS-AWAIT? HAS-AWAITED? SUPPRESSED RESOURCED INDEX THROWING 509 return false; 510 } 511 512 if (!bce_->emitUnpickN(3)) { 513 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING SUPPRESSED RESOURCED INDEX 514 return false; 515 } 516 517 // Step 3.e.iii.2. Else, 518 // Step 3.e.iii.2.a. Set completion to result. 519 if (!ifException.emitElse()) { 520 // [stack] NEEDS-AWAIT? HAS-AWAITED? RESOURCES INDEX EXC2 EXC 521 return false; 522 } 523 524 if (!bce_->emit1(JSOp::Pop)) { 525 // [stack] NEEDS-AWAIT? HAS-AWAITED? RESOURCES INDEX EXC2 526 return false; 527 } 528 529 if (!bce_->emitUnpickN(2)) { 530 // [stack] NEEDS-AWAIT? HAS-AWAITED? EXC2 RESOURCES INDEX 531 return false; 532 } 533 534 if (!bce_->emit1(JSOp::True)) { 535 // [stack] NEEDS-AWAIT? HAS-AWAITED? EXC2 RESOURCES INDEX THROWING 536 return false; 537 } 538 539 if (!bce_->emitUnpickN(3)) { 540 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC2 RESOURCES INDEX 541 return false; 542 } 543 544 if (!ifException.emitEnd()) { 545 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC RESOURCES INDEX 546 return false; 547 } 548 549 if (!tryCall.emitEnd()) { 550 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC RESOURCES INDEX 551 return false; 552 } 553 554 // [stack] ... THROWING EXC RESOURCES INDEX 555 556 // Step 3.f. Else, 557 // Step 3.f.i. Assert: hint is async-dispose. 558 // (implicit) 559 if (!ifMethodNotUndefined.emitElse()) { 560 // [stack] ... THROWING EXC RESOURCES INDEX METHOD 561 return false; 562 } 563 564 if (!bce_->emit1(JSOp::Pop)) { 565 // [stack] ... THROWING EXC RESOURCES INDEX 566 return false; 567 } 568 569 if (hasAsyncDisposables_) { 570 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX 571 572 // Step 3.f.ii. Set needsAwait to true. 573 if (!bce_->emitPickN(5)) { 574 // [stack] HAS-AWAITED THROWING EXC RESOURCES INDEX NEEDS-AWAIT 575 return false; 576 } 577 578 if (!bce_->emit1(JSOp::Pop)) { 579 // [stack] HAS-AWAITED THROWING EXC RESOURCES INDEX 580 return false; 581 } 582 583 if (!bce_->emit1(JSOp::True)) { 584 // [stack] HAS-AWAITED THROWING EXC RESOURCES INDEX NEEDS-AWAIT 585 return false; 586 } 587 588 if (!bce_->emitUnpickN(5)) { 589 // [stack] NEEDS-AWAIT HAS-AWAITED THROWING EXC RESOURCES INDEX 590 return false; 591 } 592 } 593 594 if (!ifMethodNotUndefined.emitEnd()) { 595 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC RESOURCES INDEX 596 return false; 597 } 598 599 if (!bce_->emit1(JSOp::Dec)) { 600 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC RESOURCES INDEX 601 return false; 602 } 603 604 if (!wh.emitEnd()) { 605 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC RESOURCES INDEX 606 return false; 607 } 608 609 if (!bce_->emitPopN(2)) { 610 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC 611 return false; 612 } 613 614 if (hasAsyncDisposables_) { 615 // Step 4. If needsAwait is true and hasAwaited is false, then 616 if (!bce_->emitPickN(3)) { 617 // [stack] HAS-AWAITED THROWING EXC NEEDS-AWAIT 618 return false; 619 } 620 621 if (!bce_->emitPickN(3)) { 622 // [stack] THROWING EXC NEEDS-AWAIT HAS-AWAITED 623 return false; 624 } 625 626 if (!bce_->emit1(JSOp::Not)) { 627 // [stack] THROWING EXC NEEDS-AWAIT (!HAS-AWAITED) 628 return false; 629 } 630 631 if (!bce_->emit1(JSOp::BitAnd)) { 632 // [stack] THROWING EXC (NEEDS-AWAIT & !HAS-AWAITED) 633 return false; 634 } 635 636 InternalIfEmitter ifNeedsUndefinedAwait(bce_); 637 638 if (!ifNeedsUndefinedAwait.emitThen()) { 639 // [stack] THROWING EXC 640 return false; 641 } 642 643 if (!bce_->emit1(JSOp::Undefined)) { 644 // [stack] THROWING EXC UNDEF 645 return false; 646 } 647 648 if (!bce_->emitAwaitInScope(es)) { 649 // [stack] THROWING EXC RESOLVED 650 return false; 651 } 652 653 if (!bce_->emit1(JSOp::Pop)) { 654 // [stack] THROWING EXC 655 return false; 656 } 657 658 if (!ifNeedsUndefinedAwait.emitEnd()) { 659 // [stack] THROWING EXC 660 return false; 661 } 662 } 663 664 // Step 7. Return ? completion. 665 if (!bce_->emit1(JSOp::Swap)) { 666 // [stack] EXC THROWING 667 return false; 668 } 669 670 #ifdef DEBUG 671 state_ = State::End; 672 #endif 673 return true; 674 } 675 676 bool UsingEmitter::emitDisposeResourcesForEnvironment(EmitterScope& es) { 677 // [stack] THROWING EXC 678 679 DisposalEmitter de(bce_, hasAwaitUsing_); 680 if (!de.prepareForDisposeCapability()) { 681 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC 682 return false; 683 } 684 685 // Explicit Resource Management Proposal 686 // DisposeResources ( disposeCapability, completion ) 687 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-disposeresources 688 // 689 // Step 6. Set disposeCapability.[[DisposableResourceStack]] to a new empty 690 // List. 691 if (!emitTakeDisposeCapability()) { 692 // [stack] NEEDS-AWAIT? HAS-AWAITED? THROWING EXC RESOURCES COUNT 693 return false; 694 } 695 696 if (!de.emitEnd(es)) { 697 // [stack] EXC THROWING 698 return false; 699 } 700 701 return true; 702 } 703 704 bool UsingEmitter::prepareForDisposableScopeBody(BlockKind blockKind) { 705 MOZ_ASSERT(state_ == State::Start); 706 707 // For-of loops are already wrapped in try-catch and have special case 708 // handling for the same. 709 // See ForOfLoopControl::emitEndCodeNeedingIteratorClose. 710 if (blockKind != BlockKind::ForOf) { 711 tryEmitter_ = bce_->fc->getAllocator()->make_unique<TryEmitter>( 712 bce_, TryEmitter::Kind::TryFinally, TryEmitter::ControlKind::Disposal); 713 if (!tryEmitter_) { 714 return false; 715 } 716 717 if (!tryEmitter_->emitTry()) { 718 return false; 719 } 720 } 721 722 #ifdef DEBUG 723 state_ = State::DisposableScopeBody; 724 #endif 725 return true; 726 } 727 728 // Explicit Resource Management Proposal 729 // GetDisposeMethod ( V, hint ) 730 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-getdisposemethod 731 // Steps 1.a-1.b.i., 2-3. 732 bool UsingEmitter::emitGetDisposeMethod(UsingHint hint) { 733 // [stack] VAL 734 735 // Step 1. If hint is async-dispose, then 736 if (hint == UsingHint::Async) { 737 // Step 1.a. Let method be ? GetMethod(V, @@asyncDispose). 738 if (!bce_->emit1(JSOp::Dup)) { 739 // [stack] VAL VAL 740 return false; 741 } 742 743 if (!bce_->emit1(JSOp::Dup)) { 744 // [stack] VAL VAL VAL 745 return false; 746 } 747 748 if (!bce_->emit2(JSOp::Symbol, uint8_t(JS::SymbolCode::asyncDispose))) { 749 // [stack] VAL VAL VAL SYMBOL 750 return false; 751 } 752 753 if (!bce_->emit1(JSOp::GetElem)) { 754 // [stack] VAL VAL ASYNC-DISPOSE 755 return false; 756 } 757 758 // Step 1.b. If method is undefined, then 759 // GetMethod returns undefined if the function is null but 760 // since we do not do the conversion here we check for 761 // null or undefined here. 762 if (!bce_->emit1(JSOp::IsNullOrUndefined)) { 763 // [stack] VAL VAL ASYNC-DISPOSE IS-NULL-OR-UNDEF 764 return false; 765 } 766 767 InternalIfEmitter ifAsyncDisposeNullOrUndefined(bce_); 768 769 if (!ifAsyncDisposeNullOrUndefined.emitThenElse()) { 770 // [stack] VAL VAL ASYNC-DISPOSE 771 return false; 772 } 773 774 if (!bce_->emit1(JSOp::Pop)) { 775 // [stack] VAL VAL 776 return false; 777 } 778 779 if (!bce_->emit1(JSOp::Dup)) { 780 // [stack] VAL VAL VAL 781 return false; 782 } 783 784 if (!bce_->emit2(JSOp::Symbol, uint8_t(JS::SymbolCode::dispose))) { 785 // [stack] VAL VAL VAL SYMBOL 786 return false; 787 } 788 789 // Step 1.b.i. Set method to ? GetMethod(V, @@dispose). 790 if (!bce_->emit1(JSOp::GetElem)) { 791 // [stack] VAL VAL DISPOSE 792 return false; 793 } 794 795 if (!bce_->emit1(JSOp::True)) { 796 // [stack] VAL VAL DISPOSE NEEDS-CLOSURE 797 return false; 798 } 799 800 if (!ifAsyncDisposeNullOrUndefined.emitElse()) { 801 // [stack] VAL VAL ASYNC-DISPOSE 802 return false; 803 } 804 805 if (!bce_->emit1(JSOp::False)) { 806 // [stack] VAL VAL ASYNC-DISPOSE NEEDS-CLOSURE 807 return false; 808 } 809 810 if (!ifAsyncDisposeNullOrUndefined.emitEnd()) { 811 // [stack] VAL VAL METHOD NEEDS-CLOSURE 812 return false; 813 } 814 815 } else { 816 MOZ_ASSERT(hint == UsingHint::Sync); 817 818 // Step 2. Else, 819 // Step 2.a. Let method be ? GetMethod(V, @@dispose). 820 if (!bce_->emit1(JSOp::Dup)) { 821 // [stack] VAL VAL 822 return false; 823 } 824 825 if (!bce_->emit1(JSOp::Dup)) { 826 // [stack] VAL VAL VAL 827 return false; 828 } 829 830 if (!bce_->emit2(JSOp::Symbol, uint8_t(JS::SymbolCode::dispose))) { 831 // [stack] VAL VAL VAL SYMBOL 832 return false; 833 } 834 835 if (!bce_->emit1(JSOp::GetElem)) { 836 // [stack] VAL VAL DISPOSE 837 return false; 838 } 839 840 if (!bce_->emit1(JSOp::False)) { 841 // [stack] VAL VAL DISPOSE NEEDS-CLOSURE 842 return false; 843 } 844 } 845 846 if (!bce_->emitDupAt(1)) { 847 // [stack] VAL VAL METHOD NEEDS-CLOSURE METHOD 848 return false; 849 } 850 851 // According to spec GetMethod throws TypeError if the method is not callable 852 // and returns undefined if the value is either undefined or null. 853 // but the caller of this function, emitCreateDisposableResource is 854 // supposed to throw TypeError as well if returned value is undefined, 855 // thus we combine the steps here. 856 if (!bce_->emitCheckIsCallable()) { 857 // [stack] VAL VAL METHOD NEEDS-CLOSURE METHOD IS-CALLABLE 858 return false; 859 } 860 861 InternalIfEmitter ifMethodNotCallable(bce_); 862 863 if (!ifMethodNotCallable.emitThen(IfEmitter::ConditionKind::Negative)) { 864 // [stack] VAL VAL METHOD NEEDS-CLOSURE METHOD 865 return false; 866 } 867 868 if (!bce_->emit2(JSOp::ThrowMsg, uint8_t(ThrowMsgKind::DisposeNotCallable))) { 869 // [stack] VAL VAL METHOD NEEDS-CLOSURE METHOD 870 return false; 871 } 872 873 if (!ifMethodNotCallable.emitEnd()) { 874 // [stack] VAL VAL METHOD NEEDS-CLOSURE METHOD 875 return false; 876 } 877 878 if (!bce_->emit1(JSOp::Pop)) { 879 // [stack] VAL VAL METHOD NEEDS-CLOSURE 880 return false; 881 } 882 883 return true; 884 } 885 886 // Explicit Resource Management Proposal 887 // CreateDisposableResource ( V, hint [ , method ] ) 888 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-createdisposableresource 889 bool UsingEmitter::emitCreateDisposableResource(UsingHint hint) { 890 // [stack] VAL 891 892 // Step 1. If method is not present, then (implicit) 893 // Step 1.a. If V is either null or undefined, then 894 if (!bce_->emit1(JSOp::IsNullOrUndefined)) { 895 // [stack] VAL IS-NULL-OR-UNDEF 896 return false; 897 } 898 899 InternalIfEmitter ifNullUndefined(bce_); 900 901 if (!ifNullUndefined.emitThenElse()) { 902 // [stack] VAL 903 return false; 904 } 905 906 // Step 1.a.i. Set V to undefined. 907 if (!bce_->emit1(JSOp::Undefined)) { 908 // [stack] VAL UNDEF 909 return false; 910 } 911 912 // Step 1.a.ii. Set method to undefined. 913 if (!bce_->emit1(JSOp::Undefined)) { 914 // [stack] VAL UNDEF UNDEF 915 return false; 916 } 917 918 if (!bce_->emit1(JSOp::False)) { 919 // [stack] VAL UNDEF UNDEF NEEDS-CLOSURE 920 return false; 921 } 922 923 // Step 1.b. Else, 924 if (!ifNullUndefined.emitElse()) { 925 // [stack] VAL 926 return false; 927 } 928 929 // Step 1.b.i. If V is not an Object, throw a TypeError exception. 930 if (!bce_->emitCheckIsObj(CheckIsObjectKind::Disposable)) { 931 // [stack] VAL 932 return false; 933 } 934 935 // Step 1.b.ii. Set method to ? GetDisposeMethod(V, hint). 936 // Step 1.b.iii. If method is undefined, throw a TypeError exception. 937 if (!emitGetDisposeMethod(hint)) { 938 // [stack] VAL VAL METHOD NEEDS-CLOSURE 939 return false; 940 } 941 942 if (!ifNullUndefined.emitEnd()) { 943 // [stack] VAL VAL METHOD NEEDS-CLOSURE 944 return false; 945 } 946 947 return true; 948 } 949 950 // Explicit Resource Management Proposal 951 // 7.5.4 AddDisposableResource ( disposeCapability, V, hint [ , method ] ) 952 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-adddisposableresource 953 // Steps 1, 3-4. 954 bool UsingEmitter::prepareForAssignment(UsingHint hint) { 955 MOZ_ASSERT(state_ == State::DisposableScopeBody); 956 MOZ_ASSERT(bce_->innermostEmitterScope()->hasDisposables()); 957 958 if (hint == UsingHint::Async) { 959 setHasAwaitUsing(true); 960 } 961 962 // [stack] VAL 963 964 // Step 1. If method is not present, then (implicit) 965 // Step 1.a. If V is either null or undefined and hint is sync-dispose, return 966 // unused. 967 if (hint == UsingHint::Sync) { 968 if (!bce_->emit1(JSOp::IsNullOrUndefined)) { 969 // [stack] VAL IS-NULL-OR-UNDEF 970 return false; 971 } 972 973 if (!bce_->emit1(JSOp::Not)) { 974 // [stack] VAL !IS-NULL-OR-UNDEF 975 return false; 976 } 977 } else { 978 MOZ_ASSERT(hint == UsingHint::Async); 979 if (!bce_->emit1(JSOp::True)) { 980 // [stack] VAL TRUE 981 return false; 982 } 983 } 984 985 // [stack] VAL SHOULD-CREATE-RESOURCE 986 987 InternalIfEmitter ifCreateResource(bce_); 988 989 if (!ifCreateResource.emitThen()) { 990 // [stack] VAL 991 return false; 992 } 993 994 // Step 1.c. Let resource be ? CreateDisposableResource(V, hint). 995 if (!emitCreateDisposableResource(hint)) { 996 // [stack] VAL VAL METHOD NEEDS-CLOSURE 997 return false; 998 } 999 1000 // Step 3. Append resource to disposeCapability.[[DisposableResourceStack]]. 1001 if (!bce_->emit2(JSOp::AddDisposable, uint8_t(hint))) { 1002 // [stack] VAL 1003 return false; 1004 } 1005 1006 if (!ifCreateResource.emitEnd()) { 1007 // [stack] VAL 1008 return false; 1009 } 1010 1011 // Step 4. Return unused. 1012 return true; 1013 } 1014 1015 bool ForOfDisposalEmitter::prepareForForOfLoopIteration() { 1016 MOZ_ASSERT(state_ == State::Start); 1017 EmitterScope* es = bce_->innermostEmitterScopeNoCheck(); 1018 MOZ_ASSERT(es->hasDisposables()); 1019 1020 if (!bce_->emit1(JSOp::False)) { 1021 // [stack] THROWING 1022 return false; 1023 } 1024 1025 if (!bce_->emit1(JSOp::Undefined)) { 1026 // [stack] THROWING UNDEF 1027 return false; 1028 } 1029 1030 if (!emitDisposeResourcesForEnvironment(*es)) { 1031 // [stack] EXC THROWING 1032 return false; 1033 } 1034 1035 if (!emitThrowIfException()) { 1036 // [stack] 1037 return false; 1038 } 1039 1040 #ifdef DEBUG 1041 state_ = State::Iteration; 1042 #endif 1043 return true; 1044 } 1045 1046 bool ForOfDisposalEmitter::emitEnd() { 1047 MOZ_ASSERT(state_ == State::Iteration); 1048 EmitterScope* es = bce_->innermostEmitterScopeNoCheck(); 1049 MOZ_ASSERT(es->hasDisposables()); 1050 1051 // [stack] EXC STACK 1052 1053 if (!bce_->emit1(JSOp::Swap)) { 1054 // [stack] STACK EXC 1055 return false; 1056 } 1057 1058 if (!bce_->emit1(JSOp::True)) { 1059 // [stack] STACK EXC THROWING 1060 return false; 1061 } 1062 1063 if (!bce_->emit1(JSOp::Swap)) { 1064 // [stack] STACK THROWING EXC 1065 return false; 1066 } 1067 1068 if (!emitDisposeResourcesForEnvironment(*es)) { 1069 // [stack] STACK EXC THROWING 1070 return false; 1071 } 1072 1073 if (!bce_->emit1(JSOp::Pop)) { 1074 // [stack] STACK EXC 1075 return false; 1076 } 1077 1078 if (!bce_->emit1(JSOp::Swap)) { 1079 // [stack] EXC STACK 1080 return false; 1081 } 1082 1083 #ifdef DEBUG 1084 state_ = State::End; 1085 #endif 1086 return true; 1087 } 1088 1089 bool UsingEmitter::emitEnd() { 1090 MOZ_ASSERT(state_ == State::DisposableScopeBody); 1091 EmitterScope* es = bce_->innermostEmitterScopeNoCheck(); 1092 MOZ_ASSERT(es->hasDisposables()); 1093 MOZ_ASSERT(tryEmitter_.get() != nullptr); 1094 1095 if (!tryEmitter_->emitFinally()) { 1096 // [stack] EXC-OR-RESUME STACK THROWING RVAL? 1097 return false; 1098 } 1099 1100 if (!bce_->emitDupAt(tryEmitter_->shouldUpdateRval() ? 1 : 0)) { 1101 // [stack] EXC-OR-RESUME STACK THROWING RVAL? THROWING 1102 return false; 1103 } 1104 1105 InternalIfEmitter ifThrowing(bce_); 1106 1107 if (!ifThrowing.emitThenElse()) { 1108 // [stack] EXC STACK THROWING RVAL? 1109 return false; 1110 } 1111 1112 if (!bce_->emit1(JSOp::True)) { 1113 // [stack] EXC STACK THROWING RVAL? THROWING 1114 return false; 1115 } 1116 1117 if (!bce_->emitDupAt(tryEmitter_->shouldUpdateRval() ? 4 : 3)) { 1118 // [stack] EXC STACK THROWING RVAL? THROWING EXC 1119 return false; 1120 } 1121 1122 if (!ifThrowing.emitElse()) { 1123 // [stack] RESUME STACK THROWING RVAL? 1124 return false; 1125 } 1126 1127 if (!bce_->emit1(JSOp::False)) { 1128 // [stack] RESUME STACK THROWING RVAL? THROWING 1129 return false; 1130 } 1131 1132 if (!bce_->emit1(JSOp::Undefined)) { 1133 // [stack] RESUME STACK THROWING RVAL? THROWING UNDEF 1134 return false; 1135 } 1136 1137 if (!ifThrowing.emitEnd()) { 1138 // [stack] EXC-OR-RESUME STACK THROWING RVAL? THROWING EXC-OR-UNDEF 1139 return false; 1140 } 1141 1142 if (!emitDisposeResourcesForEnvironment(*es)) { 1143 // [stack] EXC-OR-RESUME STACK THROWING RVAL? DISPOSAL-EXC DISPOSAL-THROWING 1144 return false; 1145 } 1146 1147 if (bce_->sc->isSuspendableContext() && 1148 bce_->sc->asSuspendableContext()->isGenerator()) { 1149 // [stack] ... DISP-EXC DISP-THROWING 1150 1151 if (!bce_->emit1(JSOp::Swap)) { 1152 // [stack] ... DISP-THROWING DISP-EXC 1153 return false; 1154 } 1155 1156 if (!bce_->emit1(JSOp::IsGenClosing)) { 1157 // [stack] ... DISP-THROWING DISP-EXC GEN-CLOSING 1158 return false; 1159 } 1160 1161 if (!bce_->emit1(JSOp::Not)) { 1162 // [stack] ... DISP-THROWING DISP-EXC !GEN-CLOSING 1163 return false; 1164 } 1165 1166 if (!bce_->emitPickN(2)) { 1167 // [stack] ... DISP-EXC !GEN-CLOSING DISP-THROWING 1168 return false; 1169 } 1170 1171 if (!bce_->emit1(JSOp::BitAnd)) { 1172 // [stack] ... DISP-EXC (DISP-THROWING & !GEN-CLOSING) 1173 return false; 1174 } 1175 } 1176 1177 if (!emitThrowIfException()) { 1178 // [stack] EXC-OR-RESUME STACK THROWING RVAL? 1179 return false; 1180 } 1181 1182 if (!tryEmitter_->emitEnd()) { 1183 // [stack] 1184 return false; 1185 } 1186 1187 #ifdef DEBUG 1188 state_ = State::End; 1189 #endif 1190 return true; 1191 } 1192 1193 // Explicit Resource Management Proposal 1194 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset 1195 // Step 9.k.i. 1196 bool NonLocalIteratorCloseUsingEmitter::prepareForIteratorClose( 1197 EmitterScope& es) { 1198 MOZ_ASSERT(state_ == State::Start); 1199 // In this function we prepare for the closure of the iterator but first 1200 // emitting the dispose loop and preseving exceptions on the stack and after 1201 // that emitting a try to wrap the iterator closure code that shall come after 1202 // this. 1203 if (!es.hasDisposables()) { 1204 #ifdef DEBUG 1205 state_ = State::IteratorClose; 1206 #endif 1207 return true; 1208 } 1209 1210 setHasAwaitUsing(es.hasAsyncDisposables()); 1211 1212 // [stack] ITER 1213 1214 if (!bce_->emit1(JSOp::False)) { 1215 // [stack] THROWING 1216 return false; 1217 } 1218 1219 if (!bce_->emit1(JSOp::Undefined)) { 1220 // [stack] THROWING UNDEF 1221 return false; 1222 } 1223 1224 if (!emitDisposeResourcesForEnvironment(es)) { 1225 // [stack] ITER EXC-DISPOSE DISPOSE-THROWING 1226 return false; 1227 } 1228 1229 if (!bce_->emitPickN(2)) { 1230 // [stack] EXC-DISPOSE DISPOSE-THROWING ITER 1231 return false; 1232 } 1233 1234 tryClosingIterator_ = bce_->fc->getAllocator()->make_unique<TryEmitter>( 1235 bce_, TryEmitter::Kind::TryCatch, TryEmitter::ControlKind::NonSyntactic); 1236 if (!tryClosingIterator_) { 1237 return false; 1238 } 1239 1240 if (!tryClosingIterator_->emitTry()) { 1241 // [stack] EXC-DISPOSE DISPOSE-THROWING ITER 1242 return false; 1243 } 1244 1245 #ifdef DEBUG 1246 state_ = State::IteratorClose; 1247 #endif 1248 return true; 1249 } 1250 1251 // Explicit Resource Management Proposal 1252 // 7.4.9 IteratorClose ( iteratorRecord, completion ) 1253 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-iteratorclose&secAll=true 1254 // Steps 5-6. 1255 // 1256 // 7.4.11 AsyncIteratorClose ( iteratorRecord, completion ) 1257 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-asynciteratorclose&secAll=true 1258 // Steps 5-6. 1259 bool NonLocalIteratorCloseUsingEmitter::emitEnd() { 1260 // This function handles the steps after the iterator close operation which 1261 // may or may not have thrown. note that prepareForIteratorClose would have 1262 // already wrapped the iterator closure with a try and have preserved any 1263 // exception by the disposal operation on the stack now this function does the 1264 // equivalent of the following pseudocode (consider excDispose and 1265 // disposeThrowing and iter equal to corresponding values left on stack by 1266 // prepareForIteratorClose): 1267 // 1268 // 1269 // let excToThrow, throwing = disposeThrowing; 1270 // try { 1271 // IteratorClose(iter); 1272 // } catch (excIterClose) { 1273 // throwing = true; 1274 // if (disposeThrowing) { 1275 // excToThrow = excDispose; 1276 // } else { 1277 // excToThrow = excIterClose; 1278 // } 1279 // } 1280 // if (throwing) { 1281 // throw excToThrow; 1282 // } 1283 // 1284 MOZ_ASSERT(state_ == State::IteratorClose); 1285 1286 if (!tryClosingIterator_) { 1287 #ifdef DEBUG 1288 state_ = State::End; 1289 #endif 1290 return true; 1291 } 1292 1293 // [stack] EXC-DISPOSE DISPOSE-THROWING ITER 1294 1295 if (!tryClosingIterator_->emitCatch()) { 1296 // [stack] EXC-DISPOSE DISPOSE-THROWING ITER EXC-ITER-CLOSE 1297 return false; 1298 } 1299 1300 if (!bce_->emitPickN(2)) { 1301 // [stack] EXC-DISPOSE ITER EXC-ITER-CLOSE DISPOSE-THROWING 1302 return false; 1303 } 1304 1305 InternalIfEmitter ifDisposeWasThrowing(bce_); 1306 1307 if (!ifDisposeWasThrowing.emitThenElse()) { 1308 // [stack] EXC-DISPOSE ITER EXC-ITER-CLOSE 1309 return false; 1310 } 1311 1312 if (!bce_->emit1(JSOp::Pop)) { 1313 // [stack] EXC-DISPOSE ITER 1314 return false; 1315 } 1316 1317 if (!bce_->emit1(JSOp::True)) { 1318 // [stack] EXC-DISPOSE ITER THROWING 1319 return false; 1320 } 1321 1322 // This swap operation is to make the stack state consistent with the 1323 // the non-throwing case. 1324 if (!bce_->emit1(JSOp::Swap)) { 1325 // [stack] EXC-DISPOSE THROWING ITER 1326 return false; 1327 } 1328 1329 if (!ifDisposeWasThrowing.emitElse()) { 1330 // [stack] EXC-DISPOSE ITER EXC-ITER-CLOSE 1331 return false; 1332 } 1333 1334 if (!bce_->emitPickN(2)) { 1335 // [stack] ITER EXC-ITER-CLOSE EXC-DISPOSE 1336 return false; 1337 } 1338 1339 if (!bce_->emit1(JSOp::Pop)) { 1340 // [stack] ITER EXC-ITER-CLOSE 1341 return false; 1342 } 1343 1344 if (!bce_->emit1(JSOp::Swap)) { 1345 // [stack] EXC-ITER-CLOSE ITER 1346 return false; 1347 } 1348 1349 if (!bce_->emit1(JSOp::True)) { 1350 // [stack] EXC-ITER-CLOSE ITER THROWING 1351 return false; 1352 } 1353 1354 if (!bce_->emit1(JSOp::Swap)) { 1355 // [stack] EXC-ITER-CLOSE THROWING ITER 1356 return false; 1357 } 1358 1359 if (!ifDisposeWasThrowing.emitEnd()) { 1360 // [stack] EXC THROWING ITER 1361 return false; 1362 } 1363 1364 if (!tryClosingIterator_->emitEnd()) { 1365 // [stack] EXC THROWING ITER 1366 return false; 1367 } 1368 1369 if (!bce_->emit1(JSOp::Swap)) { 1370 // [stack] EXC ITER THROWING 1371 return false; 1372 } 1373 1374 InternalIfEmitter ifThrowing(bce_); 1375 1376 if (!ifThrowing.emitThenElse()) { 1377 // [stack] EXC ITER 1378 return false; 1379 } 1380 1381 if (!bce_->emit1(JSOp::Swap)) { 1382 // [stack] ITER EXC 1383 return false; 1384 } 1385 1386 if (!bce_->emit1(JSOp::Throw)) { 1387 // [stack] ITER 1388 return false; 1389 } 1390 1391 if (!ifThrowing.emitElse()) { 1392 // [stack] EXC ITER 1393 return false; 1394 } 1395 1396 if (!bce_->emit1(JSOp::Swap)) { 1397 // [stack] ITER EXC 1398 return false; 1399 } 1400 1401 if (!bce_->emit1(JSOp::Pop)) { 1402 // [stack] ITER 1403 return false; 1404 } 1405 1406 if (!ifThrowing.emitEnd()) { 1407 // [stack] ITER 1408 return false; 1409 } 1410 1411 #ifdef DEBUG 1412 state_ = State::End; 1413 #endif 1414 return true; 1415 }