Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / frontend / PrivateOpEmitter.cpp
blobc10463eb56a72603d68372c1a6e40d17c556dab2
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
14 using namespace js;
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);
28 return true;
31 bool PrivateOpEmitter::emitLoad(TaggedParserAtomIndex name,
32 const NameLocation& loc) {
33 NameOpEmitter noe(bce_, name, loc, NameOpEmitter::Kind::Get);
34 return noe.emitGet();
37 bool PrivateOpEmitter::emitLoadPrivateBrand() {
38 return emitLoad(TaggedParserAtomIndex::WellKnown::dot_privateBrand_(),
39 *brandLoc_);
42 bool PrivateOpEmitter::emitBrandCheck() {
43 MOZ_ASSERT(state_ == State::Reference);
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;
54 return true;
57 // [stack] OBJ KEY
58 if (isFieldInit()) {
59 if (!bce_->emitCheckPrivateField(ThrowCondition::ThrowHas,
60 ThrowMsgKind::PrivateDoubleInit)) {
61 // [stack] OBJ KEY false
62 return false;
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;
76 return true;
79 bool PrivateOpEmitter::emitReference() {
80 MOZ_ASSERT(state_ == State::Start);
82 if (!init()) {
83 return false;
86 if (brandLoc_) {
87 if (!emitLoadPrivateBrand()) {
88 // [stack] OBJ BRAND
89 return false;
91 } else {
92 if (!emitLoad(name_, loc_.ref())) {
93 // [stack] OBJ NAME
94 return false;
97 #ifdef DEBUG
98 state_ = State::Reference;
99 #endif
100 return true;
103 bool PrivateOpEmitter::skipReference() {
104 MOZ_ASSERT(state_ == State::Start);
106 if (!init()) {
107 return false;
110 #ifdef DEBUG
111 state_ = State::Reference;
112 #endif
113 return true;
116 bool PrivateOpEmitter::emitGet() {
117 MOZ_ASSERT(state_ == State::Reference);
119 // [stack] OBJ NAME
121 if (brandLoc_) {
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
131 return false;
134 if (isCompoundAssignment()) {
135 if (!bce_->emit1(JSOp::Pop)) {
136 // [stack] OBJ BRAND
137 return false;
139 } else if (isCall()) {
140 if (!bce_->emitPopN(2)) {
141 // [stack] OBJ
142 return false;
144 } else {
145 if (!bce_->emitPopN(3)) {
146 // [stack]
147 return false;
151 if (!emitLoad(name_, loc_.ref())) {
152 // [stack] OBJ BRAND METHOD # if isCompoundAssignment
153 // [stack] OBJ METHOD # if call
154 // [stack] METHOD # otherwise
155 return false;
157 } else {
158 if (isCall()) {
159 if (!bce_->emitDupAt(1)) {
160 // [stack] OBJ NAME OBJ
161 return false;
163 if (!bce_->emit1(JSOp::Swap)) {
164 // [stack] OBJ OBJ NAME
165 return false;
168 // [stack] OBJ? OBJ NAME
169 if (!emitBrandCheck()) {
170 // [stack] OBJ? OBJ NAME true
171 return false;
173 if (!bce_->emit1(JSOp::Pop)) {
174 // [stack] OBJ? OBJ NAME
175 return false;
178 if (isCompoundAssignment()) {
179 if (!bce_->emit1(JSOp::Dup2)) {
180 // [stack] OBJ NAME OBJ NAME
181 return false;
185 if (!bce_->emitElemOpBase(JSOp::GetElem)) {
186 // [stack] OBJ NAME VALUE # if isCompoundAssignment
187 // [stack] OBJ METHOD # if Call
188 // [stack] VALUE # otherwise
189 return false;
193 if (isCall()) {
194 if (!bce_->emit1(JSOp::Swap)) {
195 // [stack] METHOD OBJ
196 return false;
200 // [stack] OBJ NAME VALUE # if isCompoundAssignment
201 // [stack] METHOD OBJ # if call
202 // [stack] VALUE # otherwise
204 #ifdef DEBUG
205 state_ = State::Get;
206 #endif
207 return true;
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
219 if (brandLoc_) {
220 if (!bce_->emit2(JSOp::ThrowMsg,
221 uint8_t(ThrowMsgKind::AssignToPrivateMethod))) {
222 return false;
225 // Balance the expression stack.
226 if (!bce_->emitPopN(2)) {
227 // [stack] OBJ
228 return false;
230 } else {
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
237 return false;
239 if (!emitBrandCheck()) {
240 // [stack] RHS OBJ KEY BOOL
241 return false;
243 if (!bce_->emit1(JSOp::Pop)) {
244 // [stack] RHS OBJ KEY
245 return false;
247 if (!bce_->emitPickN(2)) {
248 // [stack] OBJ KEY RHS
249 return false;
253 JSOp setOp = isFieldInit() ? JSOp::InitElem : JSOp::StrictSetElem;
254 if (!bce_->emitElemOpBase(setOp)) {
255 // [stack] RHS
256 return false;
260 #ifdef DEBUG
261 state_ = State::Assignment;
262 #endif
263 return true;
266 bool PrivateOpEmitter::emitIncDec(ValueUsage valueUsage) {
267 MOZ_ASSERT(state_ == State::Reference);
268 MOZ_ASSERT(isIncDec());
269 // [stack] OBJ NAME
271 if (!bce_->emitDupAt(1, 2)) {
272 // [stack] OBJ NAME OBJ NAME
273 return false;
276 if (!emitGet()) {
277 // [stack] OBJ NAME VALUE
278 return false;
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
287 return false;
289 if (isPostIncDec() && valueUsage == ValueUsage::WantValue) {
290 // [stack] OBJ NAME N
291 if (!bce_->emit1(JSOp::Dup)) {
292 // [stack] OBJ NAME N N
293 return false;
295 if (!bce_->emit2(JSOp::Unpick, 3)) {
296 // [stack] N OBJ NAME N
297 return false;
300 if (!bce_->emit1(incOp)) {
301 // [stack] N? OBJ NAME N+1
302 return false;
305 if (brandLoc_) {
306 if (!bce_->emit2(JSOp::ThrowMsg,
307 uint8_t(ThrowMsgKind::AssignToPrivateMethod))) {
308 return false;
311 // Balance the expression stack.
312 if (!bce_->emitPopN(2)) {
313 // [stack] N? N+1
314 return false;
316 } else {
317 if (!bce_->emitElemOpBase(JSOp::StrictSetElem)) {
318 // [stack] N? N+1
319 return false;
323 if (isPostIncDec() && valueUsage == ValueUsage::WantValue) {
324 if (!bce_->emit1(JSOp::Pop)) {
325 // [stack] N
326 return false;
330 return true;