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