1 // Copyright 2015 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_UTILS_IDENTITY_MAP_H_ 6 #define V8_UTILS_IDENTITY_MAP_H_ 7 8 #include <type_traits> 9 10 #include "src/base/functional.h" 11 #include "src/handles/handles.h" 12 #include "src/objects/heap-object.h" 13 14 namespace v8 { 15 namespace internal { 16 17 // Forward declarations. 18 class Heap; 19 class StrongRootsEntry; 20 21 template <typename T> 22 struct IdentityMapFindResult { 23 T* entry; 24 bool already_exists; 25 }; 26 27 // Base class of identity maps contains shared code for all template 28 // instantions. 29 class V8_EXPORT_PRIVATE IdentityMapBase { 30 public: 31 IdentityMapBase(const IdentityMapBase&) = delete; 32 IdentityMapBase& operator=(const IdentityMapBase&) = delete; empty()33 bool empty() const { return size_ == 0; } size()34 int size() const { return size_; } capacity()35 int capacity() const { return capacity_; } is_iterable()36 bool is_iterable() const { return is_iterable_; } 37 38 protected: 39 // Allow Tester to access internals, including changing the address of objects 40 // within the {keys_} array in order to simulate a moving GC. 41 friend class IdentityMapTester; 42 43 using RawEntry = uintptr_t*; 44 IdentityMapBase(Heap * heap)45 explicit IdentityMapBase(Heap* heap) 46 : heap_(heap), 47 gc_counter_(-1), 48 size_(0), 49 capacity_(0), 50 mask_(0), 51 keys_(nullptr), 52 strong_roots_entry_(nullptr), 53 values_(nullptr), 54 is_iterable_(false) {} 55 virtual ~IdentityMapBase(); 56 57 IdentityMapFindResult<uintptr_t> FindOrInsertEntry(Address key); 58 RawEntry FindEntry(Address key) const; 59 RawEntry InsertEntry(Address key); 60 bool DeleteEntry(Address key, uintptr_t* deleted_value); 61 void Clear(); 62 63 Address KeyAtIndex(int index) const; 64 65 RawEntry EntryAtIndex(int index) const; 66 int NextIndex(int index) const; 67 68 void EnableIteration(); 69 void DisableIteration(); 70 71 virtual uintptr_t* NewPointerArray(size_t length) = 0; 72 virtual void DeletePointerArray(uintptr_t* array, size_t length) = 0; 73 74 private: 75 // Internal implementation should not be called directly by subclasses. 76 int ScanKeysFor(Address address, uint32_t hash) const; 77 std::pair<int, bool> InsertKey(Address address, uint32_t hash); 78 int Lookup(Address key) const; 79 std::pair<int, bool> LookupOrInsert(Address key); 80 bool DeleteIndex(int index, uintptr_t* deleted_value); 81 void Rehash(); 82 void Resize(int new_capacity); 83 uint32_t Hash(Address address) const; 84 85 base::hash<uintptr_t> hasher_; 86 Heap* heap_; 87 int gc_counter_; 88 int size_; 89 int capacity_; 90 int mask_; 91 Address* keys_; 92 StrongRootsEntry* strong_roots_entry_; 93 uintptr_t* values_; 94 bool is_iterable_; 95 }; 96 97 // Implements an identity map from object addresses to a given value type {V}. 98 // The map is robust w.r.t. garbage collection by synchronization with the 99 // supplied {heap}. 100 // * Keys are treated as strong roots. 101 // * The value type {V} must be reinterpret_cast'able to {uintptr_t} 102 // * The value type {V} must not be a heap type. 103 template <typename V, class AllocationPolicy> 104 class IdentityMap : public IdentityMapBase { 105 public: 106 STATIC_ASSERT(sizeof(V) <= sizeof(uintptr_t)); 107 STATIC_ASSERT(std::is_trivially_copyable<V>::value); 108 STATIC_ASSERT(std::is_trivially_destructible<V>::value); 109 110 explicit IdentityMap(Heap* heap, 111 AllocationPolicy allocator = AllocationPolicy()) IdentityMapBase(heap)112 : IdentityMapBase(heap), allocator_(allocator) {} 113 IdentityMap(const IdentityMap&) = delete; 114 IdentityMap& operator=(const IdentityMap&) = delete; ~IdentityMap()115 ~IdentityMap() override { Clear(); } 116 117 // Searches this map for the given key using the object's address 118 // as the identity, returning: 119 // found => a pointer to the storage location for the value, true 120 // not found => a pointer to a new storage location for the value, false FindOrInsert(Handle<Object> key)121 IdentityMapFindResult<V> FindOrInsert(Handle<Object> key) { 122 return FindOrInsert(*key); 123 } FindOrInsert(Object key)124 IdentityMapFindResult<V> FindOrInsert(Object key) { 125 auto raw = FindOrInsertEntry(key.ptr()); 126 return {reinterpret_cast<V*>(raw.entry), raw.already_exists}; 127 } 128 129 // Searches this map for the given key using the object's address 130 // as the identity, returning: 131 // found => a pointer to the storage location for the value 132 // not found => {nullptr} Find(Handle<Object> key)133 V* Find(Handle<Object> key) const { return Find(*key); } Find(Object key)134 V* Find(Object key) const { 135 return reinterpret_cast<V*>(FindEntry(key.ptr())); 136 } 137 138 // Insert the value for the given key. The key must not have previously 139 // existed. Insert(Handle<Object> key,V v)140 void Insert(Handle<Object> key, V v) { Insert(*key, v); } Insert(Object key,V v)141 void Insert(Object key, V v) { 142 *reinterpret_cast<V*>(InsertEntry(key.ptr())) = v; 143 } 144 Delete(Handle<Object> key,V * deleted_value)145 bool Delete(Handle<Object> key, V* deleted_value) { 146 return Delete(*key, deleted_value); 147 } Delete(Object key,V * deleted_value)148 bool Delete(Object key, V* deleted_value) { 149 uintptr_t v; 150 bool deleted_something = DeleteEntry(key.ptr(), &v); 151 if (deleted_value != nullptr && deleted_something) { 152 *deleted_value = *reinterpret_cast<V*>(&v); 153 } 154 return deleted_something; 155 } 156 157 // Removes all elements from the map. Clear()158 void Clear() { IdentityMapBase::Clear(); } 159 160 // Iterator over IdentityMap. The IteratableScope used to create this Iterator 161 // must be live for the duration of the iteration. 162 class Iterator { 163 public: 164 Iterator& operator++() { 165 index_ = map_->NextIndex(index_); 166 return *this; 167 } 168 key()169 Object key() const { return Object(map_->KeyAtIndex(index_)); } entry()170 V* entry() const { 171 return reinterpret_cast<V*>(map_->EntryAtIndex(index_)); 172 } 173 174 V* operator*() { return entry(); } 175 V* operator->() { return entry(); } 176 bool operator!=(const Iterator& other) { return index_ != other.index_; } 177 bool operator==(const Iterator& other) { return index_ == other.index_; } 178 179 private: Iterator(IdentityMap * map,int index)180 Iterator(IdentityMap* map, int index) : map_(map), index_(index) {} 181 182 IdentityMap* map_; 183 int index_; 184 185 friend class IdentityMap; 186 }; 187 188 class V8_NODISCARD IteratableScope { 189 public: IteratableScope(IdentityMap * map)190 explicit IteratableScope(IdentityMap* map) : map_(map) { 191 CHECK(!map_->is_iterable()); 192 map_->EnableIteration(); 193 } 194 IteratableScope(const IteratableScope&) = delete; 195 IteratableScope& operator=(const IteratableScope&) = delete; ~IteratableScope()196 ~IteratableScope() { 197 CHECK(map_->is_iterable()); 198 map_->DisableIteration(); 199 } 200 begin()201 Iterator begin() { return Iterator(map_, map_->NextIndex(-1)); } end()202 Iterator end() { return Iterator(map_, map_->capacity()); } 203 204 private: 205 IdentityMap* map_; 206 }; 207 208 protected: 209 // This struct is just a type tag for Zone::NewArray<T>(size_t) call. 210 struct Buffer {}; 211 212 // TODO(ishell): consider removing virtual methods in favor of combining 213 // IdentityMapBase and IdentityMap into one class. This would also save 214 // space when sizeof(V) is less than sizeof(uintptr_t). NewPointerArray(size_t length)215 uintptr_t* NewPointerArray(size_t length) override { 216 return allocator_.template NewArray<uintptr_t, Buffer>(length); 217 } DeletePointerArray(uintptr_t * array,size_t length)218 void DeletePointerArray(uintptr_t* array, size_t length) override { 219 allocator_.template DeleteArray<uintptr_t, Buffer>(array, length); 220 } 221 222 private: 223 AllocationPolicy allocator_; 224 }; 225 226 } // namespace internal 227 } // namespace v8 228 229 #endif // V8_UTILS_IDENTITY_MAP_H_ 230