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 #ifndef V8_OBJECTS_ORDERED_HASH_TABLE_H_ 6 #define V8_OBJECTS_ORDERED_HASH_TABLE_H_ 7 8 #include "src/globals.h" 9 #include "src/objects/fixed-array.h" 10 11 // Has to be the last include (doesn't have include guards): 12 #include "src/objects/object-macros.h" 13 14 namespace v8 { 15 namespace internal { 16 17 // Non-templatized base class for {OrderedHashTable}s. 18 // TODO(hash): Unify this with the HashTableBase above. 19 class OrderedHashTableBase : public FixedArray { 20 public: 21 static const int kNotFound = -1; 22 static const int kMinCapacity = 4; 23 24 static const int kNumberOfElementsIndex = 0; 25 // The next table is stored at the same index as the nof elements. 26 static const int kNextTableIndex = kNumberOfElementsIndex; 27 static const int kNumberOfDeletedElementsIndex = kNumberOfElementsIndex + 1; 28 static const int kNumberOfBucketsIndex = kNumberOfDeletedElementsIndex + 1; 29 static const int kHashTableStartIndex = kNumberOfBucketsIndex + 1; 30 static const int kRemovedHolesIndex = kHashTableStartIndex; 31 32 static constexpr const int kNumberOfElementsOffset = 33 FixedArray::OffsetOfElementAt(kNumberOfElementsIndex); 34 static constexpr const int kNextTableOffset = 35 FixedArray::OffsetOfElementAt(kNextTableIndex); 36 static constexpr const int kNumberOfDeletedElementsOffset = 37 FixedArray::OffsetOfElementAt(kNumberOfDeletedElementsIndex); 38 static constexpr const int kNumberOfBucketsOffset = 39 FixedArray::OffsetOfElementAt(kNumberOfBucketsIndex); 40 static constexpr const int kHashTableStartOffset = 41 FixedArray::OffsetOfElementAt(kHashTableStartIndex); 42 43 static const int kLoadFactor = 2; 44 45 // NumberOfDeletedElements is set to kClearedTableSentinel when 46 // the table is cleared, which allows iterator transitions to 47 // optimize that case. 48 static const int kClearedTableSentinel = -1; 49 }; 50 51 // OrderedHashTable is a HashTable with Object keys that preserves 52 // insertion order. There are Map and Set interfaces (OrderedHashMap 53 // and OrderedHashTable, below). It is meant to be used by JSMap/JSSet. 54 // 55 // Only Object* keys are supported, with Object::SameValueZero() used as the 56 // equality operator and Object::GetHash() for the hash function. 57 // 58 // Based on the "Deterministic Hash Table" as described by Jason Orendorff at 59 // https://wiki.mozilla.org/User:Jorend/Deterministic_hash_tables 60 // Originally attributed to Tyler Close. 61 // 62 // Memory layout: 63 // [0]: element count 64 // [1]: deleted element count 65 // [2]: bucket count 66 // [3..(3 + NumberOfBuckets() - 1)]: "hash table", where each item is an 67 // offset into the data table (see below) where the 68 // first item in this bucket is stored. 69 // [3 + NumberOfBuckets()..length]: "data table", an array of length 70 // Capacity() * kEntrySize, where the first entrysize 71 // items are handled by the derived class and the 72 // item at kChainOffset is another entry into the 73 // data table indicating the next entry in this hash 74 // bucket. 75 // 76 // When we transition the table to a new version we obsolete it and reuse parts 77 // of the memory to store information how to transition an iterator to the new 78 // table: 79 // 80 // Memory layout for obsolete table: 81 // [0]: bucket count 82 // [1]: Next newer table 83 // [2]: Number of removed holes or -1 when the table was cleared. 84 // [3..(3 + NumberOfRemovedHoles() - 1)]: The indexes of the removed holes. 85 // [3 + NumberOfRemovedHoles()..length]: Not used 86 // 87 template <class Derived, int entrysize> 88 class OrderedHashTable : public OrderedHashTableBase { 89 public: 90 // Returns an OrderedHashTable with a capacity of at least |capacity|. 91 static Handle<Derived> Allocate(Isolate* isolate, int capacity, 92 PretenureFlag pretenure = NOT_TENURED); 93 94 // Returns an OrderedHashTable (possibly |table|) with enough space 95 // to add at least one new element. 96 static Handle<Derived> EnsureGrowable(Isolate* isolate, 97 Handle<Derived> table); 98 99 // Returns an OrderedHashTable (possibly |table|) that's shrunken 100 // if possible. 101 static Handle<Derived> Shrink(Isolate* isolate, Handle<Derived> table); 102 103 // Returns a new empty OrderedHashTable and records the clearing so that 104 // existing iterators can be updated. 105 static Handle<Derived> Clear(Isolate* isolate, Handle<Derived> table); 106 107 // Returns true if the OrderedHashTable contains the key 108 static bool HasKey(Isolate* isolate, Derived* table, Object* key); 109 110 // Returns a true value if the OrderedHashTable contains the key and 111 // the key has been deleted. This does not shrink the table. 112 static bool Delete(Isolate* isolate, Derived* table, Object* key); 113 NumberOfElements()114 int NumberOfElements() const { 115 return Smi::ToInt(get(kNumberOfElementsIndex)); 116 } 117 NumberOfDeletedElements()118 int NumberOfDeletedElements() const { 119 return Smi::ToInt(get(kNumberOfDeletedElementsIndex)); 120 } 121 122 // Returns the number of contiguous entries in the data table, starting at 0, 123 // that either are real entries or have been deleted. UsedCapacity()124 int UsedCapacity() const { 125 return NumberOfElements() + NumberOfDeletedElements(); 126 } 127 NumberOfBuckets()128 int NumberOfBuckets() const { return Smi::ToInt(get(kNumberOfBucketsIndex)); } 129 130 // Returns an index into |this| for the given entry. EntryToIndex(int entry)131 int EntryToIndex(int entry) { 132 return kHashTableStartIndex + NumberOfBuckets() + (entry * kEntrySize); 133 } 134 HashToBucket(int hash)135 int HashToBucket(int hash) { return hash & (NumberOfBuckets() - 1); } 136 HashToEntry(int hash)137 int HashToEntry(int hash) { 138 int bucket = HashToBucket(hash); 139 Object* entry = this->get(kHashTableStartIndex + bucket); 140 return Smi::ToInt(entry); 141 } 142 KeyToFirstEntry(Isolate * isolate,Object * key)143 int KeyToFirstEntry(Isolate* isolate, Object* key) { 144 // This special cases for Smi, so that we avoid the HandleScope 145 // creation below. 146 if (key->IsSmi()) { 147 uint32_t hash = ComputeIntegerHash(Smi::ToInt(key)); 148 return HashToEntry(hash & Smi::kMaxValue); 149 } 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 kNotFound; 154 return HashToEntry(Smi::ToInt(hash)); 155 } 156 FindEntry(Isolate * isolate,Object * key)157 int FindEntry(Isolate* isolate, Object* key) { 158 int entry = KeyToFirstEntry(isolate, key); 159 // Walk the chain in the bucket to find the key. 160 while (entry != kNotFound) { 161 Object* candidate_key = KeyAt(entry); 162 if (candidate_key->SameValueZero(key)) break; 163 entry = NextChainEntry(entry); 164 } 165 166 return entry; 167 } 168 NextChainEntry(int entry)169 int NextChainEntry(int entry) { 170 Object* next_entry = get(EntryToIndex(entry) + kChainOffset); 171 return Smi::ToInt(next_entry); 172 } 173 174 // use KeyAt(i)->IsTheHole(isolate) to determine if this is a deleted entry. KeyAt(int entry)175 Object* KeyAt(int entry) { 176 DCHECK_LT(entry, this->UsedCapacity()); 177 return get(EntryToIndex(entry)); 178 } 179 IsObsolete()180 bool IsObsolete() { return !get(kNextTableIndex)->IsSmi(); } 181 182 // The next newer table. This is only valid if the table is obsolete. NextTable()183 Derived* NextTable() { return Derived::cast(get(kNextTableIndex)); } 184 185 // When the table is obsolete we store the indexes of the removed holes. RemovedIndexAt(int index)186 int RemovedIndexAt(int index) { 187 return Smi::ToInt(get(kRemovedHolesIndex + index)); 188 } 189 190 static const int kEntrySize = entrysize + 1; 191 static const int kChainOffset = entrysize; 192 193 static const int kMaxCapacity = 194 (FixedArray::kMaxLength - kHashTableStartIndex) / 195 (1 + (kEntrySize * kLoadFactor)); 196 197 protected: 198 static Handle<Derived> Rehash(Isolate* isolate, Handle<Derived> table, 199 int new_capacity); 200 SetNumberOfBuckets(int num)201 void SetNumberOfBuckets(int num) { 202 set(kNumberOfBucketsIndex, Smi::FromInt(num)); 203 } 204 SetNumberOfElements(int num)205 void SetNumberOfElements(int num) { 206 set(kNumberOfElementsIndex, Smi::FromInt(num)); 207 } 208 SetNumberOfDeletedElements(int num)209 void SetNumberOfDeletedElements(int num) { 210 set(kNumberOfDeletedElementsIndex, Smi::FromInt(num)); 211 } 212 213 // Returns the number elements that can fit into the allocated buffer. Capacity()214 int Capacity() { return NumberOfBuckets() * kLoadFactor; } 215 SetNextTable(Derived * next_table)216 void SetNextTable(Derived* next_table) { set(kNextTableIndex, next_table); } 217 SetRemovedIndexAt(int index,int removed_index)218 void SetRemovedIndexAt(int index, int removed_index) { 219 return set(kRemovedHolesIndex + index, Smi::FromInt(removed_index)); 220 } 221 }; 222 223 class OrderedHashSet : public OrderedHashTable<OrderedHashSet, 1> { 224 public: 225 DECL_CAST(OrderedHashSet) 226 227 static Handle<OrderedHashSet> Add(Isolate* isolate, 228 Handle<OrderedHashSet> table, 229 Handle<Object> value); 230 static Handle<FixedArray> ConvertToKeysArray(Isolate* isolate, 231 Handle<OrderedHashSet> table, 232 GetKeysConversion convert); 233 static HeapObject* GetEmpty(ReadOnlyRoots ro_roots); 234 static inline int GetMapRootIndex(); 235 static inline bool Is(Handle<HeapObject> table); 236 }; 237 238 class OrderedHashMap : public OrderedHashTable<OrderedHashMap, 2> { 239 public: 240 DECL_CAST(OrderedHashMap) 241 242 // Returns a value if the OrderedHashMap contains the key, otherwise 243 // returns undefined. 244 static Handle<OrderedHashMap> Add(Isolate* isolate, 245 Handle<OrderedHashMap> table, 246 Handle<Object> key, Handle<Object> value); 247 Object* ValueAt(int entry); 248 249 static Object* GetHash(Isolate* isolate, Object* key); 250 251 static HeapObject* GetEmpty(ReadOnlyRoots ro_roots); 252 static inline int GetMapRootIndex(); 253 static inline bool Is(Handle<HeapObject> table); 254 255 static const int kValueOffset = 1; 256 }; 257 258 // This is similar to the OrderedHashTable, except for the memory 259 // layout where we use byte instead of Smi. The max capacity of this 260 // is only 254, we transition to an OrderedHashTable beyond that 261 // limit. 262 // 263 // Each bucket and chain value is a byte long. The padding exists so 264 // that the DataTable entries start aligned. A bucket or chain value 265 // of 255 is used to denote an unknown entry. 266 // 267 // Memory layout: [ Header ] [ Padding ] [ DataTable ] [ HashTable ] [ Chains ] 268 // 269 // The index are represented as bytes, on a 64 bit machine with 270 // kEntrySize = 1, capacity = 4 and entries = 2: 271 // 272 // [ Header ] : 273 // [0] : Number of elements 274 // [1] : Number of deleted elements 275 // [2] : Number of buckets 276 // 277 // [ Padding ] : 278 // [3 .. 7] : Padding 279 // 280 // [ DataTable ] : 281 // [8 .. 15] : Entry 1 282 // [16 .. 23] : Entry 2 283 // [24 .. 31] : empty 284 // [32 .. 39] : empty 285 // 286 // [ HashTable ] : 287 // [40] : First chain-link for bucket 1 288 // [41] : empty 289 // 290 // [ Chains ] : 291 // [42] : Next chain link for bucket 1 292 // [43] : empty 293 // [44] : empty 294 // [45] : empty 295 // 296 template <class Derived> 297 class SmallOrderedHashTable : public HeapObject { 298 public: 299 // Offset points to a relative location in the table 300 typedef int Offset; 301 302 // ByteIndex points to a index in the table that needs to be 303 // converted to an Offset. 304 typedef int ByteIndex; 305 306 void Initialize(Isolate* isolate, int capacity); 307 308 static Handle<Derived> Allocate(Isolate* isolate, int capacity, 309 PretenureFlag pretenure = NOT_TENURED); 310 311 // Returns a true if the OrderedHashTable contains the key 312 bool HasKey(Isolate* isolate, Handle<Object> key); 313 314 // Returns a true value if the table contains the key and 315 // the key has been deleted. This does not shrink the table. 316 static bool Delete(Isolate* isolate, Derived* table, Object* key); 317 318 // Returns an SmallOrderedHashTable (possibly |table|) with enough 319 // space to add at least one new element. Returns empty handle if 320 // we've already reached MaxCapacity. 321 static MaybeHandle<Derived> Grow(Isolate* isolate, Handle<Derived> table); 322 323 static Handle<Derived> Rehash(Isolate* isolate, Handle<Derived> table, 324 int new_capacity); 325 326 // Iterates only fields in the DataTable. 327 class BodyDescriptor; 328 329 // No weak fields. 330 typedef BodyDescriptor BodyDescriptorWeak; 331 332 // Returns total size in bytes required for a table of given 333 // capacity. SizeFor(int capacity)334 static int SizeFor(int capacity) { 335 DCHECK_GE(capacity, kMinCapacity); 336 DCHECK_LE(capacity, kMaxCapacity); 337 338 int data_table_size = DataTableSizeFor(capacity); 339 int hash_table_size = capacity / kLoadFactor; 340 int chain_table_size = capacity; 341 int total_size = kDataTableStartOffset + data_table_size + hash_table_size + 342 chain_table_size; 343 344 return RoundUp(total_size, kPointerSize); 345 } 346 347 // Returns the number elements that can fit into the allocated table. Capacity()348 int Capacity() const { 349 int capacity = NumberOfBuckets() * kLoadFactor; 350 DCHECK_GE(capacity, kMinCapacity); 351 DCHECK_LE(capacity, kMaxCapacity); 352 353 return capacity; 354 } 355 356 // Returns the number elements that are present in the table. NumberOfElements()357 int NumberOfElements() const { 358 int nof_elements = getByte(kNumberOfElementsOffset, 0); 359 DCHECK_LE(nof_elements, Capacity()); 360 361 return nof_elements; 362 } 363 NumberOfDeletedElements()364 int NumberOfDeletedElements() const { 365 int nof_deleted_elements = getByte(kNumberOfDeletedElementsOffset, 0); 366 DCHECK_LE(nof_deleted_elements, Capacity()); 367 368 return nof_deleted_elements; 369 } 370 NumberOfBuckets()371 int NumberOfBuckets() const { return getByte(kNumberOfBucketsOffset, 0); } 372 373 DECL_VERIFIER(SmallOrderedHashTable) 374 375 static const int kMinCapacity = 4; 376 static const byte kNotFound = 0xFF; 377 378 // We use the value 255 to indicate kNotFound for chain and bucket 379 // values, which means that this value can't be used a valid 380 // index. 381 static const int kMaxCapacity = 254; 382 STATIC_ASSERT(kMaxCapacity < kNotFound); 383 384 // The load factor is used to derive the number of buckets from 385 // capacity during Allocation. We also depend on this to calaculate 386 // the capacity from number of buckets after allocation. If we 387 // decide to change kLoadFactor to something other than 2, capacity 388 // should be stored as another field of this object. 389 static const int kLoadFactor = 2; 390 391 // Our growth strategy involves doubling the capacity until we reach 392 // kMaxCapacity, but since the kMaxCapacity is always less than 256, 393 // we will never fully utilize this table. We special case for 256, 394 // by changing the new capacity to be kMaxCapacity in 395 // SmallOrderedHashTable::Grow. 396 static const int kGrowthHack = 256; 397 398 protected: 399 void SetDataEntry(int entry, int relative_index, Object* value); 400 401 // TODO(gsathya): Calculate all the various possible values for this 402 // at compile time since capacity can only be 4 different values. GetBucketsStartOffset()403 Offset GetBucketsStartOffset() const { 404 int capacity = Capacity(); 405 int data_table_size = DataTableSizeFor(capacity); 406 return kDataTableStartOffset + data_table_size; 407 } 408 GetHashTableStartAddress(int capacity)409 Address GetHashTableStartAddress(int capacity) const { 410 return FIELD_ADDR(this, kDataTableStartOffset + DataTableSizeFor(capacity)); 411 } 412 SetFirstEntry(int bucket,byte value)413 void SetFirstEntry(int bucket, byte value) { 414 DCHECK_LE(static_cast<unsigned>(bucket), NumberOfBuckets()); 415 setByte(GetBucketsStartOffset(), bucket, value); 416 } 417 GetFirstEntry(int bucket)418 int GetFirstEntry(int bucket) const { 419 DCHECK_LE(static_cast<unsigned>(bucket), NumberOfBuckets()); 420 return getByte(GetBucketsStartOffset(), bucket); 421 } 422 423 // TODO(gsathya): Calculate all the various possible values for this 424 // at compile time since capacity can only be 4 different values. GetChainTableOffset()425 Offset GetChainTableOffset() const { 426 int nof_buckets = NumberOfBuckets(); 427 int capacity = nof_buckets * kLoadFactor; 428 DCHECK_EQ(Capacity(), capacity); 429 430 int data_table_size = DataTableSizeFor(capacity); 431 int hash_table_size = nof_buckets; 432 return kDataTableStartOffset + data_table_size + hash_table_size; 433 } 434 SetNextEntry(int entry,int next_entry)435 void SetNextEntry(int entry, int next_entry) { 436 DCHECK_LT(static_cast<unsigned>(entry), Capacity()); 437 DCHECK_GE(static_cast<unsigned>(next_entry), 0); 438 DCHECK(next_entry <= Capacity() || next_entry == kNotFound); 439 setByte(GetChainTableOffset(), entry, next_entry); 440 } 441 GetNextEntry(int entry)442 int GetNextEntry(int entry) const { 443 DCHECK_LT(entry, Capacity()); 444 return getByte(GetChainTableOffset(), entry); 445 } 446 GetDataEntry(int entry,int relative_index)447 Object* GetDataEntry(int entry, int relative_index) { 448 DCHECK_LT(entry, Capacity()); 449 DCHECK_LE(static_cast<unsigned>(relative_index), Derived::kEntrySize); 450 Offset entry_offset = GetDataEntryOffset(entry, relative_index); 451 return READ_FIELD(this, entry_offset); 452 } 453 KeyAt(int entry)454 Object* KeyAt(int entry) const { 455 DCHECK_LT(entry, Capacity()); 456 Offset entry_offset = GetDataEntryOffset(entry, Derived::kKeyIndex); 457 return READ_FIELD(this, entry_offset); 458 } 459 HashToBucket(int hash)460 int HashToBucket(int hash) const { return hash & (NumberOfBuckets() - 1); } 461 HashToFirstEntry(int hash)462 int HashToFirstEntry(int hash) const { 463 int bucket = HashToBucket(hash); 464 int entry = GetFirstEntry(bucket); 465 DCHECK(entry < Capacity() || entry == kNotFound); 466 return entry; 467 } 468 SetNumberOfBuckets(int num)469 void SetNumberOfBuckets(int num) { setByte(kNumberOfBucketsOffset, 0, num); } 470 SetNumberOfElements(int num)471 void SetNumberOfElements(int num) { 472 DCHECK_LE(static_cast<unsigned>(num), Capacity()); 473 setByte(kNumberOfElementsOffset, 0, num); 474 } 475 SetNumberOfDeletedElements(int num)476 void SetNumberOfDeletedElements(int num) { 477 DCHECK_LE(static_cast<unsigned>(num), Capacity()); 478 setByte(kNumberOfDeletedElementsOffset, 0, num); 479 } 480 FindEntry(Isolate * isolate,Object * key)481 int FindEntry(Isolate* isolate, Object* key) { 482 DisallowHeapAllocation no_gc; 483 Object* hash = key->GetHash(); 484 485 if (hash->IsUndefined(isolate)) return kNotFound; 486 int entry = HashToFirstEntry(Smi::ToInt(hash)); 487 488 // Walk the chain in the bucket to find the key. 489 while (entry != kNotFound) { 490 Object* candidate_key = KeyAt(entry); 491 if (candidate_key->SameValueZero(key)) return entry; 492 entry = GetNextEntry(entry); 493 } 494 return kNotFound; 495 } 496 497 static const Offset kNumberOfElementsOffset = kHeaderSize; 498 static const Offset kNumberOfDeletedElementsOffset = 499 kNumberOfElementsOffset + kOneByteSize; 500 static const Offset kNumberOfBucketsOffset = 501 kNumberOfDeletedElementsOffset + kOneByteSize; 502 static const constexpr Offset kDataTableStartOffset = 503 RoundUp<kPointerSize>(kNumberOfBucketsOffset); 504 DataTableSizeFor(int capacity)505 static constexpr int DataTableSizeFor(int capacity) { 506 return capacity * Derived::kEntrySize * kPointerSize; 507 } 508 509 // This is used for accessing the non |DataTable| part of the 510 // structure. getByte(Offset offset,ByteIndex index)511 byte getByte(Offset offset, ByteIndex index) const { 512 DCHECK(offset < kDataTableStartOffset || offset >= GetBucketsStartOffset()); 513 return READ_BYTE_FIELD(this, offset + (index * kOneByteSize)); 514 } 515 setByte(Offset offset,ByteIndex index,byte value)516 void setByte(Offset offset, ByteIndex index, byte value) { 517 DCHECK(offset < kDataTableStartOffset || offset >= GetBucketsStartOffset()); 518 WRITE_BYTE_FIELD(this, offset + (index * kOneByteSize), value); 519 } 520 GetDataEntryOffset(int entry,int relative_index)521 Offset GetDataEntryOffset(int entry, int relative_index) const { 522 DCHECK_LT(entry, Capacity()); 523 int offset_in_datatable = entry * Derived::kEntrySize * kPointerSize; 524 int offset_in_entry = relative_index * kPointerSize; 525 return kDataTableStartOffset + offset_in_datatable + offset_in_entry; 526 } 527 UsedCapacity()528 int UsedCapacity() const { 529 int used = NumberOfElements() + NumberOfDeletedElements(); 530 DCHECK_LE(used, Capacity()); 531 532 return used; 533 } 534 535 private: 536 friend class OrderedHashMapHandler; 537 friend class OrderedHashSetHandler; 538 friend class CodeStubAssembler; 539 }; 540 541 class SmallOrderedHashSet : public SmallOrderedHashTable<SmallOrderedHashSet> { 542 public: 543 DECL_CAST(SmallOrderedHashSet) 544 545 DECL_PRINTER(SmallOrderedHashSet) 546 547 static const int kKeyIndex = 0; 548 static const int kEntrySize = 1; 549 550 // Adds |value| to |table|, if the capacity isn't enough, a new 551 // table is created. The original |table| is returned if there is 552 // capacity to store |value| otherwise the new table is returned. 553 static MaybeHandle<SmallOrderedHashSet> Add(Isolate* isolate, 554 Handle<SmallOrderedHashSet> table, 555 Handle<Object> key); 556 static inline bool Is(Handle<HeapObject> table); 557 static inline int GetMapRootIndex(); 558 }; 559 560 class SmallOrderedHashMap : public SmallOrderedHashTable<SmallOrderedHashMap> { 561 public: 562 DECL_CAST(SmallOrderedHashMap) 563 564 DECL_PRINTER(SmallOrderedHashMap) 565 566 static const int kKeyIndex = 0; 567 static const int kValueIndex = 1; 568 static const int kEntrySize = 2; 569 570 // Adds |value| to |table|, if the capacity isn't enough, a new 571 // table is created. The original |table| is returned if there is 572 // capacity to store |value| otherwise the new table is returned. 573 static MaybeHandle<SmallOrderedHashMap> Add(Isolate* isolate, 574 Handle<SmallOrderedHashMap> table, 575 Handle<Object> key, 576 Handle<Object> value); 577 static inline bool Is(Handle<HeapObject> table); 578 static inline int GetMapRootIndex(); 579 }; 580 581 // TODO(gsathya): Rename this to OrderedHashTable, after we rename 582 // OrderedHashTable to LargeOrderedHashTable. Also set up a 583 // OrderedHashSetBase class as a base class for the two tables and use 584 // that instead of a HeapObject here. 585 template <class SmallTable, class LargeTable> 586 class OrderedHashTableHandler { 587 public: 588 typedef int Entry; 589 590 static Handle<HeapObject> Allocate(Isolate* isolate, int capacity); 591 static bool Delete(Handle<HeapObject> table, Handle<Object> key); 592 static bool HasKey(Isolate* isolate, Handle<HeapObject> table, 593 Handle<Object> key); 594 595 // TODO(gsathya): Move this to OrderedHashTable 596 static const int OrderedHashTableMinSize = 597 SmallOrderedHashTable<SmallTable>::kGrowthHack << 1; 598 }; 599 600 class OrderedHashMapHandler 601 : public OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap> { 602 public: 603 static Handle<HeapObject> Add(Isolate* isolate, Handle<HeapObject> table, 604 Handle<Object> key, Handle<Object> value); 605 static Handle<OrderedHashMap> AdjustRepresentation( 606 Isolate* isolate, Handle<SmallOrderedHashMap> table); 607 }; 608 609 class OrderedHashSetHandler 610 : public OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet> { 611 public: 612 static Handle<HeapObject> Add(Isolate* isolate, Handle<HeapObject> table, 613 Handle<Object> key); 614 static Handle<OrderedHashSet> AdjustRepresentation( 615 Isolate* isolate, Handle<SmallOrderedHashSet> table); 616 }; 617 618 class JSCollectionIterator : public JSObject { 619 public: 620 // [table]: the backing hash table mapping keys to values. 621 DECL_ACCESSORS(table, Object) 622 623 // [index]: The index into the data table. 624 DECL_ACCESSORS(index, Object) 625 626 // Dispatched behavior. 627 DECL_PRINTER(JSCollectionIterator) 628 629 static const int kTableOffset = JSObject::kHeaderSize; 630 static const int kIndexOffset = kTableOffset + kPointerSize; 631 static const int kSize = kIndexOffset + kPointerSize; 632 633 private: 634 DISALLOW_IMPLICIT_CONSTRUCTORS(JSCollectionIterator); 635 }; 636 637 // OrderedHashTableIterator is an iterator that iterates over the keys and 638 // values of an OrderedHashTable. 639 // 640 // The iterator has a reference to the underlying OrderedHashTable data, 641 // [table], as well as the current [index] the iterator is at. 642 // 643 // When the OrderedHashTable is rehashed it adds a reference from the old table 644 // to the new table as well as storing enough data about the changes so that the 645 // iterator [index] can be adjusted accordingly. 646 // 647 // When the [Next] result from the iterator is requested, the iterator checks if 648 // there is a newer table that it needs to transition to. 649 template <class Derived, class TableType> 650 class OrderedHashTableIterator : public JSCollectionIterator { 651 public: 652 // Whether the iterator has more elements. This needs to be called before 653 // calling |CurrentKey| and/or |CurrentValue|. 654 bool HasMore(); 655 656 // Move the index forward one. MoveNext()657 void MoveNext() { set_index(Smi::FromInt(Smi::ToInt(index()) + 1)); } 658 659 // Returns the current key of the iterator. This should only be called when 660 // |HasMore| returns true. 661 inline Object* CurrentKey(); 662 663 private: 664 // Transitions the iterator to the non obsolete backing store. This is a NOP 665 // if the [table] is not obsolete. 666 void Transition(); 667 668 DISALLOW_IMPLICIT_CONSTRUCTORS(OrderedHashTableIterator); 669 }; 670 671 } // namespace internal 672 } // namespace v8 673 674 #include "src/objects/object-macros-undef.h" 675 676 #endif // V8_OBJECTS_ORDERED_HASH_TABLE_H_ 677