// Copyright 2017 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_DICTIONARY_H_ #define V8_OBJECTS_DICTIONARY_H_ #include "src/objects/hash-table.h" #include "src/base/export-template.h" #include "src/globals.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" namespace v8 { namespace internal { template class Handle; class Isolate; template class Dictionary : public HashTable { typedef HashTable DerivedHashTable; public: // Returns the value at entry. Object* ValueAt(int entry) { return this->get(Derived::EntryToIndex(entry) + 1); } // Set the value for entry. void ValueAtPut(int entry, Object* value) { this->set(Derived::EntryToIndex(entry) + 1, value); } // Returns the property details for the property at entry. PropertyDetails DetailsAt(int entry) { return Shape::DetailsAt(static_cast(this), entry); } // Set the details for entry. void DetailsAtPut(int entry, PropertyDetails value) { Shape::DetailsAtPut(static_cast(this), entry, value); } // Returns true if property at given entry is deleted. bool IsDeleted(int entry) { return Shape::IsDeleted(static_cast(this), entry); } // Delete a property from the dictionary. static Handle DeleteProperty(Handle dictionary, int entry); // Attempt to shrink the dictionary after deletion of key. MUST_USE_RESULT static inline Handle Shrink( Handle dictionary, Key key) { return DerivedHashTable::Shrink(dictionary, key); } // Returns the number of elements in the dictionary filtering out properties // with the specified attributes. int NumberOfElementsFilterAttributes(PropertyFilter filter); // Returns the number of enumerable elements in the dictionary. int NumberOfEnumElements() { return NumberOfElementsFilterAttributes(ENUMERABLE_STRINGS); } enum SortMode { UNSORTED, SORTED }; // Return the key indices sorted by its enumeration index. static Handle IterationIndices( Handle> dictionary); // Collect the keys into the given KeyAccumulator, in ascending chronological // order of property creation. static void CollectKeysTo(Handle> dictionary, KeyAccumulator* keys); // Copies enumerable keys to preallocated fixed array. static void CopyEnumKeysTo(Handle> dictionary, Handle storage, KeyCollectionMode mode, KeyAccumulator* accumulator); // Accessors for next enumeration index. void SetNextEnumerationIndex(int index) { DCHECK(index != 0); this->set(kNextEnumerationIndexIndex, Smi::FromInt(index)); } int NextEnumerationIndex() { return Smi::cast(this->get(kNextEnumerationIndexIndex))->value(); } // Creates a new dictionary. MUST_USE_RESULT static Handle New( Isolate* isolate, int at_least_space_for, PretenureFlag pretenure = NOT_TENURED, MinimumCapacity capacity_option = USE_DEFAULT_MINIMUM_CAPACITY); // Creates an dictionary with minimal possible capacity. MUST_USE_RESULT static Handle NewEmpty( Isolate* isolate, PretenureFlag pretenure = NOT_TENURED); // Ensures that a new dictionary is created when the capacity is checked. void SetRequiresCopyOnCapacityChange(); // Ensure enough space for n additional elements. static Handle EnsureCapacity(Handle obj, int n, Key key); #ifdef OBJECT_PRINT // For our gdb macros, we should perhaps change these in the future. void Print(); void Print(std::ostream& os); // NOLINT #endif // Returns the key (slow). Object* SlowReverseLookup(Object* value); // Sets the entry to (key, value) pair. inline void SetEntry(int entry, Handle key, Handle value); inline void SetEntry(int entry, Handle key, Handle value, PropertyDetails details); MUST_USE_RESULT static Handle Add(Handle dictionary, Key key, Handle value, PropertyDetails details, int* entry_out = nullptr); static const int kMaxNumberKeyIndex = DerivedHashTable::kPrefixStartIndex; static const int kNextEnumerationIndexIndex = kMaxNumberKeyIndex + 1; static const bool kIsEnumerable = Shape::kIsEnumerable; protected: // Generic at put operation. MUST_USE_RESULT static Handle AtPut(Handle dictionary, Key key, Handle value); // Add entry to dictionary. Returns entry value. static int AddEntry(Handle dictionary, Key key, Handle value, PropertyDetails details, uint32_t hash); }; template class NameDictionaryBase : public Dictionary> { typedef Dictionary> DerivedDictionary; public: // Find entry for key, otherwise return kNotFound. Optimized version of // HashTable::FindEntry. int FindEntry(Handle key); }; template class BaseDictionaryShape : public BaseShape { public: template static inline PropertyDetails DetailsAt(Dictionary* dict, int entry) { STATIC_ASSERT(Dictionary::kEntrySize == 3); DCHECK(entry >= 0); // Not found is -1, which is not caught by get(). return PropertyDetails(Smi::cast(dict->get( Dictionary::EntryToIndex(entry) + Dictionary::kEntryDetailsIndex))); } template static inline void DetailsAtPut(Dictionary* dict, int entry, PropertyDetails value) { STATIC_ASSERT(Dictionary::kEntrySize == 3); dict->set(Dictionary::EntryToIndex(entry) + Dictionary::kEntryDetailsIndex, value.AsSmi()); } template static bool IsDeleted(Dictionary* dict, int entry) { return false; } template static inline void SetEntry(Dictionary* dict, int entry, Handle key, Handle value, PropertyDetails details); }; class NameDictionaryShape : public BaseDictionaryShape> { public: static inline bool IsMatch(Handle key, Object* other); static inline uint32_t Hash(Handle key); static inline uint32_t HashForObject(Handle key, Object* object); static inline Handle AsHandle(Isolate* isolate, Handle key); static const int kPrefixSize = 2; static const int kEntrySize = 3; static const int kEntryValueIndex = 1; static const int kEntryDetailsIndex = 2; static const bool kIsEnumerable = true; }; class NameDictionary : public NameDictionaryBase { typedef NameDictionaryBase DerivedDictionary; public: DECLARE_CAST(NameDictionary) static const int kEntryValueIndex = 1; static const int kEntryDetailsIndex = 2; static const int kInitialCapacity = 2; }; class GlobalDictionaryShape : public NameDictionaryShape { public: static const int kEntrySize = 2; // Overrides NameDictionaryShape::kEntrySize template static inline PropertyDetails DetailsAt(Dictionary* dict, int entry); template static inline void DetailsAtPut(Dictionary* dict, int entry, PropertyDetails value); template static bool IsDeleted(Dictionary* dict, int entry); template static inline void SetEntry(Dictionary* dict, int entry, Handle key, Handle value, PropertyDetails details); }; class GlobalDictionary : public NameDictionaryBase { public: DECLARE_CAST(GlobalDictionary) static const int kEntryValueIndex = 1; }; class NumberDictionaryShape : public BaseDictionaryShape { public: static inline bool IsMatch(uint32_t key, Object* other); static inline Handle AsHandle(Isolate* isolate, uint32_t key); static const bool kIsEnumerable = false; }; class SeededNumberDictionaryShape : public NumberDictionaryShape { public: static const bool UsesSeed = true; static const int kPrefixSize = 2; static const int kEntrySize = 3; static inline uint32_t SeededHash(uint32_t key, uint32_t seed); static inline uint32_t SeededHashForObject(uint32_t key, uint32_t seed, Object* object); }; class UnseededNumberDictionaryShape : public NumberDictionaryShape { public: static const int kPrefixSize = 0; static const int kEntrySize = 2; static inline uint32_t Hash(uint32_t key); static inline uint32_t HashForObject(uint32_t key, Object* object); template static inline PropertyDetails DetailsAt(Dictionary* dict, int entry) { UNREACHABLE(); return PropertyDetails::Empty(); } template static inline void DetailsAtPut(Dictionary* dict, int entry, PropertyDetails value) { UNREACHABLE(); } static inline Map* GetMap(Isolate* isolate); }; extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) HashTable; extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Dictionary; class SeededNumberDictionary : public Dictionary { public: DECLARE_CAST(SeededNumberDictionary) // Type specific at put (default NONE attributes is used when adding). MUST_USE_RESULT static Handle AtNumberPut( Handle dictionary, uint32_t key, Handle value, Handle dictionary_holder); MUST_USE_RESULT static Handle AddNumberEntry( Handle dictionary, uint32_t key, Handle value, PropertyDetails details, Handle dictionary_holder); // Set an existing entry or add a new one if needed. // Return the updated dictionary. MUST_USE_RESULT static Handle Set( Handle dictionary, uint32_t key, Handle value, PropertyDetails details, Handle dictionary_holder); void UpdateMaxNumberKey(uint32_t key, Handle dictionary_holder); // Returns true if the dictionary contains any elements that are non-writable, // non-configurable, non-enumerable, or have getters/setters. bool HasComplexElements(); // Sorting support void CopyValuesTo(FixedArray* elements); // If slow elements are required we will never go back to fast-case // for the elements kept in this dictionary. We require slow // elements if an element has been added at an index larger than // kRequiresSlowElementsLimit or set_requires_slow_elements() has been called // when defining a getter or setter with a number key. inline bool requires_slow_elements(); inline void set_requires_slow_elements(); // Get the value of the max number key that has been added to this // dictionary. max_number_key can only be called if // requires_slow_elements returns false. inline uint32_t max_number_key(); static const int kEntryValueIndex = 1; static const int kEntryDetailsIndex = 2; // Bit masks. static const int kRequiresSlowElementsMask = 1; static const int kRequiresSlowElementsTagSize = 1; static const uint32_t kRequiresSlowElementsLimit = (1 << 29) - 1; // JSObjects prefer dictionary elements if the dictionary saves this much // memory compared to a fast elements backing store. static const uint32_t kPreferFastElementsSizeFactor = 3; }; class UnseededNumberDictionary : public Dictionary { public: DECLARE_CAST(UnseededNumberDictionary) // Type specific at put (default NONE attributes is used when adding). MUST_USE_RESULT static Handle AtNumberPut( Handle dictionary, uint32_t key, Handle value); MUST_USE_RESULT static Handle AddNumberEntry( Handle dictionary, uint32_t key, Handle value); static Handle DeleteKey( Handle dictionary, uint32_t key); // Set an existing entry or add a new one if needed. // Return the updated dictionary. MUST_USE_RESULT static Handle Set( Handle dictionary, uint32_t key, Handle value); static const int kEntryValueIndex = 1; static const int kEntryDetailsIndex = 2; }; } // namespace internal } // namespace v8 #include "src/objects/object-macros-undef.h" #endif // V8_OBJECTS_DICTIONARY_H_