tor-browser

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

BytecodeControlStructures.cpp (12747B)


      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/BytecodeControlStructures.h"
      8 
      9 #include "frontend/BytecodeEmitter.h"   // BytecodeEmitter
     10 #include "frontend/EmitterScope.h"      // EmitterScope
     11 #include "frontend/ForOfLoopControl.h"  // ForOfLoopControl
     12 #include "frontend/SwitchEmitter.h"     // SwitchEmitter
     13 #include "vm/Opcodes.h"                 // JSOp
     14 
     15 using namespace js;
     16 using namespace js::frontend;
     17 
     18 using mozilla::Maybe;
     19 
     20 NestableControl::NestableControl(BytecodeEmitter* bce, StatementKind kind)
     21    : Nestable<NestableControl>(&bce->innermostNestableControl),
     22      kind_(kind),
     23      emitterScope_(bce->innermostEmitterScopeNoCheck()) {}
     24 
     25 BreakableControl::BreakableControl(BytecodeEmitter* bce, StatementKind kind)
     26    : NestableControl(bce, kind) {
     27  MOZ_ASSERT(is<BreakableControl>());
     28 }
     29 
     30 bool BreakableControl::patchBreaks(BytecodeEmitter* bce) {
     31  return bce->emitJumpTargetAndPatch(breaks);
     32 }
     33 
     34 LabelControl::LabelControl(BytecodeEmitter* bce, TaggedParserAtomIndex label,
     35                           BytecodeOffset startOffset)
     36    : BreakableControl(bce, StatementKind::Label),
     37      label_(label),
     38      startOffset_(startOffset) {}
     39 
     40 LoopControl::LoopControl(BytecodeEmitter* bce, StatementKind loopKind)
     41    : BreakableControl(bce, loopKind), tdzCache_(bce) {
     42  MOZ_ASSERT(is<LoopControl>());
     43 
     44  LoopControl* enclosingLoop = findNearest<LoopControl>(enclosing());
     45 
     46  stackDepth_ = bce->bytecodeSection().stackDepth();
     47  loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1;
     48 }
     49 
     50 bool LoopControl::emitContinueTarget(BytecodeEmitter* bce) {
     51  // Note: this is always called after emitting the loop body so we must have
     52  // emitted all 'continues' by now.
     53  return bce->emitJumpTargetAndPatch(continues);
     54 }
     55 
     56 bool LoopControl::emitLoopHead(BytecodeEmitter* bce,
     57                               const Maybe<uint32_t>& nextPos) {
     58  // Insert a Nop if needed to ensure the script does not start with a
     59  // JSOp::LoopHead. This avoids JIT issues with prologue code + try notes
     60  // or OSR. See bug 1602390 and bug 1602681.
     61  if (bce->bytecodeSection().offset().toUint32() == 0) {
     62    if (!bce->emit1(JSOp::Nop)) {
     63      return false;
     64    }
     65  }
     66 
     67  if (nextPos) {
     68    if (!bce->updateSourceCoordNotes(*nextPos)) {
     69      return false;
     70    }
     71  }
     72 
     73  MOZ_ASSERT(loopDepth_ > 0);
     74 
     75  head_ = {bce->bytecodeSection().offset()};
     76 
     77  BytecodeOffset off;
     78  if (!bce->emitJumpTargetOp(JSOp::LoopHead, &off)) {
     79    return false;
     80  }
     81  SetLoopHeadDepthHint(bce->bytecodeSection().code(off), loopDepth_);
     82 
     83  return true;
     84 }
     85 
     86 bool LoopControl::emitLoopEnd(BytecodeEmitter* bce, JSOp op,
     87                              TryNoteKind tryNoteKind) {
     88  JumpList jump;
     89  if (!bce->emitJumpNoFallthrough(op, &jump)) {
     90    return false;
     91  }
     92  bce->patchJumpsToTarget(jump, head_);
     93 
     94  // Create a fallthrough for closing iterators, and as a target for break
     95  // statements.
     96  JumpTarget breakTarget;
     97  if (!bce->emitJumpTarget(&breakTarget)) {
     98    return false;
     99  }
    100  if (!patchBreaks(bce)) {
    101    return false;
    102  }
    103  if (!bce->addTryNote(tryNoteKind, bce->bytecodeSection().stackDepth(),
    104                       headOffset(), breakTarget.offset)) {
    105    return false;
    106  }
    107  return true;
    108 }
    109 
    110 TryFinallyControl::TryFinallyControl(BytecodeEmitter* bce, StatementKind kind)
    111    : NestableControl(bce, kind) {
    112  MOZ_ASSERT(is<TryFinallyControl>());
    113 }
    114 
    115 bool TryFinallyControl::allocateContinuation(NestableControl* target,
    116                                             NonLocalExitKind kind,
    117                                             uint32_t* idx) {
    118  for (uint32_t i = 0; i < continuations_.length(); i++) {
    119    if (continuations_[i].target_ == target &&
    120        continuations_[i].kind_ == kind) {
    121      *idx = i + SpecialContinuations::Count;
    122      return true;
    123    }
    124  }
    125  *idx = continuations_.length() + SpecialContinuations::Count;
    126  return continuations_.emplaceBack(target, kind);
    127 }
    128 
    129 bool TryFinallyControl::emitContinuations(BytecodeEmitter* bce) {
    130  SwitchEmitter::TableGenerator tableGen(bce);
    131  for (uint32_t i = 0; i < continuations_.length(); i++) {
    132    if (!tableGen.addNumber(i + SpecialContinuations::Count)) {
    133      return false;
    134    }
    135  }
    136  tableGen.finish(continuations_.length());
    137  MOZ_RELEASE_ASSERT(tableGen.isValid());
    138 
    139  InternalSwitchEmitter se(bce);
    140  if (!se.validateCaseCount(continuations_.length())) {
    141    return false;
    142  }
    143  if (!se.emitTable(tableGen)) {
    144    return false;
    145  }
    146 
    147  // Continuation index 0 is special-cased to be the fallthrough block.
    148  // Non-default switch cases are numbered 1-N.
    149  uint32_t caseIdx = SpecialContinuations::Count;
    150  for (TryFinallyContinuation& continuation : continuations_) {
    151    if (!se.emitCaseBody(caseIdx++, tableGen)) {
    152      return false;
    153    }
    154    // Resume the non-local control flow that was intercepted by
    155    // this finally.
    156    NonLocalExitControl nle(bce, continuation.kind_);
    157    if (!nle.emitNonLocalJump(continuation.target_, this)) {
    158      return false;
    159    }
    160  }
    161 
    162  // The only unhandled case is the fallthrough case, which is handled
    163  // by the switch default.
    164  if (!se.emitDefaultBody()) {
    165    return false;
    166  }
    167  if (!se.emitEnd()) {
    168    return false;
    169  }
    170  return true;
    171 }
    172 
    173 NonLocalExitControl::NonLocalExitControl(BytecodeEmitter* bce,
    174                                         NonLocalExitKind kind)
    175    : bce_(bce),
    176      savedScopeNoteIndex_(bce->bytecodeSection().scopeNoteList().length()),
    177      savedDepth_(bce->bytecodeSection().stackDepth()),
    178      openScopeNoteIndex_(bce->innermostEmitterScope()->noteIndex()),
    179      kind_(kind) {}
    180 
    181 NonLocalExitControl::~NonLocalExitControl() {
    182  for (uint32_t n = savedScopeNoteIndex_;
    183       n < bce_->bytecodeSection().scopeNoteList().length(); n++) {
    184    bce_->bytecodeSection().scopeNoteList().recordEnd(
    185        n, bce_->bytecodeSection().offset());
    186  }
    187  bce_->bytecodeSection().setStackDepth(savedDepth_);
    188 }
    189 
    190 bool NonLocalExitControl::emitReturn(BytecodeOffset setRvalOffset) {
    191  MOZ_ASSERT(kind_ == NonLocalExitKind::Return);
    192  setRvalOffset_ = setRvalOffset;
    193  return emitNonLocalJump(nullptr);
    194 }
    195 
    196 bool NonLocalExitControl::leaveScope(EmitterScope* es) {
    197  if (!es->leave(bce_, /* nonLocal = */ true)) {
    198    return false;
    199  }
    200 
    201  // As we pop each scope due to the non-local jump, emit notes that
    202  // record the extent of the enclosing scope. These notes will have
    203  // their ends recorded in ~NonLocalExitControl().
    204  GCThingIndex enclosingScopeIndex = ScopeNote::NoScopeIndex;
    205  if (es->enclosingInFrame()) {
    206    enclosingScopeIndex = es->enclosingInFrame()->index();
    207  }
    208  if (!bce_->bytecodeSection().scopeNoteList().append(
    209          enclosingScopeIndex, bce_->bytecodeSection().offset(),
    210          openScopeNoteIndex_)) {
    211    return false;
    212  }
    213  openScopeNoteIndex_ = bce_->bytecodeSection().scopeNoteList().length() - 1;
    214 
    215  return true;
    216 }
    217 
    218 /*
    219 * Emit additional bytecode(s) for non-local jumps.
    220 */
    221 bool NonLocalExitControl::emitNonLocalJump(NestableControl* target,
    222                                           NestableControl* startingAfter) {
    223  NestableControl* startingControl = startingAfter
    224                                         ? startingAfter->enclosing()
    225                                         : bce_->innermostNestableControl;
    226  EmitterScope* es = startingAfter ? startingAfter->emitterScope()
    227                                   : bce_->innermostEmitterScope();
    228 
    229  int npops = 0;
    230 
    231  AutoCheckUnstableEmitterScope cues(bce_);
    232 
    233  // We emit IteratorClose bytecode inline. 'continue' statements do
    234  // not call IteratorClose for the loop they are continuing.
    235  bool emitIteratorCloseAtTarget = kind_ != NonLocalExitKind::Continue;
    236 
    237  auto flushPops = [&npops](BytecodeEmitter* bce) {
    238    if (npops && !bce->emitPopN(npops)) {
    239      return false;
    240    }
    241    npops = 0;
    242    return true;
    243  };
    244 
    245  // If we are closing multiple for-of loops, the resulting
    246  // TryNoteKind::ForOfIterClose trynotes must be appropriately nested. Each
    247  // TryNoteKind::ForOfIterClose starts when we close the corresponding for-of
    248  // iterator, and continues until the actual jump.
    249  Vector<BytecodeOffset, 4> forOfIterCloseScopeStarts(bce_->fc);
    250 
    251  // If we have to execute a finally block, then we will jump there now and
    252  // continue the non-local jump from the end of the finally block.
    253  bool jumpingToFinally = false;
    254 
    255  // Walk the nestable control stack and patch jumps.
    256  for (NestableControl* control = startingControl;
    257       control != target && !jumpingToFinally; control = control->enclosing()) {
    258    // Walk the scope stack and leave the scopes we entered. Leaving a scope
    259    // may emit administrative ops like JSOp::PopLexicalEnv but never anything
    260    // that manipulates the stack.
    261    for (; es != control->emitterScope(); es = es->enclosingInFrame()) {
    262      if (!leaveScope(es)) {
    263        return false;
    264      }
    265    }
    266 
    267    switch (control->kind()) {
    268      case StatementKind::Finally: {
    269        TryFinallyControl& finallyControl = control->as<TryFinallyControl>();
    270        if (finallyControl.emittingSubroutine()) {
    271          /*
    272           * There's a [resume-index-or-exception, exception-stack, throwing]
    273           * triple on the stack that we need to pop. If the script is not a
    274           * noScriptRval script, we also need to pop the cached rval.
    275           */
    276          if (bce_->sc->noScriptRval()) {
    277            npops += 3;
    278          } else {
    279            npops += 4;
    280          }
    281        } else {
    282          jumpingToFinally = true;
    283 
    284          if (!flushPops(bce_)) {
    285            return false;
    286          }
    287          uint32_t idx;
    288          if (!finallyControl.allocateContinuation(target, kind_, &idx)) {
    289            return false;
    290          }
    291          if (!bce_->emitJumpToFinally(&finallyControl.finallyJumps_, idx)) {
    292            return false;
    293          }
    294        }
    295        break;
    296      }
    297 
    298      case StatementKind::ForOfLoop: {
    299        if (!flushPops(bce_)) {
    300          return false;
    301        }
    302        BytecodeOffset tryNoteStart;
    303        ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>();
    304        if (!loopinfo.emitPrepareForNonLocalJumpFromScope(
    305                bce_, *es,
    306                /* isTarget = */ false, &tryNoteStart)) {
    307          //      [stack] ...
    308          return false;
    309        }
    310        if (!forOfIterCloseScopeStarts.append(tryNoteStart)) {
    311          return false;
    312        }
    313        break;
    314      }
    315 
    316      case StatementKind::ForInLoop:
    317        if (!flushPops(bce_)) {
    318          return false;
    319        }
    320 
    321        // The iterator and the current value are on the stack.
    322        if (!bce_->emit1(JSOp::EndIter)) {
    323          //        [stack] ...
    324          return false;
    325        }
    326        break;
    327 
    328      default:
    329        break;
    330    }
    331  }
    332 
    333  if (!flushPops(bce_)) {
    334    return false;
    335  }
    336 
    337  if (!jumpingToFinally) {
    338    if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) {
    339      BytecodeOffset tryNoteStart;
    340      ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>();
    341      if (!loopinfo.emitPrepareForNonLocalJumpFromScope(bce_, *es,
    342                                                        /* isTarget = */ true,
    343                                                        &tryNoteStart)) {
    344        //            [stack] ... UNDEF UNDEF UNDEF
    345        return false;
    346      }
    347      if (!forOfIterCloseScopeStarts.append(tryNoteStart)) {
    348        return false;
    349      }
    350    }
    351 
    352    EmitterScope* targetEmitterScope =
    353        target ? target->emitterScope() : bce_->varEmitterScope;
    354    for (; es != targetEmitterScope; es = es->enclosingInFrame()) {
    355      if (!leaveScope(es)) {
    356        return false;
    357      }
    358    }
    359    switch (kind_) {
    360      case NonLocalExitKind::Continue: {
    361        LoopControl* loop = &target->as<LoopControl>();
    362        if (!bce_->emitJump(JSOp::Goto, &loop->continues)) {
    363          return false;
    364        }
    365        break;
    366      }
    367      case NonLocalExitKind::Break: {
    368        BreakableControl* breakable = &target->as<BreakableControl>();
    369        if (!bce_->emitJump(JSOp::Goto, &breakable->breaks)) {
    370          return false;
    371        }
    372        break;
    373      }
    374      case NonLocalExitKind::Return:
    375        MOZ_ASSERT(!target);
    376        if (!bce_->finishReturn(setRvalOffset_)) {
    377          return false;
    378        }
    379        break;
    380    }
    381  }
    382 
    383  // Close TryNoteKind::ForOfIterClose trynotes.
    384  BytecodeOffset end = bce_->bytecodeSection().offset();
    385  for (BytecodeOffset start : forOfIterCloseScopeStarts) {
    386    if (!bce_->addTryNote(TryNoteKind::ForOfIterClose, 0, start, end)) {
    387      return false;
    388    }
    389  }
    390 
    391  return true;
    392 }