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 #ifndef frontend_NameAnalysisTypes_h
8 #define frontend_NameAnalysisTypes_h
9
10 #include <type_traits>
11
12 #include "vm/BytecodeUtil.h"
13 #include "vm/Scope.h"
14
15 namespace js {
16
17 // An "environment coordinate" describes how to get from head of the
18 // environment chain to a given lexically-enclosing variable. An environment
19 // coordinate has two dimensions:
20 // - hops: the number of environment objects on the scope chain to skip
21 // - slot: the slot on the environment object holding the variable's value
22 class EnvironmentCoordinate {
23 uint32_t hops_;
24 uint32_t slot_;
25
26 // Technically, hops_/slot_ are ENVCOORD_(HOPS|SLOT)_BITS wide. Since
27 // EnvironmentCoordinate is a temporary value, don't bother with a bitfield as
28 // this only adds overhead.
29 static_assert(ENVCOORD_HOPS_BITS <= 32, "We have enough bits below");
30 static_assert(ENVCOORD_SLOT_BITS <= 32, "We have enough bits below");
31
32 public:
EnvironmentCoordinate(jsbytecode * pc)33 explicit inline EnvironmentCoordinate(jsbytecode* pc)
34 : hops_(GET_ENVCOORD_HOPS(pc)),
35 slot_(GET_ENVCOORD_SLOT(pc + ENVCOORD_HOPS_LEN)) {
36 MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_ENVCOORD);
37 }
38
39 EnvironmentCoordinate() = default;
40
setHops(uint32_t hops)41 void setHops(uint32_t hops) {
42 MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT);
43 hops_ = hops;
44 }
45
setSlot(uint32_t slot)46 void setSlot(uint32_t slot) {
47 MOZ_ASSERT(slot < ENVCOORD_SLOT_LIMIT);
48 slot_ = slot;
49 }
50
hops()51 uint32_t hops() const {
52 MOZ_ASSERT(hops_ < ENVCOORD_HOPS_LIMIT);
53 return hops_;
54 }
55
slot()56 uint32_t slot() const {
57 MOZ_ASSERT(slot_ < ENVCOORD_SLOT_LIMIT);
58 return slot_;
59 }
60
61 bool operator==(const EnvironmentCoordinate& rhs) const {
62 return hops() == rhs.hops() && slot() == rhs.slot();
63 }
64 };
65
66 namespace frontend {
67
68 enum class ParseGoal : uint8_t { Script, Module };
69
70 // A detailed kind used for tracking declarations in the Parser. Used for
71 // specific early error semantics and better error messages.
72 enum class DeclarationKind : uint8_t {
73 PositionalFormalParameter,
74 FormalParameter,
75 CoverArrowParameter,
76 Var,
77 Let,
78 Const,
79 Class, // Handled as same as `let` after parsing.
80 Import,
81 BodyLevelFunction,
82 ModuleBodyLevelFunction,
83 LexicalFunction,
84 SloppyLexicalFunction,
85 VarForAnnexBLexicalFunction,
86 SimpleCatchParameter,
87 CatchParameter
88 };
89
DeclarationKindToBindingKind(DeclarationKind kind)90 static inline BindingKind DeclarationKindToBindingKind(DeclarationKind kind) {
91 switch (kind) {
92 case DeclarationKind::PositionalFormalParameter:
93 case DeclarationKind::FormalParameter:
94 case DeclarationKind::CoverArrowParameter:
95 return BindingKind::FormalParameter;
96
97 case DeclarationKind::Var:
98 case DeclarationKind::BodyLevelFunction:
99 case DeclarationKind::ModuleBodyLevelFunction:
100 case DeclarationKind::VarForAnnexBLexicalFunction:
101 return BindingKind::Var;
102
103 case DeclarationKind::Let:
104 case DeclarationKind::Class:
105 case DeclarationKind::LexicalFunction:
106 case DeclarationKind::SloppyLexicalFunction:
107 case DeclarationKind::SimpleCatchParameter:
108 case DeclarationKind::CatchParameter:
109 return BindingKind::Let;
110
111 case DeclarationKind::Const:
112 return BindingKind::Const;
113
114 case DeclarationKind::Import:
115 return BindingKind::Import;
116 }
117
118 MOZ_CRASH("Bad DeclarationKind");
119 }
120
DeclarationKindIsLexical(DeclarationKind kind)121 static inline bool DeclarationKindIsLexical(DeclarationKind kind) {
122 return BindingKindIsLexical(DeclarationKindToBindingKind(kind));
123 }
124
125 // Used in Parser to track declared names.
126 class DeclaredNameInfo {
127 uint32_t pos_;
128 DeclarationKind kind_;
129
130 // If the declared name is a binding, whether the binding is closed
131 // over. Its value is meaningless if the declared name is not a binding
132 // (i.e., a 'var' declared name in a non-var scope).
133 bool closedOver_;
134
135 public:
DeclaredNameInfo(DeclarationKind kind,uint32_t pos)136 explicit DeclaredNameInfo(DeclarationKind kind, uint32_t pos)
137 : pos_(pos), kind_(kind), closedOver_(false) {}
138
139 // Needed for InlineMap.
140 DeclaredNameInfo() = default;
141
kind()142 DeclarationKind kind() const { return kind_; }
143
144 static const uint32_t npos = uint32_t(-1);
145
pos()146 uint32_t pos() const { return pos_; }
147
alterKind(DeclarationKind kind)148 void alterKind(DeclarationKind kind) { kind_ = kind; }
149
setClosedOver()150 void setClosedOver() { closedOver_ = true; }
151
closedOver()152 bool closedOver() const { return closedOver_; }
153 };
154
155 // Used in BytecodeEmitter to map names to locations.
156 class NameLocation {
157 public:
158 enum class Kind : uint8_t {
159 // Cannot statically determine where the name lives. Needs to walk the
160 // environment chain to search for the name.
161 Dynamic,
162
163 // The name lives on the global or is a global lexical binding. Search
164 // for the name on the global scope.
165 Global,
166
167 // Special mode used only when emitting self-hosted scripts. See
168 // BytecodeEmitter::lookupName.
169 Intrinsic,
170
171 // In a named lambda, the name is the callee itself.
172 NamedLambdaCallee,
173
174 // The name is a positional formal parameter name and can be retrieved
175 // directly from the stack using slot_.
176 ArgumentSlot,
177
178 // The name is not closed over and lives on the frame in slot_.
179 FrameSlot,
180
181 // The name is closed over and lives on an environment hops_ away in slot_.
182 EnvironmentCoordinate,
183
184 // An imported name in a module.
185 Import,
186
187 // Cannot statically determine where the synthesized var for Annex
188 // B.3.3 lives.
189 DynamicAnnexBVar
190 };
191
192 private:
193 // Where the name lives.
194 Kind kind_;
195
196 // If the name is not Dynamic or DynamicAnnexBVar, the kind of the
197 // binding.
198 BindingKind bindingKind_;
199
200 // If the name is closed over and accessed via EnvironmentCoordinate, the
201 // number of dynamic environments to skip.
202 //
203 // Otherwise UINT8_MAX.
204 uint8_t hops_;
205
206 // If the name lives on the frame, the slot frame.
207 //
208 // If the name is closed over and accessed via EnvironmentCoordinate, the
209 // slot on the environment.
210 //
211 // Otherwise LOCALNO_LIMIT/ENVCOORD_SLOT_LIMIT.
212 uint32_t slot_ : ENVCOORD_SLOT_BITS;
213
214 static_assert(LOCALNO_BITS == ENVCOORD_SLOT_BITS,
215 "Frame and environment slots must be same sized.");
216
217 NameLocation(Kind kind, BindingKind bindingKind, uint8_t hops = UINT8_MAX,
218 uint32_t slot = ENVCOORD_SLOT_LIMIT)
kind_(kind)219 : kind_(kind), bindingKind_(bindingKind), hops_(hops), slot_(slot) {}
220
221 public:
222 // Default constructor for InlineMap.
223 NameLocation() = default;
224
Dynamic()225 static NameLocation Dynamic() { return NameLocation(); }
226
Global(BindingKind bindKind)227 static NameLocation Global(BindingKind bindKind) {
228 MOZ_ASSERT(bindKind != BindingKind::FormalParameter);
229 return NameLocation(Kind::Global, bindKind);
230 }
231
Intrinsic()232 static NameLocation Intrinsic() {
233 return NameLocation(Kind::Intrinsic, BindingKind::Var);
234 }
235
NamedLambdaCallee()236 static NameLocation NamedLambdaCallee() {
237 return NameLocation(Kind::NamedLambdaCallee,
238 BindingKind::NamedLambdaCallee);
239 }
240
ArgumentSlot(uint16_t slot)241 static NameLocation ArgumentSlot(uint16_t slot) {
242 return NameLocation(Kind::ArgumentSlot, BindingKind::FormalParameter, 0,
243 slot);
244 }
245
FrameSlot(BindingKind bindKind,uint32_t slot)246 static NameLocation FrameSlot(BindingKind bindKind, uint32_t slot) {
247 MOZ_ASSERT(slot < LOCALNO_LIMIT);
248 return NameLocation(Kind::FrameSlot, bindKind, 0, slot);
249 }
250
EnvironmentCoordinate(BindingKind bindKind,uint8_t hops,uint32_t slot)251 static NameLocation EnvironmentCoordinate(BindingKind bindKind, uint8_t hops,
252 uint32_t slot) {
253 MOZ_ASSERT(slot < ENVCOORD_SLOT_LIMIT);
254 return NameLocation(Kind::EnvironmentCoordinate, bindKind, hops, slot);
255 }
256
Import()257 static NameLocation Import() {
258 return NameLocation(Kind::Import, BindingKind::Import);
259 }
260
DynamicAnnexBVar()261 static NameLocation DynamicAnnexBVar() {
262 return NameLocation(Kind::DynamicAnnexBVar, BindingKind::Var);
263 }
264
fromBinding(BindingKind bindKind,const BindingLocation & bl)265 static NameLocation fromBinding(BindingKind bindKind,
266 const BindingLocation& bl) {
267 switch (bl.kind()) {
268 case BindingLocation::Kind::Global:
269 return Global(bindKind);
270 case BindingLocation::Kind::Argument:
271 return ArgumentSlot(bl.argumentSlot());
272 case BindingLocation::Kind::Frame:
273 return FrameSlot(bindKind, bl.slot());
274 case BindingLocation::Kind::Environment:
275 return EnvironmentCoordinate(bindKind, 0, bl.slot());
276 case BindingLocation::Kind::Import:
277 return Import();
278 case BindingLocation::Kind::NamedLambdaCallee:
279 return NamedLambdaCallee();
280 }
281 MOZ_CRASH("Bad BindingKind");
282 }
283
284 bool operator==(const NameLocation& other) const {
285 return kind_ == other.kind_ && bindingKind_ == other.bindingKind_ &&
286 hops_ == other.hops_ && slot_ == other.slot_;
287 }
288
289 bool operator!=(const NameLocation& other) const { return !(*this == other); }
290
kind()291 Kind kind() const { return kind_; }
292
argumentSlot()293 uint16_t argumentSlot() const {
294 MOZ_ASSERT(kind_ == Kind::ArgumentSlot);
295 return mozilla::AssertedCast<uint16_t>(slot_);
296 }
297
frameSlot()298 uint32_t frameSlot() const {
299 MOZ_ASSERT(kind_ == Kind::FrameSlot);
300 return slot_;
301 }
302
addHops(uint8_t more)303 NameLocation addHops(uint8_t more) {
304 MOZ_ASSERT(hops_ < ENVCOORD_HOPS_LIMIT - more);
305 MOZ_ASSERT(kind_ == Kind::EnvironmentCoordinate);
306 return NameLocation(kind_, bindingKind_, hops_ + more, slot_);
307 }
308
environmentCoordinate()309 class EnvironmentCoordinate environmentCoordinate() const {
310 MOZ_ASSERT(kind_ == Kind::EnvironmentCoordinate);
311 class EnvironmentCoordinate coord;
312 coord.setHops(hops_);
313 coord.setSlot(slot_);
314 return coord;
315 }
316
bindingKind()317 BindingKind bindingKind() const {
318 MOZ_ASSERT(kind_ != Kind::Dynamic);
319 return bindingKind_;
320 }
321
isLexical()322 bool isLexical() const { return BindingKindIsLexical(bindingKind()); }
323
isConst()324 bool isConst() const { return bindingKind() == BindingKind::Const; }
325
hasKnownSlot()326 bool hasKnownSlot() const {
327 return kind_ == Kind::ArgumentSlot || kind_ == Kind::FrameSlot ||
328 kind_ == Kind::EnvironmentCoordinate;
329 }
330 };
331
332 // These types are declared here for BaseScript::CreateLazy.
333 using AtomVector = Vector<JSAtom*, 24, SystemAllocPolicy>;
334
335 class FunctionBox;
336 // FunctionBoxes stored in this type are required to be rooted
337 // by the parser
338 using FunctionBoxVector = Vector<const FunctionBox*, 8>;
339
340 } // namespace frontend
341 } // namespace js
342
343 #endif // frontend_NameAnalysisTypes_h
344