• 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/execution/isolate.h"
8 #include "src/heap/heap-inl.h"
9 #include "src/objects/internal-index.h"
10 #include "src/objects/js-collection-inl.h"
11 #include "src/objects/objects-inl.h"
12 #include "src/objects/ordered-hash-table-inl.h"
13 #include "src/roots/roots.h"
14 
15 namespace v8 {
16 namespace internal {
17 
18 template <class Derived, int entrysize>
Allocate(Isolate * isolate,int capacity,AllocationType allocation)19 MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Allocate(
20     Isolate* isolate, int capacity, AllocationType allocation) {
21   // Capacity must be a power of two, since we depend on being able
22   // to divide and multiple by 2 (kLoadFactor) to derive capacity
23   // from number of buckets. If we decide to change kLoadFactor
24   // to something other than 2, capacity should be stored as another
25   // field of this object.
26   capacity =
27       base::bits::RoundUpToPowerOfTwo32(std::max({kInitialCapacity, capacity}));
28   if (capacity > MaxCapacity()) {
29     return MaybeHandle<Derived>();
30   }
31   int num_buckets = capacity / kLoadFactor;
32   Handle<FixedArray> backing_store = isolate->factory()->NewFixedArrayWithMap(
33       Derived::GetMap(ReadOnlyRoots(isolate)),
34       HashTableStartIndex() + num_buckets + (capacity * kEntrySize),
35       allocation);
36   Handle<Derived> table = Handle<Derived>::cast(backing_store);
37   for (int i = 0; i < num_buckets; ++i) {
38     table->set(HashTableStartIndex() + i, Smi::FromInt(kNotFound));
39   }
40   table->SetNumberOfBuckets(num_buckets);
41   table->SetNumberOfElements(0);
42   table->SetNumberOfDeletedElements(0);
43   return table;
44 }
45 
46 template <class Derived, int entrysize>
AllocateEmpty(Isolate * isolate,AllocationType allocation,RootIndex root_index)47 MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::AllocateEmpty(
48     Isolate* isolate, AllocationType allocation, RootIndex root_index) {
49   // This is only supposed to be used to create the canonical empty versions
50   // of each ordered structure, and should not be used afterwards.
51   // Requires that the map has already been set up in the roots table.
52   DCHECK(ReadOnlyRoots(isolate).at(root_index) == kNullAddress);
53 
54   Handle<FixedArray> backing_store = isolate->factory()->NewFixedArrayWithMap(
55       Derived::GetMap(ReadOnlyRoots(isolate)), HashTableStartIndex(),
56       allocation);
57   Handle<Derived> table = Handle<Derived>::cast(backing_store);
58   table->SetNumberOfBuckets(0);
59   table->SetNumberOfElements(0);
60   table->SetNumberOfDeletedElements(0);
61   return table;
62 }
63 
64 template <class Derived, int entrysize>
EnsureGrowable(Isolate * isolate,Handle<Derived> table)65 MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::EnsureGrowable(
66     Isolate* isolate, Handle<Derived> table) {
67   DCHECK(!table->IsObsolete());
68 
69   int nof = table->NumberOfElements();
70   int nod = table->NumberOfDeletedElements();
71   int capacity = table->Capacity();
72   if ((nof + nod) < capacity) return table;
73 
74   int new_capacity;
75   if (capacity == 0) {
76     // step from empty to minimum proper size
77     new_capacity = kInitialCapacity;
78   } else if (nod >= (capacity >> 1)) {
79     // Don't need to grow if we can simply clear out deleted entries instead.
80     // Note that we can't compact in place, though, so we always allocate
81     // a new table.
82     new_capacity = capacity;
83   } else {
84     new_capacity = capacity << 1;
85   }
86 
87   return Derived::Rehash(isolate, table, new_capacity);
88 }
89 
90 template <class Derived, int entrysize>
Shrink(Isolate * isolate,Handle<Derived> table)91 Handle<Derived> OrderedHashTable<Derived, entrysize>::Shrink(
92     Isolate* isolate, Handle<Derived> table) {
93   DCHECK(!table->IsObsolete());
94 
95   int nof = table->NumberOfElements();
96   int capacity = table->Capacity();
97   if (nof >= (capacity >> 2)) return table;
98   return Derived::Rehash(isolate, table, capacity / 2).ToHandleChecked();
99 }
100 
101 template <class Derived, int entrysize>
Clear(Isolate * isolate,Handle<Derived> table)102 Handle<Derived> OrderedHashTable<Derived, entrysize>::Clear(
103     Isolate* isolate, Handle<Derived> table) {
104   DCHECK(!table->IsObsolete());
105 
106   AllocationType allocation_type = Heap::InYoungGeneration(*table)
107                                        ? AllocationType::kYoung
108                                        : AllocationType::kOld;
109 
110   Handle<Derived> new_table =
111       Allocate(isolate, kInitialCapacity, allocation_type).ToHandleChecked();
112 
113   if (table->NumberOfBuckets() > 0) {
114     // Don't try to modify the empty canonical table which lives in RO space.
115     table->SetNextTable(*new_table);
116     table->SetNumberOfDeletedElements(kClearedTableSentinel);
117   }
118 
119   return new_table;
120 }
121 
122 template <class Derived, int entrysize>
HasKey(Isolate * isolate,Derived table,Object key)123 bool OrderedHashTable<Derived, entrysize>::HasKey(Isolate* isolate,
124                                                   Derived table, Object key) {
125   DCHECK_IMPLIES(entrysize == 1, table.IsOrderedHashSet());
126   DCHECK_IMPLIES(entrysize == 2, table.IsOrderedHashMap());
127   DisallowHeapAllocation no_gc;
128   InternalIndex entry = table.FindEntry(isolate, key);
129   return entry.is_found();
130 }
131 
132 template <class Derived, int entrysize>
FindEntry(Isolate * isolate,Object key)133 InternalIndex OrderedHashTable<Derived, entrysize>::FindEntry(Isolate* isolate,
134                                                               Object key) {
135   if (NumberOfElements() == 0) {
136     // This is not just an optimization but also ensures that we do the right
137     // thing if Capacity() == 0
138     return InternalIndex::NotFound();
139   }
140 
141   int raw_entry;
142   // This special cases for Smi, so that we avoid the HandleScope
143   // creation below.
144   if (key.IsSmi()) {
145     uint32_t hash = ComputeUnseededHash(Smi::ToInt(key));
146     raw_entry = HashToEntryRaw(hash & Smi::kMaxValue);
147   } else {
148     HandleScope scope(isolate);
149     Object hash = key.GetHash();
150     // If the object does not have an identity hash, it was never used as a key
151     if (hash.IsUndefined(isolate)) return InternalIndex::NotFound();
152     raw_entry = HashToEntryRaw(Smi::ToInt(hash));
153   }
154 
155   // Walk the chain in the bucket to find the key.
156   while (raw_entry != kNotFound) {
157     Object candidate_key = KeyAt(InternalIndex(raw_entry));
158     if (candidate_key.SameValueZero(key)) return InternalIndex(raw_entry);
159     raw_entry = NextChainEntryRaw(raw_entry);
160   }
161 
162   return InternalIndex::NotFound();
163 }
164 
Add(Isolate * isolate,Handle<OrderedHashSet> table,Handle<Object> key)165 MaybeHandle<OrderedHashSet> OrderedHashSet::Add(Isolate* isolate,
166                                                 Handle<OrderedHashSet> table,
167                                                 Handle<Object> key) {
168   int hash = key->GetOrCreateHash(isolate).value();
169   if (table->NumberOfElements() > 0) {
170     int raw_entry = table->HashToEntryRaw(hash);
171     // Walk the chain of the bucket and try finding the key.
172     while (raw_entry != kNotFound) {
173       Object candidate_key = table->KeyAt(InternalIndex(raw_entry));
174       // Do not add if we have the key already
175       if (candidate_key.SameValueZero(*key)) return table;
176       raw_entry = table->NextChainEntryRaw(raw_entry);
177     }
178   }
179 
180   MaybeHandle<OrderedHashSet> table_candidate =
181       OrderedHashSet::EnsureGrowable(isolate, table);
182   if (!table_candidate.ToHandle(&table)) {
183     return table_candidate;
184   }
185   // Read the existing bucket values.
186   int bucket = table->HashToBucket(hash);
187   int previous_entry = table->HashToEntryRaw(hash);
188   int nof = table->NumberOfElements();
189   // Insert a new entry at the end,
190   int new_entry = nof + table->NumberOfDeletedElements();
191   int new_index = table->EntryToIndexRaw(new_entry);
192   table->set(new_index, *key);
193   table->set(new_index + kChainOffset, Smi::FromInt(previous_entry));
194   // and point the bucket to the new entry.
195   table->set(HashTableStartIndex() + bucket, Smi::FromInt(new_entry));
196   table->SetNumberOfElements(nof + 1);
197   return table;
198 }
199 
ConvertToKeysArray(Isolate * isolate,Handle<OrderedHashSet> table,GetKeysConversion convert)200 Handle<FixedArray> OrderedHashSet::ConvertToKeysArray(
201     Isolate* isolate, Handle<OrderedHashSet> table, GetKeysConversion convert) {
202   int length = table->NumberOfElements();
203   int nof_buckets = table->NumberOfBuckets();
204   // Convert the dictionary to a linear list.
205   Handle<FixedArray> result = Handle<FixedArray>::cast(table);
206   // From this point on table is no longer a valid OrderedHashSet.
207   result->set_map(ReadOnlyRoots(isolate).fixed_array_map());
208   int const kMaxStringTableEntries =
209       isolate->heap()->MaxNumberToStringCacheSize();
210   for (int i = 0; i < length; i++) {
211     int index = HashTableStartIndex() + nof_buckets + (i * kEntrySize);
212     Object key = table->get(index);
213     uint32_t index_value;
214     if (convert == GetKeysConversion::kConvertToString) {
215       if (key.ToArrayIndex(&index_value)) {
216         // Avoid trashing the Number2String cache if indices get very large.
217         bool use_cache = i < kMaxStringTableEntries;
218         key = *isolate->factory()->Uint32ToString(index_value, use_cache);
219       } else {
220         CHECK(key.IsName());
221       }
222     } else if (convert == GetKeysConversion::kNoNumbers) {
223       DCHECK(!key.ToArrayIndex(&index_value));
224     }
225     result->set(i, key);
226   }
227   return FixedArray::ShrinkOrEmpty(isolate, result, length);
228 }
229 
GetEmpty(ReadOnlyRoots ro_roots)230 HeapObject OrderedHashSet::GetEmpty(ReadOnlyRoots ro_roots) {
231   return ro_roots.empty_ordered_hash_set();
232 }
233 
GetEmpty(ReadOnlyRoots ro_roots)234 HeapObject OrderedHashMap::GetEmpty(ReadOnlyRoots ro_roots) {
235   return ro_roots.empty_ordered_hash_map();
236 }
237 
238 template <class Derived, int entrysize>
Rehash(Isolate * isolate,Handle<Derived> table)239 MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(
240     Isolate* isolate, Handle<Derived> table) {
241   return OrderedHashTable<Derived, entrysize>::Rehash(isolate, table,
242                                                       table->Capacity());
243 }
244 
245 template <class Derived, int entrysize>
Rehash(Isolate * isolate,Handle<Derived> table,int new_capacity)246 MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(
247     Isolate* isolate, Handle<Derived> table, int new_capacity) {
248   DCHECK(!table->IsObsolete());
249 
250   MaybeHandle<Derived> new_table_candidate =
251       Derived::Allocate(isolate, new_capacity,
252                         Heap::InYoungGeneration(*table) ? AllocationType::kYoung
253                                                         : AllocationType::kOld);
254   Handle<Derived> new_table;
255   if (!new_table_candidate.ToHandle(&new_table)) {
256     return new_table_candidate;
257   }
258   int new_buckets = new_table->NumberOfBuckets();
259   int new_entry = 0;
260   int removed_holes_index = 0;
261 
262   DisallowHeapAllocation no_gc;
263 
264   for (InternalIndex old_entry : table->IterateEntries()) {
265     int old_entry_raw = old_entry.as_int();
266     Object key = table->KeyAt(old_entry);
267     if (key.IsTheHole(isolate)) {
268       table->SetRemovedIndexAt(removed_holes_index++, old_entry_raw);
269       continue;
270     }
271 
272     Object hash = key.GetHash();
273     int bucket = Smi::ToInt(hash) & (new_buckets - 1);
274     Object chain_entry = new_table->get(HashTableStartIndex() + bucket);
275     new_table->set(HashTableStartIndex() + bucket, Smi::FromInt(new_entry));
276     int new_index = new_table->EntryToIndexRaw(new_entry);
277     int old_index = table->EntryToIndexRaw(old_entry_raw);
278     for (int i = 0; i < entrysize; ++i) {
279       Object value = table->get(old_index + i);
280       new_table->set(new_index + i, value);
281     }
282     new_table->set(new_index + kChainOffset, chain_entry);
283     ++new_entry;
284   }
285 
286   DCHECK_EQ(table->NumberOfDeletedElements(), removed_holes_index);
287 
288   new_table->SetNumberOfElements(table->NumberOfElements());
289   if (table->NumberOfBuckets() > 0) {
290     // Don't try to modify the empty canonical table which lives in RO space.
291     table->SetNextTable(*new_table);
292   }
293 
294   return new_table_candidate;
295 }
296 
Rehash(Isolate * isolate,Handle<OrderedHashSet> table,int new_capacity)297 MaybeHandle<OrderedHashSet> OrderedHashSet::Rehash(Isolate* isolate,
298                                                    Handle<OrderedHashSet> table,
299                                                    int new_capacity) {
300   return Base::Rehash(isolate, table, new_capacity);
301 }
302 
Rehash(Isolate * isolate,Handle<OrderedHashSet> table)303 MaybeHandle<OrderedHashSet> OrderedHashSet::Rehash(
304     Isolate* isolate, Handle<OrderedHashSet> table) {
305   return Base::Rehash(isolate, table);
306 }
307 
Rehash(Isolate * isolate,Handle<OrderedHashMap> table)308 MaybeHandle<OrderedHashMap> OrderedHashMap::Rehash(
309     Isolate* isolate, Handle<OrderedHashMap> table) {
310   return Base::Rehash(isolate, table);
311 }
312 
Rehash(Isolate * isolate,Handle<OrderedHashMap> table,int new_capacity)313 MaybeHandle<OrderedHashMap> OrderedHashMap::Rehash(Isolate* isolate,
314                                                    Handle<OrderedHashMap> table,
315                                                    int new_capacity) {
316   return Base::Rehash(isolate, table, new_capacity);
317 }
318 
Rehash(Isolate * isolate,Handle<OrderedNameDictionary> table,int new_capacity)319 MaybeHandle<OrderedNameDictionary> OrderedNameDictionary::Rehash(
320     Isolate* isolate, Handle<OrderedNameDictionary> table, int new_capacity) {
321   MaybeHandle<OrderedNameDictionary> new_table_candidate =
322       Base::Rehash(isolate, table, new_capacity);
323   Handle<OrderedNameDictionary> new_table;
324   if (new_table_candidate.ToHandle(&new_table)) {
325     new_table->SetHash(table->Hash());
326   }
327   return new_table_candidate;
328 }
329 
330 template <class Derived, int entrysize>
Delete(Isolate * isolate,Derived table,Object key)331 bool OrderedHashTable<Derived, entrysize>::Delete(Isolate* isolate,
332                                                   Derived table, Object key) {
333   DisallowHeapAllocation no_gc;
334   InternalIndex entry = table.FindEntry(isolate, key);
335   if (entry.is_not_found()) return false;
336 
337   int nof = table.NumberOfElements();
338   int nod = table.NumberOfDeletedElements();
339   int index = table.EntryToIndex(entry);
340 
341   Object hole = ReadOnlyRoots(isolate).the_hole_value();
342   for (int i = 0; i < entrysize; ++i) {
343     table.set(index + i, hole);
344   }
345 
346   table.SetNumberOfElements(nof - 1);
347   table.SetNumberOfDeletedElements(nod + 1);
348 
349   return true;
350 }
351 
352 // Parameter |roots| only here for compatibility with HashTable<...>::ToKey.
353 template <class Derived, int entrysize>
ToKey(ReadOnlyRoots roots,InternalIndex entry,Object * out_key)354 bool OrderedHashTable<Derived, entrysize>::ToKey(ReadOnlyRoots roots,
355                                                  InternalIndex entry,
356                                                  Object* out_key) {
357   Object k = KeyAt(entry);
358   if (!IsKey(roots, k)) return false;
359   *out_key = k;
360   return true;
361 }
362 
GetHash(Isolate * isolate,Address raw_key)363 Address OrderedHashMap::GetHash(Isolate* isolate, Address raw_key) {
364   DisallowHeapAllocation no_gc;
365   Object key(raw_key);
366   Object hash = key.GetHash();
367   // If the object does not have an identity hash, it was never used as a key
368   if (hash.IsUndefined(isolate)) return Smi::FromInt(-1).ptr();
369   DCHECK(hash.IsSmi());
370   DCHECK_GE(Smi::cast(hash).value(), 0);
371   return hash.ptr();
372 }
373 
Add(Isolate * isolate,Handle<OrderedHashMap> table,Handle<Object> key,Handle<Object> value)374 MaybeHandle<OrderedHashMap> OrderedHashMap::Add(Isolate* isolate,
375                                                 Handle<OrderedHashMap> table,
376                                                 Handle<Object> key,
377                                                 Handle<Object> value) {
378   int hash = key->GetOrCreateHash(isolate).value();
379   if (table->NumberOfElements() > 0) {
380     int raw_entry = table->HashToEntryRaw(hash);
381     // Walk the chain of the bucket and try finding the key.
382     {
383       DisallowHeapAllocation no_gc;
384       Object raw_key = *key;
385       while (raw_entry != kNotFound) {
386         Object candidate_key = table->KeyAt(InternalIndex(raw_entry));
387         // Do not add if we have the key already
388         if (candidate_key.SameValueZero(raw_key)) return table;
389         raw_entry = table->NextChainEntryRaw(raw_entry);
390       }
391     }
392   }
393 
394   MaybeHandle<OrderedHashMap> table_candidate =
395       OrderedHashMap::EnsureGrowable(isolate, table);
396   if (!table_candidate.ToHandle(&table)) {
397     return table_candidate;
398   }
399   // Read the existing bucket values.
400   int bucket = table->HashToBucket(hash);
401   int previous_entry = table->HashToEntryRaw(hash);
402   int nof = table->NumberOfElements();
403   // Insert a new entry at the end,
404   int new_entry = nof + table->NumberOfDeletedElements();
405   int new_index = table->EntryToIndexRaw(new_entry);
406   table->set(new_index, *key);
407   table->set(new_index + kValueOffset, *value);
408   table->set(new_index + kChainOffset, Smi::FromInt(previous_entry));
409   // and point the bucket to the new entry.
410   table->set(HashTableStartIndex() + bucket, Smi::FromInt(new_entry));
411   table->SetNumberOfElements(nof + 1);
412   return table;
413 }
414 
FindEntry(Isolate * isolate,Object key)415 InternalIndex OrderedNameDictionary::FindEntry(Isolate* isolate, Object key) {
416   DisallowHeapAllocation no_gc;
417 
418   DCHECK(key.IsUniqueName());
419   Name raw_key = Name::cast(key);
420 
421   if (NumberOfElements() == 0) {
422     // This is not just an optimization but also ensures that we do the right
423     // thing if Capacity() == 0
424     return InternalIndex::NotFound();
425   }
426 
427   int raw_entry = HashToEntryRaw(raw_key.Hash());
428   while (raw_entry != kNotFound) {
429     InternalIndex entry(raw_entry);
430     Object candidate_key = KeyAt(entry);
431     DCHECK(candidate_key.IsTheHole() ||
432            Name::cast(candidate_key).IsUniqueName());
433     if (candidate_key == raw_key) return entry;
434 
435     // TODO(gsathya): This is loading the bucket count from the hash
436     // table for every iteration. This should be peeled out of the
437     // loop.
438     raw_entry = NextChainEntryRaw(raw_entry);
439   }
440 
441   return InternalIndex::NotFound();
442 }
443 
444 // TODO(emrich): This is almost an identical copy of
445 // Dictionary<..>::SlowReverseLookup.
446 // Consolidate both versions elsewhere (e.g., hash-table-utils)?
SlowReverseLookup(Isolate * isolate,Object value)447 Object OrderedNameDictionary::SlowReverseLookup(Isolate* isolate,
448                                                 Object value) {
449   ReadOnlyRoots roots(isolate);
450   for (InternalIndex i : IterateEntries()) {
451     Object k;
452     if (!ToKey(roots, i, &k)) continue;
453     Object e = this->ValueAt(i);
454     if (e == value) return k;
455   }
456   return roots.undefined_value();
457 }
458 
459 // TODO(emrich): This is almost an identical copy of
460 // HashTable<..>::NumberOfEnumerableProperties.
461 // Consolidate both versions elsewhere (e.g., hash-table-utils)?
NumberOfEnumerableProperties()462 int OrderedNameDictionary::NumberOfEnumerableProperties() {
463   ReadOnlyRoots roots = this->GetReadOnlyRoots();
464   int result = 0;
465   for (InternalIndex i : this->IterateEntries()) {
466     Object k;
467     if (!this->ToKey(roots, i, &k)) continue;
468     if (k.FilterKey(ENUMERABLE_STRINGS)) continue;
469     PropertyDetails details = this->DetailsAt(i);
470     PropertyAttributes attr = details.attributes();
471     if ((attr & ONLY_ENUMERABLE) == 0) result++;
472   }
473   return result;
474 }
475 
Add(Isolate * isolate,Handle<OrderedNameDictionary> table,Handle<Name> key,Handle<Object> value,PropertyDetails details)476 MaybeHandle<OrderedNameDictionary> OrderedNameDictionary::Add(
477     Isolate* isolate, Handle<OrderedNameDictionary> table, Handle<Name> key,
478     Handle<Object> value, PropertyDetails details) {
479   DCHECK(table->FindEntry(isolate, *key).is_not_found());
480 
481   MaybeHandle<OrderedNameDictionary> table_candidate =
482       OrderedNameDictionary::EnsureGrowable(isolate, table);
483   if (!table_candidate.ToHandle(&table)) {
484     return table_candidate;
485   }
486   // Read the existing bucket values.
487   int hash = key->Hash();
488   int bucket = table->HashToBucket(hash);
489   int previous_entry = table->HashToEntryRaw(hash);
490   int nof = table->NumberOfElements();
491   // Insert a new entry at the end,
492   int new_entry = nof + table->NumberOfDeletedElements();
493   int new_index = table->EntryToIndexRaw(new_entry);
494   table->set(new_index, *key);
495   table->set(new_index + kValueOffset, *value);
496 
497   // TODO(gsathya): Optimize how PropertyDetails are stored in this
498   // dictionary to save memory (by reusing padding?) and performance
499   // (by not doing the Smi conversion).
500   table->set(new_index + kPropertyDetailsOffset, details.AsSmi());
501 
502   table->set(new_index + kChainOffset, Smi::FromInt(previous_entry));
503   // and point the bucket to the new entry.
504   table->set(HashTableStartIndex() + bucket, Smi::FromInt(new_entry));
505   table->SetNumberOfElements(nof + 1);
506   return table;
507 }
508 
SetEntry(InternalIndex entry,Object key,Object value,PropertyDetails details)509 void OrderedNameDictionary::SetEntry(InternalIndex entry, Object key,
510                                      Object value, PropertyDetails details) {
511   DisallowHeapAllocation gc;
512   DCHECK_IMPLIES(!key.IsName(), key.IsTheHole());
513   DisallowHeapAllocation no_gc;
514   int index = EntryToIndex(entry);
515   this->set(index, key);
516   this->set(index + kValueOffset, value);
517 
518   // TODO(gsathya): Optimize how PropertyDetails are stored in this
519   // dictionary to save memory (by reusing padding?) and performance
520   // (by not doing the Smi conversion).
521   this->set(index + kPropertyDetailsOffset, details.AsSmi());
522 }
523 
DeleteEntry(Isolate * isolate,Handle<OrderedNameDictionary> table,InternalIndex entry)524 Handle<OrderedNameDictionary> OrderedNameDictionary::DeleteEntry(
525     Isolate* isolate, Handle<OrderedNameDictionary> table,
526     InternalIndex entry) {
527   DCHECK(entry.is_found());
528 
529   Object hole = ReadOnlyRoots(isolate).the_hole_value();
530   PropertyDetails details = PropertyDetails::Empty();
531   table->SetEntry(entry, hole, hole, details);
532 
533   int nof = table->NumberOfElements();
534   table->SetNumberOfElements(nof - 1);
535   int nod = table->NumberOfDeletedElements();
536   table->SetNumberOfDeletedElements(nod + 1);
537 
538   return Shrink(isolate, table);
539 }
540 
Allocate(Isolate * isolate,int capacity,AllocationType allocation)541 MaybeHandle<OrderedHashSet> OrderedHashSet::Allocate(
542     Isolate* isolate, int capacity, AllocationType allocation) {
543   return Base::Allocate(isolate, capacity, allocation);
544 }
545 
Allocate(Isolate * isolate,int capacity,AllocationType allocation)546 MaybeHandle<OrderedHashMap> OrderedHashMap::Allocate(
547     Isolate* isolate, int capacity, AllocationType allocation) {
548   return Base::Allocate(isolate, capacity, allocation);
549 }
550 
Allocate(Isolate * isolate,int capacity,AllocationType allocation)551 MaybeHandle<OrderedNameDictionary> OrderedNameDictionary::Allocate(
552     Isolate* isolate, int capacity, AllocationType allocation) {
553   MaybeHandle<OrderedNameDictionary> table_candidate =
554       Base::Allocate(isolate, capacity, allocation);
555   Handle<OrderedNameDictionary> table;
556   if (table_candidate.ToHandle(&table)) {
557     table->SetHash(PropertyArray::kNoHashSentinel);
558   }
559   return table_candidate;
560 }
561 
AllocateEmpty(Isolate * isolate,AllocationType allocation)562 MaybeHandle<OrderedHashSet> OrderedHashSet::AllocateEmpty(
563     Isolate* isolate, AllocationType allocation) {
564   RootIndex ri = RootIndex::kEmptyOrderedHashSet;
565   return Base::AllocateEmpty(isolate, allocation, ri);
566 }
567 
AllocateEmpty(Isolate * isolate,AllocationType allocation)568 MaybeHandle<OrderedHashMap> OrderedHashMap::AllocateEmpty(
569     Isolate* isolate, AllocationType allocation) {
570   RootIndex ri = RootIndex::kEmptyOrderedHashMap;
571   return Base::AllocateEmpty(isolate, allocation, ri);
572 }
573 
AllocateEmpty(Isolate * isolate,AllocationType allocation)574 MaybeHandle<OrderedNameDictionary> OrderedNameDictionary::AllocateEmpty(
575     Isolate* isolate, AllocationType allocation) {
576   RootIndex ri = RootIndex::kEmptyOrderedPropertyDictionary;
577   MaybeHandle<OrderedNameDictionary> table_candidate =
578       Base::AllocateEmpty(isolate, allocation, ri);
579   Handle<OrderedNameDictionary> table;
580   if (table_candidate.ToHandle(&table)) {
581     table->SetHash(PropertyArray::kNoHashSentinel);
582   }
583 
584   return table_candidate;
585 }
586 
587 template V8_EXPORT_PRIVATE MaybeHandle<OrderedHashSet>
588 OrderedHashTable<OrderedHashSet, 1>::EnsureGrowable(
589     Isolate* isolate, Handle<OrderedHashSet> table);
590 
591 template V8_EXPORT_PRIVATE Handle<OrderedHashSet>
592 OrderedHashTable<OrderedHashSet, 1>::Shrink(Isolate* isolate,
593                                             Handle<OrderedHashSet> table);
594 
595 template V8_EXPORT_PRIVATE Handle<OrderedHashSet>
596 OrderedHashTable<OrderedHashSet, 1>::Clear(Isolate* isolate,
597                                            Handle<OrderedHashSet> table);
598 
599 template V8_EXPORT_PRIVATE bool OrderedHashTable<OrderedHashSet, 1>::HasKey(
600     Isolate* isolate, OrderedHashSet table, Object key);
601 
602 template V8_EXPORT_PRIVATE bool OrderedHashTable<OrderedHashSet, 1>::Delete(
603     Isolate* isolate, OrderedHashSet table, Object key);
604 
605 template V8_EXPORT_PRIVATE InternalIndex
606 OrderedHashTable<OrderedHashSet, 1>::FindEntry(Isolate* isolate, Object key);
607 
608 template V8_EXPORT_PRIVATE MaybeHandle<OrderedHashMap>
609 OrderedHashTable<OrderedHashMap, 2>::EnsureGrowable(
610     Isolate* isolate, Handle<OrderedHashMap> table);
611 
612 template V8_EXPORT_PRIVATE Handle<OrderedHashMap>
613 OrderedHashTable<OrderedHashMap, 2>::Shrink(Isolate* isolate,
614                                             Handle<OrderedHashMap> table);
615 
616 template V8_EXPORT_PRIVATE Handle<OrderedHashMap>
617 OrderedHashTable<OrderedHashMap, 2>::Clear(Isolate* isolate,
618                                            Handle<OrderedHashMap> table);
619 
620 template V8_EXPORT_PRIVATE bool OrderedHashTable<OrderedHashMap, 2>::HasKey(
621     Isolate* isolate, OrderedHashMap table, Object key);
622 
623 template V8_EXPORT_PRIVATE bool OrderedHashTable<OrderedHashMap, 2>::Delete(
624     Isolate* isolate, OrderedHashMap table, Object key);
625 
626 template V8_EXPORT_PRIVATE InternalIndex
627 OrderedHashTable<OrderedHashMap, 2>::FindEntry(Isolate* isolate, Object key);
628 
629 template V8_EXPORT_PRIVATE Handle<OrderedNameDictionary>
630 OrderedHashTable<OrderedNameDictionary, 3>::Shrink(
631     Isolate* isolate, Handle<OrderedNameDictionary> table);
632 
633 template MaybeHandle<OrderedNameDictionary>
634 OrderedHashTable<OrderedNameDictionary, 3>::EnsureGrowable(
635     Isolate* isolate, Handle<OrderedNameDictionary> table);
636 
637 template <>
638 Handle<SmallOrderedHashSet>
Allocate(Isolate * isolate,int capacity,AllocationType allocation)639 SmallOrderedHashTable<SmallOrderedHashSet>::Allocate(
640     Isolate* isolate, int capacity, AllocationType allocation) {
641   return isolate->factory()->NewSmallOrderedHashSet(capacity, allocation);
642 }
643 
644 template <>
645 Handle<SmallOrderedHashMap>
Allocate(Isolate * isolate,int capacity,AllocationType allocation)646 SmallOrderedHashTable<SmallOrderedHashMap>::Allocate(
647     Isolate* isolate, int capacity, AllocationType allocation) {
648   return isolate->factory()->NewSmallOrderedHashMap(capacity, allocation);
649 }
650 
651 template <>
652 Handle<SmallOrderedNameDictionary>
Allocate(Isolate * isolate,int capacity,AllocationType allocation)653 SmallOrderedHashTable<SmallOrderedNameDictionary>::Allocate(
654     Isolate* isolate, int capacity, AllocationType allocation) {
655   return isolate->factory()->NewSmallOrderedNameDictionary(capacity,
656                                                            allocation);
657 }
658 
659 template <class Derived>
Initialize(Isolate * isolate,int capacity)660 void SmallOrderedHashTable<Derived>::Initialize(Isolate* isolate,
661                                                 int capacity) {
662   DisallowHeapAllocation no_gc;
663   int num_buckets = capacity / kLoadFactor;
664   int num_chains = capacity;
665 
666   SetNumberOfBuckets(num_buckets);
667   SetNumberOfElements(0);
668   SetNumberOfDeletedElements(0);
669   memset(reinterpret_cast<void*>(field_address(PaddingOffset())), 0,
670          PaddingSize());
671 
672   Address hashtable_start = GetHashTableStartAddress(capacity);
673   memset(reinterpret_cast<byte*>(hashtable_start), kNotFound,
674          num_buckets + num_chains);
675 
676   if (Heap::InYoungGeneration(*this)) {
677     MemsetTagged(RawField(DataTableStartOffset()),
678                  ReadOnlyRoots(isolate).the_hole_value(),
679                  capacity * Derived::kEntrySize);
680   } else {
681     for (int i = 0; i < capacity; i++) {
682       for (int j = 0; j < Derived::kEntrySize; j++) {
683         SetDataEntry(i, j, ReadOnlyRoots(isolate).the_hole_value());
684       }
685     }
686   }
687 
688 #ifdef DEBUG
689   for (int i = 0; i < num_buckets; ++i) {
690     DCHECK_EQ(kNotFound, GetFirstEntry(i));
691   }
692 
693   for (int i = 0; i < num_chains; ++i) {
694     DCHECK_EQ(kNotFound, GetNextEntry(i));
695   }
696 
697   for (int i = 0; i < capacity; ++i) {
698     for (int j = 0; j < Derived::kEntrySize; j++) {
699       DCHECK_EQ(ReadOnlyRoots(isolate).the_hole_value(), GetDataEntry(i, j));
700     }
701   }
702 #endif  // DEBUG
703 }
704 
Add(Isolate * isolate,Handle<SmallOrderedHashSet> table,Handle<Object> key)705 MaybeHandle<SmallOrderedHashSet> SmallOrderedHashSet::Add(
706     Isolate* isolate, Handle<SmallOrderedHashSet> table, Handle<Object> key) {
707   if (table->HasKey(isolate, key)) return table;
708 
709   if (table->UsedCapacity() >= table->Capacity()) {
710     MaybeHandle<SmallOrderedHashSet> new_table =
711         SmallOrderedHashSet::Grow(isolate, table);
712     if (!new_table.ToHandle(&table)) {
713       return MaybeHandle<SmallOrderedHashSet>();
714     }
715   }
716 
717   int hash = key->GetOrCreateHash(isolate).value();
718   int nof = table->NumberOfElements();
719 
720   // Read the existing bucket values.
721   int bucket = table->HashToBucket(hash);
722   int previous_entry = table->HashToFirstEntry(hash);
723 
724   // Insert a new entry at the end,
725   int new_entry = nof + table->NumberOfDeletedElements();
726 
727   table->SetDataEntry(new_entry, SmallOrderedHashSet::kKeyIndex, *key);
728   table->SetFirstEntry(bucket, new_entry);
729   table->SetNextEntry(new_entry, previous_entry);
730 
731   // and update book keeping.
732   table->SetNumberOfElements(nof + 1);
733 
734   return table;
735 }
736 
Delete(Isolate * isolate,SmallOrderedHashSet table,Object key)737 bool SmallOrderedHashSet::Delete(Isolate* isolate, SmallOrderedHashSet table,
738                                  Object key) {
739   return SmallOrderedHashTable<SmallOrderedHashSet>::Delete(isolate, table,
740                                                             key);
741 }
742 
HasKey(Isolate * isolate,Handle<Object> key)743 bool SmallOrderedHashSet::HasKey(Isolate* isolate, Handle<Object> key) {
744   return SmallOrderedHashTable<SmallOrderedHashSet>::HasKey(isolate, key);
745 }
746 
Add(Isolate * isolate,Handle<SmallOrderedHashMap> table,Handle<Object> key,Handle<Object> value)747 MaybeHandle<SmallOrderedHashMap> SmallOrderedHashMap::Add(
748     Isolate* isolate, Handle<SmallOrderedHashMap> table, Handle<Object> key,
749     Handle<Object> value) {
750   if (table->HasKey(isolate, key)) return table;
751 
752   if (table->UsedCapacity() >= table->Capacity()) {
753     MaybeHandle<SmallOrderedHashMap> new_table =
754         SmallOrderedHashMap::Grow(isolate, table);
755     if (!new_table.ToHandle(&table)) {
756       return MaybeHandle<SmallOrderedHashMap>();
757     }
758   }
759 
760   int hash = key->GetOrCreateHash(isolate).value();
761   int nof = table->NumberOfElements();
762 
763   // Read the existing bucket values.
764   int bucket = table->HashToBucket(hash);
765   int previous_entry = table->HashToFirstEntry(hash);
766 
767   // Insert a new entry at the end,
768   int new_entry = nof + table->NumberOfDeletedElements();
769 
770   table->SetDataEntry(new_entry, SmallOrderedHashMap::kValueIndex, *value);
771   table->SetDataEntry(new_entry, SmallOrderedHashMap::kKeyIndex, *key);
772   table->SetFirstEntry(bucket, new_entry);
773   table->SetNextEntry(new_entry, previous_entry);
774 
775   // and update book keeping.
776   table->SetNumberOfElements(nof + 1);
777 
778   return table;
779 }
780 
Delete(Isolate * isolate,SmallOrderedHashMap table,Object key)781 bool SmallOrderedHashMap::Delete(Isolate* isolate, SmallOrderedHashMap table,
782                                  Object key) {
783   return SmallOrderedHashTable<SmallOrderedHashMap>::Delete(isolate, table,
784                                                             key);
785 }
786 
HasKey(Isolate * isolate,Handle<Object> key)787 bool SmallOrderedHashMap::HasKey(Isolate* isolate, Handle<Object> key) {
788   return SmallOrderedHashTable<SmallOrderedHashMap>::HasKey(isolate, key);
789 }
790 
791 template <>
792 InternalIndex V8_EXPORT_PRIVATE
FindEntry(Isolate * isolate,Object key)793 SmallOrderedHashTable<SmallOrderedNameDictionary>::FindEntry(Isolate* isolate,
794                                                              Object key) {
795   DisallowHeapAllocation no_gc;
796   DCHECK(key.IsUniqueName());
797   Name raw_key = Name::cast(key);
798 
799   int raw_entry = HashToFirstEntry(raw_key.Hash());
800 
801   // Walk the chain in the bucket to find the key.
802   while (raw_entry != kNotFound) {
803     InternalIndex entry(raw_entry);
804     Object candidate_key = KeyAt(entry);
805     if (candidate_key == key) return entry;
806     raw_entry = GetNextEntry(raw_entry);
807   }
808 
809   return InternalIndex::NotFound();
810 }
811 
Add(Isolate * isolate,Handle<SmallOrderedNameDictionary> table,Handle<Name> key,Handle<Object> value,PropertyDetails details)812 MaybeHandle<SmallOrderedNameDictionary> SmallOrderedNameDictionary::Add(
813     Isolate* isolate, Handle<SmallOrderedNameDictionary> table,
814     Handle<Name> key, Handle<Object> value, PropertyDetails details) {
815   DCHECK(table->FindEntry(isolate, *key).is_not_found());
816 
817   if (table->UsedCapacity() >= table->Capacity()) {
818     MaybeHandle<SmallOrderedNameDictionary> new_table =
819         SmallOrderedNameDictionary::Grow(isolate, table);
820     if (!new_table.ToHandle(&table)) {
821       return MaybeHandle<SmallOrderedNameDictionary>();
822     }
823   }
824 
825   int nof = table->NumberOfElements();
826 
827   // Read the existing bucket values.
828   int hash = key->Hash();
829   int bucket = table->HashToBucket(hash);
830   int previous_entry = table->HashToFirstEntry(hash);
831 
832   // Insert a new entry at the end,
833   int new_entry = nof + table->NumberOfDeletedElements();
834 
835   table->SetDataEntry(new_entry, SmallOrderedNameDictionary::kValueIndex,
836                       *value);
837   table->SetDataEntry(new_entry, SmallOrderedNameDictionary::kKeyIndex, *key);
838 
839   // TODO(gsathya): PropertyDetails should be stored as part of the
840   // data table to save more memory.
841   table->SetDataEntry(new_entry,
842                       SmallOrderedNameDictionary::kPropertyDetailsIndex,
843                       details.AsSmi());
844   table->SetFirstEntry(bucket, new_entry);
845   table->SetNextEntry(new_entry, previous_entry);
846 
847   // and update book keeping.
848   table->SetNumberOfElements(nof + 1);
849 
850   return table;
851 }
852 
SetEntry(InternalIndex entry,Object key,Object value,PropertyDetails details)853 void SmallOrderedNameDictionary::SetEntry(InternalIndex entry, Object key,
854                                           Object value,
855                                           PropertyDetails details) {
856   int raw_entry = entry.as_int();
857   DCHECK_IMPLIES(!key.IsName(), key.IsTheHole());
858   SetDataEntry(raw_entry, SmallOrderedNameDictionary::kValueIndex, value);
859   SetDataEntry(raw_entry, SmallOrderedNameDictionary::kKeyIndex, key);
860 
861   // TODO(gsathya): PropertyDetails should be stored as part of the
862   // data table to save more memory.
863   SetDataEntry(raw_entry, SmallOrderedNameDictionary::kPropertyDetailsIndex,
864                details.AsSmi());
865 }
866 
867 template <class Derived>
HasKey(Isolate * isolate,Handle<Object> key)868 bool SmallOrderedHashTable<Derived>::HasKey(Isolate* isolate,
869                                             Handle<Object> key) {
870   DisallowHeapAllocation no_gc;
871   return FindEntry(isolate, *key).is_found();
872 }
873 
874 template <class Derived>
Delete(Isolate * isolate,Derived table,Object key)875 bool SmallOrderedHashTable<Derived>::Delete(Isolate* isolate, Derived table,
876                                             Object key) {
877   DisallowHeapAllocation no_gc;
878   InternalIndex entry = table.FindEntry(isolate, key);
879   if (entry.is_not_found()) return false;
880 
881   int nof = table.NumberOfElements();
882   int nod = table.NumberOfDeletedElements();
883 
884   Object hole = ReadOnlyRoots(isolate).the_hole_value();
885   for (int j = 0; j < Derived::kEntrySize; j++) {
886     table.SetDataEntry(entry.as_int(), j, hole);
887   }
888 
889   table.SetNumberOfElements(nof - 1);
890   table.SetNumberOfDeletedElements(nod + 1);
891 
892   return true;
893 }
894 
DeleteEntry(Isolate * isolate,Handle<SmallOrderedNameDictionary> table,InternalIndex entry)895 Handle<SmallOrderedNameDictionary> SmallOrderedNameDictionary::DeleteEntry(
896     Isolate* isolate, Handle<SmallOrderedNameDictionary> table,
897     InternalIndex entry) {
898   DCHECK(entry.is_found());
899   {
900     DisallowHeapAllocation no_gc;
901     Object hole = ReadOnlyRoots(isolate).the_hole_value();
902     PropertyDetails details = PropertyDetails::Empty();
903     table->SetEntry(entry, hole, hole, details);
904 
905     int nof = table->NumberOfElements();
906     table->SetNumberOfElements(nof - 1);
907     int nod = table->NumberOfDeletedElements();
908     table->SetNumberOfDeletedElements(nod + 1);
909   }
910   return Shrink(isolate, table);
911 }
912 
913 template <class Derived>
Rehash(Isolate * isolate,Handle<Derived> table,int new_capacity)914 Handle<Derived> SmallOrderedHashTable<Derived>::Rehash(Isolate* isolate,
915                                                        Handle<Derived> table,
916                                                        int new_capacity) {
917   DCHECK_GE(kMaxCapacity, new_capacity);
918 
919   Handle<Derived> new_table = SmallOrderedHashTable<Derived>::Allocate(
920       isolate, new_capacity,
921       Heap::InYoungGeneration(*table) ? AllocationType::kYoung
922                                       : AllocationType::kOld);
923   int new_entry = 0;
924 
925   {
926     DisallowHeapAllocation no_gc;
927     for (InternalIndex old_entry : table->IterateEntries()) {
928       Object key = table->KeyAt(old_entry);
929       if (key.IsTheHole(isolate)) continue;
930 
931       int hash = Smi::ToInt(key.GetHash());
932       int bucket = new_table->HashToBucket(hash);
933       int chain = new_table->GetFirstEntry(bucket);
934 
935       new_table->SetFirstEntry(bucket, new_entry);
936       new_table->SetNextEntry(new_entry, chain);
937 
938       for (int i = 0; i < Derived::kEntrySize; ++i) {
939         Object value = table->GetDataEntry(old_entry.as_int(), i);
940         new_table->SetDataEntry(new_entry, i, value);
941       }
942 
943       ++new_entry;
944     }
945 
946     new_table->SetNumberOfElements(table->NumberOfElements());
947   }
948   return new_table;
949 }
950 
Rehash(Isolate * isolate,Handle<SmallOrderedHashSet> table,int new_capacity)951 Handle<SmallOrderedHashSet> SmallOrderedHashSet::Rehash(
952     Isolate* isolate, Handle<SmallOrderedHashSet> table, int new_capacity) {
953   return SmallOrderedHashTable<SmallOrderedHashSet>::Rehash(isolate, table,
954                                                             new_capacity);
955 }
956 
Rehash(Isolate * isolate,Handle<SmallOrderedHashMap> table,int new_capacity)957 Handle<SmallOrderedHashMap> SmallOrderedHashMap::Rehash(
958     Isolate* isolate, Handle<SmallOrderedHashMap> table, int new_capacity) {
959   return SmallOrderedHashTable<SmallOrderedHashMap>::Rehash(isolate, table,
960                                                             new_capacity);
961 }
962 
Rehash(Isolate * isolate,Handle<SmallOrderedNameDictionary> table,int new_capacity)963 Handle<SmallOrderedNameDictionary> SmallOrderedNameDictionary::Rehash(
964     Isolate* isolate, Handle<SmallOrderedNameDictionary> table,
965     int new_capacity) {
966   Handle<SmallOrderedNameDictionary> new_table =
967       SmallOrderedHashTable<SmallOrderedNameDictionary>::Rehash(isolate, table,
968                                                                 new_capacity);
969   new_table->SetHash(table->Hash());
970   return new_table;
971 }
972 
973 template <class Derived>
Shrink(Isolate * isolate,Handle<Derived> table)974 Handle<Derived> SmallOrderedHashTable<Derived>::Shrink(Isolate* isolate,
975                                                        Handle<Derived> table) {
976   int nof = table->NumberOfElements();
977   int capacity = table->Capacity();
978   if (nof >= (capacity >> 2)) return table;
979   return Derived::Rehash(isolate, table, capacity / 2);
980 }
981 
982 template <class Derived>
Grow(Isolate * isolate,Handle<Derived> table)983 MaybeHandle<Derived> SmallOrderedHashTable<Derived>::Grow(
984     Isolate* isolate, Handle<Derived> table) {
985   int capacity = table->Capacity();
986   int new_capacity = capacity;
987 
988   // Don't need to grow if we can simply clear out deleted entries instead.
989   // TODO(gsathya): Compact in place, instead of allocating a new table.
990   if (table->NumberOfDeletedElements() < (capacity >> 1)) {
991     new_capacity = capacity << 1;
992 
993     // The max capacity of our table is 254. We special case for 256 to
994     // account for our growth strategy, otherwise we would only fill up
995     // to 128 entries in our table.
996     if (new_capacity == kGrowthHack) {
997       new_capacity = kMaxCapacity;
998     }
999 
1000     // We need to migrate to a bigger hash table.
1001     if (new_capacity > kMaxCapacity) {
1002       return MaybeHandle<Derived>();
1003     }
1004   }
1005 
1006   return Derived::Rehash(isolate, table, new_capacity);
1007 }
1008 
1009 template <class Derived>
FindEntry(Isolate * isolate,Object key)1010 InternalIndex SmallOrderedHashTable<Derived>::FindEntry(Isolate* isolate,
1011                                                         Object key) {
1012   DisallowHeapAllocation no_gc;
1013   Object hash = key.GetHash();
1014 
1015   if (hash.IsUndefined(isolate)) return InternalIndex::NotFound();
1016   int raw_entry = HashToFirstEntry(Smi::ToInt(hash));
1017 
1018   // Walk the chain in the bucket to find the key.
1019   while (raw_entry != kNotFound) {
1020     InternalIndex entry(raw_entry);
1021     Object candidate_key = KeyAt(entry);
1022     if (candidate_key.SameValueZero(key)) return entry;
1023     raw_entry = GetNextEntry(raw_entry);
1024   }
1025   return InternalIndex::NotFound();
1026 }
1027 
1028 template bool EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
1029     SmallOrderedHashTable<SmallOrderedHashSet>::HasKey(Isolate* isolate,
1030                                                        Handle<Object> key);
1031 template V8_EXPORT_PRIVATE Handle<SmallOrderedHashSet>
1032 SmallOrderedHashTable<SmallOrderedHashSet>::Rehash(
1033     Isolate* isolate, Handle<SmallOrderedHashSet> table, int new_capacity);
1034 template V8_EXPORT_PRIVATE Handle<SmallOrderedHashSet>
1035 SmallOrderedHashTable<SmallOrderedHashSet>::Shrink(
1036     Isolate* isolate, Handle<SmallOrderedHashSet> table);
1037 template V8_EXPORT_PRIVATE MaybeHandle<SmallOrderedHashSet>
1038 SmallOrderedHashTable<SmallOrderedHashSet>::Grow(
1039     Isolate* isolate, Handle<SmallOrderedHashSet> table);
1040 template V8_EXPORT_PRIVATE void
1041 SmallOrderedHashTable<SmallOrderedHashSet>::Initialize(Isolate* isolate,
1042                                                        int capacity);
1043 template V8_EXPORT_PRIVATE bool
1044 SmallOrderedHashTable<SmallOrderedHashSet>::Delete(Isolate* isolate,
1045                                                    SmallOrderedHashSet table,
1046                                                    Object key);
1047 
1048 template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) bool SmallOrderedHashTable<
1049     SmallOrderedHashMap>::HasKey(Isolate* isolate, Handle<Object> key);
1050 template V8_EXPORT_PRIVATE Handle<SmallOrderedHashMap>
1051 SmallOrderedHashTable<SmallOrderedHashMap>::Rehash(
1052     Isolate* isolate, Handle<SmallOrderedHashMap> table, int new_capacity);
1053 template V8_EXPORT_PRIVATE Handle<SmallOrderedHashMap>
1054 SmallOrderedHashTable<SmallOrderedHashMap>::Shrink(
1055     Isolate* isolate, Handle<SmallOrderedHashMap> table);
1056 template V8_EXPORT_PRIVATE MaybeHandle<SmallOrderedHashMap>
1057 SmallOrderedHashTable<SmallOrderedHashMap>::Grow(
1058     Isolate* isolate, Handle<SmallOrderedHashMap> table);
1059 template V8_EXPORT_PRIVATE void
1060 SmallOrderedHashTable<SmallOrderedHashMap>::Initialize(Isolate* isolate,
1061                                                        int capacity);
1062 
1063 template V8_EXPORT_PRIVATE bool
1064 SmallOrderedHashTable<SmallOrderedHashMap>::Delete(Isolate* isolate,
1065                                                    SmallOrderedHashMap table,
1066                                                    Object key);
1067 
1068 template V8_EXPORT_PRIVATE void
1069 SmallOrderedHashTable<SmallOrderedNameDictionary>::Initialize(Isolate* isolate,
1070                                                               int capacity);
1071 template V8_EXPORT_PRIVATE Handle<SmallOrderedNameDictionary>
1072 SmallOrderedHashTable<SmallOrderedNameDictionary>::Shrink(
1073     Isolate* isolate, Handle<SmallOrderedNameDictionary> table);
1074 
1075 template <class SmallTable, class LargeTable>
1076 MaybeHandle<HeapObject>
Allocate(Isolate * isolate,int capacity)1077 OrderedHashTableHandler<SmallTable, LargeTable>::Allocate(Isolate* isolate,
1078                                                           int capacity) {
1079   if (capacity < SmallTable::kMaxCapacity) {
1080     return SmallTable::Allocate(isolate, capacity);
1081   }
1082 
1083   return LargeTable::Allocate(isolate, capacity);
1084 }
1085 
1086 template V8_EXPORT_PRIVATE MaybeHandle<HeapObject>
1087 OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet>::Allocate(
1088     Isolate* isolate, int capacity);
1089 template V8_EXPORT_PRIVATE MaybeHandle<HeapObject>
1090 OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap>::Allocate(
1091     Isolate* isolate, int capacity);
1092 template V8_EXPORT_PRIVATE MaybeHandle<HeapObject>
1093 OrderedHashTableHandler<SmallOrderedNameDictionary,
1094                         OrderedNameDictionary>::Allocate(Isolate* isolate,
1095                                                          int capacity);
1096 
1097 template <class SmallTable, class LargeTable>
Delete(Isolate * isolate,Handle<HeapObject> table,Handle<Object> key)1098 bool OrderedHashTableHandler<SmallTable, LargeTable>::Delete(
1099     Isolate* isolate, Handle<HeapObject> table, Handle<Object> key) {
1100   if (SmallTable::Is(table)) {
1101     return SmallTable::Delete(isolate, *Handle<SmallTable>::cast(table), *key);
1102   }
1103 
1104   DCHECK(LargeTable::Is(table));
1105   // Note: Once we migrate to the a big hash table, we never migrate
1106   // down to a smaller hash table.
1107   return LargeTable::Delete(isolate, *Handle<LargeTable>::cast(table), *key);
1108 }
1109 
1110 template <class SmallTable, class LargeTable>
HasKey(Isolate * isolate,Handle<HeapObject> table,Handle<Object> key)1111 bool OrderedHashTableHandler<SmallTable, LargeTable>::HasKey(
1112     Isolate* isolate, Handle<HeapObject> table, Handle<Object> key) {
1113   if (SmallTable::Is(table)) {
1114     return Handle<SmallTable>::cast(table)->HasKey(isolate, key);
1115   }
1116 
1117   DCHECK(LargeTable::Is(table));
1118   return LargeTable::HasKey(isolate, LargeTable::cast(*table), *key);
1119 }
1120 
1121 template bool
1122 OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet>::HasKey(
1123     Isolate* isolate, Handle<HeapObject> table, Handle<Object> key);
1124 template bool
1125 OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap>::HasKey(
1126     Isolate* isolate, Handle<HeapObject> table, Handle<Object> key);
1127 
1128 template bool
1129 OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet>::Delete(
1130     Isolate* isolate, Handle<HeapObject> table, Handle<Object> key);
1131 template bool
1132 OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap>::Delete(
1133     Isolate* isolate, Handle<HeapObject> table, Handle<Object> key);
1134 template bool
1135 OrderedHashTableHandler<SmallOrderedNameDictionary,
1136                         OrderedNameDictionary>::Delete(Isolate* isolate,
1137                                                        Handle<HeapObject> table,
1138                                                        Handle<Object> key);
1139 
AdjustRepresentation(Isolate * isolate,Handle<SmallOrderedHashMap> table)1140 MaybeHandle<OrderedHashMap> OrderedHashMapHandler::AdjustRepresentation(
1141     Isolate* isolate, Handle<SmallOrderedHashMap> table) {
1142   MaybeHandle<OrderedHashMap> new_table_candidate =
1143       OrderedHashMap::Allocate(isolate, OrderedHashTableMinSize);
1144   Handle<OrderedHashMap> new_table;
1145   if (!new_table_candidate.ToHandle(&new_table)) {
1146     return new_table_candidate;
1147   }
1148 
1149   // TODO(gsathya): Optimize the lookup to not re calc offsets. Also,
1150   // unhandlify this code as we preallocate the new backing store with
1151   // the proper capacity.
1152   for (InternalIndex entry : table->IterateEntries()) {
1153     Handle<Object> key = handle(table->KeyAt(entry), isolate);
1154     if (key->IsTheHole(isolate)) continue;
1155     Handle<Object> value = handle(
1156         table->GetDataEntry(entry.as_int(), SmallOrderedHashMap::kValueIndex),
1157         isolate);
1158     new_table_candidate = OrderedHashMap::Add(isolate, new_table, key, value);
1159     if (!new_table_candidate.ToHandle(&new_table)) {
1160       return new_table_candidate;
1161     }
1162   }
1163 
1164   return new_table_candidate;
1165 }
1166 
AdjustRepresentation(Isolate * isolate,Handle<SmallOrderedHashSet> table)1167 MaybeHandle<OrderedHashSet> OrderedHashSetHandler::AdjustRepresentation(
1168     Isolate* isolate, Handle<SmallOrderedHashSet> table) {
1169   MaybeHandle<OrderedHashSet> new_table_candidate =
1170       OrderedHashSet::Allocate(isolate, OrderedHashTableMinSize);
1171   Handle<OrderedHashSet> new_table;
1172   if (!new_table_candidate.ToHandle(&new_table)) {
1173     return new_table_candidate;
1174   }
1175 
1176   // TODO(gsathya): Optimize the lookup to not re calc offsets. Also,
1177   // unhandlify this code as we preallocate the new backing store with
1178   // the proper capacity.
1179   for (InternalIndex entry : table->IterateEntries()) {
1180     Handle<Object> key = handle(table->KeyAt(entry), isolate);
1181     if (key->IsTheHole(isolate)) continue;
1182     new_table_candidate = OrderedHashSet::Add(isolate, new_table, key);
1183     if (!new_table_candidate.ToHandle(&new_table)) {
1184       return new_table_candidate;
1185     }
1186   }
1187 
1188   return new_table_candidate;
1189 }
1190 
1191 MaybeHandle<OrderedNameDictionary>
AdjustRepresentation(Isolate * isolate,Handle<SmallOrderedNameDictionary> table)1192 OrderedNameDictionaryHandler::AdjustRepresentation(
1193     Isolate* isolate, Handle<SmallOrderedNameDictionary> table) {
1194   MaybeHandle<OrderedNameDictionary> new_table_candidate =
1195       OrderedNameDictionary::Allocate(isolate, OrderedHashTableMinSize);
1196   Handle<OrderedNameDictionary> new_table;
1197   if (!new_table_candidate.ToHandle(&new_table)) {
1198     return new_table_candidate;
1199   }
1200 
1201   // TODO(gsathya): Optimize the lookup to not re calc offsets. Also,
1202   // unhandlify this code as we preallocate the new backing store with
1203   // the proper capacity.
1204   for (InternalIndex entry : table->IterateEntries()) {
1205     Handle<Name> key(Name::cast(table->KeyAt(entry)), isolate);
1206     if (key->IsTheHole(isolate)) continue;
1207     Handle<Object> value(table->ValueAt(entry), isolate);
1208     PropertyDetails details = table->DetailsAt(entry);
1209     new_table_candidate =
1210         OrderedNameDictionary::Add(isolate, new_table, key, value, details);
1211     if (!new_table_candidate.ToHandle(&new_table)) {
1212       return new_table_candidate;
1213     }
1214   }
1215 
1216   return new_table_candidate;
1217 }
1218 
Add(Isolate * isolate,Handle<HeapObject> table,Handle<Object> key,Handle<Object> value)1219 MaybeHandle<HeapObject> OrderedHashMapHandler::Add(Isolate* isolate,
1220                                                    Handle<HeapObject> table,
1221                                                    Handle<Object> key,
1222                                                    Handle<Object> value) {
1223   if (table->IsSmallOrderedHashMap()) {
1224     Handle<SmallOrderedHashMap> small_map =
1225         Handle<SmallOrderedHashMap>::cast(table);
1226     MaybeHandle<SmallOrderedHashMap> new_map =
1227         SmallOrderedHashMap::Add(isolate, small_map, key, value);
1228     if (!new_map.is_null()) return new_map.ToHandleChecked();
1229 
1230     // We couldn't add to the small table, let's migrate to the
1231     // big table.
1232     MaybeHandle<OrderedHashMap> table_candidate =
1233         OrderedHashMapHandler::AdjustRepresentation(isolate, small_map);
1234     if (!table_candidate.ToHandle(&table)) {
1235       return table_candidate;
1236     }
1237   }
1238 
1239   DCHECK(table->IsOrderedHashMap());
1240   return OrderedHashMap::Add(isolate, Handle<OrderedHashMap>::cast(table), key,
1241                              value);
1242 }
1243 
Add(Isolate * isolate,Handle<HeapObject> table,Handle<Object> key)1244 MaybeHandle<HeapObject> OrderedHashSetHandler::Add(Isolate* isolate,
1245                                                    Handle<HeapObject> table,
1246                                                    Handle<Object> key) {
1247   if (table->IsSmallOrderedHashSet()) {
1248     Handle<SmallOrderedHashSet> small_set =
1249         Handle<SmallOrderedHashSet>::cast(table);
1250     MaybeHandle<SmallOrderedHashSet> new_set =
1251         SmallOrderedHashSet::Add(isolate, small_set, key);
1252     if (!new_set.is_null()) return new_set.ToHandleChecked();
1253 
1254     // We couldn't add to the small table, let's migrate to the
1255     // big table.
1256     MaybeHandle<OrderedHashSet> table_candidate =
1257         OrderedHashSetHandler::AdjustRepresentation(isolate, small_set);
1258     if (!table_candidate.ToHandle(&table)) {
1259       return table_candidate;
1260     }
1261   }
1262 
1263   DCHECK(table->IsOrderedHashSet());
1264   return OrderedHashSet::Add(isolate, Handle<OrderedHashSet>::cast(table), key);
1265 }
1266 
Add(Isolate * isolate,Handle<HeapObject> table,Handle<Name> key,Handle<Object> value,PropertyDetails details)1267 MaybeHandle<HeapObject> OrderedNameDictionaryHandler::Add(
1268     Isolate* isolate, Handle<HeapObject> table, Handle<Name> key,
1269     Handle<Object> value, PropertyDetails details) {
1270   if (table->IsSmallOrderedNameDictionary()) {
1271     Handle<SmallOrderedNameDictionary> small_dict =
1272         Handle<SmallOrderedNameDictionary>::cast(table);
1273     MaybeHandle<SmallOrderedNameDictionary> new_dict =
1274         SmallOrderedNameDictionary::Add(isolate, small_dict, key, value,
1275                                         details);
1276     if (!new_dict.is_null()) return new_dict.ToHandleChecked();
1277 
1278     // We couldn't add to the small table, let's migrate to the
1279     // big table.
1280     MaybeHandle<OrderedNameDictionary> table_candidate =
1281         OrderedNameDictionaryHandler::AdjustRepresentation(isolate, small_dict);
1282     if (!table_candidate.ToHandle(&table)) {
1283       return table_candidate;
1284     }
1285   }
1286 
1287   DCHECK(table->IsOrderedNameDictionary());
1288   return OrderedNameDictionary::Add(
1289       isolate, Handle<OrderedNameDictionary>::cast(table), key, value, details);
1290 }
1291 
SetEntry(HeapObject table,InternalIndex entry,Object key,Object value,PropertyDetails details)1292 void OrderedNameDictionaryHandler::SetEntry(HeapObject table,
1293                                             InternalIndex entry, Object key,
1294                                             Object value,
1295                                             PropertyDetails details) {
1296   DisallowHeapAllocation no_gc;
1297   if (table.IsSmallOrderedNameDictionary()) {
1298     return SmallOrderedNameDictionary::cast(table).SetEntry(entry, key, value,
1299                                                             details);
1300   }
1301 
1302   DCHECK(table.IsOrderedNameDictionary());
1303   return OrderedNameDictionary::cast(table).SetEntry(InternalIndex(entry), key,
1304                                                      value, details);
1305 }
1306 
FindEntry(Isolate * isolate,HeapObject table,Name key)1307 InternalIndex OrderedNameDictionaryHandler::FindEntry(Isolate* isolate,
1308                                                       HeapObject table,
1309                                                       Name key) {
1310   DisallowHeapAllocation no_gc;
1311   if (table.IsSmallOrderedNameDictionary()) {
1312     return SmallOrderedNameDictionary::cast(table).FindEntry(isolate, key);
1313   }
1314 
1315   DCHECK(table.IsOrderedNameDictionary());
1316   return OrderedNameDictionary::cast(table).FindEntry(isolate, key);
1317 }
1318 
ValueAt(HeapObject table,InternalIndex entry)1319 Object OrderedNameDictionaryHandler::ValueAt(HeapObject table,
1320                                              InternalIndex entry) {
1321   if (table.IsSmallOrderedNameDictionary()) {
1322     return SmallOrderedNameDictionary::cast(table).ValueAt(entry);
1323   }
1324 
1325   DCHECK(table.IsOrderedNameDictionary());
1326   return OrderedNameDictionary::cast(table).ValueAt(entry);
1327 }
1328 
ValueAtPut(HeapObject table,InternalIndex entry,Object value)1329 void OrderedNameDictionaryHandler::ValueAtPut(HeapObject table,
1330                                               InternalIndex entry,
1331                                               Object value) {
1332   if (table.IsSmallOrderedNameDictionary()) {
1333     return SmallOrderedNameDictionary::cast(table).ValueAtPut(entry, value);
1334   }
1335 
1336   DCHECK(table.IsOrderedNameDictionary());
1337   OrderedNameDictionary::cast(table).ValueAtPut(entry, value);
1338 }
1339 
DetailsAt(HeapObject table,InternalIndex entry)1340 PropertyDetails OrderedNameDictionaryHandler::DetailsAt(HeapObject table,
1341                                                         InternalIndex entry) {
1342   if (table.IsSmallOrderedNameDictionary()) {
1343     return SmallOrderedNameDictionary::cast(table).DetailsAt(entry);
1344   }
1345 
1346   DCHECK(table.IsOrderedNameDictionary());
1347   return OrderedNameDictionary::cast(table).DetailsAt(entry);
1348 }
1349 
DetailsAtPut(HeapObject table,InternalIndex entry,PropertyDetails details)1350 void OrderedNameDictionaryHandler::DetailsAtPut(HeapObject table,
1351                                                 InternalIndex entry,
1352                                                 PropertyDetails details) {
1353   if (table.IsSmallOrderedNameDictionary()) {
1354     return SmallOrderedNameDictionary::cast(table).DetailsAtPut(entry, details);
1355   }
1356 
1357   DCHECK(table.IsOrderedNameDictionary());
1358   OrderedNameDictionary::cast(table).DetailsAtPut(entry, details);
1359 }
1360 
Hash(HeapObject table)1361 int OrderedNameDictionaryHandler::Hash(HeapObject table) {
1362   if (table.IsSmallOrderedNameDictionary()) {
1363     return SmallOrderedNameDictionary::cast(table).Hash();
1364   }
1365 
1366   DCHECK(table.IsOrderedNameDictionary());
1367   return OrderedNameDictionary::cast(table).Hash();
1368 }
1369 
SetHash(HeapObject table,int hash)1370 void OrderedNameDictionaryHandler::SetHash(HeapObject table, int hash) {
1371   if (table.IsSmallOrderedNameDictionary()) {
1372     return SmallOrderedNameDictionary::cast(table).SetHash(hash);
1373   }
1374 
1375   DCHECK(table.IsOrderedNameDictionary());
1376   OrderedNameDictionary::cast(table).SetHash(hash);
1377 }
1378 
KeyAt(HeapObject table,InternalIndex entry)1379 Name OrderedNameDictionaryHandler::KeyAt(HeapObject table,
1380                                          InternalIndex entry) {
1381   if (table.IsSmallOrderedNameDictionary()) {
1382     return Name::cast(SmallOrderedNameDictionary::cast(table).KeyAt(entry));
1383   }
1384 
1385   return Name::cast(
1386       OrderedNameDictionary::cast(table).KeyAt(InternalIndex(entry)));
1387 }
1388 
NumberOfElements(HeapObject table)1389 int OrderedNameDictionaryHandler::NumberOfElements(HeapObject table) {
1390   if (table.IsSmallOrderedNameDictionary()) {
1391     return SmallOrderedNameDictionary::cast(table).NumberOfElements();
1392   }
1393 
1394   return OrderedNameDictionary::cast(table).NumberOfElements();
1395 }
1396 
Capacity(HeapObject table)1397 int OrderedNameDictionaryHandler::Capacity(HeapObject table) {
1398   if (table.IsSmallOrderedNameDictionary()) {
1399     return SmallOrderedNameDictionary::cast(table).Capacity();
1400   }
1401 
1402   return OrderedNameDictionary::cast(table).Capacity();
1403 }
1404 
Shrink(Isolate * isolate,Handle<HeapObject> table)1405 Handle<HeapObject> OrderedNameDictionaryHandler::Shrink(
1406     Isolate* isolate, Handle<HeapObject> table) {
1407   if (table->IsSmallOrderedNameDictionary()) {
1408     Handle<SmallOrderedNameDictionary> small_dict =
1409         Handle<SmallOrderedNameDictionary>::cast(table);
1410     return SmallOrderedNameDictionary::Shrink(isolate, small_dict);
1411   }
1412 
1413   Handle<OrderedNameDictionary> large_dict =
1414       Handle<OrderedNameDictionary>::cast(table);
1415   return OrderedNameDictionary::Shrink(isolate, large_dict);
1416 }
1417 
DeleteEntry(Isolate * isolate,Handle<HeapObject> table,InternalIndex entry)1418 Handle<HeapObject> OrderedNameDictionaryHandler::DeleteEntry(
1419     Isolate* isolate, Handle<HeapObject> table, InternalIndex entry) {
1420   DisallowHeapAllocation no_gc;
1421   if (table->IsSmallOrderedNameDictionary()) {
1422     Handle<SmallOrderedNameDictionary> small_dict =
1423         Handle<SmallOrderedNameDictionary>::cast(table);
1424     return SmallOrderedNameDictionary::DeleteEntry(isolate, small_dict, entry);
1425   }
1426 
1427   Handle<OrderedNameDictionary> large_dict =
1428       Handle<OrderedNameDictionary>::cast(table);
1429   return OrderedNameDictionary::DeleteEntry(isolate, large_dict,
1430                                             InternalIndex(entry));
1431 }
1432 
1433 template <class Derived, class TableType>
Transition()1434 void OrderedHashTableIterator<Derived, TableType>::Transition() {
1435   DisallowHeapAllocation no_allocation;
1436   TableType table = TableType::cast(this->table());
1437   if (!table.IsObsolete()) return;
1438 
1439   int index = Smi::ToInt(this->index());
1440   DCHECK_LE(0, index);
1441   while (table.IsObsolete()) {
1442     TableType next_table = table.NextTable();
1443 
1444     if (index > 0) {
1445       int nod = table.NumberOfDeletedElements();
1446 
1447       if (nod == TableType::kClearedTableSentinel) {
1448         index = 0;
1449       } else {
1450         int old_index = index;
1451         for (int i = 0; i < nod; ++i) {
1452           int removed_index = table.RemovedIndexAt(i);
1453           if (removed_index >= old_index) break;
1454           --index;
1455         }
1456       }
1457     }
1458 
1459     table = next_table;
1460   }
1461 
1462   set_table(table);
1463   set_index(Smi::FromInt(index));
1464 }
1465 
1466 template <class Derived, class TableType>
HasMore()1467 bool OrderedHashTableIterator<Derived, TableType>::HasMore() {
1468   DisallowHeapAllocation no_allocation;
1469   ReadOnlyRoots ro_roots = GetReadOnlyRoots();
1470 
1471   Transition();
1472 
1473   TableType table = TableType::cast(this->table());
1474   int index = Smi::ToInt(this->index());
1475   int used_capacity = table.UsedCapacity();
1476 
1477   while (index < used_capacity &&
1478          table.KeyAt(InternalIndex(index)).IsTheHole(ro_roots)) {
1479     index++;
1480   }
1481 
1482   set_index(Smi::FromInt(index));
1483 
1484   if (index < used_capacity) return true;
1485 
1486   set_table(TableType::GetEmpty(ro_roots));
1487   return false;
1488 }
1489 
1490 template bool
1491 OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::HasMore();
1492 
1493 template void
1494 OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::MoveNext();
1495 
1496 template Object
1497 OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::CurrentKey();
1498 
1499 template void
1500 OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Transition();
1501 
1502 template bool
1503 OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::HasMore();
1504 
1505 template void
1506 OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::MoveNext();
1507 
1508 template Object
1509 OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::CurrentKey();
1510 
1511 template void
1512 OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Transition();
1513 
1514 }  // namespace internal
1515 }  // namespace v8
1516