tor-browser

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

ForOfLoopControl.cpp (8713B)


      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/ForOfLoopControl.h"
      8 
      9 #include "frontend/BytecodeEmitter.h"  // BytecodeEmitter
     10 #include "frontend/EmitterScope.h"     // EmitterScope
     11 #include "frontend/IfEmitter.h"        // InternalIfEmitter
     12 #include "vm/CompletionKind.h"         // CompletionKind
     13 #include "vm/Opcodes.h"                // JSOp
     14 
     15 using namespace js;
     16 using namespace js::frontend;
     17 
     18 ForOfLoopControl::ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth,
     19                                   SelfHostedIter selfHostedIter,
     20                                   IteratorKind iterKind)
     21    : LoopControl(bce, StatementKind::ForOfLoop),
     22      iterDepth_(iterDepth),
     23      numYieldsAtBeginCodeNeedingIterClose_(UINT32_MAX),
     24      selfHostedIter_(selfHostedIter),
     25      iterKind_(iterKind) {}
     26 
     27 bool ForOfLoopControl::emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce) {
     28  tryCatch_.emplace(bce, TryEmitter::Kind::TryCatch,
     29                    TryEmitter::ControlKind::NonSyntactic);
     30 
     31  if (!tryCatch_->emitTry()) {
     32    return false;
     33  }
     34 
     35  MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX);
     36  numYieldsAtBeginCodeNeedingIterClose_ = bce->bytecodeSection().numYields();
     37 
     38  return true;
     39 }
     40 
     41 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
     42 bool ForOfLoopControl::prepareForForOfLoopIteration(
     43    BytecodeEmitter* bce, const EmitterScope* headLexicalEmitterScope,
     44    bool hasAwaitUsing) {
     45  MOZ_ASSERT(headLexicalEmitterScope);
     46  if (headLexicalEmitterScope->hasDisposables()) {
     47    forOfDisposalEmitter_.emplace(bce, hasAwaitUsing);
     48    return forOfDisposalEmitter_->prepareForForOfLoopIteration();
     49  }
     50  return true;
     51 }
     52 #endif
     53 
     54 bool ForOfLoopControl::emitEndCodeNeedingIteratorClose(BytecodeEmitter* bce) {
     55  if (!tryCatch_->emitCatch(TryEmitter::ExceptionStack::Yes)) {
     56    //              [stack] ITER ... EXCEPTION STACK
     57    return false;
     58  }
     59 
     60 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
     61  // Explicit Resource Management Proposal
     62  // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset
     63  // Step 9.i.i.1 Set result to
     64  // Completion(DisposeResources(iterationEnv.[[DisposeCapability]], result)).
     65  if (forOfDisposalEmitter_.isSome()) {
     66    if (!forOfDisposalEmitter_->emitEnd()) {
     67      //              [stack] ITER ... EXCEPTION STACK
     68      return false;
     69    }
     70  }
     71 #endif
     72 
     73  unsigned slotFromTop = bce->bytecodeSection().stackDepth() - iterDepth_;
     74  if (!bce->emitDupAt(slotFromTop)) {
     75    //              [stack] ITER ... EXCEPTION STACK ITER
     76    return false;
     77  }
     78 
     79  if (!emitIteratorCloseInInnermostScopeWithTryNote(bce,
     80                                                    CompletionKind::Throw)) {
     81    return false;  // ITER ... EXCEPTION STACK
     82  }
     83 
     84  if (!bce->emit1(JSOp::ThrowWithStack)) {
     85    //              [stack] ITER ...
     86    return false;
     87  }
     88 
     89  // If any yields were emitted, then this for-of loop is inside a star
     90  // generator and must handle the case of Generator.return. Like in
     91  // yield*, it is handled with a finally block. If the generator is
     92  // closing, then the exception/resumeindex value (third value on
     93  // the stack) will be a magic JS_GENERATOR_CLOSING value.
     94  // TODO: Refactor this to eliminate the swaps.
     95  uint32_t numYieldsEmitted = bce->bytecodeSection().numYields();
     96  if (numYieldsEmitted > numYieldsAtBeginCodeNeedingIterClose_) {
     97    if (!tryCatch_->emitFinally()) {
     98      return false;
     99    }
    100    //              [stack] ITER ... FVALUE FSTACK FTHROWING
    101    InternalIfEmitter ifGeneratorClosing(bce);
    102    if (!bce->emitPickN(2)) {
    103      //            [stack] ITER ... FSTACK FTHROWING FVALUE
    104      return false;
    105    }
    106    if (!bce->emit1(JSOp::IsGenClosing)) {
    107      //            [stack] ITER ... FSTACK FTHROWING FVALUE CLOSING
    108      return false;
    109    }
    110    if (!ifGeneratorClosing.emitThen()) {
    111      //            [stack] ITER ... FSTACK FTHROWING FVALUE
    112      return false;
    113    }
    114    if (!bce->emitDupAt(slotFromTop + 1)) {
    115      //            [stack] ITER ... FSTACK FTHROWING FVALUE ITER
    116      return false;
    117    }
    118    if (!emitIteratorCloseInInnermostScopeWithTryNote(bce,
    119                                                      CompletionKind::Normal)) {
    120      //            [stack] ITER ... FSTACK FTHROWING FVALUE
    121      return false;
    122    }
    123    if (!ifGeneratorClosing.emitEnd()) {
    124      //            [stack] ITER ... FSTACK FTHROWING FVALUE
    125      return false;
    126    }
    127    if (!bce->emitUnpickN(2)) {
    128      //            [stack] ITER ... FVALUE FSTACK FTHROWING
    129      return false;
    130    }
    131  }
    132 
    133  if (!tryCatch_->emitEnd()) {
    134    return false;
    135  }
    136 
    137  tryCatch_.reset();
    138  numYieldsAtBeginCodeNeedingIterClose_ = UINT32_MAX;
    139 
    140  return true;
    141 }
    142 
    143 bool ForOfLoopControl::emitIteratorCloseInInnermostScopeWithTryNote(
    144    BytecodeEmitter* bce, CompletionKind completionKind) {
    145  BytecodeOffset start = bce->bytecodeSection().offset();
    146  if (!emitIteratorCloseInScope(bce, *bce->innermostEmitterScope(),
    147                                completionKind)) {
    148    return false;
    149  }
    150  BytecodeOffset end = bce->bytecodeSection().offset();
    151  return bce->addTryNote(TryNoteKind::ForOfIterClose, 0, start, end);
    152 }
    153 
    154 bool ForOfLoopControl::emitIteratorCloseInScope(BytecodeEmitter* bce,
    155                                                EmitterScope& currentScope,
    156                                                CompletionKind completionKind) {
    157  return bce->emitIteratorCloseInScope(currentScope, iterKind_, completionKind,
    158                                       selfHostedIter_);
    159 }
    160 
    161 // Since we're in the middle of emitting code that will leave
    162 // |bce->innermostEmitterScope()|, passing the innermost emitter scope to
    163 // emitIteratorCloseInScope and looking up .generator there would be very,
    164 // very wrong.  We'd find .generator in the function environment, and we'd
    165 // compute a NameLocation with the correct slot, but we'd compute a
    166 // hop-count to the function environment that was too big.  At runtime we'd
    167 // either crash, or we'd find a user-controllable value in that slot, and
    168 // Very Bad Things would ensue as we reinterpreted that value as an
    169 // iterator.
    170 bool ForOfLoopControl::emitPrepareForNonLocalJumpFromScope(
    171    BytecodeEmitter* bce, EmitterScope& currentScope, bool isTarget,
    172    BytecodeOffset* tryNoteStart) {
    173  // Pop unnecessary value from the stack.  Effectively this means
    174  // leaving try-catch block.  However, the performing IteratorClose can
    175  // reach the depth for try-catch, and effectively re-enter the
    176  // try-catch block.
    177  if (!bce->emit1(JSOp::Pop)) {
    178    //              [stack] NEXT ITER
    179    return false;
    180  }
    181 
    182  // Pop the iterator's next method.
    183  if (!bce->emit1(JSOp::Swap)) {
    184    //              [stack] ITER NEXT
    185    return false;
    186  }
    187  if (!bce->emit1(JSOp::Pop)) {
    188    //              [stack] ITER
    189    return false;
    190  }
    191 
    192 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    193  // Explicit Resource Management Proposal
    194  // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset
    195  // Step 9.k.i. Set result to
    196  // Completion(DisposeResources(iterationEnv.[[DisposeCapability]], result)).
    197  NonLocalIteratorCloseUsingEmitter disposeBeforeIterClose(bce);
    198 
    199  if (!disposeBeforeIterClose.prepareForIteratorClose(currentScope)) {
    200    //              [stack] EXC-DISPOSE? DISPOSE-THROWING? ITER
    201    return false;
    202  }
    203 #endif
    204 
    205  if (!bce->emit1(JSOp::Dup)) {
    206    //              [stack] EXC-DISPOSE? DISPOSE-THROWING? ITER ITER
    207    return false;
    208  }
    209 
    210  *tryNoteStart = bce->bytecodeSection().offset();
    211  if (!emitIteratorCloseInScope(bce, currentScope, CompletionKind::Normal)) {
    212    //              [stack] EXC-DISPOSE? DISPOSE-THROWING? ITER
    213    return false;
    214  }
    215 
    216 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    217  if (!disposeBeforeIterClose.emitEnd()) {
    218    //              [stack] ITER
    219    return false;
    220  }
    221 #endif
    222 
    223  if (isTarget) {
    224    // At the level of the target block, there's bytecode after the
    225    // loop that will pop the next method, the iterator, and the
    226    // value, so push two undefineds to balance the stack.
    227    if (!bce->emit1(JSOp::Undefined)) {
    228      //            [stack] ITER UNDEF
    229      return false;
    230    }
    231    if (!bce->emit1(JSOp::Undefined)) {
    232      //            [stack] ITER UNDEF UNDEF
    233      return false;
    234    }
    235  } else {
    236    if (!bce->emit1(JSOp::Pop)) {
    237      //            [stack]
    238      return false;
    239    }
    240  }
    241 
    242  return true;
    243 }