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