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