// Copyright 2014 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_OBJECTS_LOOKUP_H_ #define V8_OBJECTS_LOOKUP_H_ #include "src/common/globals.h" #include "src/execution/isolate.h" #include "src/heap/factory.h" #include "src/objects/descriptor-array.h" #include "src/objects/js-objects.h" #include "src/objects/map.h" #include "src/objects/objects.h" namespace v8 { namespace internal { class V8_EXPORT_PRIVATE LookupIterator final { public: enum Configuration { // Configuration bits. kInterceptor = 1 << 0, kPrototypeChain = 1 << 1, // Convenience combinations of bits. OWN_SKIP_INTERCEPTOR = 0, OWN = kInterceptor, PROTOTYPE_CHAIN_SKIP_INTERCEPTOR = kPrototypeChain, PROTOTYPE_CHAIN = kPrototypeChain | kInterceptor, DEFAULT = PROTOTYPE_CHAIN }; enum State { ACCESS_CHECK, INTEGER_INDEXED_EXOTIC, INTERCEPTOR, JSPROXY, NOT_FOUND, ACCESSOR, DATA, TRANSITION, // Set state_ to BEFORE_PROPERTY to ensure that the next lookup will be a // PROPERTY lookup. BEFORE_PROPERTY = INTERCEPTOR }; class Key { public: inline Key(Isolate* isolate, double index); // {name} might be a string representation of an element index. inline Key(Isolate* isolate, Handle name); // {valid_key} is a Name or Number. inline Key(Isolate* isolate, Handle valid_key); // {key} could be anything. Key(Isolate* isolate, Handle key, bool* success); bool is_element() { return index_ != LookupIterator::kInvalidIndex; } Handle name() const { return name_; } size_t index() const { return index_; } inline Handle GetName(Isolate* isolate); private: Handle name_; size_t index_; }; // {name} is guaranteed to be a property name (and not e.g. "123"). inline LookupIterator(Isolate* isolate, Handle receiver, Handle name, Configuration configuration = DEFAULT); inline LookupIterator(Isolate* isolate, Handle receiver, Handle name, Handle lookup_start_object, Configuration configuration = DEFAULT); inline LookupIterator(Isolate* isolate, Handle receiver, size_t index, Configuration configuration = DEFAULT); inline LookupIterator(Isolate* isolate, Handle receiver, size_t index, Handle lookup_start_object, Configuration configuration = DEFAULT); inline LookupIterator(Isolate* isolate, Handle receiver, const Key& key, Configuration configuration = DEFAULT); inline LookupIterator(Isolate* isolate, Handle receiver, const Key& key, Handle lookup_start_object, Configuration configuration = DEFAULT); void Restart() { InterceptorState state = InterceptorState::kUninitialized; IsElement() ? RestartInternal(state) : RestartInternal(state); } Isolate* isolate() const { return isolate_; } State state() const { return state_; } inline Handle name() const; inline Handle GetName(); size_t index() const { return index_; } uint32_t array_index() const { DCHECK_LE(index_, JSArray::kMaxArrayIndex); return static_cast(index_); } // Returns true if this LookupIterator has an index in the range // [0, size_t::max). bool IsElement() const { return index_ != kInvalidIndex; } // Returns true if this LookupIterator has an index that counts as an // element for the given object (up to kMaxArrayIndex for JSArrays, // any integer for JSTypedArrays). inline bool IsElement(JSReceiver object) const; bool IsFound() const { return state_ != NOT_FOUND; } void Next(); void NotFound() { has_property_ = false; state_ = NOT_FOUND; } Heap* heap() const { return isolate_->heap(); } Factory* factory() const { return isolate_->factory(); } Handle GetReceiver() const { return receiver_; } template inline Handle GetStoreTarget() const; inline bool is_dictionary_holder() const; inline Handle transition_map() const; inline Handle transition_cell() const; template inline Handle GetHolder() const; Handle lookup_start_object() const { return lookup_start_object_; } bool HolderIsReceiver() const; bool HolderIsReceiverOrHiddenPrototype() const; bool check_prototype_chain() const { return (configuration_ & kPrototypeChain) != 0; } /* ACCESS_CHECK */ bool HasAccess() const; /* PROPERTY */ inline bool ExtendingNonExtensible(Handle receiver); void PrepareForDataProperty(Handle value); void PrepareTransitionToDataProperty(Handle receiver, Handle value, PropertyAttributes attributes, StoreOrigin store_origin); inline bool IsCacheableTransition(); void ApplyTransitionToDataProperty(Handle receiver); void ReconfigureDataProperty(Handle value, PropertyAttributes attributes); void Delete(); void TransitionToAccessorProperty(Handle getter, Handle setter, PropertyAttributes attributes); void TransitionToAccessorPair(Handle pair, PropertyAttributes attributes); PropertyDetails property_details() const { DCHECK(has_property_); return property_details_; } PropertyAttributes property_attributes() const { return property_details().attributes(); } bool IsConfigurable() const { return property_details().IsConfigurable(); } bool IsReadOnly() const { return property_details().IsReadOnly(); } bool IsEnumerable() const { return property_details().IsEnumerable(); } Representation representation() const { return property_details().representation(); } PropertyLocation location() const { return property_details().location(); } PropertyConstness constness() const { return property_details().constness(); } FieldIndex GetFieldIndex() const; int GetFieldDescriptorIndex() const; int GetAccessorIndex() const; Handle GetPropertyCell() const; Handle GetAccessors() const; inline Handle GetInterceptor() const; Handle GetInterceptorForFailedAccessCheck() const; Handle GetDataValue(AllocationPolicy allocation_policy = AllocationPolicy::kAllocationAllowed) const; void WriteDataValue(Handle value, bool initializing_store); inline void UpdateProtector(); static inline void UpdateProtector(Isolate* isolate, Handle receiver, Handle name); // Lookup a 'cached' private property for an accessor. // If not found returns false and leaves the LookupIterator unmodified. bool TryLookupCachedProperty(Handle accessor); bool TryLookupCachedProperty(); private: static const size_t kInvalidIndex = std::numeric_limits::max(); bool LookupCachedProperty(Handle accessor); inline LookupIterator(Isolate* isolate, Handle receiver, Handle name, size_t index, Handle lookup_start_object, Configuration configuration); // For |ForTransitionHandler|. LookupIterator(Isolate* isolate, Handle receiver, Handle name, Handle transition_map, PropertyDetails details, bool has_property); static void InternalUpdateProtector(Isolate* isolate, Handle receiver, Handle name); enum class InterceptorState { kUninitialized, kSkipNonMasking, kProcessNonMasking }; Handle GetReceiverMap() const; V8_WARN_UNUSED_RESULT inline JSReceiver NextHolder(Map map); bool is_js_array_element(bool is_element) const { return is_element && index_ <= JSArray::kMaxArrayIndex; } template V8_EXPORT_PRIVATE void Start(); template void NextInternal(Map map, JSReceiver holder); template inline State LookupInHolder(Map map, JSReceiver holder) { return map.IsSpecialReceiverMap() ? LookupInSpecialHolder(map, holder) : LookupInRegularHolder(map, holder); } template State LookupInRegularHolder(Map map, JSReceiver holder); template State LookupInSpecialHolder(Map map, JSReceiver holder); template void RestartLookupForNonMaskingInterceptors() { RestartInternal(InterceptorState::kProcessNonMasking); } template void RestartInternal(InterceptorState interceptor_state); Handle FetchValue(AllocationPolicy allocation_policy = AllocationPolicy::kAllocationAllowed) const; bool IsConstFieldValueEqualTo(Object value) const; bool IsConstDictValueEqualTo(Object value) const; template void ReloadPropertyInformation(); template bool SkipInterceptor(JSObject holder); template inline InterceptorInfo GetInterceptor(JSObject holder) const; bool check_interceptor() const { return (configuration_ & kInterceptor) != 0; } inline InternalIndex descriptor_number() const; inline InternalIndex dictionary_entry() const; static inline Configuration ComputeConfiguration(Isolate* isolate, Configuration configuration, Handle name); static Handle GetRootForNonJSReceiver( Isolate* isolate, Handle lookup_start_object, size_t index = kInvalidIndex); static inline Handle GetRoot(Isolate* isolate, Handle lookup_start_object, size_t index = kInvalidIndex); State NotFound(JSReceiver const holder) const; // If configuration_ becomes mutable, update // HolderIsReceiverOrHiddenPrototype. const Configuration configuration_; State state_ = NOT_FOUND; bool has_property_ = false; InterceptorState interceptor_state_ = InterceptorState::kUninitialized; PropertyDetails property_details_ = PropertyDetails::Empty(); Isolate* const isolate_; Handle name_; Handle transition_; const Handle receiver_; Handle holder_; const Handle lookup_start_object_; const size_t index_; InternalIndex number_ = InternalIndex::NotFound(); }; // Similar to the LookupIterator, but for concurrent accesses from a background // thread. // // Note: This is a work in progress, intended to bundle code related to // concurrent lookups here. In its current state, the class is obviously not an // 'iterator'. Still, keeping the name for now, with the intent to clarify // names and implementation once we've gotten some experience with more // involved logic. // TODO(jgruber, v8:7790): Consider using a LookupIterator-style interface. // TODO(jgruber, v8:7790): Consider merging back into the LookupIterator once // functionality and constraints are better known. class ConcurrentLookupIterator final : public AllStatic { public: // Tri-state to distinguish between 'not-present' and 'who-knows' failures. enum Result { kPresent, // The value was found. kNotPresent, // No value exists. kGaveUp, // The operation can't be completed. }; // Implements the own data property lookup for the specialized case of // fixed_cow_array backing stores (these are only in use for array literal // boilerplates). The contract is that the elements, elements kind, and array // length passed to this function should all be read from the same JSArray // instance; but due to concurrency it's possible that they may not be // consistent among themselves (e.g. the elements kind may not match the // given elements backing store). We are thus extra-careful to handle // exceptional situations. V8_EXPORT_PRIVATE static base::Optional TryGetOwnCowElement( Isolate* isolate, FixedArray array_elements, ElementsKind elements_kind, int array_length, size_t index); // Unlike above, the contract is that holder, elements, and elements_kind are // a consistent view of the world; and index must be a valid element index. V8_EXPORT_PRIVATE static Result TryGetOwnConstantElement( Object* result_out, Isolate* isolate, LocalIsolate* local_isolate, JSObject holder, FixedArrayBase elements, ElementsKind elements_kind, size_t index); }; } // namespace internal } // namespace v8 #endif // V8_OBJECTS_LOOKUP_H_