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/. */
7 #include "frontend/PrivateOpEmitter.h"
9 #include "frontend/BytecodeEmitter.h"
10 #include "frontend/NameOpEmitter.h"
11 #include "vm/Opcodes.h"
12 #include "vm/ThrowMsgKind.h" // ThrowMsgKind
15 using namespace js::frontend
;
17 PrivateOpEmitter::PrivateOpEmitter(BytecodeEmitter
* bce
, Kind kind
,
18 TaggedParserAtomIndex name
)
19 : bce_(bce
), kind_(kind
), name_(name
) {
20 MOZ_ASSERT(kind_
!= Kind::Delete
);
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
);
31 bool PrivateOpEmitter::emitLoad(TaggedParserAtomIndex name
,
32 const NameLocation
& loc
) {
33 NameOpEmitter
noe(bce_
, name
, loc
, NameOpEmitter::Kind::Get
);
37 bool PrivateOpEmitter::emitLoadPrivateBrand() {
38 return emitLoad(TaggedParserAtomIndex::WellKnown::dot_privateBrand_(),
42 bool PrivateOpEmitter::emitBrandCheck() {
43 MOZ_ASSERT(state_
== State::Reference
);
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
59 if (!bce_
->emitCheckPrivateField(ThrowCondition::ThrowHas
,
60 ThrowMsgKind::PrivateDoubleInit
)) {
61 // [stack] OBJ KEY false
66 isSimpleAssignment() || isCompoundAssignment() || isIncDec();
67 if (!bce_
->emitCheckPrivateField(ThrowCondition::ThrowHasNot
,
69 ? ThrowMsgKind::MissingPrivateOnSet
70 : ThrowMsgKind::MissingPrivateOnGet
)) {
71 // [stack] OBJ KEY true
79 bool PrivateOpEmitter::emitReference() {
80 MOZ_ASSERT(state_
== State::Start
);
87 if (!emitLoadPrivateBrand()) {
92 if (!emitLoad(name_
, loc_
.ref())) {
98 state_
= State::Reference
;
103 bool PrivateOpEmitter::skipReference() {
104 MOZ_ASSERT(state_
== State::Start
);
111 state_
= State::Reference
;
116 bool PrivateOpEmitter::emitGet() {
117 MOZ_ASSERT(state_
== State::Reference
);
122 // Note that the decision of what we leave on the stack depends on kind_,
123 // not loc_->bindingKind(). We can't emit code for a call just because this
124 // private member is a method. `obj.#method` is allowed without a call,
125 // just fetching the function object (it's useful in code like
126 // `obj.#method.bind(...)`). Even if the user says `obj.#method += 7`, we
127 // emit honest bytecode for the brand check, method load, and addition, and
128 // throw the error later. This preserves stack nuses/ndefs balance.
129 if (!emitBrandCheck()) {
130 // [stack] OBJ BRAND true
134 if (isCompoundAssignment()) {
135 if (!bce_
->emit1(JSOp::Pop
)) {
139 } else if (isCall()) {
140 if (!bce_
->emitPopN(2)) {
145 if (!bce_
->emitPopN(3)) {
151 if (!emitLoad(name_
, loc_
.ref())) {
152 // [stack] OBJ BRAND METHOD # if isCompoundAssignment
153 // [stack] OBJ METHOD # if call
154 // [stack] METHOD # otherwise
159 if (!bce_
->emitDupAt(1)) {
160 // [stack] OBJ NAME OBJ
163 if (!bce_
->emit1(JSOp::Swap
)) {
164 // [stack] OBJ OBJ NAME
168 // [stack] OBJ? OBJ NAME
169 if (!emitBrandCheck()) {
170 // [stack] OBJ? OBJ NAME true
173 if (!bce_
->emit1(JSOp::Pop
)) {
174 // [stack] OBJ? OBJ NAME
178 if (isCompoundAssignment()) {
179 if (!bce_
->emit1(JSOp::Dup2
)) {
180 // [stack] OBJ NAME OBJ NAME
185 if (!bce_
->emitElemOpBase(JSOp::GetElem
)) {
186 // [stack] OBJ NAME VALUE # if isCompoundAssignment
187 // [stack] OBJ METHOD # if Call
188 // [stack] VALUE # otherwise
194 if (!bce_
->emit1(JSOp::Swap
)) {
195 // [stack] METHOD OBJ
200 // [stack] OBJ NAME VALUE # if isCompoundAssignment
201 // [stack] METHOD OBJ # if call
202 // [stack] VALUE # otherwise
210 bool PrivateOpEmitter::emitGetForCallOrNew() { return emitGet(); }
212 bool PrivateOpEmitter::emitAssignment() {
213 MOZ_ASSERT(isSimpleAssignment() || isFieldInit() || isCompoundAssignment());
214 MOZ_ASSERT_IF(!isCompoundAssignment(), state_
== State::Reference
);
215 MOZ_ASSERT_IF(isCompoundAssignment(), state_
== State::Get
);
217 // [stack] OBJ KEY RHS
220 if (!bce_
->emit2(JSOp::ThrowMsg
,
221 uint8_t(ThrowMsgKind::AssignToPrivateMethod
))) {
225 // Balance the expression stack.
226 if (!bce_
->emitPopN(2)) {
231 // Emit a brand check. If this is compound assignment, emitGet() already
232 // emitted a check for this object and key. There's no point checking
233 // again--a private field can't be removed from an object.
234 if (!isCompoundAssignment()) {
235 if (!bce_
->emitUnpickN(2)) {
236 // [stack] RHS OBJ KEY
239 if (!emitBrandCheck()) {
240 // [stack] RHS OBJ KEY BOOL
243 if (!bce_
->emit1(JSOp::Pop
)) {
244 // [stack] RHS OBJ KEY
247 if (!bce_
->emitPickN(2)) {
248 // [stack] OBJ KEY RHS
253 JSOp setOp
= isFieldInit() ? JSOp::InitElem
: JSOp::StrictSetElem
;
254 if (!bce_
->emitElemOpBase(setOp
)) {
261 state_
= State::Assignment
;
266 bool PrivateOpEmitter::emitIncDec(ValueUsage valueUsage
) {
267 MOZ_ASSERT(state_
== State::Reference
);
268 MOZ_ASSERT(isIncDec());
271 if (!bce_
->emitDupAt(1, 2)) {
272 // [stack] OBJ NAME OBJ NAME
277 // [stack] OBJ NAME VALUE
281 MOZ_ASSERT(state_
== State::Get
);
283 JSOp incOp
= isInc() ? JSOp::Inc
: JSOp::Dec
;
285 if (!bce_
->emit1(JSOp::ToNumeric
)) {
286 // [stack] OBJ NAME N
289 if (isPostIncDec() && valueUsage
== ValueUsage::WantValue
) {
290 // [stack] OBJ NAME N
291 if (!bce_
->emit1(JSOp::Dup
)) {
292 // [stack] OBJ NAME N N
295 if (!bce_
->emit2(JSOp::Unpick
, 3)) {
296 // [stack] N OBJ NAME N
300 if (!bce_
->emit1(incOp
)) {
301 // [stack] N? OBJ NAME N+1
306 if (!bce_
->emit2(JSOp::ThrowMsg
,
307 uint8_t(ThrowMsgKind::AssignToPrivateMethod
))) {
311 // Balance the expression stack.
312 if (!bce_
->emitPopN(2)) {
317 if (!bce_
->emitElemOpBase(JSOp::StrictSetElem
)) {
323 if (isPostIncDec() && valueUsage
== ValueUsage::WantValue
) {
324 if (!bce_
->emit1(JSOp::Pop
)) {