tor-browser

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

CForEmitter.cpp (5054B)


      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/CForEmitter.h"
      8 
      9 #include "frontend/BytecodeEmitter.h"  // BytecodeEmitter
     10 #include "frontend/EmitterScope.h"     // EmitterScope
     11 #include "vm/Opcodes.h"                // JSOp
     12 #include "vm/ScopeKind.h"              // ScopeKind
     13 #include "vm/StencilEnums.h"           // TryNoteKind
     14 
     15 using namespace js;
     16 using namespace js::frontend;
     17 
     18 using mozilla::Maybe;
     19 
     20 CForEmitter::CForEmitter(BytecodeEmitter* bce,
     21                         const EmitterScope* headLexicalEmitterScopeForLet)
     22    : bce_(bce),
     23      headLexicalEmitterScopeForLet_(headLexicalEmitterScopeForLet) {}
     24 
     25 bool CForEmitter::emitInit(const Maybe<uint32_t>& initPos) {
     26  MOZ_ASSERT(state_ == State::Start);
     27 
     28  loopInfo_.emplace(bce_, StatementKind::ForLoop);
     29 
     30  if (initPos) {
     31    if (!bce_->updateSourceCoordNotes(*initPos)) {
     32      return false;
     33    }
     34  }
     35 
     36 #ifdef DEBUG
     37  state_ = State::Init;
     38 #endif
     39  return true;
     40 }
     41 
     42 bool CForEmitter::emitCond(const Maybe<uint32_t>& condPos) {
     43  MOZ_ASSERT(state_ == State::Init);
     44 
     45  // ES 13.7.4.8 step 2. The initial freshening.
     46  //
     47  // If an initializer let-declaration may be captured during loop
     48  // iteration, the current scope has an environment.  If so, freshen the
     49  // current environment to expose distinct bindings for each loop
     50  // iteration.
     51  if (headLexicalEmitterScopeForLet_) {
     52    // The environment chain only includes an environment for the
     53    // for(;;) loop head's let-declaration *if* a scope binding is
     54    // captured, thus requiring a fresh environment each iteration. If
     55    // a lexical scope exists for the head, it must be the innermost
     56    // one. If that scope has closed-over bindings inducing an
     57    // environment, recreate the current environment.
     58    MOZ_ASSERT(headLexicalEmitterScopeForLet_ == bce_->innermostEmitterScope());
     59    MOZ_ASSERT(headLexicalEmitterScopeForLet_->scope(bce_).kind() ==
     60               ScopeKind::Lexical);
     61 
     62    if (headLexicalEmitterScopeForLet_->hasEnvironment()) {
     63      if (!bce_->emitInternedScopeOp(headLexicalEmitterScopeForLet_->index(),
     64                                     JSOp::FreshenLexicalEnv)) {
     65        return false;
     66      }
     67    }
     68  }
     69 
     70  if (!loopInfo_->emitLoopHead(bce_, condPos)) {
     71    //              [stack]
     72    return false;
     73  }
     74 
     75 #ifdef DEBUG
     76  state_ = State::Cond;
     77 #endif
     78  return true;
     79 }
     80 
     81 bool CForEmitter::emitBody(Cond cond) {
     82  MOZ_ASSERT(state_ == State::Cond);
     83  cond_ = cond;
     84 
     85  if (cond_ == Cond::Present) {
     86    if (!bce_->emitJump(JSOp::JumpIfFalse, &loopInfo_->breaks)) {
     87      return false;
     88    }
     89  }
     90 
     91  tdzCache_.emplace(bce_);
     92 
     93 #ifdef DEBUG
     94  state_ = State::Body;
     95 #endif
     96  return true;
     97 }
     98 
     99 bool CForEmitter::emitUpdate(Update update, const Maybe<uint32_t>& updatePos) {
    100  MOZ_ASSERT(state_ == State::Body);
    101  update_ = update;
    102  tdzCache_.reset();
    103 
    104  // Set loop and enclosing "update" offsets, for continue.  Note that we
    105  // continue to immediately *before* the block-freshening: continuing must
    106  // refresh the block.
    107  if (!loopInfo_->emitContinueTarget(bce_)) {
    108    return false;
    109  }
    110 
    111  // ES 13.7.4.8 step 3.e. The per-iteration freshening.
    112  if (headLexicalEmitterScopeForLet_) {
    113    MOZ_ASSERT(headLexicalEmitterScopeForLet_ == bce_->innermostEmitterScope());
    114    MOZ_ASSERT(headLexicalEmitterScopeForLet_->scope(bce_).kind() ==
    115               ScopeKind::Lexical);
    116 
    117    if (headLexicalEmitterScopeForLet_->hasEnvironment()) {
    118      if (!bce_->emitInternedScopeOp(headLexicalEmitterScopeForLet_->index(),
    119                                     JSOp::FreshenLexicalEnv)) {
    120        return false;
    121      }
    122    }
    123  }
    124 
    125  // The update code may not be executed at all; it needs its own TDZ
    126  // cache.
    127  if (update_ == Update::Present) {
    128    tdzCache_.emplace(bce_);
    129 
    130    if (updatePos) {
    131      if (!bce_->updateSourceCoordNotes(*updatePos)) {
    132        return false;
    133      }
    134    }
    135  }
    136 
    137 #ifdef DEBUG
    138  state_ = State::Update;
    139 #endif
    140  return true;
    141 }
    142 
    143 bool CForEmitter::emitEnd(uint32_t forPos) {
    144  MOZ_ASSERT(state_ == State::Update);
    145 
    146  if (update_ == Update::Present) {
    147    tdzCache_.reset();
    148 
    149    //              [stack] UPDATE
    150 
    151    if (!bce_->emit1(JSOp::Pop)) {
    152      //            [stack]
    153      return false;
    154    }
    155  }
    156 
    157  if (cond_ == Cond::Missing && update_ == Update::Missing) {
    158    // If there is no condition clause and no update clause, mark
    159    // the loop-ending "goto" with the location of the "for".
    160    // This ensures that the debugger will stop on each loop
    161    // iteration.
    162    if (!bce_->updateSourceCoordNotes(forPos)) {
    163      return false;
    164    }
    165  }
    166 
    167  // Emit the loop-closing jump.
    168  if (!loopInfo_->emitLoopEnd(bce_, JSOp::Goto, TryNoteKind::Loop)) {
    169    //              [stack]
    170    return false;
    171  }
    172 
    173  loopInfo_.reset();
    174 
    175 #ifdef DEBUG
    176  state_ = State::End;
    177 #endif
    178  return true;
    179 }