tor-browser

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

ForOfEmitter.cpp (8627B)


      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/ForOfEmitter.h"
      8 
      9 #include "frontend/BytecodeEmitter.h"
     10 #include "frontend/EmitterScope.h"
     11 #include "frontend/ParserAtom.h"  // TaggedParserAtomIndex
     12 #include "frontend/UsingEmitter.h"
     13 #include "vm/Opcodes.h"
     14 #include "vm/StencilEnums.h"  // TryNoteKind
     15 
     16 using namespace js;
     17 using namespace js::frontend;
     18 
     19 using mozilla::Nothing;
     20 
     21 ForOfEmitter::ForOfEmitter(BytecodeEmitter* bce,
     22                           const EmitterScope* headLexicalEmitterScope,
     23                           SelfHostedIter selfHostedIter, IteratorKind iterKind
     24 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
     25                           ,
     26                           HeadUsingDeclarationKind usingDeclarationInHead
     27 #endif
     28                           )
     29    : bce_(bce),
     30      selfHostedIter_(selfHostedIter),
     31      iterKind_(iterKind),
     32      headLexicalEmitterScope_(headLexicalEmitterScope)
     33 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
     34      ,
     35      usingDeclarationInHead_(usingDeclarationInHead)
     36 #endif
     37 {
     38 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
     39  // The using bindings are closed over and stored in the lexical environment
     40  // object for headLexicalEmitterScope.
     41  // Mark that the environment has disposables for them to be disposed on
     42  // every iteration.
     43  MOZ_ASSERT_IF(usingDeclarationInHead != HeadUsingDeclarationKind::None,
     44                headLexicalEmitterScope->hasEnvironment() &&
     45                    headLexicalEmitterScope == bce_->innermostEmitterScope() &&
     46                    headLexicalEmitterScope->hasDisposables());
     47  MOZ_ASSERT_IF(
     48      headLexicalEmitterScope && headLexicalEmitterScope->hasDisposables(),
     49      usingDeclarationInHead != HeadUsingDeclarationKind::None);
     50 #endif
     51 }
     52 
     53 bool ForOfEmitter::emitIterated() {
     54  MOZ_ASSERT(state_ == State::Start);
     55 
     56  // Evaluate the expression being iterated. The forHeadExpr should use a
     57  // distinct TDZCheckCache to evaluate since (abstractly) it runs in its
     58  // own LexicalEnvironment.
     59  tdzCacheForIteratedValue_.emplace(bce_);
     60 
     61 #ifdef DEBUG
     62  state_ = State::Iterated;
     63 #endif
     64  return true;
     65 }
     66 
     67 bool ForOfEmitter::emitInitialize(uint32_t forPos) {
     68  MOZ_ASSERT(state_ == State::Iterated);
     69 
     70  tdzCacheForIteratedValue_.reset();
     71 
     72  //                [stack] # if AllowContentWithNext
     73  //                [stack] NEXT ITER
     74  //                [stack] # elif AllowContentWith
     75  //                [stack] ITERABLE ITERFN SYNC_ITERFN?
     76  //                [stack] # else
     77  //                [stack] ITERABLE
     78 
     79  if (iterKind_ == IteratorKind::Async) {
     80    if (!bce_->emitAsyncIterator(selfHostedIter_)) {
     81      //            [stack] NEXT ITER
     82      return false;
     83    }
     84  } else {
     85    if (!bce_->emitIterator(selfHostedIter_)) {
     86      //            [stack] NEXT ITER
     87      return false;
     88    }
     89  }
     90 
     91  // For-of loops have the iterator next method and the iterator itself on the
     92  // stack.
     93 
     94  int32_t iterDepth = bce_->bytecodeSection().stackDepth();
     95  loopInfo_.emplace(bce_, iterDepth, selfHostedIter_, iterKind_);
     96 
     97  if (!loopInfo_->emitLoopHead(bce_, Nothing())) {
     98    //              [stack] NEXT ITER
     99    return false;
    100  }
    101 
    102  // If the loop had an escaping lexical declaration, replace the current
    103  // environment with an dead zoned one to implement TDZ semantics.
    104  if (headLexicalEmitterScope_) {
    105    // The environment chain only includes an environment for the for-of
    106    // loop head *if* a scope binding is captured, thereby requiring
    107    // recreation each iteration. If a lexical scope exists for the head,
    108    // it must be the innermost one. If that scope has closed-over
    109    // bindings inducing an environment, recreate the current environment.
    110    MOZ_ASSERT(headLexicalEmitterScope_ == bce_->innermostEmitterScope());
    111    MOZ_ASSERT(headLexicalEmitterScope_->scope(bce_).kind() ==
    112               ScopeKind::Lexical);
    113 
    114    if (headLexicalEmitterScope_->hasEnvironment()) {
    115 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    116      // Before recreation of the lexical environment, we must dispose
    117      // the disposables of the previous iteration.
    118      //
    119      // Emitting the bytecode to dispose over here means
    120      // that we will have one extra disposal at the start of the loop which
    121      // is a no op because there arent any disposables added yet.
    122      //
    123      // There also wouldn't be a dispose operation for the environment
    124      // object recreated for the last iteration, where it leaves the loop
    125      // before evaluating the body statement.
    126      if (!loopInfo_->prepareForForOfLoopIteration(
    127              bce_, headLexicalEmitterScope_,
    128              usingDeclarationInHead_ == HeadUsingDeclarationKind::Async)) {
    129        return false;
    130      }
    131 #endif
    132      if (!bce_->emitInternedScopeOp(headLexicalEmitterScope_->index(),
    133                                     JSOp::RecreateLexicalEnv)) {
    134        //          [stack] NEXT ITER
    135        return false;
    136      }
    137    }
    138 
    139    // For uncaptured bindings, put them back in TDZ.
    140    if (!headLexicalEmitterScope_->deadZoneFrameSlots(bce_)) {
    141      return false;
    142    }
    143  }
    144 
    145 #ifdef DEBUG
    146  loopDepth_ = bce_->bytecodeSection().stackDepth();
    147 #endif
    148 
    149  // Make sure this code is attributed to the "for".
    150  if (!bce_->updateSourceCoordNotes(forPos)) {
    151    return false;
    152  }
    153 
    154  if (!bce_->emit1(JSOp::Dup2)) {
    155    //              [stack] NEXT ITER NEXT ITER
    156    return false;
    157  }
    158 
    159  if (!bce_->emitIteratorNext(mozilla::Some(forPos), iterKind_,
    160                              selfHostedIter_)) {
    161    //              [stack] NEXT ITER RESULT
    162    return false;
    163  }
    164 
    165  if (!bce_->emit1(JSOp::Dup)) {
    166    //              [stack] NEXT ITER RESULT RESULT
    167    return false;
    168  }
    169  if (!bce_->emitAtomOp(JSOp::GetProp,
    170                        TaggedParserAtomIndex::WellKnown::done())) {
    171    //              [stack] NEXT ITER RESULT DONE
    172    return false;
    173  }
    174 
    175  // if (done) break;
    176  MOZ_ASSERT(bce_->innermostNestableControl == loopInfo_.ptr(),
    177             "must be at the top-level of the loop");
    178  if (!bce_->emitJump(JSOp::JumpIfTrue, &loopInfo_->breaks)) {
    179    //              [stack] NEXT ITER RESULT
    180    return false;
    181  }
    182 
    183  // Emit code to assign result.value to the iteration variable.
    184  //
    185  // Note that ES 13.7.5.13, step 5.c says getting result.value does not
    186  // call IteratorClose, so start TryNoteKind::ForOfIterClose after the GetProp.
    187  if (!bce_->emitAtomOp(JSOp::GetProp,
    188                        TaggedParserAtomIndex::WellKnown::value())) {
    189    //              [stack] NEXT ITER VALUE
    190    return false;
    191  }
    192 
    193  if (!loopInfo_->emitBeginCodeNeedingIteratorClose(bce_)) {
    194    return false;
    195  }
    196 
    197 #ifdef DEBUG
    198  state_ = State::Initialize;
    199 #endif
    200  return true;
    201 }
    202 
    203 bool ForOfEmitter::emitBody() {
    204  MOZ_ASSERT(state_ == State::Initialize);
    205 
    206  MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_ + 1,
    207             "the stack must be balanced around the initializing "
    208             "operation");
    209 
    210 #ifdef DEBUG
    211  state_ = State::Body;
    212 #endif
    213  return true;
    214 }
    215 
    216 bool ForOfEmitter::emitEnd(uint32_t iteratedPos) {
    217  MOZ_ASSERT(state_ == State::Body);
    218 
    219  MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_ + 1,
    220             "the stack must be balanced around the for-of body");
    221 
    222  if (!loopInfo_->emitEndCodeNeedingIteratorClose(bce_)) {
    223    //              [stack] NEXT ITER VALUE
    224    return false;
    225  }
    226 
    227  if (!loopInfo_->emitContinueTarget(bce_)) {
    228    //              [stack] NEXT ITER VALUE
    229    return false;
    230  }
    231 
    232  // We use the iterated value's position to attribute the backedge,
    233  // which corresponds to the iteration protocol.
    234  // This is a bit misleading for 2nd and later iterations and might need
    235  // some fix (bug 1482003).
    236  if (!bce_->updateSourceCoordNotes(iteratedPos)) {
    237    return false;
    238  }
    239 
    240  if (!bce_->emit1(JSOp::Pop)) {
    241    //              [stack] NEXT ITER
    242    return false;
    243  }
    244 
    245  if (!loopInfo_->emitLoopEnd(bce_, JSOp::Goto, TryNoteKind::ForOf)) {
    246    //              [stack] NEXT ITER
    247    return false;
    248  }
    249 
    250  // All jumps/breaks to this point still have an extra value on the stack.
    251  MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_);
    252  bce_->bytecodeSection().setStackDepth(bce_->bytecodeSection().stackDepth() +
    253                                        1);
    254 
    255  if (!bce_->emitPopN(3)) {
    256    //              [stack]
    257    return false;
    258  }
    259 
    260  loopInfo_.reset();
    261 
    262 #ifdef DEBUG
    263  state_ = State::End;
    264 #endif
    265  return true;
    266 }