tor-browser

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

PrivateOpEmitter.cpp (8808B)


      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/PrivateOpEmitter.h"
      8 
      9 #include "frontend/BytecodeEmitter.h"
     10 #include "frontend/NameOpEmitter.h"
     11 #include "vm/Opcodes.h"
     12 #include "vm/ThrowMsgKind.h"  // ThrowMsgKind
     13 
     14 using namespace js;
     15 using namespace js::frontend;
     16 
     17 PrivateOpEmitter::PrivateOpEmitter(BytecodeEmitter* bce, Kind kind,
     18                                   TaggedParserAtomIndex name)
     19    : bce_(bce), kind_(kind), name_(name) {
     20  MOZ_ASSERT(kind_ != Kind::Delete);
     21 }
     22 
     23 bool PrivateOpEmitter::init() {
     24  // Static analysis needs us to initialise this to something, so use Dynamic()
     25  NameLocation loc = NameLocation::Dynamic();
     26  bce_->lookupPrivate(name_, loc, brandLoc_);
     27  loc_ = mozilla::Some(loc);
     28  return true;
     29 }
     30 
     31 bool PrivateOpEmitter::emitLoad(TaggedParserAtomIndex name,
     32                                const NameLocation& loc) {
     33  NameOpEmitter noe(bce_, name, loc, NameOpEmitter::Kind::Get);
     34  return noe.emitGet();
     35 }
     36 
     37 bool PrivateOpEmitter::emitLoadPrivateBrand() {
     38  return emitLoad(TaggedParserAtomIndex::WellKnown::dot_privateBrand_(),
     39                  *brandLoc_);
     40 }
     41 
     42 bool PrivateOpEmitter::emitBrandCheck() {
     43  MOZ_ASSERT(state_ == State::Reference);
     44 
     45  if (isBrandCheck()) {
     46    // Emit a CheckPrivateField CheckRhs; note: The message is irrelvant here,
     47    // it will never be thrown, so DoubleInit was chosen arbitrarily.
     48    if (!bce_->emitCheckPrivateField(ThrowCondition::OnlyCheckRhs,
     49                                     ThrowMsgKind::PrivateDoubleInit)) {
     50      //            [stack] OBJ KEY BBOOL
     51      return false;
     52    }
     53 
     54    return true;
     55  }
     56 
     57  //                [stack] OBJ KEY
     58  if (isFieldInit()) {
     59    if (!bce_->emitCheckPrivateField(ThrowCondition::ThrowHas,
     60                                     ThrowMsgKind::PrivateDoubleInit)) {
     61      //            [stack] OBJ KEY false
     62      return false;
     63    }
     64  } else {
     65    bool assigning =
     66        isSimpleAssignment() || isCompoundAssignment() || isIncDec();
     67    if (!bce_->emitCheckPrivateField(ThrowCondition::ThrowHasNot,
     68                                     assigning
     69                                         ? ThrowMsgKind::MissingPrivateOnSet
     70                                         : ThrowMsgKind::MissingPrivateOnGet)) {
     71      //            [stack] OBJ KEY true
     72      return false;
     73    }
     74  }
     75 
     76  return true;
     77 }
     78 
     79 bool PrivateOpEmitter::emitReference() {
     80  MOZ_ASSERT(state_ == State::Start);
     81 
     82  if (!init()) {
     83    return false;
     84  }
     85 
     86  if (brandLoc_) {
     87    if (!emitLoadPrivateBrand()) {
     88      //            [stack] OBJ BRAND
     89      return false;
     90    }
     91  } else {
     92    if (!emitLoad(name_, loc_.ref())) {
     93      //            [stack] OBJ NAME
     94      return false;
     95    }
     96  }
     97 #ifdef DEBUG
     98  state_ = State::Reference;
     99 #endif
    100  return true;
    101 }
    102 
    103 bool PrivateOpEmitter::emitGet() {
    104  MOZ_ASSERT(state_ == State::Reference);
    105 
    106  //                [stack] OBJ NAME
    107 
    108  if (brandLoc_) {
    109    // Note that the decision of what we leave on the stack depends on kind_,
    110    // not loc_->bindingKind().  We can't emit code for a call just because this
    111    // private member is a method. `obj.#method` is allowed without a call,
    112    // just fetching the function object (it's useful in code like
    113    // `obj.#method.bind(...)`). Even if the user says `obj.#method += 7`, we
    114    // emit honest bytecode for the brand check, method load, and addition, and
    115    // throw the error later. This preserves stack nuses/ndefs balance.
    116    if (!emitBrandCheck()) {
    117      //            [stack] OBJ BRAND true
    118      return false;
    119    }
    120 
    121    if (isCompoundAssignment()) {
    122      if (!bce_->emit1(JSOp::Pop)) {
    123        //          [stack] OBJ BRAND
    124        return false;
    125      }
    126    } else if (isCall()) {
    127      if (!bce_->emitPopN(2)) {
    128        //          [stack] OBJ
    129        return false;
    130      }
    131    } else {
    132      if (!bce_->emitPopN(3)) {
    133        //          [stack]
    134        return false;
    135      }
    136    }
    137 
    138    if (!emitLoad(name_, loc_.ref())) {
    139      //            [stack] OBJ BRAND METHOD  # if isCompoundAssignment
    140      //            [stack] OBJ METHOD        # if call
    141      //            [stack] METHOD            # otherwise
    142      return false;
    143    }
    144  } else {
    145    if (isCall()) {
    146      if (!bce_->emitDupAt(1)) {
    147        //          [stack] OBJ NAME OBJ
    148        return false;
    149      }
    150      if (!bce_->emit1(JSOp::Swap)) {
    151        //          [stack] OBJ OBJ NAME
    152        return false;
    153      }
    154    }
    155    //              [stack] OBJ? OBJ NAME
    156    if (!emitBrandCheck()) {
    157      //            [stack] OBJ? OBJ NAME true
    158      return false;
    159    }
    160    if (!bce_->emit1(JSOp::Pop)) {
    161      //            [stack] OBJ? OBJ NAME
    162      return false;
    163    }
    164 
    165    if (isCompoundAssignment()) {
    166      if (!bce_->emit1(JSOp::Dup2)) {
    167        //          [stack] OBJ NAME OBJ NAME
    168        return false;
    169      }
    170    }
    171 
    172    if (!bce_->emitElemOpBase(JSOp::GetElem)) {
    173      //            [stack] OBJ NAME VALUE  # if isCompoundAssignment
    174      //            [stack] OBJ METHOD      # if Call
    175      //            [stack] VALUE           # otherwise
    176      return false;
    177    }
    178  }
    179 
    180  if (isCall()) {
    181    if (!bce_->emit1(JSOp::Swap)) {
    182      //            [stack] METHOD OBJ
    183      return false;
    184    }
    185  }
    186 
    187  //                [stack] OBJ NAME VALUE  # if isCompoundAssignment
    188  //                [stack] METHOD OBJ      # if call
    189  //                [stack] VALUE           # otherwise
    190 
    191 #ifdef DEBUG
    192  state_ = State::Get;
    193 #endif
    194  return true;
    195 }
    196 
    197 bool PrivateOpEmitter::emitGetForCallOrNew() { return emitGet(); }
    198 
    199 bool PrivateOpEmitter::emitAssignment() {
    200  MOZ_ASSERT(isSimpleAssignment() || isFieldInit() || isCompoundAssignment());
    201  MOZ_ASSERT_IF(!isCompoundAssignment(), state_ == State::Reference);
    202  MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get);
    203 
    204  //                [stack] OBJ KEY RHS
    205 
    206  if (brandLoc_) {
    207    if (!bce_->emit2(JSOp::ThrowMsg,
    208                     uint8_t(ThrowMsgKind::AssignToPrivateMethod))) {
    209      return false;
    210    }
    211 
    212    // Balance the expression stack.
    213    if (!bce_->emitPopN(2)) {
    214      //            [stack] OBJ
    215      return false;
    216    }
    217  } else {
    218    // Emit a brand check. If this is compound assignment, emitGet() already
    219    // emitted a check for this object and key. There's no point checking
    220    // again--a private field can't be removed from an object.
    221    if (!isCompoundAssignment()) {
    222      if (!bce_->emitUnpickN(2)) {
    223        //          [stack] RHS OBJ KEY
    224        return false;
    225      }
    226      if (!emitBrandCheck()) {
    227        //          [stack] RHS OBJ KEY BOOL
    228        return false;
    229      }
    230      if (!bce_->emit1(JSOp::Pop)) {
    231        //          [stack] RHS OBJ KEY
    232        return false;
    233      }
    234      if (!bce_->emitPickN(2)) {
    235        //          [stack] OBJ KEY RHS
    236        return false;
    237      }
    238    }
    239 
    240    JSOp setOp = isFieldInit() ? JSOp::InitHiddenElem : JSOp::StrictSetElem;
    241    if (!bce_->emitElemOpBase(setOp)) {
    242      //            [stack] RHS
    243      return false;
    244    }
    245  }
    246 
    247 #ifdef DEBUG
    248  state_ = State::Assignment;
    249 #endif
    250  return true;
    251 }
    252 
    253 bool PrivateOpEmitter::emitIncDec(ValueUsage valueUsage) {
    254  MOZ_ASSERT(state_ == State::Reference);
    255  MOZ_ASSERT(isIncDec());
    256  //                [stack] OBJ NAME
    257 
    258  if (!bce_->emitDupAt(1, 2)) {
    259    //              [stack] OBJ NAME OBJ NAME
    260    return false;
    261  }
    262 
    263  if (!emitGet()) {
    264    //              [stack] OBJ NAME VALUE
    265    return false;
    266  }
    267 
    268  MOZ_ASSERT(state_ == State::Get);
    269 
    270  JSOp incOp = isInc() ? JSOp::Inc : JSOp::Dec;
    271 
    272  if (!bce_->emit1(JSOp::ToNumeric)) {
    273    //              [stack] OBJ NAME N
    274    return false;
    275  }
    276  if (isPostIncDec() && valueUsage == ValueUsage::WantValue) {
    277    //              [stack] OBJ NAME N
    278    if (!bce_->emit1(JSOp::Dup)) {
    279      //            [stack] OBJ NAME N N
    280      return false;
    281    }
    282    if (!bce_->emit2(JSOp::Unpick, 3)) {
    283      //            [stack] N OBJ NAME N
    284      return false;
    285    }
    286  }
    287  if (!bce_->emit1(incOp)) {
    288    //              [stack] N? OBJ NAME N+1
    289    return false;
    290  }
    291 
    292  if (brandLoc_) {
    293    if (!bce_->emit2(JSOp::ThrowMsg,
    294                     uint8_t(ThrowMsgKind::AssignToPrivateMethod))) {
    295      return false;
    296    }
    297 
    298    // Balance the expression stack.
    299    if (!bce_->emitPopN(2)) {
    300      //            [stack] N? N+1
    301      return false;
    302    }
    303  } else {
    304    if (!bce_->emitElemOpBase(JSOp::StrictSetElem)) {
    305      //            [stack] N? N+1
    306      return false;
    307    }
    308  }
    309 
    310  if (isPostIncDec() && valueUsage == ValueUsage::WantValue) {
    311    if (!bce_->emit1(JSOp::Pop)) {
    312      //            [stack] N
    313      return false;
    314    }
    315  }
    316 
    317  return true;
    318 }