1 // Copyright 2014 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_OBJECTS_LOOKUP_H_ 6 #define V8_OBJECTS_LOOKUP_H_ 7 8 #include "src/common/globals.h" 9 #include "src/execution/isolate.h" 10 #include "src/heap/factory.h" 11 #include "src/objects/descriptor-array.h" 12 #include "src/objects/js-objects.h" 13 #include "src/objects/map.h" 14 #include "src/objects/objects.h" 15 16 #if V8_ENABLE_WEBASSEMBLY 17 #include "src/wasm/value-type.h" 18 #endif // V8_ENABLE_WEBASSEMBLY 19 20 namespace v8 { 21 namespace internal { 22 23 class PropertyKey { 24 public: 25 inline PropertyKey(Isolate* isolate, double index); 26 // {name} might be a string representation of an element index. 27 inline PropertyKey(Isolate* isolate, Handle<Name> name); 28 // {valid_key} is a Name or Number. 29 inline PropertyKey(Isolate* isolate, Handle<Object> valid_key); 30 // {key} could be anything. 31 PropertyKey(Isolate* isolate, Handle<Object> key, bool* success); 32 33 inline bool is_element() const; name()34 Handle<Name> name() const { return name_; } index()35 size_t index() const { return index_; } 36 inline Handle<Name> GetName(Isolate* isolate); 37 38 private: 39 Handle<Name> name_; 40 size_t index_; 41 }; 42 43 class V8_EXPORT_PRIVATE LookupIterator final { 44 public: 45 enum Configuration { 46 // Configuration bits. 47 kInterceptor = 1 << 0, 48 kPrototypeChain = 1 << 1, 49 50 // Convenience combinations of bits. 51 OWN_SKIP_INTERCEPTOR = 0, 52 OWN = kInterceptor, 53 PROTOTYPE_CHAIN_SKIP_INTERCEPTOR = kPrototypeChain, 54 PROTOTYPE_CHAIN = kPrototypeChain | kInterceptor, 55 DEFAULT = PROTOTYPE_CHAIN 56 }; 57 58 enum State { 59 ACCESS_CHECK, 60 INTEGER_INDEXED_EXOTIC, 61 INTERCEPTOR, 62 JSPROXY, 63 NOT_FOUND, 64 ACCESSOR, 65 DATA, 66 TRANSITION, 67 // Set state_ to BEFORE_PROPERTY to ensure that the next lookup will be a 68 // PROPERTY lookup. 69 BEFORE_PROPERTY = INTERCEPTOR 70 }; 71 72 // {name} is guaranteed to be a property name (and not e.g. "123"). 73 inline LookupIterator(Isolate* isolate, Handle<Object> receiver, 74 Handle<Name> name, 75 Configuration configuration = DEFAULT); 76 inline LookupIterator(Isolate* isolate, Handle<Object> receiver, 77 Handle<Name> name, Handle<Object> lookup_start_object, 78 Configuration configuration = DEFAULT); 79 80 inline LookupIterator(Isolate* isolate, Handle<Object> receiver, size_t index, 81 Configuration configuration = DEFAULT); 82 inline LookupIterator(Isolate* isolate, Handle<Object> receiver, size_t index, 83 Handle<Object> lookup_start_object, 84 Configuration configuration = DEFAULT); 85 86 inline LookupIterator(Isolate* isolate, Handle<Object> receiver, 87 const PropertyKey& key, 88 Configuration configuration = DEFAULT); 89 inline LookupIterator(Isolate* isolate, Handle<Object> receiver, 90 const PropertyKey& key, 91 Handle<Object> lookup_start_object, 92 Configuration configuration = DEFAULT); 93 Restart()94 void Restart() { 95 InterceptorState state = InterceptorState::kUninitialized; 96 IsElement() ? RestartInternal<true>(state) : RestartInternal<false>(state); 97 } 98 isolate()99 Isolate* isolate() const { return isolate_; } state()100 State state() const { return state_; } 101 102 inline Handle<Name> name() const; 103 inline Handle<Name> GetName(); index()104 size_t index() const { return index_; } array_index()105 uint32_t array_index() const { 106 DCHECK_LE(index_, JSArray::kMaxArrayIndex); 107 return static_cast<uint32_t>(index_); 108 } 109 110 // Returns true if this LookupIterator has an index in the range 111 // [0, size_t::max). IsElement()112 bool IsElement() const { return index_ != kInvalidIndex; } 113 // Returns true if this LookupIterator has an index that counts as an 114 // element for the given object (up to kMaxArrayIndex for JSArrays, 115 // any integer for JSTypedArrays). 116 inline bool IsElement(JSReceiver object) const; 117 IsFound()118 bool IsFound() const { return state_ != NOT_FOUND; } 119 void Next(); NotFound()120 void NotFound() { 121 has_property_ = false; 122 state_ = NOT_FOUND; 123 } 124 heap()125 Heap* heap() const { return isolate_->heap(); } factory()126 Factory* factory() const { return isolate_->factory(); } GetReceiver()127 Handle<Object> GetReceiver() const { return receiver_; } 128 129 template <class T> 130 inline Handle<T> GetStoreTarget() const; 131 inline bool is_dictionary_holder() const; 132 inline Handle<Map> transition_map() const; 133 inline Handle<PropertyCell> transition_cell() const; 134 template <class T> 135 inline Handle<T> GetHolder() const; 136 lookup_start_object()137 Handle<Object> lookup_start_object() const { return lookup_start_object_; } 138 139 bool HolderIsReceiver() const; 140 bool HolderIsReceiverOrHiddenPrototype() const; 141 check_prototype_chain()142 bool check_prototype_chain() const { 143 return (configuration_ & kPrototypeChain) != 0; 144 } 145 146 /* ACCESS_CHECK */ 147 bool HasAccess() const; 148 149 /* PROPERTY */ 150 inline bool ExtendingNonExtensible(Handle<JSReceiver> receiver); 151 void PrepareForDataProperty(Handle<Object> value); 152 void PrepareTransitionToDataProperty(Handle<JSReceiver> receiver, 153 Handle<Object> value, 154 PropertyAttributes attributes, 155 StoreOrigin store_origin); 156 inline bool IsCacheableTransition(); 157 void ApplyTransitionToDataProperty(Handle<JSReceiver> receiver); 158 void ReconfigureDataProperty(Handle<Object> value, 159 PropertyAttributes attributes); 160 void Delete(); 161 void TransitionToAccessorProperty(Handle<Object> getter, 162 Handle<Object> setter, 163 PropertyAttributes attributes); 164 void TransitionToAccessorPair(Handle<Object> pair, 165 PropertyAttributes attributes); property_details()166 PropertyDetails property_details() const { 167 DCHECK(has_property_); 168 return property_details_; 169 } property_attributes()170 PropertyAttributes property_attributes() const { 171 return property_details().attributes(); 172 } IsConfigurable()173 bool IsConfigurable() const { return property_details().IsConfigurable(); } IsReadOnly()174 bool IsReadOnly() const { return property_details().IsReadOnly(); } IsEnumerable()175 bool IsEnumerable() const { return property_details().IsEnumerable(); } representation()176 Representation representation() const { 177 return property_details().representation(); 178 } location()179 PropertyLocation location() const { return property_details().location(); } constness()180 PropertyConstness constness() const { return property_details().constness(); } 181 FieldIndex GetFieldIndex() const; 182 int GetFieldDescriptorIndex() const; 183 int GetAccessorIndex() const; 184 Handle<PropertyCell> GetPropertyCell() const; 185 Handle<Object> GetAccessors() const; 186 inline Handle<InterceptorInfo> GetInterceptor() const; 187 Handle<InterceptorInfo> GetInterceptorForFailedAccessCheck() const; 188 Handle<Object> GetDataValue(AllocationPolicy allocation_policy = 189 AllocationPolicy::kAllocationAllowed) const; 190 void WriteDataValue(Handle<Object> value, bool initializing_store); 191 Handle<Object> GetDataValue(SeqCstAccessTag tag) const; 192 void WriteDataValue(Handle<Object> value, SeqCstAccessTag tag); 193 Handle<Object> SwapDataValue(Handle<Object> value, SeqCstAccessTag tag); 194 inline void UpdateProtector(); 195 static inline void UpdateProtector(Isolate* isolate, Handle<Object> receiver, 196 Handle<Name> name); 197 198 #if V8_ENABLE_WEBASSEMBLY 199 // Fetches type of WasmStruct's field or WasmArray's elements, it 200 // is used for preparing the value for storing into WasmObjects. 201 wasm::ValueType wasm_value_type() const; 202 void WriteDataValueToWasmObject(Handle<Object> value); 203 #endif // V8_ENABLE_WEBASSEMBLY 204 205 // Lookup a 'cached' private property for an accessor. 206 // If not found returns false and leaves the LookupIterator unmodified. 207 bool TryLookupCachedProperty(Handle<AccessorPair> accessor); 208 bool TryLookupCachedProperty(); 209 210 private: 211 friend PropertyKey; 212 213 static const size_t kInvalidIndex = std::numeric_limits<size_t>::max(); 214 215 bool LookupCachedProperty(Handle<AccessorPair> accessor); 216 inline LookupIterator(Isolate* isolate, Handle<Object> receiver, 217 Handle<Name> name, size_t index, 218 Handle<Object> lookup_start_object, 219 Configuration configuration); 220 221 // For |ForTransitionHandler|. 222 LookupIterator(Isolate* isolate, Handle<Object> receiver, Handle<Name> name, 223 Handle<Map> transition_map, PropertyDetails details, 224 bool has_property); 225 226 static void InternalUpdateProtector(Isolate* isolate, Handle<Object> receiver, 227 Handle<Name> name); 228 229 enum class InterceptorState { 230 kUninitialized, 231 kSkipNonMasking, 232 kProcessNonMasking 233 }; 234 235 Handle<Map> GetReceiverMap() const; 236 237 V8_WARN_UNUSED_RESULT inline JSReceiver NextHolder(Map map); 238 is_js_array_element(bool is_element)239 bool is_js_array_element(bool is_element) const { 240 return is_element && index_ <= JSArray::kMaxArrayIndex; 241 } 242 template <bool is_element> 243 V8_EXPORT_PRIVATE void Start(); 244 template <bool is_element> 245 void NextInternal(Map map, JSReceiver holder); 246 template <bool is_element> LookupInHolder(Map map,JSReceiver holder)247 inline State LookupInHolder(Map map, JSReceiver holder) { 248 return map.IsSpecialReceiverMap() 249 ? LookupInSpecialHolder<is_element>(map, holder) 250 : LookupInRegularHolder<is_element>(map, holder); 251 } 252 template <bool is_element> 253 State LookupInRegularHolder(Map map, JSReceiver holder); 254 template <bool is_element> 255 State LookupInSpecialHolder(Map map, JSReceiver holder); 256 template <bool is_element> RestartLookupForNonMaskingInterceptors()257 void RestartLookupForNonMaskingInterceptors() { 258 RestartInternal<is_element>(InterceptorState::kProcessNonMasking); 259 } 260 template <bool is_element> 261 void RestartInternal(InterceptorState interceptor_state); 262 Handle<Object> FetchValue(AllocationPolicy allocation_policy = 263 AllocationPolicy::kAllocationAllowed) const; 264 bool IsConstFieldValueEqualTo(Object value) const; 265 bool IsConstDictValueEqualTo(Object value) const; 266 267 template <bool is_element> 268 void ReloadPropertyInformation(); 269 270 template <bool is_element> 271 bool SkipInterceptor(JSObject holder); 272 template <bool is_element> 273 inline InterceptorInfo GetInterceptor(JSObject holder) const; 274 check_interceptor()275 bool check_interceptor() const { 276 return (configuration_ & kInterceptor) != 0; 277 } 278 inline InternalIndex descriptor_number() const; 279 inline InternalIndex dictionary_entry() const; 280 281 static inline Configuration ComputeConfiguration(Isolate* isolate, 282 Configuration configuration, 283 Handle<Name> name); 284 285 static Handle<JSReceiver> GetRootForNonJSReceiver( 286 Isolate* isolate, Handle<Object> lookup_start_object, 287 size_t index = kInvalidIndex); 288 static inline Handle<JSReceiver> GetRoot(Isolate* isolate, 289 Handle<Object> lookup_start_object, 290 size_t index = kInvalidIndex); 291 292 State NotFound(JSReceiver const holder) const; 293 294 // If configuration_ becomes mutable, update 295 // HolderIsReceiverOrHiddenPrototype. 296 const Configuration configuration_; 297 State state_ = NOT_FOUND; 298 bool has_property_ = false; 299 InterceptorState interceptor_state_ = InterceptorState::kUninitialized; 300 PropertyDetails property_details_ = PropertyDetails::Empty(); 301 Isolate* const isolate_; 302 Handle<Name> name_; 303 Handle<Object> transition_; 304 const Handle<Object> receiver_; 305 Handle<JSReceiver> holder_; 306 const Handle<Object> lookup_start_object_; 307 const size_t index_; 308 InternalIndex number_ = InternalIndex::NotFound(); 309 }; 310 311 // Similar to the LookupIterator, but for concurrent accesses from a background 312 // thread. 313 // 314 // Note: This is a work in progress, intended to bundle code related to 315 // concurrent lookups here. In its current state, the class is obviously not an 316 // 'iterator'. Still, keeping the name for now, with the intent to clarify 317 // names and implementation once we've gotten some experience with more 318 // involved logic. 319 // TODO(jgruber, v8:7790): Consider using a LookupIterator-style interface. 320 // TODO(jgruber, v8:7790): Consider merging back into the LookupIterator once 321 // functionality and constraints are better known. 322 class ConcurrentLookupIterator final : public AllStatic { 323 public: 324 // Tri-state to distinguish between 'not-present' and 'who-knows' failures. 325 enum Result { 326 kPresent, // The value was found. 327 kNotPresent, // No value exists. 328 kGaveUp, // The operation can't be completed. 329 }; 330 331 // Implements the own data property lookup for the specialized case of 332 // fixed_cow_array backing stores (these are only in use for array literal 333 // boilerplates). The contract is that the elements, elements kind, and array 334 // length passed to this function should all be read from the same JSArray 335 // instance; but due to concurrency it's possible that they may not be 336 // consistent among themselves (e.g. the elements kind may not match the 337 // given elements backing store). We are thus extra-careful to handle 338 // exceptional situations. 339 V8_EXPORT_PRIVATE static base::Optional<Object> TryGetOwnCowElement( 340 Isolate* isolate, FixedArray array_elements, ElementsKind elements_kind, 341 int array_length, size_t index); 342 343 // As above, the contract is that the elements and elements kind should be 344 // read from the same holder, but this function is implemented defensively to 345 // tolerate concurrency issues. 346 V8_EXPORT_PRIVATE static Result TryGetOwnConstantElement( 347 Object* result_out, Isolate* isolate, LocalIsolate* local_isolate, 348 JSObject holder, FixedArrayBase elements, ElementsKind elements_kind, 349 size_t index); 350 351 // Implements the own data property lookup for the specialized case of 352 // strings. 353 V8_EXPORT_PRIVATE static Result TryGetOwnChar(String* result_out, 354 Isolate* isolate, 355 LocalIsolate* local_isolate, 356 String string, size_t index); 357 358 // This method reimplements the following sequence in a concurrent setting: 359 // 360 // LookupIterator it(holder, isolate, name, LookupIterator::OWN); 361 // it.TryLookupCachedProperty(); 362 // if (it.state() == LookupIterator::DATA) it.GetPropertyCell(); 363 V8_EXPORT_PRIVATE static base::Optional<PropertyCell> TryGetPropertyCell( 364 Isolate* isolate, LocalIsolate* local_isolate, 365 Handle<JSGlobalObject> holder, Handle<Name> name); 366 }; 367 368 } // namespace internal 369 } // namespace v8 370 371 #endif // V8_OBJECTS_LOOKUP_H_ 372