• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_AST_VARIABLES_H_
6 #define V8_AST_VARIABLES_H_
7 
8 #include "src/ast/ast-value-factory.h"
9 #include "src/base/threaded-list.h"
10 #include "src/common/globals.h"
11 #include "src/execution/isolate.h"
12 #include "src/zone/zone.h"
13 
14 namespace v8 {
15 namespace internal {
16 
17 // The AST refers to variables via VariableProxies - placeholders for the actual
18 // variables. Variables themselves are never directly referred to from the AST,
19 // they are maintained by scopes, and referred to from VariableProxies and Slots
20 // after binding and variable allocation.
21 class Variable final : public ZoneObject {
22  public:
23   Variable(Scope* scope, const AstRawString* name, VariableMode mode,
24            VariableKind kind, InitializationFlag initialization_flag,
25            MaybeAssignedFlag maybe_assigned_flag = kNotAssigned,
26            IsStaticFlag is_static_flag = IsStaticFlag::kNotStatic)
scope_(scope)27       : scope_(scope),
28         name_(name),
29         local_if_not_shadowed_(nullptr),
30         next_(nullptr),
31         index_(-1),
32         initializer_position_(kNoSourcePosition),
33         bit_field_(MaybeAssignedFlagField::encode(maybe_assigned_flag) |
34                    InitializationFlagField::encode(initialization_flag) |
35                    VariableModeField::encode(mode) |
36                    IsUsedField::encode(false) |
37                    ForceContextAllocationBit::encode(false) |
38                    ForceHoleInitializationField::encode(false) |
39                    LocationField::encode(VariableLocation::UNALLOCATED) |
40                    VariableKindField::encode(kind) |
41                    IsStaticFlagField::encode(is_static_flag)) {
42     // Var declared variables never need initialization.
43     DCHECK(!(mode == VariableMode::kVar &&
44              initialization_flag == kNeedsInitialization));
45     DCHECK_IMPLIES(is_static_flag == IsStaticFlag::kStatic,
46                    IsConstVariableMode(mode));
47   }
48 
49   explicit Variable(Variable* other);
50 
51   // The source code for an eval() call may refer to a variable that is
52   // in an outer scope about which we don't know anything (it may not
53   // be the script scope). scope() is nullptr in that case. Currently the
54   // scope is only used to follow the context chain length.
scope()55   Scope* scope() const { return scope_; }
56 
57   // This is for adjusting the scope of temporaries used when desugaring
58   // parameter initializers.
set_scope(Scope * scope)59   void set_scope(Scope* scope) { scope_ = scope; }
60 
name()61   Handle<String> name() const { return name_->string(); }
raw_name()62   const AstRawString* raw_name() const { return name_; }
mode()63   VariableMode mode() const { return VariableModeField::decode(bit_field_); }
set_mode(VariableMode mode)64   void set_mode(VariableMode mode) {
65     bit_field_ = VariableModeField::update(bit_field_, mode);
66   }
set_is_static_flag(IsStaticFlag is_static_flag)67   void set_is_static_flag(IsStaticFlag is_static_flag) {
68     bit_field_ = IsStaticFlagField::update(bit_field_, is_static_flag);
69   }
is_static_flag()70   IsStaticFlag is_static_flag() const {
71     return IsStaticFlagField::decode(bit_field_);
72   }
is_static()73   bool is_static() const { return is_static_flag() == IsStaticFlag::kStatic; }
74 
has_forced_context_allocation()75   bool has_forced_context_allocation() const {
76     return ForceContextAllocationBit::decode(bit_field_);
77   }
ForceContextAllocation()78   void ForceContextAllocation() {
79     DCHECK(IsUnallocated() || IsContextSlot() || IsLookupSlot() ||
80            location() == VariableLocation::MODULE);
81     bit_field_ = ForceContextAllocationBit::update(bit_field_, true);
82   }
is_used()83   bool is_used() { return IsUsedField::decode(bit_field_); }
set_is_used()84   void set_is_used() { bit_field_ = IsUsedField::update(bit_field_, true); }
maybe_assigned()85   MaybeAssignedFlag maybe_assigned() const {
86     return MaybeAssignedFlagField::decode(bit_field_);
87   }
clear_maybe_assigned()88   void clear_maybe_assigned() {
89     bit_field_ = MaybeAssignedFlagField::update(bit_field_, kNotAssigned);
90   }
SetMaybeAssigned()91   void SetMaybeAssigned() {
92     if (mode() == VariableMode::kConst) return;
93     // Private names are only initialized once by us.
94     if (name_->IsPrivateName()) {
95       return;
96     }
97     // If this variable is dynamically shadowing another variable, then that
98     // variable could also be assigned (in the non-shadowing case).
99     if (has_local_if_not_shadowed()) {
100       // Avoid repeatedly marking the same tree of variables by only recursing
101       // when this variable's maybe_assigned status actually changes.
102       if (!maybe_assigned()) {
103         local_if_not_shadowed()->SetMaybeAssigned();
104       }
105       DCHECK_IMPLIES(local_if_not_shadowed()->mode() != VariableMode::kConst,
106                      local_if_not_shadowed()->maybe_assigned());
107     }
108     set_maybe_assigned();
109   }
110 
requires_brand_check()111   bool requires_brand_check() const {
112     return IsPrivateMethodOrAccessorVariableMode(mode());
113   }
114 
initializer_position()115   int initializer_position() { return initializer_position_; }
set_initializer_position(int pos)116   void set_initializer_position(int pos) { initializer_position_ = pos; }
117 
IsUnallocated()118   bool IsUnallocated() const {
119     return location() == VariableLocation::UNALLOCATED;
120   }
IsParameter()121   bool IsParameter() const { return location() == VariableLocation::PARAMETER; }
IsStackLocal()122   bool IsStackLocal() const { return location() == VariableLocation::LOCAL; }
IsStackAllocated()123   bool IsStackAllocated() const { return IsParameter() || IsStackLocal(); }
IsContextSlot()124   bool IsContextSlot() const { return location() == VariableLocation::CONTEXT; }
IsLookupSlot()125   bool IsLookupSlot() const { return location() == VariableLocation::LOOKUP; }
126   bool IsGlobalObjectProperty() const;
127 
128   // True for 'let' and 'const' variables declared in the script scope of a REPL
129   // script.
130   bool IsReplGlobal() const;
131 
is_dynamic()132   bool is_dynamic() const { return IsDynamicVariableMode(mode()); }
133 
134   // Returns the InitializationFlag this Variable was created with.
135   // Scope analysis may allow us to relax this initialization
136   // requirement, which will be reflected in the return value of
137   // binding_needs_init().
initialization_flag()138   InitializationFlag initialization_flag() const {
139     return InitializationFlagField::decode(bit_field_);
140   }
141 
142   // Whether this variable needs to be initialized with the hole at
143   // declaration time. Only returns valid results after scope analysis.
binding_needs_init()144   bool binding_needs_init() const {
145     DCHECK_IMPLIES(initialization_flag() == kNeedsInitialization,
146                    IsLexicalVariableMode(mode()) ||
147                        IsPrivateMethodOrAccessorVariableMode(mode()));
148     DCHECK_IMPLIES(ForceHoleInitializationField::decode(bit_field_),
149                    initialization_flag() == kNeedsInitialization);
150 
151     // Always initialize if hole initialization was forced during
152     // scope analysis.
153     if (ForceHoleInitializationField::decode(bit_field_)) return true;
154 
155     // If initialization was not forced, no need for initialization
156     // for stack allocated variables, since UpdateNeedsHoleCheck()
157     // in scopes.cc has proven that no VariableProxy refers to
158     // this variable in such a way that a runtime hole check
159     // would be generated.
160     if (IsStackAllocated()) return false;
161 
162     // Otherwise, defer to the flag set when this Variable was constructed.
163     return initialization_flag() == kNeedsInitialization;
164   }
165 
166   // Called during scope analysis when a VariableProxy is found to
167   // reference this Variable in such a way that a hole check will
168   // be required at runtime.
ForceHoleInitialization()169   void ForceHoleInitialization() {
170     DCHECK_EQ(kNeedsInitialization, initialization_flag());
171     DCHECK(IsLexicalVariableMode(mode()) ||
172            IsPrivateMethodOrAccessorVariableMode(mode()));
173     bit_field_ = ForceHoleInitializationField::update(bit_field_, true);
174   }
175 
throw_on_const_assignment(LanguageMode language_mode)176   bool throw_on_const_assignment(LanguageMode language_mode) const {
177     return kind() != SLOPPY_FUNCTION_NAME_VARIABLE || is_strict(language_mode);
178   }
179 
is_this()180   bool is_this() const { return kind() == THIS_VARIABLE; }
is_sloppy_function_name()181   bool is_sloppy_function_name() const {
182     return kind() == SLOPPY_FUNCTION_NAME_VARIABLE;
183   }
184 
is_parameter()185   bool is_parameter() const { return kind() == PARAMETER_VARIABLE; }
is_sloppy_block_function()186   bool is_sloppy_block_function() {
187     return kind() == SLOPPY_BLOCK_FUNCTION_VARIABLE;
188   }
189 
local_if_not_shadowed()190   Variable* local_if_not_shadowed() const {
191     DCHECK((mode() == VariableMode::kDynamicLocal ||
192             mode() == VariableMode::kDynamic) &&
193            has_local_if_not_shadowed());
194     return local_if_not_shadowed_;
195   }
196 
has_local_if_not_shadowed()197   bool has_local_if_not_shadowed() const {
198     return local_if_not_shadowed_ != nullptr;
199   }
200 
set_local_if_not_shadowed(Variable * local)201   void set_local_if_not_shadowed(Variable* local) {
202     local_if_not_shadowed_ = local;
203   }
204 
location()205   VariableLocation location() const {
206     return LocationField::decode(bit_field_);
207   }
kind()208   VariableKind kind() const { return VariableKindField::decode(bit_field_); }
209 
index()210   int index() const { return index_; }
211 
IsReceiver()212   bool IsReceiver() const {
213     DCHECK(IsParameter());
214 
215     return index_ == -1;
216   }
217 
IsExport()218   bool IsExport() const {
219     DCHECK_EQ(location(), VariableLocation::MODULE);
220     DCHECK_NE(index(), 0);
221     return index() > 0;
222   }
223 
AllocateTo(VariableLocation location,int index)224   void AllocateTo(VariableLocation location, int index) {
225     DCHECK(IsUnallocated() ||
226            (this->location() == location && this->index() == index));
227     DCHECK_IMPLIES(location == VariableLocation::MODULE, index != 0);
228     bit_field_ = LocationField::update(bit_field_, location);
229     DCHECK_EQ(location, this->location());
230     index_ = index;
231   }
232 
MakeParameterNonSimple()233   void MakeParameterNonSimple() {
234     DCHECK(is_parameter());
235     bit_field_ = VariableModeField::update(bit_field_, VariableMode::kLet);
236     bit_field_ =
237         InitializationFlagField::update(bit_field_, kNeedsInitialization);
238   }
239 
DefaultInitializationFlag(VariableMode mode)240   static InitializationFlag DefaultInitializationFlag(VariableMode mode) {
241     DCHECK(IsDeclaredVariableMode(mode));
242     return mode == VariableMode::kVar ? kCreatedInitialized
243                                       : kNeedsInitialization;
244   }
245 
246   // Rewrites the VariableLocation of repl script scope 'lets' to REPL_GLOBAL.
247   void RewriteLocationForRepl();
248 
249   using List = base::ThreadedList<Variable>;
250 
251  private:
252   Scope* scope_;
253   const AstRawString* name_;
254 
255   // If this field is set, this variable references the stored locally bound
256   // variable, but it might be shadowed by variable bindings introduced by with
257   // blocks or sloppy 'eval' calls between the reference scope (inclusive) and
258   // the binding scope (exclusive).
259   Variable* local_if_not_shadowed_;
260   Variable* next_;
261   int index_;
262   int initializer_position_;
263   uint16_t bit_field_;
264 
set_maybe_assigned()265   void set_maybe_assigned() {
266     bit_field_ = MaybeAssignedFlagField::update(bit_field_, kMaybeAssigned);
267   }
268 
269   using VariableModeField = base::BitField16<VariableMode, 0, 4>;
270   using VariableKindField = VariableModeField::Next<VariableKind, 3>;
271   using LocationField = VariableKindField::Next<VariableLocation, 3>;
272   using ForceContextAllocationBit = LocationField::Next<bool, 1>;
273   using IsUsedField = ForceContextAllocationBit::Next<bool, 1>;
274   using InitializationFlagField = IsUsedField::Next<InitializationFlag, 1>;
275   using ForceHoleInitializationField = InitializationFlagField::Next<bool, 1>;
276   using MaybeAssignedFlagField =
277       ForceHoleInitializationField::Next<MaybeAssignedFlag, 1>;
278   using IsStaticFlagField = MaybeAssignedFlagField::Next<IsStaticFlag, 1>;
279 
next()280   Variable** next() { return &next_; }
281   friend List;
282   friend base::ThreadedListTraits<Variable>;
283 };
284 }  // namespace internal
285 }  // namespace v8
286 
287 #endif  // V8_AST_VARIABLES_H_
288