• 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' variables declared in the script scope of a REPL script.
129   bool IsReplGlobalLet() const;
130 
is_dynamic()131   bool is_dynamic() const { return IsDynamicVariableMode(mode()); }
132 
133   // Returns the InitializationFlag this Variable was created with.
134   // Scope analysis may allow us to relax this initialization
135   // requirement, which will be reflected in the return value of
136   // binding_needs_init().
initialization_flag()137   InitializationFlag initialization_flag() const {
138     return InitializationFlagField::decode(bit_field_);
139   }
140 
141   // Whether this variable needs to be initialized with the hole at
142   // declaration time. Only returns valid results after scope analysis.
binding_needs_init()143   bool binding_needs_init() const {
144     DCHECK_IMPLIES(initialization_flag() == kNeedsInitialization,
145                    IsLexicalVariableMode(mode()) ||
146                        IsPrivateMethodOrAccessorVariableMode(mode()));
147     DCHECK_IMPLIES(ForceHoleInitializationField::decode(bit_field_),
148                    initialization_flag() == kNeedsInitialization);
149 
150     // Always initialize if hole initialization was forced during
151     // scope analysis.
152     if (ForceHoleInitializationField::decode(bit_field_)) return true;
153 
154     // If initialization was not forced, no need for initialization
155     // for stack allocated variables, since UpdateNeedsHoleCheck()
156     // in scopes.cc has proven that no VariableProxy refers to
157     // this variable in such a way that a runtime hole check
158     // would be generated.
159     if (IsStackAllocated()) return false;
160 
161     // Otherwise, defer to the flag set when this Variable was constructed.
162     return initialization_flag() == kNeedsInitialization;
163   }
164 
165   // Called during scope analysis when a VariableProxy is found to
166   // reference this Variable in such a way that a hole check will
167   // be required at runtime.
ForceHoleInitialization()168   void ForceHoleInitialization() {
169     DCHECK_EQ(kNeedsInitialization, initialization_flag());
170     DCHECK(IsLexicalVariableMode(mode()) ||
171            IsPrivateMethodOrAccessorVariableMode(mode()));
172     bit_field_ = ForceHoleInitializationField::update(bit_field_, true);
173   }
174 
throw_on_const_assignment(LanguageMode language_mode)175   bool throw_on_const_assignment(LanguageMode language_mode) const {
176     return kind() != SLOPPY_FUNCTION_NAME_VARIABLE || is_strict(language_mode);
177   }
178 
is_this()179   bool is_this() const { return kind() == THIS_VARIABLE; }
is_sloppy_function_name()180   bool is_sloppy_function_name() const {
181     return kind() == SLOPPY_FUNCTION_NAME_VARIABLE;
182   }
183 
is_parameter()184   bool is_parameter() const { return kind() == PARAMETER_VARIABLE; }
is_sloppy_block_function()185   bool is_sloppy_block_function() {
186     return kind() == SLOPPY_BLOCK_FUNCTION_VARIABLE;
187   }
188 
local_if_not_shadowed()189   Variable* local_if_not_shadowed() const {
190     DCHECK((mode() == VariableMode::kDynamicLocal ||
191             mode() == VariableMode::kDynamic) &&
192            has_local_if_not_shadowed());
193     return local_if_not_shadowed_;
194   }
195 
has_local_if_not_shadowed()196   bool has_local_if_not_shadowed() const {
197     return local_if_not_shadowed_ != nullptr;
198   }
199 
set_local_if_not_shadowed(Variable * local)200   void set_local_if_not_shadowed(Variable* local) {
201     local_if_not_shadowed_ = local;
202   }
203 
location()204   VariableLocation location() const {
205     return LocationField::decode(bit_field_);
206   }
kind()207   VariableKind kind() const { return VariableKindField::decode(bit_field_); }
208 
index()209   int index() const { return index_; }
210 
IsReceiver()211   bool IsReceiver() const {
212     DCHECK(IsParameter());
213 
214     return index_ == -1;
215   }
216 
IsExport()217   bool IsExport() const {
218     DCHECK_EQ(location(), VariableLocation::MODULE);
219     DCHECK_NE(index(), 0);
220     return index() > 0;
221   }
222 
AllocateTo(VariableLocation location,int index)223   void AllocateTo(VariableLocation location, int index) {
224     DCHECK(IsUnallocated() ||
225            (this->location() == location && this->index() == index));
226     DCHECK_IMPLIES(location == VariableLocation::MODULE, index != 0);
227     bit_field_ = LocationField::update(bit_field_, location);
228     DCHECK_EQ(location, this->location());
229     index_ = index;
230   }
231 
MakeParameterNonSimple()232   void MakeParameterNonSimple() {
233     DCHECK(is_parameter());
234     bit_field_ = VariableModeField::update(bit_field_, VariableMode::kLet);
235     bit_field_ =
236         InitializationFlagField::update(bit_field_, kNeedsInitialization);
237   }
238 
DefaultInitializationFlag(VariableMode mode)239   static InitializationFlag DefaultInitializationFlag(VariableMode mode) {
240     DCHECK(IsDeclaredVariableMode(mode));
241     return mode == VariableMode::kVar ? kCreatedInitialized
242                                       : kNeedsInitialization;
243   }
244 
245   // Rewrites the VariableLocation of repl script scope 'lets' to REPL_GLOBAL.
246   void RewriteLocationForRepl();
247 
248   using List = base::ThreadedList<Variable>;
249 
250  private:
251   Scope* scope_;
252   const AstRawString* name_;
253 
254   // If this field is set, this variable references the stored locally bound
255   // variable, but it might be shadowed by variable bindings introduced by with
256   // blocks or sloppy 'eval' calls between the reference scope (inclusive) and
257   // the binding scope (exclusive).
258   Variable* local_if_not_shadowed_;
259   Variable* next_;
260   int index_;
261   int initializer_position_;
262   uint16_t bit_field_;
263 
set_maybe_assigned()264   void set_maybe_assigned() {
265     bit_field_ = MaybeAssignedFlagField::update(bit_field_, kMaybeAssigned);
266   }
267 
268   using VariableModeField = base::BitField16<VariableMode, 0, 4>;
269   using VariableKindField = VariableModeField::Next<VariableKind, 3>;
270   using LocationField = VariableKindField::Next<VariableLocation, 3>;
271   using ForceContextAllocationBit = LocationField::Next<bool, 1>;
272   using IsUsedField = ForceContextAllocationBit::Next<bool, 1>;
273   using InitializationFlagField = IsUsedField::Next<InitializationFlag, 1>;
274   using ForceHoleInitializationField = InitializationFlagField::Next<bool, 1>;
275   using MaybeAssignedFlagField =
276       ForceHoleInitializationField::Next<MaybeAssignedFlag, 1>;
277   using IsStaticFlagField = MaybeAssignedFlagField::Next<IsStaticFlag, 1>;
278 
next()279   Variable** next() { return &next_; }
280   friend List;
281   friend base::ThreadedListTraits<Variable>;
282 };
283 }  // namespace internal
284 }  // namespace v8
285 
286 #endif  // V8_AST_VARIABLES_H_
287