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