• 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(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 
202 // LinkedHashSet
Create(const JSThread * thread,int numberOfElements,MemSpaceKind spaceKind)203 JSHandle<LinkedHashSet> LinkedHashSet::Create(const JSThread *thread, int numberOfElements, MemSpaceKind spaceKind)
204 {
205     return LinkedHashTable<LinkedHashSet, LinkedHashSetObject>::Create(thread, numberOfElements, spaceKind);
206 }
207 
Delete(const JSThread * thread,const JSHandle<LinkedHashSet> & obj,const JSHandle<JSTaggedValue> & key)208 JSHandle<LinkedHashSet> LinkedHashSet::Delete(const JSThread *thread, const JSHandle<LinkedHashSet> &obj,
209                                               const JSHandle<JSTaggedValue> &key)
210 {
211     return LinkedHashTable<LinkedHashSet, LinkedHashSetObject>::Remove(thread, obj, key);
212 }
213 
Add(const JSThread * thread,const JSHandle<LinkedHashSet> & obj,const JSHandle<JSTaggedValue> & key)214 JSHandle<LinkedHashSet> LinkedHashSet::Add(const JSThread *thread, const JSHandle<LinkedHashSet> &obj,
215                                            const JSHandle<JSTaggedValue> &key)
216 {
217     return LinkedHashTable<LinkedHashSet, LinkedHashSetObject>::Insert(thread, obj, key, key);
218 }
219 
AddWeakRef(const JSThread * thread,const JSHandle<LinkedHashSet> & obj,const JSHandle<JSTaggedValue> & key)220 JSHandle<LinkedHashSet> LinkedHashSet::AddWeakRef(const JSThread *thread, const JSHandle<LinkedHashSet> &obj,
221     const JSHandle<JSTaggedValue> &key)
222 {
223     return LinkedHashTable<LinkedHashSet, LinkedHashSetObject>::InsertWeakRef(thread, obj, key, key);
224 }
225 
Has(const JSThread * thread,JSTaggedValue key) const226 bool LinkedHashSet::Has(const JSThread *thread, JSTaggedValue key) const
227 {
228     int entry = FindElement(thread, key);
229     return entry != -1;
230 }
231 
Clear(const JSThread * thread,const JSHandle<LinkedHashSet> & table)232 JSHandle<LinkedHashSet> LinkedHashSet::Clear(const JSThread *thread, const JSHandle<LinkedHashSet> &table)
233 {
234     if (table->Capacity() == LinkedHashSet::MIN_CAPACITY) {
235         table->FillRangeWithSpecialValue(JSTaggedValue::Hole(), LinkedHashSet::ELEMENTS_START_INDEX,
236                                          table->GetLength());
237         table->SetNumberOfDeletedElements(thread, table->NumberOfDeletedElements() + table->NumberOfElements());
238         table->SetNumberOfElements(thread, 0);
239         return table;
240     }
241     JSHandle<LinkedHashSet> newSet = LinkedHashSet::Create(thread, LinkedHashSet::MIN_CAPACITY,
242         table.GetTaggedValue().IsInSharedHeap() ? MemSpaceKind::SHARED : MemSpaceKind::LOCAL);
243     if (table->Capacity() > 0) {
244         table->SetNextTable(thread, newSet.GetTaggedValue());
245         table->SetNumberOfDeletedElements(thread, -1);
246     }
247     return newSet;
248 }
249 
Shrink(const JSThread * thread,const JSHandle<LinkedHashSet> & table,int additionalCapacity)250 JSHandle<LinkedHashSet> LinkedHashSet::Shrink(const JSThread *thread, const JSHandle<LinkedHashSet> &table,
251     int additionalCapacity)
252 {
253     return LinkedHashTable<LinkedHashSet, LinkedHashSetObject>::Shrink(thread, table, additionalCapacity);
254 }
255 
Hash(const JSThread * thread,JSTaggedValue key)256 int LinkedHash::Hash(const JSThread *thread, JSTaggedValue key)
257 {
258     if (key.IsInt()) {
259         return key.GetInt();
260     }
261     if (key.IsSymbol()) {
262         auto symbolString = JSSymbol::Cast(key.GetTaggedObject());
263         return symbolString->GetHashField();
264     }
265     if (key.IsString()) {
266         auto keyString = reinterpret_cast<EcmaString *>(key.GetTaggedObject());
267         return EcmaStringAccessor(keyString).GetHashcode();
268     }
269     if (key.IsECMAObject()) {
270         int32_t hash = ECMAObject::Cast(key.GetTaggedObject())->GetHash();
271         if (hash == 0) {
272             hash = base::RandomGenerator::GenerateIdentityHash();
273             JSHandle<ECMAObject> ecmaObj(thread, key);
274             ECMAObject::SetHash(thread, hash, ecmaObj);
275         }
276         return hash;
277     }
278     if (key.IsBigInt()) {
279         uint32_t keyValue = BigInt::Cast(key.GetTaggedObject())->GetDigit(0);
280         return GetHash32(reinterpret_cast<uint8_t *>(&keyValue), sizeof(keyValue) / sizeof(uint8_t));
281     }
282     // Int, Double, Special and HeapObject(except symbol and string)
283     if (key.IsDouble()) {
284         key = JSTaggedValue::TryCastDoubleToInt32(key.GetDouble());
285         if (key.IsInt()) {
286             return key.GetInt();
287         }
288     }
289     uint64_t keyValue = key.GetRawData();
290     return GetHash32(reinterpret_cast<uint8_t *>(&keyValue), sizeof(keyValue) / sizeof(uint8_t));
291 }
292 }  // namespace panda::ecmascript
293