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