tor-browser

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

CallOrNewEmitter.cpp (9783B)


      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/CallOrNewEmitter.h"
      8 
      9 #include "frontend/BytecodeEmitter.h"
     10 #include "frontend/NameOpEmitter.h"
     11 #include "vm/ConstantCompareOperand.h"
     12 #include "vm/Opcodes.h"
     13 
     14 using namespace js;
     15 using namespace js::frontend;
     16 
     17 CallOrNewEmitter::CallOrNewEmitter(BytecodeEmitter* bce, JSOp op,
     18                                   ArgumentsKind argumentsKind,
     19                                   ValueUsage valueUsage)
     20    : bce_(bce), op_(op), argumentsKind_(argumentsKind) {
     21  if (op_ == JSOp::Call && valueUsage == ValueUsage::IgnoreValue) {
     22    op_ = JSOp::CallIgnoresRv;
     23  }
     24 
     25  MOZ_ASSERT(isCall() || isNew() || isSuperCall());
     26 }
     27 
     28 bool CallOrNewEmitter::emitNameCallee(TaggedParserAtomIndex name) {
     29  MOZ_ASSERT(state_ == State::Start);
     30 
     31  //                [stack]
     32 
     33  NameOpEmitter noe(
     34      bce_, name,
     35      isCall() ? NameOpEmitter::Kind::Call : NameOpEmitter::Kind::Get);
     36  if (!noe.emitGet()) {
     37    //              [stack] # if isCall()
     38    //              [stack] CALLEE THIS
     39    //              [stack] # if isNew() or isSuperCall()
     40    //              [stack] CALLEE
     41    return false;
     42  }
     43 
     44  state_ = State::NameCallee;
     45  return true;
     46 }
     47 
     48 [[nodiscard]] PropOpEmitter& CallOrNewEmitter::prepareForPropCallee(
     49    bool isSuperProp) {
     50  MOZ_ASSERT(state_ == State::Start);
     51  MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
     52 
     53  //                [stack]
     54 
     55  poe_.emplace(bce_,
     56               isCall() ? PropOpEmitter::Kind::Call : PropOpEmitter::Kind::Get,
     57               isSuperProp ? PropOpEmitter::ObjKind::Super
     58                           : PropOpEmitter::ObjKind::Other);
     59 
     60  state_ = State::PropCallee;
     61  return *poe_;
     62 }
     63 
     64 [[nodiscard]] ElemOpEmitter& CallOrNewEmitter::prepareForElemCallee(
     65    bool isSuperElem) {
     66  MOZ_ASSERT(state_ == State::Start);
     67  MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
     68 
     69  //                [stack]
     70 
     71  eoe_.emplace(bce_,
     72               isCall() ? ElemOpEmitter::Kind::Call : ElemOpEmitter::Kind::Get,
     73               isSuperElem ? ElemOpEmitter::ObjKind::Super
     74                           : ElemOpEmitter::ObjKind::Other);
     75 
     76  state_ = State::ElemCallee;
     77  return *eoe_;
     78 }
     79 
     80 PrivateOpEmitter& CallOrNewEmitter::prepareForPrivateCallee(
     81    TaggedParserAtomIndex privateName) {
     82  MOZ_ASSERT(state_ == State::Start);
     83  MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
     84 
     85  //                [stack]
     86 
     87  xoe_.emplace(
     88      bce_,
     89      isCall() ? PrivateOpEmitter::Kind::Call : PrivateOpEmitter::Kind::Get,
     90      privateName);
     91  state_ = State::PrivateCallee;
     92  return *xoe_;
     93 }
     94 
     95 bool CallOrNewEmitter::prepareForFunctionCallee() {
     96  MOZ_ASSERT(state_ == State::Start);
     97  MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
     98 
     99  //                [stack]
    100 
    101  state_ = State::FunctionCallee;
    102  return true;
    103 }
    104 
    105 bool CallOrNewEmitter::emitSuperCallee() {
    106  MOZ_ASSERT(state_ == State::Start);
    107  MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
    108 
    109  //                [stack]
    110 
    111  if (!bce_->emitThisEnvironmentCallee()) {
    112    //              [stack] CALLEE
    113    return false;
    114  }
    115  if (!bce_->emit1(JSOp::SuperFun)) {
    116    //              [stack] SUPER_FUN
    117    return false;
    118  }
    119  if (!bce_->emit1(JSOp::IsConstructing)) {
    120    //              [stack] SUPER_FUN IS_CONSTRUCTING
    121    return false;
    122  }
    123 
    124  state_ = State::SuperCallee;
    125  return true;
    126 }
    127 
    128 bool CallOrNewEmitter::prepareForOtherCallee() {
    129  MOZ_ASSERT(state_ == State::Start);
    130  MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
    131 
    132  //                [stack]
    133 
    134  state_ = State::OtherCallee;
    135  return true;
    136 }
    137 
    138 bool CallOrNewEmitter::emitThis() {
    139  MOZ_ASSERT(state_ == State::NameCallee || state_ == State::PropCallee ||
    140             state_ == State::ElemCallee || state_ == State::PrivateCallee ||
    141             state_ == State::FunctionCallee || state_ == State::SuperCallee ||
    142             state_ == State::OtherCallee);
    143 
    144  //                [stack] # if isCall()
    145  //                [stack] CALLEE THIS?
    146  //                [stack] # if isNew() or isSuperCall()
    147  //                [stack] CALLEE
    148 
    149  bool needsThis = false;
    150  switch (state_) {
    151    case State::NameCallee:
    152      if (!isCall()) {
    153        needsThis = true;
    154      }
    155      break;
    156    case State::PropCallee:
    157      poe_.reset();
    158      if (!isCall()) {
    159        needsThis = true;
    160      }
    161      break;
    162    case State::ElemCallee:
    163      eoe_.reset();
    164      if (!isCall()) {
    165        needsThis = true;
    166      }
    167      break;
    168    case State::PrivateCallee:
    169      xoe_.reset();
    170      if (!isCall()) {
    171        needsThis = true;
    172      }
    173      break;
    174    case State::FunctionCallee:
    175      needsThis = true;
    176      break;
    177    case State::SuperCallee:
    178      break;
    179    case State::OtherCallee:
    180      needsThis = true;
    181      break;
    182    default:;
    183  }
    184  if (needsThis) {
    185    if (isNew() || isSuperCall()) {
    186      if (!bce_->emit1(JSOp::IsConstructing)) {
    187        //          [stack] CALLEE IS_CONSTRUCTING
    188        return false;
    189      }
    190    } else {
    191      if (!bce_->emit1(JSOp::Undefined)) {
    192        //          [stack] CALLEE THIS
    193        return false;
    194      }
    195    }
    196  }
    197 
    198  //                [stack] CALLEE THIS
    199 
    200  state_ = State::This;
    201  return true;
    202 }
    203 
    204 bool CallOrNewEmitter::prepareForNonSpreadArguments() {
    205  MOZ_ASSERT(state_ == State::This);
    206  MOZ_ASSERT(!isSpread());
    207 
    208  //                [stack] CALLEE THIS
    209 
    210  state_ = State::Arguments;
    211  return true;
    212 }
    213 
    214 // See the usage in the comment at the top of the class.
    215 bool CallOrNewEmitter::wantSpreadOperand() {
    216  MOZ_ASSERT(state_ == State::This);
    217  MOZ_ASSERT(isSpread());
    218 
    219  //                [stack] CALLEE THIS
    220 
    221  state_ = State::WantSpreadOperand;
    222  return isSingleSpread() || isPassthroughRest();
    223 }
    224 
    225 bool CallOrNewEmitter::prepareForSpreadArguments() {
    226  MOZ_ASSERT(state_ == State::WantSpreadOperand);
    227  MOZ_ASSERT(isSpread());
    228  MOZ_ASSERT(!isSingleSpread() && !isPassthroughRest());
    229 
    230  //                [stack] CALLEE THIS
    231 
    232  state_ = State::Arguments;
    233  return true;
    234 }
    235 
    236 bool CallOrNewEmitter::emitSpreadArgumentsTest() {
    237  // Caller should check wantSpreadOperand before this.
    238  MOZ_ASSERT(state_ == State::WantSpreadOperand);
    239  MOZ_ASSERT(isSpread());
    240  MOZ_ASSERT(isSingleSpread() || isPassthroughRest());
    241 
    242  //                [stack] CALLEE THIS ARG0
    243 
    244  if (isSingleSpread()) {
    245    // Emit a preparation code to optimize the spread call:
    246    //
    247    //   g(...args);
    248    //
    249    // If the spread operand is a packed array, skip the spread
    250    // operation and pass it directly to spread call operation.
    251    // See the comment in OptimizeSpreadCall in Interpreter.cpp
    252    // for the optimizable conditions.
    253    //              [stack] CALLEE THIS ARG0
    254 
    255    ifNotOptimizable_.emplace(bce_);
    256    if (!bce_->emit1(JSOp::Dup)) {
    257      //            [stack] CALLEE THIS ARG0 ARG0
    258      return false;
    259    }
    260    if (!bce_->emit1(JSOp::OptimizeSpreadCall)) {
    261      //            [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF
    262      return false;
    263    }
    264 
    265    if (!bce_->emit1(JSOp::Dup)) {
    266      //            [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF ARRAY_OR_UNDEF
    267      return false;
    268    }
    269 
    270    ConstantCompareOperand operand(
    271        ConstantCompareOperand::EncodedType::Undefined);
    272    if (!bce_->emitUint16Operand(JSOp::StrictConstantEq, operand.rawValue())) {
    273      //            [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF EQ
    274      return false;
    275    }
    276 
    277    if (!ifNotOptimizable_->emitThenElse()) {
    278      //            [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF
    279      return false;
    280    }
    281    if (!bce_->emit1(JSOp::Pop)) {
    282      //            [stack] CALLEE THIS ARG0
    283      return false;
    284    }
    285  }
    286 
    287  state_ = State::SpreadArgumentsTest;
    288  return true;
    289 }
    290 
    291 bool CallOrNewEmitter::wantSpreadIteration() {
    292  MOZ_ASSERT(state_ == State::SpreadArgumentsTest);
    293  MOZ_ASSERT(isSpread());
    294 
    295  state_ = State::SpreadIteration;
    296  return !isPassthroughRest();
    297 }
    298 
    299 bool CallOrNewEmitter::emitSpreadArgumentsTestEnd() {
    300  MOZ_ASSERT(state_ == State::SpreadIteration);
    301  MOZ_ASSERT(isSpread());
    302 
    303  if (isSingleSpread()) {
    304    if (!ifNotOptimizable_->emitElse()) {
    305      //            [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF
    306      return false;
    307    }
    308    if (!bce_->emit1(JSOp::Swap)) {
    309      //            [stack] CALLEE THIS ARRAY_OR_UNDEF ARG0
    310      return false;
    311    }
    312    if (!bce_->emit1(JSOp::Pop)) {
    313      //            [stack] CALLEE THIS ARRAY_OR_UNDEF
    314      return false;
    315    }
    316 
    317    if (!ifNotOptimizable_->emitEnd()) {
    318      //            [stack] CALLEE THIS ARR
    319      return false;
    320    }
    321 
    322    ifNotOptimizable_.reset();
    323  }
    324 
    325  state_ = State::Arguments;
    326  return true;
    327 }
    328 
    329 bool CallOrNewEmitter::emitEnd(uint32_t argc, uint32_t beginPos) {
    330  MOZ_ASSERT(state_ == State::Arguments);
    331 
    332  //                [stack] # if isCall()
    333  //                [stack] CALLEE THIS ARG0 ... ARGN
    334  //                [stack] # if isNew() or isSuperCall()
    335  //                [stack] CALLEE IS_CONSTRUCTING ARG0 ... ARGN NEW.TARGET?
    336 
    337  if (!bce_->updateSourceCoordNotes(beginPos)) {
    338    return false;
    339  }
    340  if (!bce_->markSimpleBreakpoint()) {
    341    return false;
    342  }
    343  if (!isSpread()) {
    344    if (!bce_->emitCall(op_, argc)) {
    345      //            [stack] RVAL
    346      return false;
    347    }
    348  } else {
    349    if (!bce_->emit1(op_)) {
    350      //            [stack] RVAL
    351      return false;
    352    }
    353  }
    354 
    355  if (isEval()) {
    356    uint32_t lineNum = bce_->errorReporter().lineAt(beginPos);
    357    if (!bce_->emitUint32Operand(JSOp::Lineno, lineNum)) {
    358      //            [stack] RVAL
    359      return false;
    360    }
    361  }
    362 
    363  state_ = State::End;
    364  return true;
    365 }