tor-browser

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

NameOpEmitter.cpp (16533B)


      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/NameOpEmitter.h"
      8 
      9 #include "frontend/AbstractScopePtr.h"
     10 #include "frontend/BytecodeEmitter.h"
     11 #include "frontend/ParserAtom.h"  // ParserAtom
     12 #include "frontend/SharedContext.h"
     13 #include "frontend/TDZCheckCache.h"
     14 #include "frontend/ValueUsage.h"
     15 #include "js/Value.h"
     16 #include "vm/Opcodes.h"
     17 
     18 using namespace js;
     19 using namespace js::frontend;
     20 
     21 NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, TaggedParserAtomIndex name,
     22                             Kind kind)
     23    : bce_(bce), kind_(kind), name_(name), loc_(bce_->lookupName(name_)) {}
     24 
     25 NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, TaggedParserAtomIndex name,
     26                             const NameLocation& loc, Kind kind)
     27    : bce_(bce), kind_(kind), name_(name), loc_(loc) {}
     28 
     29 bool NameOpEmitter::emitGet() {
     30  MOZ_ASSERT(state_ == State::Start);
     31 
     32  bool needsImplicitThis = false;
     33  if (isCall()) {
     34    switch (loc_.kind()) {
     35      case NameLocation::Kind::Dynamic:
     36        if (bce_->needsImplicitThis()) {
     37          needsImplicitThis = true;
     38          break;
     39        }
     40        [[fallthrough]];
     41      case NameLocation::Kind::Global:
     42        MOZ_ASSERT(bce_->outermostScope().hasNonSyntacticScopeOnChain() ==
     43                   bce_->sc->hasNonSyntacticScope());
     44        needsImplicitThis = bce_->sc->hasNonSyntacticScope();
     45        break;
     46      case NameLocation::Kind::Intrinsic:
     47      case NameLocation::Kind::NamedLambdaCallee:
     48      case NameLocation::Kind::Import:
     49      case NameLocation::Kind::ArgumentSlot:
     50      case NameLocation::Kind::FrameSlot:
     51      case NameLocation::Kind::EnvironmentCoordinate:
     52      case NameLocation::Kind::DebugEnvironmentCoordinate:
     53      case NameLocation::Kind::DynamicAnnexBVar:
     54        break;
     55    }
     56  }
     57 
     58  switch (loc_.kind()) {
     59    case NameLocation::Kind::Global:
     60      MOZ_ASSERT(bce_->outermostScope().hasNonSyntacticScopeOnChain() ==
     61                 bce_->sc->hasNonSyntacticScope());
     62      if (!bce_->sc->hasNonSyntacticScope()) {
     63        MOZ_ASSERT(!needsImplicitThis);
     64 
     65        // Some names on the global are not configurable and have fixed values
     66        // which we can emit instead.
     67        if (name_ == TaggedParserAtomIndex::WellKnown::undefined()) {
     68          if (!bce_->emit1(JSOp::Undefined)) {
     69            return false;
     70          }
     71        } else if (name_ == TaggedParserAtomIndex::WellKnown::NaN()) {
     72          if (!bce_->emitDouble(JS::GenericNaN())) {
     73            return false;
     74          }
     75        } else if (name_ == TaggedParserAtomIndex::WellKnown::Infinity()) {
     76          if (!bce_->emitDouble(JS::Infinity())) {
     77            return false;
     78          }
     79        } else {
     80          if (!bce_->emitAtomOp(JSOp::GetGName, name_)) {
     81            //      [stack] VAL
     82            return false;
     83          }
     84        }
     85        break;
     86      }
     87      [[fallthrough]];
     88    case NameLocation::Kind::Dynamic:
     89      if (needsImplicitThis) {
     90        if (!bce_->emitAtomOp(JSOp::BindName, name_)) {
     91          //        [stack] ENV
     92          return false;
     93        }
     94        if (!bce_->emit1(JSOp::Dup)) {
     95          //        [stack] ENV ENV
     96          return false;
     97        }
     98        if (!bce_->emitAtomOp(JSOp::GetBoundName, name_)) {
     99          //        [stack] ENV V
    100          return false;
    101        }
    102      } else {
    103        if (!bce_->emitAtomOp(JSOp::GetName, name_)) {
    104          //        [stack] VAL
    105          return false;
    106        }
    107      }
    108      break;
    109    case NameLocation::Kind::Intrinsic:
    110      if (name_ == TaggedParserAtomIndex::WellKnown::undefined()) {
    111        if (!bce_->emit1(JSOp::Undefined)) {
    112          //        [stack] Undefined
    113          return false;
    114        }
    115      } else {
    116        if (!bce_->emitAtomOp(JSOp::GetIntrinsic, name_)) {
    117          //        [stack] VAL
    118          return false;
    119        }
    120      }
    121      break;
    122    case NameLocation::Kind::NamedLambdaCallee:
    123      if (!bce_->emit1(JSOp::Callee)) {
    124        //          [stack] VAL
    125        return false;
    126      }
    127      break;
    128    case NameLocation::Kind::Import:
    129      if (!bce_->emitAtomOp(JSOp::GetImport, name_)) {
    130        //          [stack] VAL
    131        return false;
    132      }
    133      break;
    134    case NameLocation::Kind::ArgumentSlot:
    135      if (!bce_->emitArgOp(JSOp::GetArg, loc_.argumentSlot())) {
    136        //          [stack] VAL
    137        return false;
    138      }
    139      break;
    140    case NameLocation::Kind::FrameSlot:
    141      if (!bce_->emitLocalOp(JSOp::GetLocal, loc_.frameSlot())) {
    142        //          [stack] VAL
    143        return false;
    144      }
    145      if (loc_.isLexical()) {
    146        if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::Yes)) {
    147          //        [stack] VAL
    148          return false;
    149        }
    150      }
    151      break;
    152    case NameLocation::Kind::EnvironmentCoordinate:
    153    case NameLocation::Kind::DebugEnvironmentCoordinate:
    154      if (!bce_->emitEnvCoordOp(
    155              loc_.kind() == NameLocation::Kind::EnvironmentCoordinate
    156                  ? JSOp::GetAliasedVar
    157                  : JSOp::GetAliasedDebugVar,
    158              loc_.environmentCoordinate())) {
    159        //          [stack] VAL
    160        return false;
    161      }
    162      if (loc_.isLexical()) {
    163        if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::Yes)) {
    164          //        [stack] VAL
    165          return false;
    166        }
    167      }
    168      break;
    169    case NameLocation::Kind::DynamicAnnexBVar:
    170      MOZ_CRASH(
    171          "Synthesized vars for Annex B.3.3 should only be used in "
    172          "initialization");
    173  }
    174 
    175  if (isCall()) {
    176    switch (loc_.kind()) {
    177      case NameLocation::Kind::Dynamic:
    178      case NameLocation::Kind::Global:
    179        MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
    180        if (needsImplicitThis) {
    181          if (!bce_->emit1(JSOp::Swap)) {
    182            //      [stack] CALLEE ENV
    183            return false;
    184          }
    185          if (!bce_->emit1(JSOp::ImplicitThis)) {
    186            //      [stack] CALLEE THIS
    187            return false;
    188          }
    189        } else {
    190          if (!bce_->emit1(JSOp::Undefined)) {
    191            //      [stack] CALLEE UNDEF
    192            return false;
    193          }
    194        }
    195        break;
    196      case NameLocation::Kind::Intrinsic:
    197      case NameLocation::Kind::NamedLambdaCallee:
    198      case NameLocation::Kind::Import:
    199      case NameLocation::Kind::ArgumentSlot:
    200      case NameLocation::Kind::FrameSlot:
    201      case NameLocation::Kind::EnvironmentCoordinate:
    202        if (bce_->emitterMode == BytecodeEmitter::SelfHosting) {
    203          if (!bce_->emitDebugCheckSelfHosted()) {
    204            //      [stack] CALLEE
    205            return false;
    206          }
    207        }
    208        if (!bce_->emit1(JSOp::Undefined)) {
    209          //        [stack] CALLEE UNDEF
    210          return false;
    211        }
    212        break;
    213      case NameLocation::Kind::DebugEnvironmentCoordinate:
    214        MOZ_CRASH(
    215            "DebugEnvironmentCoordinate should only be used to get the private "
    216            "brand, and so should never call.");
    217        break;
    218      case NameLocation::Kind::DynamicAnnexBVar:
    219        MOZ_CRASH(
    220            "Synthesized vars for Annex B.3.3 should only be used in "
    221            "initialization");
    222    }
    223  }
    224 
    225 #ifdef DEBUG
    226  state_ = State::Get;
    227 #endif
    228  return true;
    229 }
    230 
    231 bool NameOpEmitter::prepareForRhs() {
    232  MOZ_ASSERT(state_ == State::Start);
    233 
    234  switch (loc_.kind()) {
    235    case NameLocation::Kind::Dynamic:
    236    case NameLocation::Kind::Import:
    237      if (!bce_->makeAtomIndex(name_, ParserAtom::Atomize::Yes, &atomIndex_)) {
    238        return false;
    239      }
    240      if (!bce_->emitAtomOp(JSOp::BindUnqualifiedName, atomIndex_)) {
    241        //          [stack] ENV
    242        return false;
    243      }
    244      emittedBindOp_ = true;
    245      break;
    246    case NameLocation::Kind::DynamicAnnexBVar:
    247      // Annex B vars always go on the nearest variable environment, even if
    248      // lexical environments in between contain same-named bindings.
    249      if (!bce_->emit1(JSOp::BindVar)) {
    250        //          [stack] ENV
    251        return false;
    252      }
    253      emittedBindOp_ = true;
    254      break;
    255    case NameLocation::Kind::Global:
    256      if (!bce_->makeAtomIndex(name_, ParserAtom::Atomize::Yes, &atomIndex_)) {
    257        return false;
    258      }
    259      MOZ_ASSERT(bce_->outermostScope().hasNonSyntacticScopeOnChain() ==
    260                 bce_->sc->hasNonSyntacticScope());
    261 
    262      if (loc_.isLexical() && isInitialize()) {
    263        // InitGLexical always gets the global lexical scope. It doesn't
    264        // need a BindUnqualifiedName/BindUnqualifiedGName.
    265        MOZ_ASSERT(bce_->innermostScope().is<GlobalScope>());
    266      } else {
    267        JSOp op;
    268        if (bce_->sc->hasNonSyntacticScope()) {
    269          op = JSOp::BindUnqualifiedName;
    270        } else {
    271          op = JSOp::BindUnqualifiedGName;
    272        }
    273        if (!bce_->emitAtomOp(op, atomIndex_)) {
    274          //        [stack] ENV
    275          return false;
    276        }
    277        emittedBindOp_ = true;
    278      }
    279      break;
    280    case NameLocation::Kind::Intrinsic:
    281    case NameLocation::Kind::NamedLambdaCallee:
    282    case NameLocation::Kind::ArgumentSlot:
    283    case NameLocation::Kind::FrameSlot:
    284    case NameLocation::Kind::DebugEnvironmentCoordinate:
    285    case NameLocation::Kind::EnvironmentCoordinate:
    286      break;
    287  }
    288 
    289  // For compound assignments, first get the LHS value, then emit
    290  // the RHS and the op.
    291  if (isCompoundAssignment() || isIncDec()) {
    292    if (loc_.kind() == NameLocation::Kind::Dynamic) {
    293      // For dynamic accesses we need to emit GetBoundName instead of
    294      // GetName for correctness: looking up @@unscopables on the
    295      // environment chain (due to 'with' environments) must only happen
    296      // once.
    297      //
    298      // GetBoundName uses the environment already pushed on the stack
    299      // from the earlier BindName.
    300      if (!bce_->emit1(JSOp::Dup)) {
    301        //          [stack] ENV ENV
    302        return false;
    303      }
    304      if (!bce_->emitAtomOp(JSOp::GetBoundName, atomIndex_)) {
    305        //          [stack] ENV V
    306        return false;
    307      }
    308    } else {
    309      if (!emitGet()) {
    310        //          [stack] ENV? V
    311        return false;
    312      }
    313    }
    314  }
    315 
    316 #ifdef DEBUG
    317  state_ = State::Rhs;
    318 #endif
    319  return true;
    320 }
    321 
    322 JSOp NameOpEmitter::strictifySetNameOp(JSOp op) const {
    323  switch (op) {
    324    case JSOp::SetName:
    325      if (bce_->sc->strict()) {
    326        op = JSOp::StrictSetName;
    327      }
    328      break;
    329    case JSOp::SetGName:
    330      if (bce_->sc->strict()) {
    331        op = JSOp::StrictSetGName;
    332      }
    333      break;
    334    default:
    335      MOZ_CRASH("Invalid SetName op");
    336  }
    337  return op;
    338 }
    339 
    340 #if defined(__clang__) && defined(XP_WIN) && \
    341    (defined(_M_X64) || defined(__x86_64__))
    342 // Work around a CPU bug. See bug 1524257.
    343 __attribute__((__aligned__(32)))
    344 #endif
    345 bool NameOpEmitter::emitAssignment() {
    346  MOZ_ASSERT(state_ == State::Rhs);
    347 
    348  //                [stack] # If emittedBindOp_
    349  //                [stack] ENV V
    350  //                [stack] # else
    351  //                [stack] V
    352 
    353  switch (loc_.kind()) {
    354    case NameLocation::Kind::Dynamic:
    355    case NameLocation::Kind::Import:
    356      MOZ_ASSERT(emittedBindOp_);
    357      if (!bce_->emitAtomOp(strictifySetNameOp(JSOp::SetName), atomIndex_)) {
    358        return false;
    359      }
    360      break;
    361    case NameLocation::Kind::DynamicAnnexBVar:
    362      MOZ_ASSERT(emittedBindOp_);
    363      if (!bce_->emitAtomOp(strictifySetNameOp(JSOp::SetName), name_)) {
    364        return false;
    365      }
    366      break;
    367    case NameLocation::Kind::Global: {
    368      JSOp op;
    369      if (emittedBindOp_) {
    370        MOZ_ASSERT(bce_->outermostScope().hasNonSyntacticScopeOnChain() ==
    371                   bce_->sc->hasNonSyntacticScope());
    372        if (bce_->sc->hasNonSyntacticScope()) {
    373          op = strictifySetNameOp(JSOp::SetName);
    374        } else {
    375          op = strictifySetNameOp(JSOp::SetGName);
    376        }
    377      } else {
    378        op = JSOp::InitGLexical;
    379      }
    380      if (!bce_->emitAtomOp(op, atomIndex_)) {
    381        return false;
    382      }
    383      break;
    384    }
    385    case NameLocation::Kind::Intrinsic:
    386      if (!bce_->emitAtomOp(JSOp::SetIntrinsic, name_)) {
    387        return false;
    388      }
    389      break;
    390    case NameLocation::Kind::NamedLambdaCallee:
    391      // Assigning to the named lambda is a no-op in sloppy mode but
    392      // throws in strict mode.
    393      if (bce_->sc->strict()) {
    394        if (!bce_->emitAtomOp(JSOp::ThrowSetConst, name_)) {
    395          return false;
    396        }
    397      }
    398      break;
    399    case NameLocation::Kind::ArgumentSlot:
    400      if (!bce_->emitArgOp(JSOp::SetArg, loc_.argumentSlot())) {
    401        return false;
    402      }
    403      break;
    404    case NameLocation::Kind::FrameSlot: {
    405      JSOp op = JSOp::SetLocal;
    406      // Lexicals, Synthetics and Private Methods have very similar handling
    407      // around a variety of areas, including initialization.
    408      if (loc_.isLexical() || loc_.isPrivateMethod() || loc_.isSynthetic()) {
    409        if (isInitialize()) {
    410          op = JSOp::InitLexical;
    411        } else {
    412          if (loc_.isConst()) {
    413            op = JSOp::ThrowSetConst;
    414          }
    415          if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::No)) {
    416            return false;
    417          }
    418        }
    419      }
    420      if (op == JSOp::ThrowSetConst) {
    421        if (!bce_->emitAtomOp(op, name_)) {
    422          return false;
    423        }
    424      } else {
    425        if (!bce_->emitLocalOp(op, loc_.frameSlot())) {
    426          return false;
    427        }
    428      }
    429      if (op == JSOp::InitLexical) {
    430        if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_,
    431                                                        DontCheckTDZ)) {
    432          return false;
    433        }
    434      }
    435      break;
    436    }
    437    case NameLocation::Kind::EnvironmentCoordinate: {
    438      JSOp op = JSOp::SetAliasedVar;
    439      // Lexicals, Synthetics and Private Methods have very similar handling
    440      // around a variety of areas, including initialization.
    441      if (loc_.isLexical() || loc_.isPrivateMethod() || loc_.isSynthetic()) {
    442        if (isInitialize()) {
    443          op = JSOp::InitAliasedLexical;
    444        } else {
    445          if (loc_.isConst()) {
    446            op = JSOp::ThrowSetConst;
    447          }
    448          if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::No)) {
    449            return false;
    450          }
    451        }
    452      } else if (loc_.bindingKind() == BindingKind::NamedLambdaCallee) {
    453        // Assigning to the named lambda is a no-op in sloppy mode and throws
    454        // in strict mode.
    455        if (!bce_->sc->strict()) {
    456          break;
    457        }
    458        op = JSOp::ThrowSetConst;
    459      }
    460      if (op == JSOp::ThrowSetConst) {
    461        if (!bce_->emitAtomOp(op, name_)) {
    462          return false;
    463        }
    464      } else {
    465        if (!bce_->emitEnvCoordOp(op, loc_.environmentCoordinate())) {
    466          return false;
    467        }
    468      }
    469      if (op == JSOp::InitAliasedLexical) {
    470        if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_,
    471                                                        DontCheckTDZ)) {
    472          return false;
    473        }
    474      }
    475      break;
    476    }
    477    case NameLocation::Kind::DebugEnvironmentCoordinate:
    478      MOZ_CRASH("Shouldn't be assigning to a private brand");
    479      break;
    480  }
    481 
    482 #ifdef DEBUG
    483  state_ = State::Assignment;
    484 #endif
    485  return true;
    486 }
    487 
    488 bool NameOpEmitter::emitIncDec(ValueUsage valueUsage) {
    489  MOZ_ASSERT(state_ == State::Start);
    490 
    491  JSOp incOp = isInc() ? JSOp::Inc : JSOp::Dec;
    492  if (!prepareForRhs()) {
    493    //              [stack] ENV? V
    494    return false;
    495  }
    496  if (!bce_->emit1(JSOp::ToNumeric)) {
    497    //              [stack] ENV? N
    498    return false;
    499  }
    500  if (isPostIncDec() && valueUsage == ValueUsage::WantValue) {
    501    if (!bce_->emit1(JSOp::Dup)) {
    502      //            [stack] ENV? N N
    503      return false;
    504    }
    505  }
    506  if (!bce_->emit1(incOp)) {
    507    //              [stack] ENV? N? N+1
    508    return false;
    509  }
    510  if (isPostIncDec() && emittedBindOp() &&
    511      valueUsage == ValueUsage::WantValue) {
    512    if (!bce_->emit2(JSOp::Pick, 2)) {
    513      //            [stack] N N+1 ENV
    514      return false;
    515    }
    516    if (!bce_->emit1(JSOp::Swap)) {
    517      //            [stack] N ENV N+1
    518      return false;
    519    }
    520  }
    521  if (!emitAssignment()) {
    522    //              [stack] N? N+1
    523    return false;
    524  }
    525  if (isPostIncDec() && valueUsage == ValueUsage::WantValue) {
    526    if (!bce_->emit1(JSOp::Pop)) {
    527      //            [stack] N
    528      return false;
    529    }
    530  }
    531 
    532 #ifdef DEBUG
    533  state_ = State::IncDec;
    534 #endif
    535  return true;
    536 }