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