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 }