• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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 #include "src/objects/ordered-hash-table.h"
6 
7 #include "src/isolate.h"
8 #include "src/objects-inl.h"
9 #include "src/objects/js-collection-inl.h"
10 #include "src/objects/ordered-hash-table-inl.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 template <class Derived, int entrysize>
Allocate(Isolate * isolate,int capacity,PretenureFlag pretenure)16 Handle<Derived> OrderedHashTable<Derived, entrysize>::Allocate(
17     Isolate* isolate, int capacity, PretenureFlag pretenure) {
18   // Capacity must be a power of two, since we depend on being able
19   // to divide and multiple by 2 (kLoadFactor) to derive capacity
20   // from number of buckets. If we decide to change kLoadFactor
21   // to something other than 2, capacity should be stored as another
22   // field of this object.
23   capacity = base::bits::RoundUpToPowerOfTwo32(Max(kMinCapacity, capacity));
24   if (capacity > kMaxCapacity) {
25     isolate->heap()->FatalProcessOutOfMemory("invalid table size");
26   }
27   int num_buckets = capacity / kLoadFactor;
28   Handle<FixedArray> backing_store = isolate->factory()->NewFixedArrayWithMap(
29       static_cast<Heap::RootListIndex>(Derived::GetMapRootIndex()),
30       kHashTableStartIndex + num_buckets + (capacity * kEntrySize), pretenure);
31   Handle<Derived> table = Handle<Derived>::cast(backing_store);
32   for (int i = 0; i < num_buckets; ++i) {
33     table->set(kHashTableStartIndex + i, Smi::FromInt(kNotFound));
34   }
35   table->SetNumberOfBuckets(num_buckets);
36   table->SetNumberOfElements(0);
37   table->SetNumberOfDeletedElements(0);
38   return table;
39 }
40 
41 template <class Derived, int entrysize>
EnsureGrowable(Isolate * isolate,Handle<Derived> table)42 Handle<Derived> OrderedHashTable<Derived, entrysize>::EnsureGrowable(
43     Isolate* isolate, Handle<Derived> table) {
44   DCHECK(!table->IsObsolete());
45 
46   int nof = table->NumberOfElements();
47   int nod = table->NumberOfDeletedElements();
48   int capacity = table->Capacity();
49   if ((nof + nod) < capacity) return table;
50   // Don't need to grow if we can simply clear out deleted entries instead.
51   // Note that we can't compact in place, though, so we always allocate
52   // a new table.
53   return Rehash(isolate, table,
54                 (nod < (capacity >> 1)) ? capacity << 1 : capacity);
55 }
56 
57 template <class Derived, int entrysize>
Shrink(Isolate * isolate,Handle<Derived> table)58 Handle<Derived> OrderedHashTable<Derived, entrysize>::Shrink(
59     Isolate* isolate, Handle<Derived> table) {
60   DCHECK(!table->IsObsolete());
61 
62   int nof = table->NumberOfElements();
63   int capacity = table->Capacity();
64   if (nof >= (capacity >> 2)) return table;
65   return Rehash(isolate, table, capacity / 2);
66 }
67 
68 template <class Derived, int entrysize>
Clear(Isolate * isolate,Handle<Derived> table)69 Handle<Derived> OrderedHashTable<Derived, entrysize>::Clear(
70     Isolate* isolate, Handle<Derived> table) {
71   DCHECK(!table->IsObsolete());
72 
73   Handle<Derived> new_table = Allocate(
74       isolate, kMinCapacity, Heap::InNewSpace(*table) ? NOT_TENURED : TENURED);
75 
76   table->SetNextTable(*new_table);
77   table->SetNumberOfDeletedElements(kClearedTableSentinel);
78 
79   return new_table;
80 }
81 
82 template <class Derived, int entrysize>
HasKey(Isolate * isolate,Derived * table,Object * key)83 bool OrderedHashTable<Derived, entrysize>::HasKey(Isolate* isolate,
84                                                   Derived* table, Object* key) {
85   DCHECK((entrysize == 1 && table->IsOrderedHashSet()) ||
86          (entrysize == 2 && table->IsOrderedHashMap()));
87   DisallowHeapAllocation no_gc;
88   int entry = table->FindEntry(isolate, key);
89   return entry != kNotFound;
90 }
91 
Add(Isolate * isolate,Handle<OrderedHashSet> table,Handle<Object> key)92 Handle<OrderedHashSet> OrderedHashSet::Add(Isolate* isolate,
93                                            Handle<OrderedHashSet> table,
94                                            Handle<Object> key) {
95   int hash = key->GetOrCreateHash(isolate)->value();
96   int entry = table->HashToEntry(hash);
97   // Walk the chain of the bucket and try finding the key.
98   while (entry != kNotFound) {
99     Object* candidate_key = table->KeyAt(entry);
100     // Do not add if we have the key already
101     if (candidate_key->SameValueZero(*key)) return table;
102     entry = table->NextChainEntry(entry);
103   }
104 
105   table = OrderedHashSet::EnsureGrowable(isolate, table);
106   // Read the existing bucket values.
107   int bucket = table->HashToBucket(hash);
108   int previous_entry = table->HashToEntry(hash);
109   int nof = table->NumberOfElements();
110   // Insert a new entry at the end,
111   int new_entry = nof + table->NumberOfDeletedElements();
112   int new_index = table->EntryToIndex(new_entry);
113   table->set(new_index, *key);
114   table->set(new_index + kChainOffset, Smi::FromInt(previous_entry));
115   // and point the bucket to the new entry.
116   table->set(kHashTableStartIndex + bucket, Smi::FromInt(new_entry));
117   table->SetNumberOfElements(nof + 1);
118   return table;
119 }
120 
ConvertToKeysArray(Isolate * isolate,Handle<OrderedHashSet> table,GetKeysConversion convert)121 Handle<FixedArray> OrderedHashSet::ConvertToKeysArray(
122     Isolate* isolate, Handle<OrderedHashSet> table, GetKeysConversion convert) {
123   int length = table->NumberOfElements();
124   int nof_buckets = table->NumberOfBuckets();
125   // Convert the dictionary to a linear list.
126   Handle<FixedArray> result = Handle<FixedArray>::cast(table);
127   // From this point on table is no longer a valid OrderedHashSet.
128   result->set_map(ReadOnlyRoots(isolate).fixed_array_map());
129   int const kMaxStringTableEntries =
130       isolate->heap()->MaxNumberToStringCacheSize();
131   for (int i = 0; i < length; i++) {
132     int index = kHashTableStartIndex + nof_buckets + (i * kEntrySize);
133     Object* key = table->get(index);
134     if (convert == GetKeysConversion::kConvertToString) {
135       uint32_t index_value;
136       if (key->ToArrayIndex(&index_value)) {
137         // Avoid trashing the Number2String cache if indices get very large.
138         bool use_cache = i < kMaxStringTableEntries;
139         key = *isolate->factory()->Uint32ToString(index_value, use_cache);
140       } else {
141         CHECK(key->IsName());
142       }
143     }
144     result->set(i, key);
145   }
146   return FixedArray::ShrinkOrEmpty(isolate, result, length);
147 }
148 
GetEmpty(ReadOnlyRoots ro_roots)149 HeapObject* OrderedHashSet::GetEmpty(ReadOnlyRoots ro_roots) {
150   return ro_roots.empty_ordered_hash_set();
151 }
152 
GetEmpty(ReadOnlyRoots ro_roots)153 HeapObject* OrderedHashMap::GetEmpty(ReadOnlyRoots ro_roots) {
154   return ro_roots.empty_ordered_hash_map();
155 }
156 
157 template <class Derived, int entrysize>
Rehash(Isolate * isolate,Handle<Derived> table,int new_capacity)158 Handle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(
159     Isolate* isolate, Handle<Derived> table, int new_capacity) {
160   DCHECK(!table->IsObsolete());
161 
162   Handle<Derived> new_table = Allocate(
163       isolate, new_capacity, Heap::InNewSpace(*table) ? NOT_TENURED : TENURED);
164   int nof = table->NumberOfElements();
165   int nod = table->NumberOfDeletedElements();
166   int new_buckets = new_table->NumberOfBuckets();
167   int new_entry = 0;
168   int removed_holes_index = 0;
169 
170   DisallowHeapAllocation no_gc;
171   for (int old_entry = 0; old_entry < (nof + nod); ++old_entry) {
172     Object* key = table->KeyAt(old_entry);
173     if (key->IsTheHole(isolate)) {
174       table->SetRemovedIndexAt(removed_holes_index++, old_entry);
175       continue;
176     }
177 
178     Object* hash = key->GetHash();
179     int bucket = Smi::ToInt(hash) & (new_buckets - 1);
180     Object* chain_entry = new_table->get(kHashTableStartIndex + bucket);
181     new_table->set(kHashTableStartIndex + bucket, Smi::FromInt(new_entry));
182     int new_index = new_table->EntryToIndex(new_entry);
183     int old_index = table->EntryToIndex(old_entry);
184     for (int i = 0; i < entrysize; ++i) {
185       Object* value = table->get(old_index + i);
186       new_table->set(new_index + i, value);
187     }
188     new_table->set(new_index + kChainOffset, chain_entry);
189     ++new_entry;
190   }
191 
192   DCHECK_EQ(nod, removed_holes_index);
193 
194   new_table->SetNumberOfElements(nof);
195   table->SetNextTable(*new_table);
196 
197   return new_table;
198 }
199 
200 template <class Derived, int entrysize>
Delete(Isolate * isolate,Derived * table,Object * key)201 bool OrderedHashTable<Derived, entrysize>::Delete(Isolate* isolate,
202                                                   Derived* table, Object* key) {
203   DisallowHeapAllocation no_gc;
204   int entry = table->FindEntry(isolate, key);
205   if (entry == kNotFound) return false;
206 
207   int nof = table->NumberOfElements();
208   int nod = table->NumberOfDeletedElements();
209   int index = table->EntryToIndex(entry);
210 
211   Object* hole = ReadOnlyRoots(isolate).the_hole_value();
212   for (int i = 0; i < entrysize; ++i) {
213     table->set(index + i, hole);
214   }
215 
216   table->SetNumberOfElements(nof - 1);
217   table->SetNumberOfDeletedElements(nod + 1);
218 
219   return true;
220 }
221 
GetHash(Isolate * isolate,Object * key)222 Object* OrderedHashMap::GetHash(Isolate* isolate, Object* key) {
223   DisallowHeapAllocation no_gc;
224 
225   Object* hash = key->GetHash();
226   // If the object does not have an identity hash, it was never used as a key
227   if (hash->IsUndefined(isolate)) return Smi::FromInt(-1);
228   DCHECK(hash->IsSmi());
229   DCHECK_GE(Smi::cast(hash)->value(), 0);
230   return hash;
231 }
232 
Add(Isolate * isolate,Handle<OrderedHashMap> table,Handle<Object> key,Handle<Object> value)233 Handle<OrderedHashMap> OrderedHashMap::Add(Isolate* isolate,
234                                            Handle<OrderedHashMap> table,
235                                            Handle<Object> key,
236                                            Handle<Object> value) {
237   int hash = key->GetOrCreateHash(isolate)->value();
238   int entry = table->HashToEntry(hash);
239   // Walk the chain of the bucket and try finding the key.
240   {
241     DisallowHeapAllocation no_gc;
242     Object* raw_key = *key;
243     while (entry != kNotFound) {
244       Object* candidate_key = table->KeyAt(entry);
245       // Do not add if we have the key already
246       if (candidate_key->SameValueZero(raw_key)) return table;
247       entry = table->NextChainEntry(entry);
248     }
249   }
250 
251   table = OrderedHashMap::EnsureGrowable(isolate, table);
252   // Read the existing bucket values.
253   int bucket = table->HashToBucket(hash);
254   int previous_entry = table->HashToEntry(hash);
255   int nof = table->NumberOfElements();
256   // Insert a new entry at the end,
257   int new_entry = nof + table->NumberOfDeletedElements();
258   int new_index = table->EntryToIndex(new_entry);
259   table->set(new_index, *key);
260   table->set(new_index + kValueOffset, *value);
261   table->set(new_index + kChainOffset, Smi::FromInt(previous_entry));
262   // and point the bucket to the new entry.
263   table->set(kHashTableStartIndex + bucket, Smi::FromInt(new_entry));
264   table->SetNumberOfElements(nof + 1);
265   return table;
266 }
267 
268 template Handle<OrderedHashSet> OrderedHashTable<OrderedHashSet, 1>::Allocate(
269     Isolate* isolate, int capacity, PretenureFlag pretenure);
270 
271 template Handle<OrderedHashSet>
272 OrderedHashTable<OrderedHashSet, 1>::EnsureGrowable(
273     Isolate* isolate, Handle<OrderedHashSet> table);
274 
275 template Handle<OrderedHashSet> OrderedHashTable<OrderedHashSet, 1>::Shrink(
276     Isolate* isolate, Handle<OrderedHashSet> table);
277 
278 template Handle<OrderedHashSet> OrderedHashTable<OrderedHashSet, 1>::Clear(
279     Isolate* isolate, Handle<OrderedHashSet> table);
280 
281 template bool OrderedHashTable<OrderedHashSet, 1>::HasKey(Isolate* isolate,
282                                                           OrderedHashSet* table,
283                                                           Object* key);
284 
285 template bool OrderedHashTable<OrderedHashSet, 1>::Delete(Isolate* isolate,
286                                                           OrderedHashSet* table,
287                                                           Object* key);
288 
289 template Handle<OrderedHashMap> OrderedHashTable<OrderedHashMap, 2>::Allocate(
290     Isolate* isolate, int capacity, PretenureFlag pretenure);
291 
292 template Handle<OrderedHashMap>
293 OrderedHashTable<OrderedHashMap, 2>::EnsureGrowable(
294     Isolate* isolate, Handle<OrderedHashMap> table);
295 
296 template Handle<OrderedHashMap> OrderedHashTable<OrderedHashMap, 2>::Shrink(
297     Isolate* isolate, Handle<OrderedHashMap> table);
298 
299 template Handle<OrderedHashMap> OrderedHashTable<OrderedHashMap, 2>::Clear(
300     Isolate* isolate, Handle<OrderedHashMap> table);
301 
302 template bool OrderedHashTable<OrderedHashMap, 2>::HasKey(Isolate* isolate,
303                                                           OrderedHashMap* table,
304                                                           Object* key);
305 
306 template bool OrderedHashTable<OrderedHashMap, 2>::Delete(Isolate* isolate,
307                                                           OrderedHashMap* table,
308                                                           Object* key);
309 
310 template <>
311 Handle<SmallOrderedHashSet>
Allocate(Isolate * isolate,int capacity,PretenureFlag pretenure)312 SmallOrderedHashTable<SmallOrderedHashSet>::Allocate(Isolate* isolate,
313                                                      int capacity,
314                                                      PretenureFlag pretenure) {
315   return isolate->factory()->NewSmallOrderedHashSet(capacity, pretenure);
316 }
317 
318 template <>
319 Handle<SmallOrderedHashMap>
Allocate(Isolate * isolate,int capacity,PretenureFlag pretenure)320 SmallOrderedHashTable<SmallOrderedHashMap>::Allocate(Isolate* isolate,
321                                                      int capacity,
322                                                      PretenureFlag pretenure) {
323   return isolate->factory()->NewSmallOrderedHashMap(capacity, pretenure);
324 }
325 
326 template <class Derived>
Initialize(Isolate * isolate,int capacity)327 void SmallOrderedHashTable<Derived>::Initialize(Isolate* isolate,
328                                                 int capacity) {
329   DisallowHeapAllocation no_gc;
330   int num_buckets = capacity / kLoadFactor;
331   int num_chains = capacity;
332 
333   SetNumberOfBuckets(num_buckets);
334   SetNumberOfElements(0);
335   SetNumberOfDeletedElements(0);
336 
337   Address hashtable_start = GetHashTableStartAddress(capacity);
338   memset(reinterpret_cast<byte*>(hashtable_start), kNotFound,
339          num_buckets + num_chains);
340 
341   if (Heap::InNewSpace(this)) {
342     MemsetPointer(RawField(this, kDataTableStartOffset),
343                   ReadOnlyRoots(isolate).the_hole_value(),
344                   capacity * Derived::kEntrySize);
345   } else {
346     for (int i = 0; i < capacity; i++) {
347       for (int j = 0; j < Derived::kEntrySize; j++) {
348         SetDataEntry(i, j, ReadOnlyRoots(isolate).the_hole_value());
349       }
350     }
351   }
352 
353 #ifdef DEBUG
354   for (int i = 0; i < num_buckets; ++i) {
355     DCHECK_EQ(kNotFound, GetFirstEntry(i));
356   }
357 
358   for (int i = 0; i < num_chains; ++i) {
359     DCHECK_EQ(kNotFound, GetNextEntry(i));
360   }
361 
362   for (int i = 0; i < capacity; ++i) {
363     for (int j = 0; j < Derived::kEntrySize; j++) {
364       DCHECK_EQ(ReadOnlyRoots(isolate).the_hole_value(), GetDataEntry(i, j));
365     }
366   }
367 #endif  // DEBUG
368 }
369 
Add(Isolate * isolate,Handle<SmallOrderedHashSet> table,Handle<Object> key)370 MaybeHandle<SmallOrderedHashSet> SmallOrderedHashSet::Add(
371     Isolate* isolate, Handle<SmallOrderedHashSet> table, Handle<Object> key) {
372   if (table->HasKey(isolate, key)) return table;
373 
374   if (table->UsedCapacity() >= table->Capacity()) {
375     MaybeHandle<SmallOrderedHashSet> new_table =
376         SmallOrderedHashSet::Grow(isolate, table);
377     if (!new_table.ToHandle(&table)) {
378       return MaybeHandle<SmallOrderedHashSet>();
379     }
380   }
381 
382   int hash = key->GetOrCreateHash(isolate)->value();
383   int nof = table->NumberOfElements();
384 
385   // Read the existing bucket values.
386   int bucket = table->HashToBucket(hash);
387   int previous_entry = table->HashToFirstEntry(hash);
388 
389   // Insert a new entry at the end,
390   int new_entry = nof + table->NumberOfDeletedElements();
391 
392   table->SetDataEntry(new_entry, SmallOrderedHashSet::kKeyIndex, *key);
393   table->SetFirstEntry(bucket, new_entry);
394   table->SetNextEntry(new_entry, previous_entry);
395 
396   // and update book keeping.
397   table->SetNumberOfElements(nof + 1);
398 
399   return table;
400 }
401 
Add(Isolate * isolate,Handle<SmallOrderedHashMap> table,Handle<Object> key,Handle<Object> value)402 MaybeHandle<SmallOrderedHashMap> SmallOrderedHashMap::Add(
403     Isolate* isolate, Handle<SmallOrderedHashMap> table, Handle<Object> key,
404     Handle<Object> value) {
405   if (table->HasKey(isolate, key)) return table;
406 
407   if (table->UsedCapacity() >= table->Capacity()) {
408     MaybeHandle<SmallOrderedHashMap> new_table =
409         SmallOrderedHashMap::Grow(isolate, table);
410     if (!new_table.ToHandle(&table)) {
411       return MaybeHandle<SmallOrderedHashMap>();
412     }
413   }
414 
415   int hash = key->GetOrCreateHash(isolate)->value();
416   int nof = table->NumberOfElements();
417 
418   // Read the existing bucket values.
419   int bucket = table->HashToBucket(hash);
420   int previous_entry = table->HashToFirstEntry(hash);
421 
422   // Insert a new entry at the end,
423   int new_entry = nof + table->NumberOfDeletedElements();
424 
425   table->SetDataEntry(new_entry, SmallOrderedHashMap::kValueIndex, *value);
426   table->SetDataEntry(new_entry, SmallOrderedHashMap::kKeyIndex, *key);
427   table->SetFirstEntry(bucket, new_entry);
428   table->SetNextEntry(new_entry, previous_entry);
429 
430   // and update book keeping.
431   table->SetNumberOfElements(nof + 1);
432 
433   return table;
434 }
435 
436 template <class Derived>
HasKey(Isolate * isolate,Handle<Object> key)437 bool SmallOrderedHashTable<Derived>::HasKey(Isolate* isolate,
438                                             Handle<Object> key) {
439   DisallowHeapAllocation no_gc;
440   return FindEntry(isolate, *key) != kNotFound;
441 }
442 
443 template <class Derived>
Delete(Isolate * isolate,Derived * table,Object * key)444 bool SmallOrderedHashTable<Derived>::Delete(Isolate* isolate, Derived* table,
445                                             Object* key) {
446   DisallowHeapAllocation no_gc;
447   int entry = table->FindEntry(isolate, key);
448   if (entry == kNotFound) return false;
449 
450   int nof = table->NumberOfElements();
451   int nod = table->NumberOfDeletedElements();
452 
453   Object* hole = ReadOnlyRoots(isolate).the_hole_value();
454   for (int j = 0; j < Derived::kEntrySize; j++) {
455     table->SetDataEntry(entry, j, hole);
456   }
457 
458   table->SetNumberOfElements(nof - 1);
459   table->SetNumberOfDeletedElements(nod + 1);
460 
461   return true;
462 }
463 
464 template <class Derived>
Rehash(Isolate * isolate,Handle<Derived> table,int new_capacity)465 Handle<Derived> SmallOrderedHashTable<Derived>::Rehash(Isolate* isolate,
466                                                        Handle<Derived> table,
467                                                        int new_capacity) {
468   DCHECK_GE(kMaxCapacity, new_capacity);
469 
470   Handle<Derived> new_table = SmallOrderedHashTable<Derived>::Allocate(
471       isolate, new_capacity, Heap::InNewSpace(*table) ? NOT_TENURED : TENURED);
472   int nof = table->NumberOfElements();
473   int nod = table->NumberOfDeletedElements();
474   int new_entry = 0;
475 
476   {
477     DisallowHeapAllocation no_gc;
478     for (int old_entry = 0; old_entry < (nof + nod); ++old_entry) {
479       Object* key = table->KeyAt(old_entry);
480       if (key->IsTheHole(isolate)) continue;
481 
482       int hash = Smi::ToInt(key->GetHash());
483       int bucket = new_table->HashToBucket(hash);
484       int chain = new_table->GetFirstEntry(bucket);
485 
486       new_table->SetFirstEntry(bucket, new_entry);
487       new_table->SetNextEntry(new_entry, chain);
488 
489       for (int i = 0; i < Derived::kEntrySize; ++i) {
490         Object* value = table->GetDataEntry(old_entry, i);
491         new_table->SetDataEntry(new_entry, i, value);
492       }
493 
494       ++new_entry;
495     }
496 
497     new_table->SetNumberOfElements(nof);
498   }
499   return new_table;
500 }
501 
502 template <class Derived>
Grow(Isolate * isolate,Handle<Derived> table)503 MaybeHandle<Derived> SmallOrderedHashTable<Derived>::Grow(
504     Isolate* isolate, Handle<Derived> table) {
505   int capacity = table->Capacity();
506   int new_capacity = capacity;
507 
508   // Don't need to grow if we can simply clear out deleted entries instead.
509   // TODO(gsathya): Compact in place, instead of allocating a new table.
510   if (table->NumberOfDeletedElements() < (capacity >> 1)) {
511     new_capacity = capacity << 1;
512 
513     // The max capacity of our table is 254. We special case for 256 to
514     // account for our growth strategy, otherwise we would only fill up
515     // to 128 entries in our table.
516     if (new_capacity == kGrowthHack) {
517       new_capacity = kMaxCapacity;
518     }
519 
520     // We need to migrate to a bigger hash table.
521     if (new_capacity > kMaxCapacity) {
522       return MaybeHandle<Derived>();
523     }
524   }
525 
526   return Rehash(isolate, table, new_capacity);
527 }
528 
529 template bool SmallOrderedHashTable<SmallOrderedHashSet>::HasKey(
530     Isolate* isolate, Handle<Object> key);
531 template Handle<SmallOrderedHashSet>
532 SmallOrderedHashTable<SmallOrderedHashSet>::Rehash(
533     Isolate* isolate, Handle<SmallOrderedHashSet> table, int new_capacity);
534 template MaybeHandle<SmallOrderedHashSet>
535 SmallOrderedHashTable<SmallOrderedHashSet>::Grow(
536     Isolate* isolate, Handle<SmallOrderedHashSet> table);
537 template void SmallOrderedHashTable<SmallOrderedHashSet>::Initialize(
538     Isolate* isolate, int capacity);
539 
540 template bool SmallOrderedHashTable<SmallOrderedHashMap>::HasKey(
541     Isolate* isolate, Handle<Object> key);
542 template Handle<SmallOrderedHashMap>
543 SmallOrderedHashTable<SmallOrderedHashMap>::Rehash(
544     Isolate* isolate, Handle<SmallOrderedHashMap> table, int new_capacity);
545 template MaybeHandle<SmallOrderedHashMap>
546 SmallOrderedHashTable<SmallOrderedHashMap>::Grow(
547     Isolate* isolate, Handle<SmallOrderedHashMap> table);
548 template void SmallOrderedHashTable<SmallOrderedHashMap>::Initialize(
549     Isolate* isolate, int capacity);
550 
551 template bool SmallOrderedHashTable<SmallOrderedHashMap>::Delete(
552     Isolate* isolate, SmallOrderedHashMap* table, Object* key);
553 template bool SmallOrderedHashTable<SmallOrderedHashSet>::Delete(
554     Isolate* isolate, SmallOrderedHashSet* table, Object* key);
555 
556 template <class SmallTable, class LargeTable>
Allocate(Isolate * isolate,int capacity)557 Handle<HeapObject> OrderedHashTableHandler<SmallTable, LargeTable>::Allocate(
558     Isolate* isolate, int capacity) {
559   if (capacity < SmallTable::kMaxCapacity) {
560     return SmallTable::Allocate(isolate, capacity);
561   }
562 
563   return LargeTable::Allocate(isolate, capacity);
564 }
565 
566 template Handle<HeapObject>
567 OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet>::Allocate(
568     Isolate* isolate, int capacity);
569 template Handle<HeapObject>
570 OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap>::Allocate(
571     Isolate* isolate, int capacity);
572 
573 template <class SmallTable, class LargeTable>
Delete(Handle<HeapObject> table,Handle<Object> key)574 bool OrderedHashTableHandler<SmallTable, LargeTable>::Delete(
575     Handle<HeapObject> table, Handle<Object> key) {
576   if (SmallTable::Is(table)) {
577     return SmallTable::Delete(Handle<SmallTable>::cast(table), key);
578   }
579 
580   DCHECK(LargeTable::Is(table));
581   // Note: Once we migrate to the a big hash table, we never migrate
582   // down to a smaller hash table.
583   return LargeTable::Delete(Handle<LargeTable>::cast(table), key);
584 }
585 
586 template <class SmallTable, class LargeTable>
HasKey(Isolate * isolate,Handle<HeapObject> table,Handle<Object> key)587 bool OrderedHashTableHandler<SmallTable, LargeTable>::HasKey(
588     Isolate* isolate, Handle<HeapObject> table, Handle<Object> key) {
589   if (SmallTable::Is(table)) {
590     return Handle<SmallTable>::cast(table)->HasKey(isolate, key);
591   }
592 
593   DCHECK(LargeTable::Is(table));
594   return LargeTable::HasKey(isolate, LargeTable::cast(*table), *key);
595 }
596 
597 template bool
598 OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet>::HasKey(
599     Isolate* isolate, Handle<HeapObject> table, Handle<Object> key);
600 template bool
601 OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap>::HasKey(
602     Isolate* isolate, Handle<HeapObject> table, Handle<Object> key);
603 
AdjustRepresentation(Isolate * isolate,Handle<SmallOrderedHashMap> table)604 Handle<OrderedHashMap> OrderedHashMapHandler::AdjustRepresentation(
605     Isolate* isolate, Handle<SmallOrderedHashMap> table) {
606   Handle<OrderedHashMap> new_table =
607       OrderedHashMap::Allocate(isolate, OrderedHashTableMinSize);
608   int nof = table->NumberOfElements();
609   int nod = table->NumberOfDeletedElements();
610 
611   // TODO(gsathya): Optimize the lookup to not re calc offsets. Also,
612   // unhandlify this code as we preallocate the new backing store with
613   // the proper capacity.
614   for (int entry = 0; entry < (nof + nod); ++entry) {
615     Handle<Object> key = handle(table->KeyAt(entry), isolate);
616     if (key->IsTheHole(isolate)) continue;
617     Handle<Object> value = handle(
618         table->GetDataEntry(entry, SmallOrderedHashMap::kValueIndex), isolate);
619     new_table = OrderedHashMap::Add(isolate, new_table, key, value);
620   }
621 
622   return new_table;
623 }
624 
AdjustRepresentation(Isolate * isolate,Handle<SmallOrderedHashSet> table)625 Handle<OrderedHashSet> OrderedHashSetHandler::AdjustRepresentation(
626     Isolate* isolate, Handle<SmallOrderedHashSet> table) {
627   Handle<OrderedHashSet> new_table =
628       OrderedHashSet::Allocate(isolate, OrderedHashTableMinSize);
629   int nof = table->NumberOfElements();
630   int nod = table->NumberOfDeletedElements();
631 
632   // TODO(gsathya): Optimize the lookup to not re calc offsets. Also,
633   // unhandlify this code as we preallocate the new backing store with
634   // the proper capacity.
635   for (int entry = 0; entry < (nof + nod); ++entry) {
636     Handle<Object> key = handle(table->KeyAt(entry), isolate);
637     if (key->IsTheHole(isolate)) continue;
638     new_table = OrderedHashSet::Add(isolate, new_table, key);
639   }
640 
641   return new_table;
642 }
643 
Add(Isolate * isolate,Handle<HeapObject> table,Handle<Object> key,Handle<Object> value)644 Handle<HeapObject> OrderedHashMapHandler::Add(Isolate* isolate,
645                                               Handle<HeapObject> table,
646                                               Handle<Object> key,
647                                               Handle<Object> value) {
648   if (table->IsSmallOrderedHashMap()) {
649     Handle<SmallOrderedHashMap> small_map =
650         Handle<SmallOrderedHashMap>::cast(table);
651     MaybeHandle<SmallOrderedHashMap> new_map =
652         SmallOrderedHashMap::Add(isolate, small_map, key, value);
653     if (!new_map.is_null()) return new_map.ToHandleChecked();
654 
655     // We couldn't add to the small table, let's migrate to the
656     // big table.
657     table = OrderedHashMapHandler::AdjustRepresentation(isolate, small_map);
658   }
659 
660   DCHECK(table->IsOrderedHashMap());
661   return OrderedHashMap::Add(isolate, Handle<OrderedHashMap>::cast(table), key,
662                              value);
663 }
664 
Add(Isolate * isolate,Handle<HeapObject> table,Handle<Object> key)665 Handle<HeapObject> OrderedHashSetHandler::Add(Isolate* isolate,
666                                               Handle<HeapObject> table,
667                                               Handle<Object> key) {
668   if (table->IsSmallOrderedHashSet()) {
669     Handle<SmallOrderedHashSet> small_set =
670         Handle<SmallOrderedHashSet>::cast(table);
671     MaybeHandle<SmallOrderedHashSet> new_set =
672         SmallOrderedHashSet::Add(isolate, small_set, key);
673     if (!new_set.is_null()) return new_set.ToHandleChecked();
674 
675     // We couldn't add to the small table, let's migrate to the
676     // big table.
677     table = OrderedHashSetHandler::AdjustRepresentation(isolate, small_set);
678   }
679 
680   DCHECK(table->IsOrderedHashSet());
681   return OrderedHashSet::Add(isolate, Handle<OrderedHashSet>::cast(table), key);
682 }
683 
684 template <class Derived, class TableType>
Transition()685 void OrderedHashTableIterator<Derived, TableType>::Transition() {
686   DisallowHeapAllocation no_allocation;
687   TableType* table = TableType::cast(this->table());
688   if (!table->IsObsolete()) return;
689 
690   int index = Smi::ToInt(this->index());
691   while (table->IsObsolete()) {
692     TableType* next_table = table->NextTable();
693 
694     if (index > 0) {
695       int nod = table->NumberOfDeletedElements();
696 
697       if (nod == TableType::kClearedTableSentinel) {
698         index = 0;
699       } else {
700         int old_index = index;
701         for (int i = 0; i < nod; ++i) {
702           int removed_index = table->RemovedIndexAt(i);
703           if (removed_index >= old_index) break;
704           --index;
705         }
706       }
707     }
708 
709     table = next_table;
710   }
711 
712   set_table(table);
713   set_index(Smi::FromInt(index));
714 }
715 
716 template <class Derived, class TableType>
HasMore()717 bool OrderedHashTableIterator<Derived, TableType>::HasMore() {
718   DisallowHeapAllocation no_allocation;
719   ReadOnlyRoots ro_roots = GetReadOnlyRoots();
720 
721   Transition();
722 
723   TableType* table = TableType::cast(this->table());
724   int index = Smi::ToInt(this->index());
725   int used_capacity = table->UsedCapacity();
726 
727   while (index < used_capacity && table->KeyAt(index)->IsTheHole(ro_roots)) {
728     index++;
729   }
730 
731   set_index(Smi::FromInt(index));
732 
733   if (index < used_capacity) return true;
734 
735   set_table(TableType::GetEmpty(ro_roots));
736   return false;
737 }
738 
739 template bool
740 OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::HasMore();
741 
742 template void
743 OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::MoveNext();
744 
745 template Object*
746 OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::CurrentKey();
747 
748 template void
749 OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Transition();
750 
751 template bool
752 OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::HasMore();
753 
754 template void
755 OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::MoveNext();
756 
757 template Object*
758 OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::CurrentKey();
759 
760 template void
761 OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Transition();
762 
763 }  // namespace internal
764 }  // namespace v8
765