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