tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }