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