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/NameOpEmitter.h"
8
9 #include "frontend/AbstractScopePtr.h"
10 #include "frontend/BytecodeEmitter.h"
11 #include "frontend/SharedContext.h"
12 #include "frontend/TDZCheckCache.h"
13 #include "vm/Opcodes.h"
14 #include "vm/Scope.h"
15 #include "vm/StringType.h"
16
17 using namespace js;
18 using namespace js::frontend;
19
NameOpEmitter(BytecodeEmitter * bce,TaggedParserAtomIndex name,Kind kind)20 NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, TaggedParserAtomIndex name,
21 Kind kind)
22 : bce_(bce), kind_(kind), name_(name), loc_(bce_->lookupName(name_)) {}
23
NameOpEmitter(BytecodeEmitter * bce,TaggedParserAtomIndex name,const NameLocation & loc,Kind kind)24 NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, TaggedParserAtomIndex name,
25 const NameLocation& loc, Kind kind)
26 : bce_(bce), kind_(kind), name_(name), loc_(loc) {}
27
emitGet()28 bool NameOpEmitter::emitGet() {
29 MOZ_ASSERT(state_ == State::Start);
30
31 switch (loc_.kind()) {
32 case NameLocation::Kind::Dynamic:
33 if (!bce_->emitAtomOp(JSOp::GetName, name_)) {
34 // [stack] VAL
35 return false;
36 }
37 break;
38 case NameLocation::Kind::Global:
39 if (!bce_->emitAtomOp(JSOp::GetGName, name_)) {
40 // [stack] VAL
41 return false;
42 }
43 break;
44 case NameLocation::Kind::Intrinsic:
45 if (!bce_->emitAtomOp(JSOp::GetIntrinsic, name_)) {
46 // [stack] VAL
47 return false;
48 }
49 break;
50 case NameLocation::Kind::NamedLambdaCallee:
51 if (!bce_->emit1(JSOp::Callee)) {
52 // [stack] VAL
53 return false;
54 }
55 break;
56 case NameLocation::Kind::Import:
57 if (!bce_->emitAtomOp(JSOp::GetImport, name_)) {
58 // [stack] VAL
59 return false;
60 }
61 break;
62 case NameLocation::Kind::ArgumentSlot:
63 if (!bce_->emitArgOp(JSOp::GetArg, loc_.argumentSlot())) {
64 // [stack] VAL
65 return false;
66 }
67 break;
68 case NameLocation::Kind::FrameSlot:
69 if (!bce_->emitLocalOp(JSOp::GetLocal, loc_.frameSlot())) {
70 // [stack] VAL
71 return false;
72 }
73 if (loc_.isLexical()) {
74 if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::Yes)) {
75 // [stack] VAL
76 return false;
77 }
78 }
79 break;
80 case NameLocation::Kind::EnvironmentCoordinate:
81 case NameLocation::Kind::DebugEnvironmentCoordinate:
82 if (!bce_->emitEnvCoordOp(
83 loc_.kind() == NameLocation::Kind::EnvironmentCoordinate
84 ? JSOp::GetAliasedVar
85 : JSOp::GetAliasedDebugVar,
86 loc_.environmentCoordinate())) {
87 // [stack] VAL
88 return false;
89 }
90 if (loc_.isLexical()) {
91 if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::Yes)) {
92 // [stack] VAL
93 return false;
94 }
95 }
96 break;
97 case NameLocation::Kind::DynamicAnnexBVar:
98 MOZ_CRASH(
99 "Synthesized vars for Annex B.3.3 should only be used in "
100 "initialization");
101 }
102
103 if (isCall()) {
104 switch (loc_.kind()) {
105 case NameLocation::Kind::Dynamic: {
106 JSOp thisOp = bce_->needsImplicitThis() ? JSOp::ImplicitThis
107 : JSOp::GImplicitThis;
108 if (!bce_->emitAtomOp(thisOp, name_)) {
109 // [stack] CALLEE THIS
110 return false;
111 }
112 break;
113 }
114 case NameLocation::Kind::Global:
115 if (!bce_->emitAtomOp(JSOp::GImplicitThis, name_)) {
116 // [stack] CALLEE THIS
117 return false;
118 }
119 break;
120 case NameLocation::Kind::Intrinsic:
121 case NameLocation::Kind::NamedLambdaCallee:
122 case NameLocation::Kind::Import:
123 case NameLocation::Kind::ArgumentSlot:
124 case NameLocation::Kind::FrameSlot:
125 case NameLocation::Kind::EnvironmentCoordinate:
126 if (!bce_->emit1(JSOp::Undefined)) {
127 // [stack] CALLEE UNDEF
128 return false;
129 }
130 break;
131 case NameLocation::Kind::DebugEnvironmentCoordinate:
132 MOZ_CRASH(
133 "DebugEnvironmentCoordinate should only be used to get the private "
134 "brand, and so should never call.");
135 break;
136 case NameLocation::Kind::DynamicAnnexBVar:
137 MOZ_CRASH(
138 "Synthesized vars for Annex B.3.3 should only be used in "
139 "initialization");
140 }
141 }
142
143 #ifdef DEBUG
144 state_ = State::Get;
145 #endif
146 return true;
147 }
148
prepareForRhs()149 bool NameOpEmitter::prepareForRhs() {
150 MOZ_ASSERT(state_ == State::Start);
151
152 switch (loc_.kind()) {
153 case NameLocation::Kind::Dynamic:
154 case NameLocation::Kind::Import:
155 case NameLocation::Kind::DynamicAnnexBVar:
156 if (!bce_->makeAtomIndex(name_, &atomIndex_)) {
157 return false;
158 }
159 if (loc_.kind() == NameLocation::Kind::DynamicAnnexBVar) {
160 // Annex B vars always go on the nearest variable environment,
161 // even if lexical environments in between contain same-named
162 // bindings.
163 if (!bce_->emit1(JSOp::BindVar)) {
164 // [stack] ENV
165 return false;
166 }
167 } else {
168 if (!bce_->emitAtomOp(JSOp::BindName, atomIndex_)) {
169 // [stack] ENV
170 return false;
171 }
172 }
173 emittedBindOp_ = true;
174 break;
175 case NameLocation::Kind::Global:
176 if (!bce_->makeAtomIndex(name_, &atomIndex_)) {
177 return false;
178 }
179 if (loc_.isLexical() && isInitialize()) {
180 // InitGLexical always gets the global lexical scope. It doesn't
181 // need a BindGName.
182 MOZ_ASSERT(bce_->innermostScope().is<GlobalScope>());
183 } else {
184 if (!bce_->emitAtomOp(JSOp::BindGName, atomIndex_)) {
185 // [stack] ENV
186 return false;
187 }
188 emittedBindOp_ = true;
189 }
190 break;
191 case NameLocation::Kind::Intrinsic:
192 break;
193 case NameLocation::Kind::NamedLambdaCallee:
194 break;
195 case NameLocation::Kind::ArgumentSlot:
196 break;
197 case NameLocation::Kind::FrameSlot:
198 break;
199 case NameLocation::Kind::DebugEnvironmentCoordinate:
200 break;
201 case NameLocation::Kind::EnvironmentCoordinate:
202 break;
203 }
204
205 // For compound assignments, first get the LHS value, then emit
206 // the RHS and the op.
207 if (isCompoundAssignment() || isIncDec()) {
208 if (loc_.kind() == NameLocation::Kind::Dynamic) {
209 // For dynamic accesses we need to emit GetBoundName instead of
210 // GetName for correctness: looking up @@unscopables on the
211 // environment chain (due to 'with' environments) must only happen
212 // once.
213 //
214 // GetBoundName uses the environment already pushed on the stack
215 // from the earlier BindName.
216 if (!bce_->emit1(JSOp::Dup)) {
217 // [stack] ENV ENV
218 return false;
219 }
220 if (!bce_->emitAtomOp(JSOp::GetBoundName, name_)) {
221 // [stack] ENV V
222 return false;
223 }
224 } else {
225 if (!emitGet()) {
226 // [stack] ENV? V
227 return false;
228 }
229 }
230 }
231
232 #ifdef DEBUG
233 state_ = State::Rhs;
234 #endif
235 return true;
236 }
237
238 #if defined(__clang__) && defined(XP_WIN) && \
239 (defined(_M_X64) || defined(__x86_64__))
240 // Work around a CPU bug. See bug 1524257.
241 __attribute__((__aligned__(32)))
242 #endif
emitAssignment()243 bool NameOpEmitter::emitAssignment() {
244 MOZ_ASSERT(state_ == State::Rhs);
245
246 switch (loc_.kind()) {
247 case NameLocation::Kind::Dynamic:
248 case NameLocation::Kind::Import:
249 case NameLocation::Kind::DynamicAnnexBVar:
250 if (!bce_->emitAtomOp(bce_->strictifySetNameOp(JSOp::SetName),
251 atomIndex_)) {
252 return false;
253 }
254 break;
255 case NameLocation::Kind::Global: {
256 JSOp op;
257 if (emittedBindOp_) {
258 op = bce_->strictifySetNameOp(JSOp::SetGName);
259 } else {
260 op = JSOp::InitGLexical;
261 }
262 if (!bce_->emitAtomOp(op, atomIndex_)) {
263 return false;
264 }
265 break;
266 }
267 case NameLocation::Kind::Intrinsic:
268 if (!bce_->emitAtomOp(JSOp::SetIntrinsic, name_)) {
269 return false;
270 }
271 break;
272 case NameLocation::Kind::NamedLambdaCallee:
273 // Assigning to the named lambda is a no-op in sloppy mode but
274 // throws in strict mode.
275 if (bce_->sc->strict()) {
276 if (!bce_->emitAtomOp(JSOp::ThrowSetConst, name_)) {
277 return false;
278 }
279 }
280 break;
281 case NameLocation::Kind::ArgumentSlot:
282 if (!bce_->emitArgOp(JSOp::SetArg, loc_.argumentSlot())) {
283 return false;
284 }
285 break;
286 case NameLocation::Kind::FrameSlot: {
287 JSOp op = JSOp::SetLocal;
288 // Lexicals, Synthetics and Private Methods have very similar handling
289 // around a variety of areas, including initialization.
290 if (loc_.isLexical() || loc_.isPrivateMethod() || loc_.isSynthetic()) {
291 if (isInitialize()) {
292 op = JSOp::InitLexical;
293 } else {
294 if (loc_.isConst()) {
295 op = JSOp::ThrowSetConst;
296 }
297 if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::No)) {
298 return false;
299 }
300 }
301 }
302 if (op == JSOp::ThrowSetConst) {
303 if (!bce_->emitAtomOp(op, name_)) {
304 return false;
305 }
306 } else {
307 if (!bce_->emitLocalOp(op, loc_.frameSlot())) {
308 return false;
309 }
310 }
311 if (op == JSOp::InitLexical) {
312 if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_,
313 DontCheckTDZ)) {
314 return false;
315 }
316 }
317 break;
318 }
319 case NameLocation::Kind::EnvironmentCoordinate: {
320 JSOp op = JSOp::SetAliasedVar;
321 // Lexicals, Synthetics and Private Methods have very similar handling
322 // around a variety of areas, including initialization.
323 if (loc_.isLexical() || loc_.isPrivateMethod() || loc_.isSynthetic()) {
324 if (isInitialize()) {
325 op = JSOp::InitAliasedLexical;
326 } else {
327 if (loc_.isConst()) {
328 op = JSOp::ThrowSetConst;
329 }
330 if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::No)) {
331 return false;
332 }
333 }
334 }
335 if (loc_.bindingKind() == BindingKind::NamedLambdaCallee) {
336 // Assigning to the named lambda is a no-op in sloppy mode and throws
337 // in strict mode.
338 op = JSOp::ThrowSetConst;
339 if (bce_->sc->strict()) {
340 if (!bce_->emitAtomOp(op, name_)) {
341 return false;
342 }
343 }
344 } else {
345 if (op == JSOp::ThrowSetConst) {
346 if (!bce_->emitAtomOp(op, name_)) {
347 return false;
348 }
349 } else {
350 if (!bce_->emitEnvCoordOp(op, loc_.environmentCoordinate())) {
351 return false;
352 }
353 }
354 }
355 if (op == JSOp::InitAliasedLexical) {
356 if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_,
357 DontCheckTDZ)) {
358 return false;
359 }
360 }
361 break;
362 }
363 case NameLocation::Kind::DebugEnvironmentCoordinate:
364 MOZ_CRASH("Shouldn't be assigning to a private brand");
365 break;
366 }
367
368 #ifdef DEBUG
369 state_ = State::Assignment;
370 #endif
371 return true;
372 }
373
emitIncDec()374 bool NameOpEmitter::emitIncDec() {
375 MOZ_ASSERT(state_ == State::Start);
376
377 JSOp incOp = isInc() ? JSOp::Inc : JSOp::Dec;
378 if (!prepareForRhs()) {
379 // [stack] ENV? V
380 return false;
381 }
382 if (!bce_->emit1(JSOp::ToNumeric)) {
383 // [stack] ENV? N
384 return false;
385 }
386 if (isPostIncDec()) {
387 if (!bce_->emit1(JSOp::Dup)) {
388 // [stack] ENV? N? N
389 return false;
390 }
391 }
392 if (!bce_->emit1(incOp)) {
393 // [stack] ENV? N? N+1
394 return false;
395 }
396 if (isPostIncDec() && emittedBindOp()) {
397 if (!bce_->emit2(JSOp::Pick, 2)) {
398 // [stack] N? N+1 ENV?
399 return false;
400 }
401 if (!bce_->emit1(JSOp::Swap)) {
402 // [stack] N? ENV? N+1
403 return false;
404 }
405 }
406 if (!emitAssignment()) {
407 // [stack] N? N+1
408 return false;
409 }
410 if (isPostIncDec()) {
411 if (!bce_->emit1(JSOp::Pop)) {
412 // [stack] N
413 return false;
414 }
415 }
416
417 #ifdef DEBUG
418 state_ = State::IncDec;
419 #endif
420 return true;
421 }
422