• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 #include "ecmascript/linked_hash_table.h"
17 
18 #include "ecmascript/js_object-inl.h"
19 
20 namespace panda::ecmascript {
21 template<typename Derived, typename HashObject>
Create(const JSThread * thread,int numberOfElements,MemSpaceKind spaceKind)22 JSHandle<Derived> LinkedHashTable<Derived, HashObject>::Create(const JSThread *thread,
23     int numberOfElements, MemSpaceKind spaceKind)
24 {
25     ASSERT_PRINT(numberOfElements > 0, "size must be a non-negative integer");
26     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
27     auto capacity = static_cast<uint32_t>(numberOfElements);
28     ASSERT_PRINT(helpers::math::IsPowerOfTwo(capacity), "capacity must be pow of '2'");
29     int length = ELEMENTS_START_INDEX + numberOfElements + numberOfElements * (HashObject::ENTRY_SIZE + 1);
30     JSHandle<Derived> table;
31     if (spaceKind == MemSpaceKind::SHARED) {
32         table = JSHandle<Derived>(factory->NewSOldSpaceTaggedArray(length));
33     } else {
34         table = JSHandle<Derived>(factory->NewTaggedArray(length));
35     }
36     table->SetNumberOfElements(thread, 0);
37     table->SetNumberOfDeletedElements(thread, 0);
38     table->SetCapacity(thread, capacity);
39     return table;
40 }
41 
42 template<typename Derived, typename HashObject>
Insert(const JSThread * thread,const JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)43 JSHandle<Derived> LinkedHashTable<Derived, HashObject>::Insert(const JSThread *thread, const JSHandle<Derived> &table,
44                                                                const JSHandle<JSTaggedValue> &key,
45                                                                const JSHandle<JSTaggedValue> &value)
46 {
47     ASSERT(IsKey(key.GetTaggedValue()));
48     int hash = LinkedHash::Hash(thread, key.GetTaggedValue());
49     int entry = table->FindElement(thread, key.GetTaggedValue());
50     if (entry != -1) {
51         table->SetValue(thread, entry, value.GetTaggedValue());
52         return table;
53     }
54 
55     JSHandle<Derived> newTable = GrowCapacity(thread, table);
56 
57     uint32_t bucket = newTable->HashToBucket(hash);
58     entry = newTable->NumberOfElements() + newTable->NumberOfDeletedElements();
59     newTable->InsertNewEntry(thread, bucket, entry);
60     newTable->SetKey(thread, entry, key.GetTaggedValue());
61     newTable->SetValue(thread, entry, value.GetTaggedValue());
62     newTable->SetNumberOfElements(thread, newTable->NumberOfElements() + 1);
63 
64     return newTable;
65 }
66 
67 template<typename Derived, typename HashObject>
InsertWeakRef(const JSThread * thread,const JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)68 JSHandle<Derived> LinkedHashTable<Derived, HashObject>::InsertWeakRef(const JSThread *thread,
69     const JSHandle<Derived> &table, const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
70 {
71     ALLOW_LOCAL_TO_SHARE_WEAK_REF_HANDLE;
72     ASSERT(IsKey(key.GetTaggedValue()));
73     int hash = LinkedHash::Hash(thread, key.GetTaggedValue());
74     int entry = table->FindElement(thread, key.GetTaggedValue());
75     if (entry != -1) {
76         table->SetValue(thread, entry, value.GetTaggedValue());
77         return table;
78     }
79 
80     JSHandle<Derived> newTable = GrowCapacity(thread, table);
81 
82     uint32_t bucket = newTable->HashToBucket(hash);
83     entry = newTable->NumberOfElements() + newTable->NumberOfDeletedElements();
84     newTable->InsertNewEntry(thread, bucket, entry);
85     JSTaggedValue weakKey(key->CreateAndGetWeakRef());
86     newTable->SetKey(thread, entry, weakKey);
87     // The ENTRY_VALUE_INDEX of LinkedHashSet is 0. SetValue will cause the overwitten key.
88     if constexpr (std::is_same_v<LinkedHashMap, Derived>) {
89         newTable->SetValue(thread, entry, value.GetTaggedValue());
90     }
91     newTable->SetNumberOfElements(thread, newTable->NumberOfElements() + 1);
92 
93     return newTable;
94 }
95 
96 template<typename Derived, typename HashObject>
GrowCapacity(const JSThread * thread,const JSHandle<Derived> & table,int numberOfAddedElements)97 JSHandle<Derived> LinkedHashTable<Derived, HashObject>::GrowCapacity(const JSThread *thread,
98     const JSHandle<Derived> &table, int numberOfAddedElements)
99 {
100     if (table->HasSufficientCapacity(numberOfAddedElements)) {
101         return table;
102     }
103     int newCapacity = ComputeCapacity(table->NumberOfElements() + numberOfAddedElements);
104     JSHandle<Derived> newTable = Create(thread, newCapacity,
105         table.GetTaggedValue().IsInSharedHeap() ? MemSpaceKind::SHARED : MemSpaceKind::LOCAL);
106     table->Rehash(thread, *newTable);
107     return newTable;
108 }
109 
110 template<typename Derived, typename HashObject>
Remove(const JSThread * thread,const JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key)111 JSHandle<Derived> LinkedHashTable<Derived, HashObject>::Remove(const JSThread *thread, const JSHandle<Derived> &table,
112                                                                const JSHandle<JSTaggedValue> &key)
113 {
114     int entry = table->FindElement(thread, key.GetTaggedValue());
115     if (entry == -1) {
116         return table;
117     }
118 
119     table->RemoveEntry(thread, entry);
120     return Shrink(thread, table);
121 }
122 
123 template<typename Derived, typename HashObject>
Shrink(const JSThread * thread,const JSHandle<Derived> & table,int additionalCapacity)124 JSHandle<Derived> LinkedHashTable<Derived, HashObject>::Shrink(const JSThread *thread, const JSHandle<Derived> &table,
125     int additionalCapacity)
126 {
127     int newCapacity = ComputeCapacityWithShrink(table->Capacity(), table->NumberOfElements() + additionalCapacity);
128     if (newCapacity == table->Capacity()) {
129         return table;
130     }
131 
132     JSHandle<Derived> newTable = Create(thread, newCapacity,
133         table.GetTaggedValue().IsInSharedHeap() ? MemSpaceKind::SHARED : MemSpaceKind::LOCAL);
134 
135     table->Rehash(thread, *newTable);
136     return newTable;
137 }
138 
139 // LinkedHashMap
Create(const JSThread * thread,int numberOfElements,MemSpaceKind spaceKind)140 JSHandle<LinkedHashMap> LinkedHashMap::Create(const JSThread *thread, int numberOfElements, MemSpaceKind spaceKind)
141 {
142     return LinkedHashTable<LinkedHashMap, LinkedHashMapObject>::Create(thread, numberOfElements, spaceKind);
143 }
144 
Delete(const JSThread * thread,const JSHandle<LinkedHashMap> & obj,const JSHandle<JSTaggedValue> & key)145 JSHandle<LinkedHashMap> LinkedHashMap::Delete(const JSThread *thread, const JSHandle<LinkedHashMap> &obj,
146                                               const JSHandle<JSTaggedValue> &key)
147 {
148     return LinkedHashTable<LinkedHashMap, LinkedHashMapObject>::Remove(thread, obj, key);
149 }
150 
Set(const JSThread * thread,const JSHandle<LinkedHashMap> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)151 JSHandle<LinkedHashMap> LinkedHashMap::Set(const JSThread *thread, const JSHandle<LinkedHashMap> &obj,
152     const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
153 {
154     return LinkedHashTable<LinkedHashMap, LinkedHashMapObject>::Insert(thread, obj, key, value);
155 }
156 
SetWeakRef(const JSThread * thread,const JSHandle<LinkedHashMap> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)157 JSHandle<LinkedHashMap> LinkedHashMap::SetWeakRef(const JSThread *thread, const JSHandle<LinkedHashMap> &obj,
158     const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
159 {
160     return LinkedHashTable<LinkedHashMap, LinkedHashMapObject>::InsertWeakRef(thread, obj, key, value);
161 }
162 
Get(const JSThread * thread,JSTaggedValue key) const163 JSTaggedValue LinkedHashMap::Get(const JSThread *thread, JSTaggedValue key) const
164 {
165     int entry = FindElement(thread, key);
166     if (entry == -1) {
167         return JSTaggedValue::Undefined();
168     }
169     return GetValue(thread, entry);
170 }
171 
Has(const JSThread * thread,JSTaggedValue key) const172 bool LinkedHashMap::Has(const JSThread *thread, JSTaggedValue key) const
173 {
174     int entry = FindElement(thread, key);
175     return entry != -1;
176 }
177 
Clear(const JSThread * thread,const JSHandle<LinkedHashMap> & table)178 JSHandle<LinkedHashMap> LinkedHashMap::Clear(const JSThread *thread, const JSHandle<LinkedHashMap> &table)
179 {
180     if (table->Capacity() == LinkedHashMap::MIN_CAPACITY) {
181         table->FillRangeWithSpecialValue(JSTaggedValue::Hole(), LinkedHashMap::ELEMENTS_START_INDEX,
182                                          table->GetLength());
183         table->SetNumberOfDeletedElements(thread, table->NumberOfDeletedElements() + table->NumberOfElements());
184         table->SetNumberOfElements(thread, 0);
185         return table;
186     }
187     JSHandle<LinkedHashMap> newMap = LinkedHashMap::Create(thread, LinkedHashMap::MIN_CAPACITY,
188         table.GetTaggedValue().IsInSharedHeap() ? MemSpaceKind::SHARED : MemSpaceKind::LOCAL);
189     if (table->Capacity() > 0) {
190         table->SetNextTable(thread, newMap.GetTaggedValue());
191         table->SetNumberOfDeletedElements(thread, -1);
192     }
193     return newMap;
194 }
195 
Shrink(const JSThread * thread,const JSHandle<LinkedHashMap> & table,int additionalCapacity)196 JSHandle<LinkedHashMap> LinkedHashMap::Shrink(const JSThread *thread, const JSHandle<LinkedHashMap> &table,
197                                               int additionalCapacity)
198 {
199     return LinkedHashTable<LinkedHashMap, LinkedHashMapObject>::Shrink(thread, table, additionalCapacity);
200 }
201 
ClearAllDeadEntries(const JSThread * thread,std::function<bool (JSTaggedValue)> & visitor)202 void LinkedHashMap::ClearAllDeadEntries(const JSThread *thread, std::function<bool(JSTaggedValue)> &visitor)
203 {
204     int entries = NumberOfElements() + NumberOfDeletedElements();
205 
206     for (int i = 0; i < entries; ++i) {
207         JSTaggedValue maybeKey = GetKey(thread, i);
208         if (maybeKey.IsHole()) {
209             // Already removed
210             continue;
211         }
212         bool dead = visitor(maybeKey);
213         if (dead) {
214             RemoveEntryFromGCThread(i);
215         }
216     }
217 }
218 
219 // LinkedHashSet
Create(const JSThread * thread,int numberOfElements,MemSpaceKind spaceKind)220 JSHandle<LinkedHashSet> LinkedHashSet::Create(const JSThread *thread, int numberOfElements, MemSpaceKind spaceKind)
221 {
222     return LinkedHashTable<LinkedHashSet, LinkedHashSetObject>::Create(thread, numberOfElements, spaceKind);
223 }
224 
Delete(const JSThread * thread,const JSHandle<LinkedHashSet> & obj,const JSHandle<JSTaggedValue> & key)225 JSHandle<LinkedHashSet> LinkedHashSet::Delete(const JSThread *thread, const JSHandle<LinkedHashSet> &obj,
226                                               const JSHandle<JSTaggedValue> &key)
227 {
228     return LinkedHashTable<LinkedHashSet, LinkedHashSetObject>::Remove(thread, obj, key);
229 }
230 
Add(const JSThread * thread,const JSHandle<LinkedHashSet> & obj,const JSHandle<JSTaggedValue> & key)231 JSHandle<LinkedHashSet> LinkedHashSet::Add(const JSThread *thread, const JSHandle<LinkedHashSet> &obj,
232                                            const JSHandle<JSTaggedValue> &key)
233 {
234     return LinkedHashTable<LinkedHashSet, LinkedHashSetObject>::Insert(thread, obj, key, key);
235 }
236 
AddWeakRef(const JSThread * thread,const JSHandle<LinkedHashSet> & obj,const JSHandle<JSTaggedValue> & key)237 JSHandle<LinkedHashSet> LinkedHashSet::AddWeakRef(const JSThread *thread, const JSHandle<LinkedHashSet> &obj,
238     const JSHandle<JSTaggedValue> &key)
239 {
240     return LinkedHashTable<LinkedHashSet, LinkedHashSetObject>::InsertWeakRef(thread, obj, key, key);
241 }
242 
Has(const JSThread * thread,JSTaggedValue key) const243 bool LinkedHashSet::Has(const JSThread *thread, JSTaggedValue key) const
244 {
245     int entry = FindElement(thread, key);
246     return entry != -1;
247 }
248 
Clear(const JSThread * thread,const JSHandle<LinkedHashSet> & table)249 JSHandle<LinkedHashSet> LinkedHashSet::Clear(const JSThread *thread, const JSHandle<LinkedHashSet> &table)
250 {
251     if (table->Capacity() == LinkedHashSet::MIN_CAPACITY) {
252         table->FillRangeWithSpecialValue(JSTaggedValue::Hole(), LinkedHashSet::ELEMENTS_START_INDEX,
253                                          table->GetLength());
254         table->SetNumberOfDeletedElements(thread, table->NumberOfDeletedElements() + table->NumberOfElements());
255         table->SetNumberOfElements(thread, 0);
256         return table;
257     }
258     JSHandle<LinkedHashSet> newSet = LinkedHashSet::Create(thread, LinkedHashSet::MIN_CAPACITY,
259         table.GetTaggedValue().IsInSharedHeap() ? MemSpaceKind::SHARED : MemSpaceKind::LOCAL);
260     if (table->Capacity() > 0) {
261         table->SetNextTable(thread, newSet.GetTaggedValue());
262         table->SetNumberOfDeletedElements(thread, -1);
263     }
264     return newSet;
265 }
266 
Shrink(const JSThread * thread,const JSHandle<LinkedHashSet> & table,int additionalCapacity)267 JSHandle<LinkedHashSet> LinkedHashSet::Shrink(const JSThread *thread, const JSHandle<LinkedHashSet> &table,
268     int additionalCapacity)
269 {
270     return LinkedHashTable<LinkedHashSet, LinkedHashSetObject>::Shrink(thread, table, additionalCapacity);
271 }
272 
Hash(const JSThread * thread,JSTaggedValue key)273 int LinkedHash::Hash(const JSThread *thread, JSTaggedValue key)
274 {
275     if (key.IsInt()) {
276         return key.GetInt();
277     }
278     if (key.IsSymbol()) {
279         auto symbolString = JSSymbol::Cast(key.GetTaggedObject());
280         return symbolString->GetHashField();
281     }
282     if (key.IsString()) {
283         auto keyString = reinterpret_cast<EcmaString *>(key.GetTaggedObject());
284         return EcmaStringAccessor(keyString).GetHashcode(thread);
285     }
286     if (key.IsECMAObject()) {
287         int32_t hash = ECMAObject::Cast(key.GetTaggedObject())->GetHash(thread);
288         if (hash == 0) {
289             hash = base::RandomGenerator::GenerateIdentityHash();
290             JSHandle<ECMAObject> ecmaObj(thread, key);
291             ECMAObject::SetHash(thread, hash, ecmaObj);
292         }
293         return hash;
294     }
295     if (key.IsBigInt()) {
296         uint32_t keyValue = BigInt::Cast(key.GetTaggedObject())->GetDigit(0);
297         return GetHash32(reinterpret_cast<uint8_t *>(&keyValue), sizeof(keyValue) / sizeof(uint8_t));
298     }
299     // Int, Double, Special and HeapObject(except symbol and string)
300     if (key.IsDouble()) {
301         key = JSTaggedValue::TryCastDoubleToInt32(key.GetDouble());
302         if (key.IsInt()) {
303             return key.GetInt();
304         }
305     }
306     uint64_t keyValue = key.GetRawData();
307     return GetHash32(reinterpret_cast<uint8_t *>(&keyValue), sizeof(keyValue) / sizeof(uint8_t));
308 }
309 }  // namespace panda::ecmascript
310