• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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_HASH_TABLE_H_
6 #define V8_OBJECTS_HASH_TABLE_H_
7 
8 #include "src/base/compiler-specific.h"
9 #include "src/base/export-template.h"
10 #include "src/base/macros.h"
11 #include "src/common/globals.h"
12 #include "src/execution/isolate-utils.h"
13 #include "src/objects/fixed-array.h"
14 #include "src/objects/smi.h"
15 #include "src/roots/roots.h"
16 
17 // Has to be the last include (doesn't have include guards):
18 #include "src/objects/object-macros.h"
19 
20 namespace v8 {
21 namespace internal {
22 
23 // HashTable is a subclass of FixedArray that implements a hash table
24 // that uses open addressing and quadratic probing.
25 //
26 // In order for the quadratic probing to work, elements that have not
27 // yet been used and elements that have been deleted are
28 // distinguished.  Probing continues when deleted elements are
29 // encountered and stops when unused elements are encountered.
30 //
31 // - Elements with key == undefined have not been used yet.
32 // - Elements with key == the_hole have been deleted.
33 //
34 // The hash table class is parameterized with a Shape.
35 // Shape must be a class with the following interface:
36 //   class ExampleShape {
37 //    public:
38 //     // Tells whether key matches other.
39 //     static bool IsMatch(Key key, Object other);
40 //     // Returns the hash value for key.
41 //     static uint32_t Hash(ReadOnlyRoots roots, Key key);
42 //     // Returns the hash value for object.
43 //     static uint32_t HashForObject(ReadOnlyRoots roots, Object object);
44 //     // Convert key to an object.
45 //     static inline Handle<Object> AsHandle(Isolate* isolate, Key key);
46 //     // The prefix size indicates number of elements in the beginning
47 //     // of the backing storage.
48 //     static const int kPrefixSize = ..;
49 //     // The Element size indicates number of elements per entry.
50 //     static const int kEntrySize = ..;
51 //     // Indicates whether IsMatch can deal with other being the_hole (a
52 //     // deleted entry).
53 //     static const bool kMatchNeedsHoleCheck = ..;
54 //   };
55 // The prefix size indicates an amount of memory in the
56 // beginning of the backing storage that can be used for non-element
57 // information by subclasses.
58 
59 template <typename KeyT>
60 class V8_EXPORT_PRIVATE BaseShape {
61  public:
62   using Key = KeyT;
Unwrap(Object key)63   static Object Unwrap(Object key) { return key; }
64 };
65 
NON_EXPORTED_BASE(FixedArray)66 class V8_EXPORT_PRIVATE HashTableBase : public NON_EXPORTED_BASE(FixedArray) {
67  public:
68   // Returns the number of elements in the hash table.
69   inline int NumberOfElements() const;
70 
71   // Returns the number of deleted elements in the hash table.
72   inline int NumberOfDeletedElements() const;
73 
74   // Returns the capacity of the hash table.
75   inline int Capacity() const;
76 
77   inline InternalIndex::Range IterateEntries() const;
78 
79   // ElementAdded should be called whenever an element is added to a
80   // hash table.
81   inline void ElementAdded();
82 
83   // ElementRemoved should be called whenever an element is removed from
84   // a hash table.
85   inline void ElementRemoved();
86   inline void ElementsRemoved(int n);
87 
88   // Computes the required capacity for a table holding the given
89   // number of elements. May be more than HashTable::kMaxCapacity.
90   static inline int ComputeCapacity(int at_least_space_for);
91 
92   static const int kNumberOfElementsIndex = 0;
93   static const int kNumberOfDeletedElementsIndex = 1;
94   static const int kCapacityIndex = 2;
95   static const int kPrefixStartIndex = 3;
96 
97   // Minimum capacity for newly created hash tables.
98   static const int kMinCapacity = 4;
99 
100  protected:
101   // Update the number of elements in the hash table.
102   inline void SetNumberOfElements(int nof);
103 
104   // Update the number of deleted elements in the hash table.
105   inline void SetNumberOfDeletedElements(int nod);
106 
107   // Returns probe entry.
108   inline static InternalIndex FirstProbe(uint32_t hash, uint32_t size) {
109     return InternalIndex(hash & (size - 1));
110   }
111 
112   inline static InternalIndex NextProbe(InternalIndex last, uint32_t number,
113                                         uint32_t size) {
114     return InternalIndex((last.as_uint32() + number) & (size - 1));
115   }
116 
117   OBJECT_CONSTRUCTORS(HashTableBase, FixedArray);
118 };
119 
120 template <typename Derived, typename Shape>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)121 class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) HashTable
122     : public HashTableBase {
123  public:
124   using ShapeT = Shape;
125   using Key = typename Shape::Key;
126 
127   // Returns a new HashTable object.
128   template <typename LocalIsolate>
129   V8_WARN_UNUSED_RESULT static Handle<Derived> New(
130       LocalIsolate* isolate, int at_least_space_for,
131       AllocationType allocation = AllocationType::kYoung,
132       MinimumCapacity capacity_option = USE_DEFAULT_MINIMUM_CAPACITY);
133 
134   static inline Handle<Map> GetMap(ReadOnlyRoots roots);
135 
136   // Garbage collection support.
137   void IteratePrefix(ObjectVisitor* visitor);
138   void IterateElements(ObjectVisitor* visitor);
139 
140   // Find entry for key otherwise return kNotFound.
141   inline InternalIndex FindEntry(IsolateRoot isolate, ReadOnlyRoots roots,
142                                  Key key, int32_t hash);
143   template <typename LocalIsolate>
144   inline InternalIndex FindEntry(LocalIsolate* isolate, Key key);
145 
146   // Rehashes the table in-place.
147   void Rehash(IsolateRoot isolate);
148 
149   // Returns whether k is a real key.  The hole and undefined are not allowed as
150   // keys and can be used to indicate missing or deleted elements.
151   static inline bool IsKey(ReadOnlyRoots roots, Object k);
152 
153   inline bool ToKey(ReadOnlyRoots roots, InternalIndex entry, Object* out_k);
154   inline bool ToKey(IsolateRoot isolate, InternalIndex entry, Object* out_k);
155 
156   // Returns the key at entry.
157   inline Object KeyAt(InternalIndex entry);
158   inline Object KeyAt(IsolateRoot isolate, InternalIndex entry);
159 
160   static const int kElementsStartIndex = kPrefixStartIndex + Shape::kPrefixSize;
161   static const int kEntrySize = Shape::kEntrySize;
162   STATIC_ASSERT(kEntrySize > 0);
163   static const int kEntryKeyIndex = 0;
164   static const int kElementsStartOffset =
165       kHeaderSize + kElementsStartIndex * kTaggedSize;
166   // Maximal capacity of HashTable. Based on maximal length of underlying
167   // FixedArray. Staying below kMaxCapacity also ensures that EntryToIndex
168   // cannot overflow.
169   static const int kMaxCapacity =
170       (FixedArray::kMaxLength - kElementsStartIndex) / kEntrySize;
171 
172   // Don't shrink a HashTable below this capacity.
173   static const int kMinShrinkCapacity = 16;
174 
175   // Pretenure hashtables above this capacity.
176   static const int kMinCapacityForPretenure = 256;
177 
178   static const int kMaxRegularCapacity = kMaxRegularHeapObjectSize / 32;
179 
180   // Returns the index for an entry (of the key)
181   static constexpr inline int EntryToIndex(InternalIndex entry) {
182     return (entry.as_int() * kEntrySize) + kElementsStartIndex;
183   }
184 
185   // Returns the entry for an index (of the key)
186   static constexpr inline InternalIndex IndexToEntry(int index) {
187     return InternalIndex((index - kElementsStartIndex) / kEntrySize);
188   }
189 
190   // Returns the index for a slot address in the object.
191   static constexpr inline int SlotToIndex(Address object, Address slot) {
192     return static_cast<int>((slot - object - kHeaderSize) / kTaggedSize);
193   }
194 
195   // Ensure enough space for n additional elements.
196   template <typename LocalIsolate>
197   V8_WARN_UNUSED_RESULT static Handle<Derived> EnsureCapacity(
198       LocalIsolate* isolate, Handle<Derived> table, int n = 1,
199       AllocationType allocation = AllocationType::kYoung);
200 
201   // Returns true if this table has sufficient capacity for adding n elements.
202   bool HasSufficientCapacityToAdd(int number_of_additional_elements);
203 
204   // Returns true if a table with the given parameters has sufficient capacity
205   // for adding n elements. Can be used to check hypothetical capacities without
206   // actually allocating a table with that capacity.
207   static bool HasSufficientCapacityToAdd(int capacity, int number_of_elements,
208                                          int number_of_deleted_elements,
209                                          int number_of_additional_elements);
210 
211  protected:
212   friend class ObjectHashTable;
213 
214   template <typename LocalIsolate>
215   V8_WARN_UNUSED_RESULT static Handle<Derived> NewInternal(
216       LocalIsolate* isolate, int capacity, AllocationType allocation);
217 
218   // Find the entry at which to insert element with the given key that
219   // has the given hash value.
220   InternalIndex FindInsertionEntry(IsolateRoot isolate, ReadOnlyRoots roots,
221                                    uint32_t hash);
222   InternalIndex FindInsertionEntry(Isolate* isolate, uint32_t hash);
223 
224   // Computes the capacity a table with the given capacity would need to have
225   // room for the given number of elements, also allowing it to shrink.
226   static int ComputeCapacityWithShrink(int current_capacity,
227                                        int at_least_room_for);
228 
229   // Shrink the hash table.
230   V8_WARN_UNUSED_RESULT static Handle<Derived> Shrink(
231       Isolate* isolate, Handle<Derived> table, int additionalCapacity = 0);
232 
233   // Rehashes this hash-table into the new table.
234   void Rehash(IsolateRoot isolate, Derived new_table);
235 
236   inline void set_key(int index, Object value);
237   inline void set_key(int index, Object value, WriteBarrierMode mode);
238 
239  private:
240   // Ensure that kMaxRegularCapacity yields a non-large object dictionary.
241   STATIC_ASSERT(EntryToIndex(InternalIndex(kMaxRegularCapacity)) <
242                 kMaxRegularLength);
243   STATIC_ASSERT(v8::base::bits::IsPowerOfTwo(kMaxRegularCapacity));
244   static const int kMaxRegularEntry = kMaxRegularCapacity / kEntrySize;
245   static const int kMaxRegularIndex =
246       EntryToIndex(InternalIndex(kMaxRegularEntry));
247   STATIC_ASSERT(OffsetOfElementAt(kMaxRegularIndex) <
248                 kMaxRegularHeapObjectSize);
249 
250   // Sets the capacity of the hash table.
251   inline void SetCapacity(int capacity);
252 
253   // Returns _expected_ if one of entries given by the first _probe_ probes is
254   // equal to  _expected_. Otherwise, returns the entry given by the probe
255   // number _probe_.
256   InternalIndex EntryForProbe(ReadOnlyRoots roots, Object k, int probe,
257                               InternalIndex expected);
258 
259   void Swap(InternalIndex entry1, InternalIndex entry2, WriteBarrierMode mode);
260 
261   OBJECT_CONSTRUCTORS(HashTable, HashTableBase);
262 };
263 
264 #define EXTERN_DECLARE_HASH_TABLE(DERIVED, SHAPE)                            \
265   extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)           \
266       HashTable<class DERIVED, SHAPE>;                                       \
267                                                                              \
268   extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
269   HashTable<DERIVED, SHAPE>::New(Isolate*, int, AllocationType,              \
270                                  MinimumCapacity);                           \
271   extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
272   HashTable<DERIVED, SHAPE>::New(LocalIsolate*, int, AllocationType,         \
273                                  MinimumCapacity);                           \
274                                                                              \
275   extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
276   HashTable<DERIVED, SHAPE>::EnsureCapacity(Isolate*, Handle<DERIVED>, int,  \
277                                             AllocationType);                 \
278   extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
279   HashTable<DERIVED, SHAPE>::EnsureCapacity(LocalIsolate*, Handle<DERIVED>,  \
280                                             int, AllocationType);
281 
282 // HashTableKey is an abstract superclass for virtual key behavior.
283 class HashTableKey {
284  public:
HashTableKey(uint32_t hash)285   explicit HashTableKey(uint32_t hash) : hash_(hash) {}
286 
287   // Returns whether the other object matches this key.
288   virtual bool IsMatch(Object other) = 0;
289   // Returns the hash value for this key.
290   // Required.
291   virtual ~HashTableKey() = default;
292 
Hash()293   uint32_t Hash() const { return hash_; }
294 
295  protected:
set_hash(uint32_t hash)296   void set_hash(uint32_t hash) {
297     DCHECK_EQ(0, hash_);
298     hash_ = hash;
299   }
300 
301  private:
302   uint32_t hash_ = 0;
303 };
304 
305 class ObjectHashTableShape : public BaseShape<Handle<Object>> {
306  public:
307   static inline bool IsMatch(Handle<Object> key, Object other);
308   static inline uint32_t Hash(ReadOnlyRoots roots, Handle<Object> key);
309   static inline uint32_t HashForObject(ReadOnlyRoots roots, Object object);
310   static inline Handle<Object> AsHandle(Handle<Object> key);
311   static const int kPrefixSize = 0;
312   static const int kEntryValueIndex = 1;
313   static const int kEntrySize = 2;
314   static const bool kMatchNeedsHoleCheck = false;
315 };
316 
317 template <typename Derived, typename Shape>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)318 class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) ObjectHashTableBase
319     : public HashTable<Derived, Shape> {
320  public:
321   // Looks up the value associated with the given key. The hole value is
322   // returned in case the key is not present.
323   Object Lookup(Handle<Object> key);
324   Object Lookup(Handle<Object> key, int32_t hash);
325   Object Lookup(IsolateRoot isolate, Handle<Object> key, int32_t hash);
326 
327   // Returns the value at entry.
328   Object ValueAt(InternalIndex entry);
329 
330   // Overwrite all keys and values with the hole value.
331   static void FillEntriesWithHoles(Handle<Derived>);
332 
333   // Adds (or overwrites) the value associated with the given key.
334   static Handle<Derived> Put(Handle<Derived> table, Handle<Object> key,
335                              Handle<Object> value);
336   static Handle<Derived> Put(Isolate* isolate, Handle<Derived> table,
337                              Handle<Object> key, Handle<Object> value,
338                              int32_t hash);
339 
340   // Returns an ObjectHashTable (possibly |table|) where |key| has been removed.
341   static Handle<Derived> Remove(Isolate* isolate, Handle<Derived> table,
342                                 Handle<Object> key, bool* was_present);
343   static Handle<Derived> Remove(Isolate* isolate, Handle<Derived> table,
344                                 Handle<Object> key, bool* was_present,
345                                 int32_t hash);
346 
347   // Returns the index to the value of an entry.
348   static inline int EntryToValueIndex(InternalIndex entry) {
349     return HashTable<Derived, Shape>::EntryToIndex(entry) +
350            Shape::kEntryValueIndex;
351   }
352 
353  protected:
354   void AddEntry(InternalIndex entry, Object key, Object value);
355   void RemoveEntry(InternalIndex entry);
356 
357   OBJECT_CONSTRUCTORS(ObjectHashTableBase, HashTable<Derived, Shape>);
358 };
359 
360 #define EXTERN_DECLARE_OBJECT_BASE_HASH_TABLE(DERIVED, SHAPE)      \
361   EXTERN_DECLARE_HASH_TABLE(DERIVED, SHAPE)                        \
362   extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) \
363       ObjectHashTableBase<class DERIVED, SHAPE>;
364 
EXTERN_DECLARE_OBJECT_BASE_HASH_TABLE(ObjectHashTable,ObjectHashTableShape)365 EXTERN_DECLARE_OBJECT_BASE_HASH_TABLE(ObjectHashTable, ObjectHashTableShape)
366 
367 // ObjectHashTable maps keys that are arbitrary objects to object values by
368 // using the identity hash of the key for hashing purposes.
369 class V8_EXPORT_PRIVATE ObjectHashTable
370     : public ObjectHashTableBase<ObjectHashTable, ObjectHashTableShape> {
371  public:
372   DECL_CAST(ObjectHashTable)
373   DECL_PRINTER(ObjectHashTable)
374 
375   OBJECT_CONSTRUCTORS(
376       ObjectHashTable,
377       ObjectHashTableBase<ObjectHashTable, ObjectHashTableShape>);
378 };
379 
EXTERN_DECLARE_OBJECT_BASE_HASH_TABLE(EphemeronHashTable,ObjectHashTableShape)380 EXTERN_DECLARE_OBJECT_BASE_HASH_TABLE(EphemeronHashTable, ObjectHashTableShape)
381 
382 // EphemeronHashTable is similar to ObjectHashTable but gets special treatment
383 // by the GC. The GC treats its entries as ephemerons: both key and value are
384 // weak references, however if the key is strongly reachable its corresponding
385 // value is also kept alive.
386 class V8_EXPORT_PRIVATE EphemeronHashTable
387     : public ObjectHashTableBase<EphemeronHashTable, ObjectHashTableShape> {
388  public:
389   static inline Handle<Map> GetMap(ReadOnlyRoots roots);
390 
391   DECL_CAST(EphemeronHashTable)
392   DECL_PRINTER(EphemeronHashTable)
393   class BodyDescriptor;
394 
395  protected:
396   friend class MarkCompactCollector;
397   friend class ScavengerCollector;
398   friend class HashTable<EphemeronHashTable, ObjectHashTableShape>;
399   friend class ObjectHashTableBase<EphemeronHashTable, ObjectHashTableShape>;
400   inline void set_key(int index, Object value);
401   inline void set_key(int index, Object value, WriteBarrierMode mode);
402 
403   OBJECT_CONSTRUCTORS(
404       EphemeronHashTable,
405       ObjectHashTableBase<EphemeronHashTable, ObjectHashTableShape>);
406 };
407 
408 class ObjectHashSetShape : public ObjectHashTableShape {
409  public:
410   static const int kPrefixSize = 0;
411   static const int kEntrySize = 1;
412 };
413 
EXTERN_DECLARE_HASH_TABLE(ObjectHashSet,ObjectHashSetShape)414 EXTERN_DECLARE_HASH_TABLE(ObjectHashSet, ObjectHashSetShape)
415 
416 class V8_EXPORT_PRIVATE ObjectHashSet
417     : public HashTable<ObjectHashSet, ObjectHashSetShape> {
418  public:
419   static Handle<ObjectHashSet> Add(Isolate* isolate, Handle<ObjectHashSet> set,
420                                    Handle<Object> key);
421 
422   inline bool Has(Isolate* isolate, Handle<Object> key, int32_t hash);
423   inline bool Has(Isolate* isolate, Handle<Object> key);
424 
425   DECL_CAST(ObjectHashSet)
426 
427   OBJECT_CONSTRUCTORS(ObjectHashSet,
428                       HashTable<ObjectHashSet, ObjectHashSetShape>);
429 };
430 
431 }  // namespace internal
432 }  // namespace v8
433 
434 #include "src/objects/object-macros-undef.h"
435 
436 #endif  // V8_OBJECTS_HASH_TABLE_H_
437