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