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 */