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