tor-browser

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

ElemOpEmitter.cpp (6520B)


      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/ElemOpEmitter.h"
      8 
      9 #include "frontend/BytecodeEmitter.h"
     10 #include "frontend/SharedContext.h"
     11 #include "vm/Opcodes.h"
     12 #include "vm/ThrowMsgKind.h"  // ThrowMsgKind
     13 
     14 using namespace js;
     15 using namespace js::frontend;
     16 
     17 ElemOpEmitter::ElemOpEmitter(BytecodeEmitter* bce, Kind kind, ObjKind objKind)
     18    : bce_(bce), kind_(kind), objKind_(objKind) {}
     19 
     20 bool ElemOpEmitter::prepareForObj() {
     21  MOZ_ASSERT(state_ == State::Start);
     22 
     23 #ifdef DEBUG
     24  state_ = State::Obj;
     25 #endif
     26  return true;
     27 }
     28 
     29 bool ElemOpEmitter::prepareForKey() {
     30  MOZ_ASSERT(state_ == State::Obj);
     31 
     32  if (isCall()) {
     33    if (!bce_->emit1(JSOp::Dup)) {
     34      //            [stack] # if Super
     35      //            [stack] THIS THIS
     36      //            [stack] # otherwise
     37      //            [stack] OBJ OBJ
     38      return false;
     39    }
     40  }
     41 
     42 #ifdef DEBUG
     43  state_ = State::Key;
     44 #endif
     45  return true;
     46 }
     47 
     48 bool ElemOpEmitter::emitGet() {
     49  MOZ_ASSERT(state_ == State::Key);
     50 
     51  if (isSuper()) {
     52    if (!bce_->emitSuperBase()) {
     53      //            [stack] THIS? THIS KEY SUPERBASE
     54      return false;
     55    }
     56  }
     57 
     58  // Inc/dec and compound assignment use the KEY twice, but if it's an object,
     59  // it must be converted by ToPropertyKey only once, per spec.
     60  if (isIncDec() || isCompoundAssignment()) {
     61    if (isSuper()) {
     62      if (!bce_->emit1(JSOp::Swap)) {
     63        //          [stack] THIS SUPERBASE KEY
     64        return false;
     65      }
     66      if (!bce_->emit1(JSOp::ToPropertyKey)) {
     67        //          [stack] THIS SUPERBASE KEY
     68        return false;
     69      }
     70      if (!bce_->emit1(JSOp::Swap)) {
     71        //          [stack] THIS KEY SUPERBASE
     72        return false;
     73      }
     74      if (!bce_->emitDupAt(2, 3)) {
     75        //          [stack] THIS KEY SUPERBASE THIS KEY SUPERBASE
     76        return false;
     77      }
     78    } else {
     79      if (!bce_->emit1(JSOp::ToPropertyKey)) {
     80        //          [stack] OBJ KEY
     81        return false;
     82      }
     83      if (!bce_->emit1(JSOp::Dup2)) {
     84        //          [stack] OBJ KEY OBJ KEY
     85        return false;
     86      }
     87    }
     88  }
     89 
     90  JSOp op;
     91  if (isSuper()) {
     92    op = JSOp::GetElemSuper;
     93  } else {
     94    op = JSOp::GetElem;
     95  }
     96  if (!bce_->emitElemOpBase(op)) {
     97    //              [stack] # if Get
     98    //              [stack] ELEM
     99    //              [stack] # if Call
    100    //              [stack] THIS ELEM
    101    //              [stack] # if Inc/Dec/Assignment, with Super
    102    //              [stack] THIS KEY SUPERBASE ELEM
    103    //              [stack] # if Inc/Dec/Assignment, other
    104    //              [stack] OBJ KEY ELEM
    105    return false;
    106  }
    107  if (isCall()) {
    108    if (!bce_->emit1(JSOp::Swap)) {
    109      //            [stack] ELEM THIS
    110      return false;
    111    }
    112  }
    113 
    114 #ifdef DEBUG
    115  state_ = State::Get;
    116 #endif
    117  return true;
    118 }
    119 
    120 bool ElemOpEmitter::prepareForRhs() {
    121  MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
    122  MOZ_ASSERT_IF(isSimpleAssignment() || isPropInit(), state_ == State::Key);
    123  MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get);
    124 
    125  if (isSimpleAssignment() || isPropInit()) {
    126    // For CompoundAssignment, SuperBase is already emitted by emitGet.
    127    if (isSuper()) {
    128      if (!bce_->emitSuperBase()) {
    129        //          [stack] THIS KEY SUPERBASE
    130        return false;
    131      }
    132    }
    133  }
    134 
    135 #ifdef DEBUG
    136  state_ = State::Rhs;
    137 #endif
    138  return true;
    139 }
    140 
    141 bool ElemOpEmitter::emitDelete() {
    142  MOZ_ASSERT(state_ == State::Key);
    143  MOZ_ASSERT(isDelete());
    144 
    145  if (isSuper()) {
    146    if (!bce_->emitSuperBase()) {
    147      //            [stack] THIS KEY SUPERBASE
    148      return false;
    149    }
    150 
    151    // Unconditionally throw when attempting to delete a super-reference.
    152    if (!bce_->emit2(JSOp::ThrowMsg, uint8_t(ThrowMsgKind::CantDeleteSuper))) {
    153      //            [stack] THIS KEY SUPERBASE
    154      return false;
    155    }
    156 
    157    // Another wrinkle: Balance the stack from the emitter's point of view.
    158    // Execution will not reach here, as the last bytecode threw.
    159    if (!bce_->emitPopN(2)) {
    160      //            [stack] THIS
    161      return false;
    162    }
    163  } else {
    164    JSOp op = bce_->sc->strict() ? JSOp::StrictDelElem : JSOp::DelElem;
    165    if (!bce_->emitElemOpBase(op)) {
    166      // SUCCEEDED
    167      return false;
    168    }
    169  }
    170 
    171 #ifdef DEBUG
    172  state_ = State::Delete;
    173 #endif
    174  return true;
    175 }
    176 
    177 bool ElemOpEmitter::emitAssignment() {
    178  MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
    179  MOZ_ASSERT(state_ == State::Rhs);
    180 
    181  MOZ_ASSERT_IF(isPropInit(), !isSuper());
    182 
    183  JSOp setOp = isPropInit() ? JSOp::InitElem
    184               : isSuper()  ? bce_->sc->strict() ? JSOp::StrictSetElemSuper
    185                                                 : JSOp::SetElemSuper
    186               : bce_->sc->strict() ? JSOp::StrictSetElem
    187                                    : JSOp::SetElem;
    188  if (!bce_->emitElemOpBase(setOp)) {
    189    //              [stack] ELEM
    190    return false;
    191  }
    192 
    193 #ifdef DEBUG
    194  state_ = State::Assignment;
    195 #endif
    196  return true;
    197 }
    198 
    199 bool ElemOpEmitter::emitIncDec(ValueUsage valueUsage) {
    200  MOZ_ASSERT(state_ == State::Key);
    201  MOZ_ASSERT(isIncDec());
    202 
    203  if (!emitGet()) {
    204    //              [stack] ... ELEM
    205    return false;
    206  }
    207 
    208  MOZ_ASSERT(state_ == State::Get);
    209 
    210  JSOp incOp = isInc() ? JSOp::Inc : JSOp::Dec;
    211  if (!bce_->emit1(JSOp::ToNumeric)) {
    212    //              [stack] ... N
    213    return false;
    214  }
    215  if (isPostIncDec() && valueUsage == ValueUsage::WantValue) {
    216    //              [stack] OBJ KEY SUPERBASE? N
    217    if (!bce_->emit1(JSOp::Dup)) {
    218      //            [stack] ... N N
    219      return false;
    220    }
    221    if (!bce_->emit2(JSOp::Unpick, 3 + isSuper())) {
    222      //            [stack] N OBJ KEY SUPERBASE? N
    223      return false;
    224    }
    225  }
    226  if (!bce_->emit1(incOp)) {
    227    //              [stack] ... N+1
    228    return false;
    229  }
    230 
    231  JSOp setOp =
    232      isSuper()
    233          ? (bce_->sc->strict() ? JSOp::StrictSetElemSuper : JSOp::SetElemSuper)
    234          : (bce_->sc->strict() ? JSOp::StrictSetElem : JSOp::SetElem);
    235  if (!bce_->emitElemOpBase(setOp)) {
    236    //              [stack] N? N+1
    237    return false;
    238  }
    239  if (isPostIncDec() && valueUsage == ValueUsage::WantValue) {
    240    if (!bce_->emit1(JSOp::Pop)) {
    241      //            [stack] N
    242      return false;
    243    }
    244  }
    245 
    246 #ifdef DEBUG
    247  state_ = State::IncDec;
    248 #endif
    249  return true;
    250 }