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