• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef ECMASCRIPT_TAGGED_HASH_TABLE_H
17 #define ECMASCRIPT_TAGGED_HASH_TABLE_H
18 
19 #include <vector>
20 
21 #include "ecmascript/ecma_vm.h"
22 #include "ecmascript/js_handle.h"
23 #include "ecmascript/object_factory.h"
24 #include "ecmascript/tagged_array.h"
25 #include "ecmascript/tagged_array-inl.h"
26 
27 namespace panda::ecmascript {
28 template<typename Derived>
29 class TaggedHashTable : public TaggedArray {
30 public:
EntriesCount()31     inline int EntriesCount() const
32     {
33         return Get(NUMBER_OF_ENTRIES_INDEX).GetInt();
34     }
35 
HoleEntriesCount()36     inline int HoleEntriesCount() const
37     {
38         return Get(NUMBER_OF_HOLE_ENTRIES_INDEX).GetInt();
39     }
40 
Size()41     inline int Size() const
42     {
43         return Get(SIZE_INDEX).GetInt();
44     }
45 
IncreaseEntries(const JSThread * thread)46     inline void IncreaseEntries(const JSThread *thread)
47     {
48         SetEntriesCount(thread, EntriesCount() + 1);
49     }
50 
51     inline void IncreaseHoleEntriesCount(const JSThread *thread, int number = 1)
52     {
53         SetEntriesCount(thread, EntriesCount() - number);
54         SetHoleEntriesCount(thread, HoleEntriesCount() + number);
55     }
56 
ComputeHashTableSize(uint32_t atLeastSize)57     inline static int ComputeHashTableSize(uint32_t atLeastSize)
58     {
59         //  increase size for hash-collision
60         uint32_t rawSize = atLeastSize + (atLeastSize >> 1UL);
61         int newSize = static_cast<int>(helpers::math::GetPowerOfTwoValue32(rawSize));
62         return (newSize > MIN_SIZE) ? newSize : MIN_SIZE;
63     }
64 
65     static JSHandle<Derived> GrowHashTable(const JSThread *thread, const JSHandle<Derived> &table,
66                                            int numOfAddedElements = 1)
67     {
68         if (!table->IsNeedGrowHashTable(numOfAddedElements)) {
69             // if deleted entries are more than half of the free entries, rehash to clear holes.
70             int freeSize = table->Size() - table->EntriesCount() - numOfAddedElements;
71             if (table->HoleEntriesCount() > freeSize / 2) { // 2: half
72                 int copyLength = Derived::GetEntryIndex(table->Size());
73                 JSHandle<Derived> copyTable = table.GetTaggedValue().IsInSharedHeap() ?
74                     JSHandle<Derived>(thread->GetEcmaVM()->GetFactory()->NewSDictionaryArray(copyLength)) :
75                     JSHandle<Derived>(thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(copyLength));
76                 copyTable->SetHashTableSize(thread, table->Size());
77                 table->Rehash(thread, *copyTable);
78                 return copyTable;
79             }
80             return table;
81         }
82         int newSize = Derived::ComputeCompactSize(table, ComputeHashTableSize(table->Size() + numOfAddedElements),
83             table->Size(), numOfAddedElements);
84         newSize = std::max(newSize, MIN_SHRINK_SIZE);
85         int length = Derived::GetEntryIndex(newSize);
86         JSHandle<Derived> newTable = table.GetTaggedValue().IsInSharedHeap() ?
87             JSHandle<Derived>(thread->GetEcmaVM()->GetFactory()->NewSDictionaryArray(length)) :
88             JSHandle<Derived>(thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(length));
89         newTable->SetHashTableSize(thread, newSize);
90         table->Rehash(thread, *newTable);
91         return newTable;
92     }
93 
94     static JSHandle<Derived> Create(const JSThread *thread, int entriesCount,
95                                     MemSpaceKind spaceKind = MemSpaceKind::LOCAL)
96     {
97         ASSERT_PRINT((entriesCount > 0), "the size must be greater than zero");
98         auto size = static_cast<uint32_t>(entriesCount);
99         ASSERT_PRINT(helpers::math::IsPowerOfTwo(static_cast<uint32_t>(entriesCount)), "the size must be power of two");
100 
101         int length = Derived::GetEntryIndex(entriesCount);
102 
103         JSHandle<Derived> table = spaceKind == MemSpaceKind::SHARED ?
104             JSHandle<Derived>(thread->GetEcmaVM()->GetFactory()->NewSDictionaryArray(length)) :
105             JSHandle<Derived>(thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(length));
106         table->SetEntriesCount(thread, 0);
107         table->SetHoleEntriesCount(thread, 0);
108         table->SetHashTableSize(thread, size);
109         return table;
110     }
111 
Insert(const JSThread * thread,JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)112     static JSHandle<Derived> Insert(const JSThread *thread, JSHandle<Derived> &table,
113                                     const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
114     {
115         int entry = table->FindEntry(key.GetTaggedValue());
116         if (entry != -1) {
117             table->SetValue(thread, entry, value.GetTaggedValue());
118             return table;
119         }
120 
121         // Make sure the key object has an identity hash code.
122         uint32_t hash = static_cast<uint32_t>(Derived::Hash(key.GetTaggedValue()));
123         JSHandle<Derived> newTable = GrowHashTable(thread, table);
124         newTable->AddElement(thread, newTable->FindInsertIndex(hash), key, value);
125         return newTable;
126     }
127 
Remove(const JSThread * thread,JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key)128     static JSHandle<Derived> Remove(const JSThread *thread, JSHandle<Derived> &table,
129                                     const JSHandle<JSTaggedValue> &key)
130     {
131         int entry = table->FindEntry(key.GetTaggedValue());
132         if (entry == -1) {
133             return table;
134         }
135 
136         table->RemoveElement(thread, entry);
137         return Derived::Shrink(thread, *table);
138     }
139 
RecalculateTableSize(int currentSize,int atLeastSize)140     inline static int RecalculateTableSize(int currentSize, int atLeastSize)
141     {
142         // When the filled entries is greater than a quart of currentSize
143         // it need not to shrink
144         if (atLeastSize > (currentSize / 4)) {  // 4 : quarter
145             return currentSize;
146         }
147         // Recalculate table size
148         int newSize = ComputeHashTableSize(atLeastSize);
149         ASSERT_PRINT(newSize > atLeastSize, "new size must greater than atLeastSize");
150         // Don't go lower than room for MIN_SHRINK_SIZE elements.
151         if (newSize < MIN_SHRINK_SIZE) {
152             return currentSize;
153         }
154         return newSize;
155     }
156 
Shrink(const JSThread * thread,const JSHandle<Derived> & table,int additionalSize)157     inline static JSHandle<Derived> Shrink(const JSThread *thread, const JSHandle<Derived> &table, int additionalSize)
158     {
159         int newSize = RecalculateTableSize(table->Size(), table->EntriesCount() + additionalSize);
160         if (newSize == table->Size()) {
161             return table;
162         }
163 
164         JSHandle<Derived> newTable = TaggedHashTable::Create(thread, newSize,
165             table.GetTaggedValue().IsInSharedHeap() ? MemSpaceKind::SHARED : MemSpaceKind::LOCAL);
166 
167         table->Rehash(thread, *newTable);
168         return newTable;
169     }
170 
IsNeedGrowHashTable(int numOfAddEntries)171     bool IsNeedGrowHashTable(int numOfAddEntries)
172     {
173         int entriesCount = EntriesCount();
174         int currentSize = Size();
175         int numberFilled = entriesCount + numOfAddEntries;
176         // needn't to grow table: after adding number entries, table have half free entries.
177         const int halfFree = 2;
178         int neededFree = numberFilled / halfFree;
179         if (numberFilled + neededFree <= currentSize) {
180             return false;
181         }
182         return true;
183     }
184 
GetKey(int entry)185     JSTaggedValue GetKey(int entry) const
186     {
187         int index = Derived::GetKeyIndex(entry);
188         if (UNLIKELY((index < 0 || index > static_cast<int>(GetLength())))) {
189             return JSTaggedValue::Undefined();
190         }
191         return Get(index);
192     }
193 
GetValue(int entry)194     JSTaggedValue GetValue(int entry) const
195     {
196         int index = Derived::GetValueIndex(entry);
197         if (UNLIKELY((index < 0 || index > static_cast<int>(GetLength())))) {
198             return JSTaggedValue::Undefined();
199         }
200         return Get(index);
201     }
202 
GetAllKeys(const JSThread * thread,int offset,TaggedArray * keyArray)203     inline void GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const
204     {
205         ASSERT_PRINT(offset + EntriesCount() <= static_cast<int>(keyArray->GetLength()),
206                      "keyArray size is not enough for dictionary");
207         int arrayIndex = 0;
208         int size = Size();
209         for (int hashIndex = 0; hashIndex < size; hashIndex++) {
210             JSTaggedValue key = GetKey(hashIndex);
211             if (!key.IsUndefined() && !key.IsHole()) {
212                 keyArray->Set(thread, arrayIndex + offset, key);
213                 arrayIndex++;
214             }
215         }
216     }
217 
GetAllKeysIntoVector(std::vector<JSTaggedValue> & vector)218     inline void GetAllKeysIntoVector(std::vector<JSTaggedValue> &vector) const
219     {
220         int capacity = Size();
221         for (int hashIndex = 0; hashIndex < capacity; hashIndex++) {
222             JSTaggedValue key = GetKey(hashIndex);
223             if (!key.IsUndefined() && !key.IsHole()) {
224                 vector.push_back(key);
225             }
226         }
227     }
228 
229     inline void Swap(const JSThread *thread, int src, int dst);
230 
231     // Find entry for key otherwise return -1.
FindEntry(const JSTaggedValue & key)232     inline int FindEntry(const JSTaggedValue &key)
233     {
234         int size = Size();
235         int count = 1;
236         JSTaggedValue keyValue;
237         int32_t hash = static_cast<int32_t>(Derived::Hash(key));
238 
239         for (uint32_t entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) {
240             keyValue = GetKey(entry);
241             if (keyValue.IsHole()) {
242                 continue;
243             }
244             if (keyValue.IsUndefined()) {
245                 return -1;
246             }
247             if (Derived::IsMatch(key, keyValue)) {
248                 return entry;
249             }
250         }
251         return -1;
252     }
253 
FindEntry(const uint8_t * str,int strSize)254     inline int FindEntry(const uint8_t* str, int strSize)
255     {
256         int size = Size();
257         int count = 1;
258         JSTaggedValue keyValue;
259         int32_t hash = static_cast<int32_t>(Derived::Hash(str, strSize));
260 
261         for (uint32_t entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) {
262             keyValue = GetKey(entry);
263             if (keyValue.IsHole()) {
264                 continue;
265             }
266             if (keyValue.IsUndefined()) {
267                 return -1;
268             }
269             // keyValue defaults to ecmaString
270             if (Derived::IsMatch(str, strSize, keyValue)) {
271                 return entry;
272             }
273         }
274         return -1;
275     }
276 
FindInsertIndex(uint32_t hash)277     inline int FindInsertIndex(uint32_t hash)
278     {
279         int size = Size();
280         int count = 1;
281         // GrowHashTable will guarantee the hash table is never full.
282         for (uint32_t entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) {
283             if (!IsKey(GetKey(entry))) {
284                 return entry;
285             }
286         }
287     }
288 
SetKey(const JSThread * thread,int entry,const JSTaggedValue & key)289     inline void SetKey(const JSThread *thread, int entry, const JSTaggedValue &key)
290     {
291         int index = Derived::GetKeyIndex(entry);
292         if (UNLIKELY(index < 0 || index > static_cast<int>(GetLength()))) {
293             return;
294         }
295         Set(thread, index, key);
296     }
297 
SetValue(const JSThread * thread,int entry,const JSTaggedValue & value)298     inline void SetValue(const JSThread *thread, int entry, const JSTaggedValue &value)
299     {
300         int index = Derived::GetValueIndex(entry);
301         if (UNLIKELY((index < 0 || index > static_cast<int>(GetLength())))) {
302             return;
303         }
304         Set(thread, index, value);
305     }
306 
307     // Rehash element to new_table
Rehash(const JSThread * thread,Derived * newTable)308     void Rehash(const JSThread *thread, Derived *newTable)
309     {
310         if ((newTable == nullptr) || (newTable->Size() < EntriesCount())) {
311             return;
312         }
313         int currentSize = this->Size();
314         // Rehash elements to new table
315         for (int i = 0; i < currentSize; i++) {
316             int fromIndex = Derived::GetKeyIndex(i);
317             JSTaggedValue k = this->GetKey(i);
318             if (!IsKey(k)) {
319                 continue;
320             }
321             uint32_t hash = static_cast<uint32_t>(Derived::Hash(k));
322             int insertionIndex = Derived::GetKeyIndex(newTable->FindInsertIndex(hash));
323             JSTaggedValue tv = Get(fromIndex);
324             newTable->Set(thread, insertionIndex, tv);
325             for (int j = 1; j < Derived::GetEntrySize(); j++) {
326                 tv = Get(fromIndex + j);
327                 newTable->Set(thread, insertionIndex + j, tv);
328             }
329         }
330         newTable->SetEntriesCount(thread, EntriesCount());
331         newTable->SetHoleEntriesCount(thread, 0);
332     }
333 
334     static constexpr int MIN_SHRINK_SIZE = 16;
335     static constexpr int MIN_SIZE = 4;
336     static constexpr int NUMBER_OF_ENTRIES_INDEX = 0;
337     static constexpr int NUMBER_OF_HOLE_ENTRIES_INDEX = 1;
338     static constexpr int SIZE_INDEX = 2;
339     static constexpr int TABLE_HEADER_SIZE = 3;
340 
341 protected:
IsKey(const JSTaggedValue & key)342     inline bool IsKey(const JSTaggedValue &key) const
343     {
344         return !key.IsHole() && !key.IsUndefined();
345     };
346 
GetFirstPosition(uint32_t hash,uint32_t size)347     inline static uint32_t GetFirstPosition(uint32_t hash, uint32_t size)
348     {
349         ASSERT(size > 0);
350         return hash & (size - 1);
351     }
352 
GetNextPosition(uint32_t last,uint32_t number,uint32_t size)353     inline static uint32_t GetNextPosition(uint32_t last, uint32_t number, uint32_t size)
354     {
355         ASSERT(size > 0);
356         return (last + (number * (number + 1)) / 2) & (size - 1);  // 2 : half
357     }
358 
SetEntriesCount(const JSThread * thread,int nof)359     inline void SetEntriesCount(const JSThread *thread, int nof)
360     {
361         Set(thread, NUMBER_OF_ENTRIES_INDEX, JSTaggedValue(nof));
362     }
363 
SetHoleEntriesCount(const JSThread * thread,int nod)364     inline void SetHoleEntriesCount(const JSThread *thread, int nod)
365     {
366         Set(thread, NUMBER_OF_HOLE_ENTRIES_INDEX, JSTaggedValue(nod));
367     }
368 
369     // Sets the size of the hash table.
SetHashTableSize(const JSThread * thread,int size)370     inline void SetHashTableSize(const JSThread *thread, int size)
371     {
372         Set(thread, SIZE_INDEX, JSTaggedValue(size));
373     }
374 
375     inline static int GetHeadSizeOfTable();
376     inline static int GetEntrySize();
377     inline static int GetKeyOffset();
378     inline static int GetValueOffset();
379 
AddElement(const JSThread * thread,int entry,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)380     inline void AddElement(const JSThread *thread, int entry, const JSHandle<JSTaggedValue> &key,
381                            const JSHandle<JSTaggedValue> &value)
382     {
383         this->SetKey(thread, entry, key.GetTaggedValue());
384         this->SetValue(thread, entry, value.GetTaggedValue());
385         this->IncreaseEntries(thread);
386     }
387 
RemoveElement(const JSThread * thread,int entry)388     inline void RemoveElement(const JSThread *thread, int entry)
389     {
390         JSTaggedValue defaultValue(JSTaggedValue::Hole());
391         this->SetKey(thread, entry, defaultValue);
392         this->SetValue(thread, entry, defaultValue);
393         this->IncreaseHoleEntriesCount(thread);
394     }
395 };
396 
397 template<typename Derived>
398 class OrderTaggedHashTable : public TaggedHashTable<Derived> {
399 public:
400     using HashTableT = TaggedHashTable<Derived>;
Cast(TaggedObject * object)401     static Derived *Cast(TaggedObject *object)
402     {
403         return reinterpret_cast<Derived *>(object);
404     }
405 
406     // Attempt to shrink the table after deletion of key.
Shrink(const JSThread * thread,const JSHandle<Derived> & table)407     static JSHandle<Derived> Shrink(const JSThread *thread, const JSHandle<Derived> &table)
408     {
409         int index = table->GetNextEnumerationIndex();
410         JSHandle<Derived> newTable = HashTableT::Shrink(thread, table, 0);
411         newTable->SetNextEnumerationIndex(thread, index);
412         return newTable;
413     }
414 
415     static JSHandle<Derived> Create(const JSThread *thread, int numberOfElements = DEFAULT_ELEMENTS_NUMBER,
416         MemSpaceKind spaceKind = MemSpaceKind::LOCAL)
417     {
418         JSHandle<Derived> dict = HashTableT::Create(thread, numberOfElements, spaceKind);
419         dict->SetNextEnumerationIndex(thread, PropertyAttributes::INITIAL_PROPERTY_INDEX);
420         return dict;
421     }
422 
PutIfAbsent(const JSThread * thread,const JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const PropertyAttributes & metaData)423     static JSHandle<Derived> PutIfAbsent(const JSThread *thread, const JSHandle<Derived> &table,
424                                          const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
425                                          const PropertyAttributes &metaData)
426     {
427         /* no need to add key if exist */
428         int entry = table->FindEntry(key.GetTaggedValue());
429         if (entry != -1) {
430             return table;
431         }
432 
433         int enumIndex = table->NextEnumerationIndex(thread);
434         PropertyAttributes attr(metaData);
435         attr.SetDictionaryOrder(enumIndex);
436         attr.SetRepresentation(Representation::TAGGED);
437         // Check whether the table should be growed.
438         JSHandle<Derived> newTable = HashTableT::GrowHashTable(thread, table);
439 
440         // Compute the key object.
441         uint32_t hash = static_cast<uint32_t>(Derived::Hash(key.GetTaggedValue()));
442         entry = newTable->FindInsertIndex(hash);
443         newTable->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr);
444 
445         newTable->IncreaseEntries(thread);
446         newTable->SetNextEnumerationIndex(thread, enumIndex + 1);
447         return newTable;
448     }
449 
Put(const JSThread * thread,const JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const PropertyAttributes & metaData)450     static JSHandle<Derived> Put(const JSThread *thread, const JSHandle<Derived> &table,
451                                  const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
452                                  const PropertyAttributes &metaData)
453     {
454         int enumIndex = table->NextEnumerationIndex(thread);
455         PropertyAttributes attr(metaData);
456         attr.SetDictionaryOrder(enumIndex);
457         attr.SetRepresentation(Representation::TAGGED);
458         attr.SetDictSharedFieldType(metaData.GetSharedFieldType());
459         int entry = table->FindEntry(key.GetTaggedValue());
460         if (entry != -1) {
461             table->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr);
462             return table;
463         }
464 
465         // Check whether the table should be extended.
466         JSHandle<Derived> newTable = HashTableT::GrowHashTable(thread, table);
467 
468         // Compute the key object.
469         uint32_t hash = static_cast<uint32_t>(Derived::Hash(key.GetTaggedValue()));
470         entry = newTable->FindInsertIndex(hash);
471         newTable->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr);
472 
473         newTable->IncreaseEntries(thread);
474         newTable->SetNextEnumerationIndex(thread, enumIndex + 1);
475         return newTable;
476     }
Remove(const JSThread * thread,const JSHandle<Derived> & table,int entry)477     static JSHandle<Derived> Remove(const JSThread *thread, const JSHandle<Derived> &table, int entry)
478     {
479         if (!(table->IsKey(table->GetKey(entry)))) {
480             return table;
481         }
482         table->ClearEntry(thread, entry);
483         table->IncreaseHoleEntriesCount(thread);
484         return Shrink(thread, table);
485     }
486 
SetNextEnumerationIndex(const JSThread * thread,int index)487     inline void SetNextEnumerationIndex(const JSThread *thread, int index)
488     {
489         HashTableT::Set(thread, NEXT_ENUMERATION_INDEX, JSTaggedValue(index));
490     }
GetNextEnumerationIndex()491     inline int GetNextEnumerationIndex() const
492     {
493         return HashTableT::Get(NEXT_ENUMERATION_INDEX).GetInt();
494     }
495 
NextEnumerationIndex(const JSThread * thread)496     inline int NextEnumerationIndex(const JSThread *thread)
497     {
498         int index = GetNextEnumerationIndex();
499         auto table = Derived::Cast(this);
500 
501         if (!PropertyAttributes::IsValidIndex(index)) {
502             std::vector<int> indexOrder = GetEnumerationOrder();
503             int length = static_cast<int>(indexOrder.size());
504             for (int i = 0; i < length; i++) {
505                 int oldIndex = indexOrder[i];
506                 int enumIndex = PropertyAttributes::INITIAL_PROPERTY_INDEX + i;
507                 PropertyAttributes attr = table->GetAttributes(oldIndex);
508                 attr.SetDictionaryOrder(enumIndex);
509                 attr.SetRepresentation(Representation::TAGGED);
510                 table->SetAttributes(thread, oldIndex, attr);
511             }
512             index = PropertyAttributes::INITIAL_PROPERTY_INDEX + length;
513         }
514         return index;
515     }
516 
GetEnumerationOrder()517     inline std::vector<int> GetEnumerationOrder()
518     {
519         std::vector<int> result;
520         auto table = Derived::Cast(this);
521         int size = table->Size();
522         for (int i = 0; i < size; i++) {
523             if (table->IsKey(table->GetKey(i))) {
524                 result.push_back(i);
525             }
526         }
527         std::sort(result.begin(), result.end(), [table](int a, int b) {
528             PropertyAttributes attrA = table->GetAttributes(a);
529             PropertyAttributes attrB = table->GetAttributes(b);
530             return attrA.GetDictionaryOrder() < attrB.GetDictionaryOrder();
531         });
532         return result;
533     }
534 
ComputeCompactSize(const JSHandle<Derived> & table,int computeHashTableSize,int tableSize,int addedElements)535     static int ComputeCompactSize([[maybe_unused]] const JSHandle<Derived> &table, int computeHashTableSize,
536         [[maybe_unused]] int tableSize, [[maybe_unused]] int addedElements)
537     {
538         return computeHashTableSize;
539     }
540 
541     static const int NEXT_ENUMERATION_INDEX = HashTableT::SIZE_INDEX + 1;
542     static const int DEFAULT_ELEMENTS_NUMBER = 128;
543     static constexpr int TABLE_HEADER_SIZE = 4;
544 };
545 }  // namespace panda::ecmascript
546 #endif  // ECMASCRIPT_NEW_HASH_TABLE_H
547