tor-browser

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

OptionalEmitter.h (7438B)


      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 #ifndef frontend_OptionalEmitter_h
      8 #define frontend_OptionalEmitter_h
      9 
     10 #include "mozilla/Attributes.h"
     11 
     12 #include "frontend/JumpList.h"
     13 #include "frontend/TDZCheckCache.h"
     14 
     15 namespace js {
     16 namespace frontend {
     17 
     18 struct BytecodeEmitter;
     19 
     20 // Class for emitting bytecode for optional expressions.
     21 //
     22 // Usage: (check for the return value is omitted for simplicity)
     23 //
     24 //   `obj?.prop;`
     25 //     OptionalEmitter oe(this);
     26 //     PropOpEmitter poe(this,
     27 //                       PropOpEmitter::Kind::Get,
     28 //                       PropOpEmitter::ObjKind::Other);
     29 //     poe.prepareForObj();
     30 //     emit(obj);
     31 //     oe.emitJumpShortCircuit();
     32 //     poe.emitGet(atom_of_prop);
     33 //     oe.emitOptionalJumpTarget(JSOp::Undefined);
     34 //
     35 //   `delete obj?.prop;`
     36 //     OptionalEmitter oe(this);
     37 //     OptionalPropOpEmitter poe(this,
     38 //                       PropOpEmitter::Kind::Delete,
     39 //                       PropOpEmitter::ObjKind::Other);
     40 //     poe.prepareForObj();
     41 //     emit(obj);
     42 //     oe.emitJumpShortCircuit();
     43 //     poe.emitDelete(atom_of_prop);
     44 //     oe.emitOptionalJumpTarget(JSOp:True);
     45 //
     46 //   `obj?.[key];`
     47 //     OptionalEmitter oe(this);
     48 //     ElemOpEmitter eoe(this,
     49 //                       ElemOpEmitter::Kind::Get,
     50 //                       ElemOpEmitter::ObjKind::Other);
     51 //     eoe.prepareForObj();
     52 //     emit(obj);
     53 //     oe.emitJumpShortCircuit();
     54 //     eoe.prepareForKey();
     55 //     emit(key);
     56 //     eoe.emitGet();
     57 //     oe.emitOptionalJumpTarget(JSOp::Undefined);
     58 //
     59 //   `delete obj?.[key];`
     60 //     OptionalEmitter oe(this);
     61 //     ElemOpEmitter eoe(this,
     62 //                       ElemOpEmitter::Kind::Delete,
     63 //                       ElemOpEmitter::ObjKind::Other);
     64 //     eoe.prepareForObj();
     65 //     emit(obj);
     66 //     oe.emitJumpShortCircuit();
     67 //     eoe.prepareForKey();
     68 //     emit(key);
     69 //     eoe.emitDelete();
     70 //     oe.emitOptionalJumpTarget(JSOp::True);
     71 //
     72 //   `print?.(arg);`
     73 //     OptionalEmitter oe(this);
     74 //     CallOrNewEmitter cone(this, JSOp::Call,
     75 //                           CallOrNewEmitter::ArgumentsKind::Other,
     76 //                           ValueUsage::WantValue);
     77 //     cone.emitNameCallee(print);
     78 //     cone.emitThis();
     79 //     oe.emitShortCircuitForCall();
     80 //     cone.prepareForNonSpreadArguments();
     81 //     emit(arg);
     82 //     cone.emitEnd(1, offset_of_callee);
     83 //     oe.emitOptionalJumpTarget(JSOp::Undefined);
     84 //
     85 //   `callee.prop?.(arg1, arg2);`
     86 //     OptionalEmitter oe(this);
     87 //     CallOrNewEmitter cone(this, JSOp::Call,
     88 //                           CallOrNewEmitter::ArgumentsKind::Other,
     89 //                           ValueUsage::WantValue);
     90 //     PropOpEmitter& poe = cone.prepareForPropCallee(false);
     91 //     ... emit `callee.prop` with `poe` here...
     92 //     cone.emitThis();
     93 //     oe.emitShortCircuitForCall();
     94 //     cone.prepareForNonSpreadArguments();
     95 //     emit(arg1);
     96 //     emit(arg2);
     97 //     cone.emitEnd(2, offset_of_callee);
     98 //     oe.emitOptionalJumpTarget(JSOp::Undefined);
     99 //
    100 //   `callee[key]?.(arg);`
    101 //     OptionalEmitter oe(this);
    102 //     CallOrNewEmitter cone(this, JSOp::Call,
    103 //                           CallOrNewEmitter::ArgumentsKind::Other,
    104 //                           ValueUsage::WantValue);
    105 //     ElemOpEmitter& eoe = cone.prepareForElemCallee(false);
    106 //     ... emit `callee[key]` with `eoe` here...
    107 //     cone.emitThis();
    108 //     oe.emitShortCircuitForCall();
    109 //     cone.prepareForNonSpreadArguments();
    110 //     emit(arg);
    111 //     cone.emitEnd(1, offset_of_callee);
    112 //     oe.emitOptionalJumpTarget(JSOp::Undefined);
    113 //
    114 //   `(function() { ... })?.(arg);`
    115 //     OptionalEmitter oe(this);
    116 //     CallOrNewEmitter cone(this, JSOp::Call,
    117 //                           CallOrNewEmitter::ArgumentsKind::Other,
    118 //                           ValueUsage::WantValue);
    119 //     cone.prepareForFunctionCallee();
    120 //     emit(function);
    121 //     cone.emitThis();
    122 //     oe.emitShortCircuitForCall();
    123 //     cone.prepareForNonSpreadArguments();
    124 //     emit(arg);
    125 //     cone.emitEnd(1, offset_of_callee);
    126 //     oe.emitOptionalJumpTarget(JSOp::Undefined);
    127 //
    128 //   `(a?b)();`
    129 //     OptionalEmitter oe(this);
    130 //     CallOrNewEmitter cone(this, JSOp::Call,
    131 //                           CallOrNewEmitter::ArgumentsKind::Other,
    132 //                           ValueUsage::WantValue);
    133 //     cone.prepareForFunctionCallee();
    134 //     emit(optionalChain);
    135 //     cone.emitThis();
    136 //     oe.emitOptionalJumpTarget(JSOp::Undefined,
    137 //                               OptionalEmitter::Kind::Reference);
    138 //     oe.emitShortCircuitForCall();
    139 //     cone.prepareForNonSpreadArguments();
    140 //     emit(arg);
    141 //     cone.emitEnd(1, offset_of_callee);
    142 //     oe.emitOptionalJumpTarget(JSOp::Undefined);
    143 //
    144 class MOZ_RAII OptionalEmitter {
    145 public:
    146  OptionalEmitter(BytecodeEmitter* bce, int32_t initialDepth);
    147 
    148 private:
    149  BytecodeEmitter* bce_;
    150 
    151  TDZCheckCache tdzCache_;
    152 
    153  // jumptarget for ShortCircuiting code, which has null or undefined values
    154  JumpList jumpShortCircuit_;
    155 
    156  // jumpTarget for code that does not shortCircuit
    157  JumpList jumpFinish_;
    158 
    159  // jumpTarget for code that does not shortCircuit
    160  int32_t initialDepth_;
    161 
    162  // The state of this emitter.
    163  //
    164  // +-------+   emitJumpShortCircuit        +--------------+
    165  // | Start |-+---------------------------->| ShortCircuit |-----------+
    166  // +-------+ |                             +--------------+           |
    167  //    +----->|                                                        |
    168  //    |      | emitJumpShortCircuitForCall +---------------------+    v
    169  //    |      +---------------------------->| ShortCircuitForCall |--->+
    170  //    |                                    +---------------------+    |
    171  //    |                                                               |
    172  //     ---------------------------------------------------------------+
    173  //                                                                    |
    174  //                                                                    |
    175  // +------------------------------------------------------------------+
    176  // |
    177  // | emitOptionalJumpTarget +---------+
    178  // +----------------------->| JumpEnd |
    179  //                          +---------+
    180  //
    181 #ifdef DEBUG
    182  enum class State {
    183    // The initial state.
    184    Start,
    185 
    186    // for shortcircuiting in most cases.
    187    ShortCircuit,
    188 
    189    // for shortcircuiting from references, which have two items on
    190    // the stack. For example function calls.
    191    ShortCircuitForCall,
    192 
    193    // internally used, end of the jump code
    194    JumpEnd
    195  };
    196 
    197  State state_ = State::Start;
    198 #endif
    199 
    200 public:
    201  enum class Kind {
    202    // Requires two values on the stack
    203    Reference,
    204    // Requires one value on the stack
    205    Other
    206  };
    207 
    208  [[nodiscard]] bool emitJumpShortCircuit();
    209  [[nodiscard]] bool emitJumpShortCircuitForCall();
    210 
    211  // JSOp is the op code to be emitted, Kind is if we are dealing with a
    212  // reference (in which case we need two elements on the stack) or other value
    213  // (which needs one element on the stack)
    214  [[nodiscard]] bool emitOptionalJumpTarget(JSOp op, Kind kind = Kind::Other);
    215 };
    216 
    217 } /* namespace frontend */
    218 } /* namespace js */
    219 
    220 #endif /* frontend_OptionalEmitter_h */