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