ElemOpEmitter.cpp (6520B)
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/ElemOpEmitter.h" 8 9 #include "frontend/BytecodeEmitter.h" 10 #include "frontend/SharedContext.h" 11 #include "vm/Opcodes.h" 12 #include "vm/ThrowMsgKind.h" // ThrowMsgKind 13 14 using namespace js; 15 using namespace js::frontend; 16 17 ElemOpEmitter::ElemOpEmitter(BytecodeEmitter* bce, Kind kind, ObjKind objKind) 18 : bce_(bce), kind_(kind), objKind_(objKind) {} 19 20 bool ElemOpEmitter::prepareForObj() { 21 MOZ_ASSERT(state_ == State::Start); 22 23 #ifdef DEBUG 24 state_ = State::Obj; 25 #endif 26 return true; 27 } 28 29 bool ElemOpEmitter::prepareForKey() { 30 MOZ_ASSERT(state_ == State::Obj); 31 32 if (isCall()) { 33 if (!bce_->emit1(JSOp::Dup)) { 34 // [stack] # if Super 35 // [stack] THIS THIS 36 // [stack] # otherwise 37 // [stack] OBJ OBJ 38 return false; 39 } 40 } 41 42 #ifdef DEBUG 43 state_ = State::Key; 44 #endif 45 return true; 46 } 47 48 bool ElemOpEmitter::emitGet() { 49 MOZ_ASSERT(state_ == State::Key); 50 51 if (isSuper()) { 52 if (!bce_->emitSuperBase()) { 53 // [stack] THIS? THIS KEY SUPERBASE 54 return false; 55 } 56 } 57 58 // Inc/dec and compound assignment use the KEY twice, but if it's an object, 59 // it must be converted by ToPropertyKey only once, per spec. 60 if (isIncDec() || isCompoundAssignment()) { 61 if (isSuper()) { 62 if (!bce_->emit1(JSOp::Swap)) { 63 // [stack] THIS SUPERBASE KEY 64 return false; 65 } 66 if (!bce_->emit1(JSOp::ToPropertyKey)) { 67 // [stack] THIS SUPERBASE KEY 68 return false; 69 } 70 if (!bce_->emit1(JSOp::Swap)) { 71 // [stack] THIS KEY SUPERBASE 72 return false; 73 } 74 if (!bce_->emitDupAt(2, 3)) { 75 // [stack] THIS KEY SUPERBASE THIS KEY SUPERBASE 76 return false; 77 } 78 } else { 79 if (!bce_->emit1(JSOp::ToPropertyKey)) { 80 // [stack] OBJ KEY 81 return false; 82 } 83 if (!bce_->emit1(JSOp::Dup2)) { 84 // [stack] OBJ KEY OBJ KEY 85 return false; 86 } 87 } 88 } 89 90 JSOp op; 91 if (isSuper()) { 92 op = JSOp::GetElemSuper; 93 } else { 94 op = JSOp::GetElem; 95 } 96 if (!bce_->emitElemOpBase(op)) { 97 // [stack] # if Get 98 // [stack] ELEM 99 // [stack] # if Call 100 // [stack] THIS ELEM 101 // [stack] # if Inc/Dec/Assignment, with Super 102 // [stack] THIS KEY SUPERBASE ELEM 103 // [stack] # if Inc/Dec/Assignment, other 104 // [stack] OBJ KEY ELEM 105 return false; 106 } 107 if (isCall()) { 108 if (!bce_->emit1(JSOp::Swap)) { 109 // [stack] ELEM THIS 110 return false; 111 } 112 } 113 114 #ifdef DEBUG 115 state_ = State::Get; 116 #endif 117 return true; 118 } 119 120 bool ElemOpEmitter::prepareForRhs() { 121 MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment()); 122 MOZ_ASSERT_IF(isSimpleAssignment() || isPropInit(), state_ == State::Key); 123 MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get); 124 125 if (isSimpleAssignment() || isPropInit()) { 126 // For CompoundAssignment, SuperBase is already emitted by emitGet. 127 if (isSuper()) { 128 if (!bce_->emitSuperBase()) { 129 // [stack] THIS KEY SUPERBASE 130 return false; 131 } 132 } 133 } 134 135 #ifdef DEBUG 136 state_ = State::Rhs; 137 #endif 138 return true; 139 } 140 141 bool ElemOpEmitter::emitDelete() { 142 MOZ_ASSERT(state_ == State::Key); 143 MOZ_ASSERT(isDelete()); 144 145 if (isSuper()) { 146 if (!bce_->emitSuperBase()) { 147 // [stack] THIS KEY SUPERBASE 148 return false; 149 } 150 151 // Unconditionally throw when attempting to delete a super-reference. 152 if (!bce_->emit2(JSOp::ThrowMsg, uint8_t(ThrowMsgKind::CantDeleteSuper))) { 153 // [stack] THIS KEY SUPERBASE 154 return false; 155 } 156 157 // Another wrinkle: Balance the stack from the emitter's point of view. 158 // Execution will not reach here, as the last bytecode threw. 159 if (!bce_->emitPopN(2)) { 160 // [stack] THIS 161 return false; 162 } 163 } else { 164 JSOp op = bce_->sc->strict() ? JSOp::StrictDelElem : JSOp::DelElem; 165 if (!bce_->emitElemOpBase(op)) { 166 // SUCCEEDED 167 return false; 168 } 169 } 170 171 #ifdef DEBUG 172 state_ = State::Delete; 173 #endif 174 return true; 175 } 176 177 bool ElemOpEmitter::emitAssignment() { 178 MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment()); 179 MOZ_ASSERT(state_ == State::Rhs); 180 181 MOZ_ASSERT_IF(isPropInit(), !isSuper()); 182 183 JSOp setOp = isPropInit() ? JSOp::InitElem 184 : isSuper() ? bce_->sc->strict() ? JSOp::StrictSetElemSuper 185 : JSOp::SetElemSuper 186 : bce_->sc->strict() ? JSOp::StrictSetElem 187 : JSOp::SetElem; 188 if (!bce_->emitElemOpBase(setOp)) { 189 // [stack] ELEM 190 return false; 191 } 192 193 #ifdef DEBUG 194 state_ = State::Assignment; 195 #endif 196 return true; 197 } 198 199 bool ElemOpEmitter::emitIncDec(ValueUsage valueUsage) { 200 MOZ_ASSERT(state_ == State::Key); 201 MOZ_ASSERT(isIncDec()); 202 203 if (!emitGet()) { 204 // [stack] ... ELEM 205 return false; 206 } 207 208 MOZ_ASSERT(state_ == State::Get); 209 210 JSOp incOp = isInc() ? JSOp::Inc : JSOp::Dec; 211 if (!bce_->emit1(JSOp::ToNumeric)) { 212 // [stack] ... N 213 return false; 214 } 215 if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { 216 // [stack] OBJ KEY SUPERBASE? N 217 if (!bce_->emit1(JSOp::Dup)) { 218 // [stack] ... N N 219 return false; 220 } 221 if (!bce_->emit2(JSOp::Unpick, 3 + isSuper())) { 222 // [stack] N OBJ KEY SUPERBASE? N 223 return false; 224 } 225 } 226 if (!bce_->emit1(incOp)) { 227 // [stack] ... N+1 228 return false; 229 } 230 231 JSOp setOp = 232 isSuper() 233 ? (bce_->sc->strict() ? JSOp::StrictSetElemSuper : JSOp::SetElemSuper) 234 : (bce_->sc->strict() ? JSOp::StrictSetElem : JSOp::SetElem); 235 if (!bce_->emitElemOpBase(setOp)) { 236 // [stack] N? N+1 237 return false; 238 } 239 if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { 240 if (!bce_->emit1(JSOp::Pop)) { 241 // [stack] N 242 return false; 243 } 244 } 245 246 #ifdef DEBUG 247 state_ = State::IncDec; 248 #endif 249 return true; 250 }