1 /*
2  * Copyright (c) 2021-2022 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/tagged_dictionary.h"
17 #include "ecmascript/ecma_string-inl.h"
18 #include "ecmascript/js_object-inl.h"
19 
20 namespace panda::ecmascript {
Hash(const JSTaggedValue &key)21 int NameDictionary::Hash(const JSTaggedValue &key)
22 {
23     if (key.IsHeapObject()) {
24         JSTaggedValue jsKey(key);
25         if (jsKey.IsSymbol()) {
26             auto symbolString = JSSymbol::Cast(key.GetTaggedObject());
27             return symbolString->GetHashField();
28         }
29         if (jsKey.IsString()) {
30             auto keyString = reinterpret_cast<EcmaString *>(key.GetTaggedObject());
31             return EcmaStringAccessor(keyString).GetHashcode();
32         }
33     }
34     // key must be object
35     LOG_ECMA(FATAL) << "this branch is unreachable";
36     UNREACHABLE();
37 }
38 
39 // for ohmurl path to compute hash code
Hash(const uint8_t* str, int strSize)40 int NameDictionary::Hash(const uint8_t* str, int strSize)
41 {
42     uint32_t hash = EcmaString::ComputeHashForData(str, strSize, 0);
43     // url path can not be small int.
44     return EcmaString::MixHashcode(hash, EcmaString::NOT_INTEGER);
45 }
46 
IsMatch(const JSTaggedValue &key, const JSTaggedValue &other)47 bool NameDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other)
48 {
49     return key == other;
50 }
51 
IsMatch(const uint8_t* str, int size, const JSTaggedValue &other)52 bool NameDictionary::IsMatch(const uint8_t* str, int size, const JSTaggedValue &other)
53 {
54     if (!other.IsString()) {
55         return false;
56     }
57     EcmaString *keyString = reinterpret_cast<EcmaString *>(other.GetTaggedObject());
58 
59     Span<const uint8_t> data1(EcmaStringAccessor(keyString).GetDataUtf8(), keyString->GetLength());
60     Span<const uint8_t> data2(str, size);
61     if (data1.Size() != data2.Size()) {
62         return false;
63     }
64     return EcmaString::StringsAreEquals(data1, data2);
65 }
66 
GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const67 void NameDictionary::GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const
68 {
69     int arrayIndex = 0;
70     int size = Size();
71     CVector<std::pair<JSTaggedValue, PropertyAttributes>> sortArr;
72     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
73         JSTaggedValue key = GetKey(hashIndex);
74         if (!key.IsUndefined() && !key.IsHole()) {
75             PropertyAttributes attr = GetAttributes(hashIndex);
76             std::pair<JSTaggedValue, PropertyAttributes> pair(key, attr);
77             sortArr.push_back(pair);
78         }
79     }
80     std::sort(sortArr.begin(), sortArr.end(), CompKey);
81     for (const auto &entry : sortArr) {
82         keyArray->Set(thread, arrayIndex + offset, entry.first);
83         arrayIndex++;
84     }
85 }
86 
UpdateAllAttributesToNoWitable(const JSThread *thread)87 void NameDictionary::UpdateAllAttributesToNoWitable(const JSThread *thread)
88 {
89     int size = Size();
90     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
91         JSTaggedValue key = GetKey(hashIndex);
92         if (!key.IsUndefined() && !key.IsHole()) {
93             PropertyAttributes attr = GetAttributes(hashIndex);
94             attr.SetWritable(false);
95             SetAttributes(thread, hashIndex, attr);
96         }
97     }
98 }
99 
GetAllKeysByFilter(const JSThread *thread, uint32_t &keyArrayEffectivelength, TaggedArray *keyArray, uint32_t filter) const100 void NameDictionary::GetAllKeysByFilter(const JSThread *thread, uint32_t &keyArrayEffectivelength,
101                                         TaggedArray *keyArray, uint32_t filter) const
102 {
103     int size = Size();
104     CVector<std::pair<JSTaggedValue, PropertyAttributes>> sortArr;
105     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
106         JSTaggedValue key = GetKey(hashIndex);
107         if (!key.IsUndefined() && !key.IsHole()) {
108             PropertyAttributes attr = GetAttributes(hashIndex);
109             bool bIgnore = FilterHelper::IgnoreKeyByFilter<PropertyAttributes>(attr, filter);
110             if (bIgnore) {
111                 continue;
112             }
113             if (key.IsString() && (filter & NATIVE_KEY_SKIP_STRINGS)) {
114                 continue;
115             }
116             if (key.IsSymbol() && (filter & NATIVE_KEY_SKIP_SYMBOLS)) {
117                 continue;
118             }
119             std::pair<JSTaggedValue, PropertyAttributes> pair(key, attr);
120             sortArr.push_back(pair);
121         }
122     }
123     std::sort(sortArr.begin(), sortArr.end(), CompKey);
124     for (const auto &entry : sortArr) {
125         keyArray->Set(thread, keyArrayEffectivelength, entry.first);
126         keyArrayEffectivelength++;
127     }
128 }
129 
GetNumOfEnumKeys() const130 std::pair<uint32_t, uint32_t> NameDictionary::GetNumOfEnumKeys() const
131 {
132     uint32_t enumKeys = 0;
133     uint32_t shadowKeys = 0;
134     int size = Size();
135     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
136         JSTaggedValue key = GetKey(hashIndex);
137         if (key.IsString()) {
138             PropertyAttributes attr = GetAttributes(hashIndex);
139             if (attr.IsEnumerable()) {
140                 enumKeys++;
141             } else {
142                 shadowKeys++;
143             }
144         }
145     }
146     return std::make_pair(enumKeys, shadowKeys);
147 }
148 
GetAllEnumKeys(JSThread *thread, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys, JSHandle<TaggedQueue> shadowQueue, int32_t lastLength) const149 void NameDictionary::GetAllEnumKeys(JSThread *thread, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys,
150                                     JSHandle<TaggedQueue> shadowQueue, int32_t lastLength) const
151 {
152     uint32_t arrayIndex = 0;
153     CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
154     int size = Size();
155     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
156         JSHandle<JSTaggedValue> keyHandle(thread, GetKey(hashIndex));
157         if (keyHandle->IsString()) {
158             PropertyAttributes attr = GetAttributes(hashIndex);
159             if (attr.IsEnumerable()) {
160                 std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(keyHandle, attr);
161                 bool isDuplicated = JSObject::IsDepulicateKeys(thread, keyArray, lastLength, shadowQueue, keyHandle);
162                 if (isDuplicated) {
163                     continue;
164                 }
165                 sortArr.push_back(pair);
166             } else {
167                 TaggedQueue::PushFixedQueue(thread, shadowQueue, keyHandle);
168             }
169         }
170     }
171     std::sort(sortArr.begin(), sortArr.end(), CompHandleKey);
172     for (auto entry : sortArr) {
173         keyArray->Set(thread, arrayIndex + static_cast<uint32_t>(offset), entry.first);
174         arrayIndex++;
175     }
176     *keys += arrayIndex;
177 }
178 
GetAllEnumKeys(JSThread *thread, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys) const179 void NameDictionary::GetAllEnumKeys(JSThread *thread, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys) const
180 {
181     uint32_t arrayIndex = 0;
182     CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
183     int size = Size();
184     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
185         JSHandle<JSTaggedValue> keyHandle(thread, GetKey(hashIndex));
186         if (keyHandle->IsString()) {
187             PropertyAttributes attr = GetAttributes(hashIndex);
188             if (attr.IsEnumerable()) {
189                 std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(keyHandle, attr);
190                 sortArr.push_back(pair);
191             }
192         }
193     }
194     std::sort(sortArr.begin(), sortArr.end(), CompHandleKey);
195     for (auto entry : sortArr) {
196         keyArray->Set(thread, arrayIndex + static_cast<uint32_t>(offset), entry.first);
197         arrayIndex++;
198     }
199     *keys += arrayIndex;
200 }
201 
Create(const JSThread *thread, int numberOfElements)202 JSHandle<NameDictionary> NameDictionary::Create(const JSThread *thread, int numberOfElements)
203 {
204     return OrderHashTableT::Create(thread, numberOfElements);
205 }
206 
CreateInSharedHeap(const JSThread *thread, int numberOfElements)207 JSHandle<NameDictionary> NameDictionary::CreateInSharedHeap(const JSThread *thread, int numberOfElements)
208 {
209     return OrderHashTableT::Create(thread, numberOfElements, MemSpaceKind::SHARED);
210 }
211 
GetAttributes(int entry) const212 PropertyAttributes NameDictionary::GetAttributes(int entry) const
213 {
214     int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX;
215     return PropertyAttributes(Get(index));
216 }
217 
SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData)218 void NameDictionary::SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData)
219 {
220     int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX;
221     Set(thread, index, metaData.GetTaggedValue());
222 }
223 
SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, const PropertyAttributes &metaData)224 void NameDictionary::SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value,
225                               const PropertyAttributes &metaData)
226 {
227     SetKey(thread, entry, key);
228     SetValue(thread, entry, value);
229     SetAttributes(thread, entry, metaData);
230 }
231 
UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, const PropertyAttributes &metaData)232 void NameDictionary::UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value,
233                                               const PropertyAttributes &metaData)
234 {
235     SetValue(thread, entry, value);
236     SetAttributes(thread, entry, metaData);
237 }
238 
UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value)239 void NameDictionary::UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value)
240 {
241     SetValue(thread, entry, value);
242 }
243 
ClearEntry(const JSThread *thread, int entry)244 void NameDictionary::ClearEntry(const JSThread *thread, int entry)
245 {
246     JSTaggedValue hole = JSTaggedValue::Hole();
247     PropertyAttributes metaData;
248     SetEntry(thread, entry, hole, hole, metaData);
249 }
250 
Hash(const JSTaggedValue &key)251 int NumberDictionary::Hash(const JSTaggedValue &key)
252 {
253     if (key.IsInt()) {
254         int keyValue = key.GetInt();
255         return GetHash32(reinterpret_cast<uint8_t *>(&keyValue), sizeof(keyValue) / sizeof(uint8_t));
256     }
257     if (key.IsDouble()) {
258         int32_t keyValue = static_cast<int32_t>(static_cast<uint32_t>(key.GetDouble()));
259         return GetHash32(reinterpret_cast<uint8_t *>(&keyValue), sizeof(keyValue) / sizeof(uint8_t));
260     }
261     // key must be object
262     LOG_ECMA(FATAL) << "this branch is unreachable";
263     UNREACHABLE();
264 }
265 
IsMatch(const JSTaggedValue &key, const JSTaggedValue &other)266 bool NumberDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other)
267 {
268     if (key.IsHole() || key.IsUndefined()) {
269         return false;
270     }
271 
272     if (key.IsInt()) {
273         if (other.IsInt()) {
274             return key.GetInt() == other.GetInt();
275         }
276         return false;
277     }
278 
279     if (key.IsDouble()) {
280         if (other.IsInt()) {
281             int32_t keyValue = static_cast<int32_t>(static_cast<uint32_t>(key.GetDouble()));
282             return keyValue == other.GetInt();
283         }
284         if (other.IsDouble()) {
285             return key.GetDouble() == other.GetDouble();
286         }
287         return false;
288     }
289     // key must be integer
290     LOG_ECMA(FATAL) << "this branch is unreachable";
291     UNREACHABLE();
292 }
293 
GetAllKeys(const JSThread *thread, const JSHandle<NumberDictionary> &obj, int offset, const JSHandle<TaggedArray> &keyArray)294 void NumberDictionary::GetAllKeys(const JSThread *thread, const JSHandle<NumberDictionary> &obj, int offset,
295                                   const JSHandle<TaggedArray> &keyArray)
296 {
297     ASSERT_PRINT(offset + obj->EntriesCount() <= static_cast<int>(keyArray->GetLength()),
298                  "keyArray capacity is not enough for dictionary");
299     int arrayIndex = 0;
300     int size = obj->Size();
301     CVector<JSTaggedValue> sortArr;
302     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
303         JSTaggedValue key = obj->GetKey(hashIndex);
304         if (!key.IsUndefined() && !key.IsHole()) {
305             sortArr.push_back(JSTaggedValue(static_cast<uint32_t>(key.GetInt())));
306         }
307     }
308     std::sort(sortArr.begin(), sortArr.end(), CompKey);
309     for (auto entry : sortArr) {
310         JSHandle<JSTaggedValue> keyHandle(thread, entry);
311         JSHandle<EcmaString> str = JSTaggedValue::ToString(const_cast<JSThread *>(thread), keyHandle);
312         ASSERT_NO_ABRUPT_COMPLETION(thread);
313         keyArray->Set(thread, arrayIndex + offset, str.GetTaggedValue());
314         arrayIndex++;
315     }
316 }
317 
GetAllKeysByFilter(const JSThread *thread, const JSHandle<NumberDictionary> &obj, uint32_t &keyArrayEffectivelength, const JSHandle<TaggedArray> &keyArray, uint32_t filter)318 void NumberDictionary::GetAllKeysByFilter(const JSThread *thread, const JSHandle<NumberDictionary> &obj,
319                                           uint32_t &keyArrayEffectivelength, const JSHandle<TaggedArray> &keyArray,
320                                           uint32_t filter)
321 {
322     ASSERT_PRINT(keyArrayEffectivelength + static_cast<uint32_t>(obj->EntriesCount()) <= keyArray->GetLength(),
323                  "keyArray capacity is not enough for dictionary");
324 
325     CVector<JSTaggedValue> sortArr;
326     uint32_t size = static_cast<uint32_t>(obj->Size());
327     for (uint32_t hashIndex = 0; hashIndex < size; hashIndex++) {
328         JSTaggedValue key = obj->GetKey(hashIndex);
329         if (key.IsUndefined() || key.IsHole()) {
330             continue;
331         }
332 
333         PropertyAttributes attr = obj->GetAttributes(hashIndex);
334         bool bIgnore = FilterHelper::IgnoreKeyByFilter<PropertyAttributes>(attr, filter);
335         if (!bIgnore) {
336             sortArr.push_back(JSTaggedValue(static_cast<uint32_t>(key.GetInt())));
337         }
338     }
339     std::sort(sortArr.begin(), sortArr.end(), CompKey);
340     ASSERT_NO_ABRUPT_COMPLETION(thread);
341     for (auto entry : sortArr) {
342         keyArray->Set(thread, keyArrayEffectivelength, entry);
343         keyArrayEffectivelength++;
344     }
345 }
346 
GetAllEnumKeys(JSThread *thread, const JSHandle<NumberDictionary> &obj, int offset, const JSHandle<TaggedArray> &keyArray, uint32_t *keys, int32_t lastLength)347 void NumberDictionary::GetAllEnumKeys(JSThread *thread, const JSHandle<NumberDictionary> &obj, int offset,
348                                       const JSHandle<TaggedArray> &keyArray, uint32_t *keys, int32_t lastLength)
349 {
350     ASSERT_PRINT(offset + obj->EntriesCount() <= static_cast<int>(keyArray->GetLength()),
351                  "keyArray capacity is not enough for dictionary");
352     uint32_t arrayIndex = 0;
353     int size = obj->Size();
354     CVector<JSTaggedValue> sortArr;
355     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
356         JSTaggedValue key = obj->GetKey(hashIndex);
357         if (!key.IsUndefined() && !key.IsHole()) {
358             PropertyAttributes attr = obj->GetAttributes(hashIndex);
359             if (attr.IsEnumerable()) {
360                 sortArr.push_back(JSTaggedValue(static_cast<uint32_t>(key.GetInt())));
361             }
362         }
363     }
364     std::sort(sortArr.begin(), sortArr.end(), CompKey);
365     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
366     JSHandle<TaggedQueue> emptyQueue = factory->GetEmptyTaggedQueue();
367     JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
368     for (auto entry : sortArr) {
369         keyHandle.Update(entry);
370         JSHandle<EcmaString> str = JSTaggedValue::ToString(const_cast<JSThread *>(thread), keyHandle);
371         ASSERT_NO_ABRUPT_COMPLETION(thread);
372         bool isDuplicated = JSObject::IsDepulicateKeys(thread, keyArray, lastLength, emptyQueue,
373                                                        JSHandle<JSTaggedValue>(str));
374         if (isDuplicated) {
375             continue;
376         }
377         keyArray->Set(thread, arrayIndex + static_cast<uint32_t>(offset), str.GetTaggedValue());
378         arrayIndex++;
379     }
380     *keys += arrayIndex;
381 }
382 
Create(const JSThread *thread, int numberOfElements)383 JSHandle<NumberDictionary> NumberDictionary::Create(const JSThread *thread, int numberOfElements)
384 {
385     return OrderHashTableT::Create(thread, numberOfElements);
386 }
387 
CreateInSharedHeap(const JSThread *thread, int numberOfElements)388 JSHandle<NumberDictionary> NumberDictionary::CreateInSharedHeap(const JSThread *thread, int numberOfElements)
389 {
390     return OrderHashTableT::Create(thread, numberOfElements, MemSpaceKind::SHARED);
391 }
392 
GetAttributes(int entry) const393 PropertyAttributes NumberDictionary::GetAttributes(int entry) const
394 {
395     int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX;
396     return PropertyAttributes(Get(index));
397 }
398 
SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData)399 void NumberDictionary::SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData)
400 {
401     int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX;
402     Set(thread, index, metaData.GetTaggedValue());
403 }
404 
SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, const PropertyAttributes &metaData)405 void NumberDictionary::SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value,
406                                 const PropertyAttributes &metaData)
407 {
408     SetKey(thread, entry, key);
409     SetValue(thread, entry, value);
410     SetAttributes(thread, entry, metaData);
411 }
412 
UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, const PropertyAttributes &metaData)413 void NumberDictionary::UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value,
414                                                 const PropertyAttributes &metaData)
415 {
416     SetValue(thread, entry, value);
417     SetAttributes(thread, entry, metaData);
418 }
419 
UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value)420 void NumberDictionary::UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value)
421 {
422     SetValue(thread, entry, value);
423 }
424 
ClearEntry(const JSThread *thread, int entry)425 void NumberDictionary::ClearEntry(const JSThread *thread, int entry)
426 {
427     JSTaggedValue hole = JSTaggedValue::Hole();
428     PropertyAttributes metaData;
429     SetEntry(thread, entry, hole, hole, metaData);
430 }
431 
Create(const JSThread *thread, int numberOfElements)432 JSHandle<PointerToIndexDictionary> PointerToIndexDictionary::Create(const JSThread *thread, int numberOfElements)
433 {
434     return OrderHashTableT::Create(thread, numberOfElements);
435 }
436 
PutIfAbsent( const JSThread *thread, const JSHandle<PointerToIndexDictionary> &dictionary, const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)437 JSHandle<PointerToIndexDictionary> PointerToIndexDictionary::PutIfAbsent(
438     const JSThread *thread,
439     const JSHandle<PointerToIndexDictionary> &dictionary,
440     const JSHandle<JSTaggedValue> &key,
441     const JSHandle<JSTaggedValue> &value)
442 {
443     /* no need to add key if exist */
444     int entry = dictionary->FindEntry(key.GetTaggedValue());
445     if (entry != -1) {
446         return dictionary;
447     }
448     // Check whether the table should be growed
449     JSHandle<PointerToIndexDictionary> newDictionary = HashTableT::GrowHashTable(thread, dictionary);
450 
451     // Compute the key object
452     uint32_t hash = static_cast<uint32_t>(PointerToIndexDictionary::Hash(key.GetTaggedValue()));
453     entry = newDictionary->FindInsertIndex(hash);
454     newDictionary->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue());
455     newDictionary->IncreaseEntries(thread);
456     return newDictionary;
457 }
458 
459 using ProtoArray = FunctionProtoTransitionTable::ProtoArray;
FunctionProtoTransitionTable(const JSThread *thread)460 FunctionProtoTransitionTable::FunctionProtoTransitionTable(const JSThread *thread)
461 {
462     protoTransitionTable_ = PointerToIndexDictionary::Create(thread, DEFAULT_FIRST_LEVEL_SIZE).GetTaggedValue();
463 }
464 
~FunctionProtoTransitionTable()465 FunctionProtoTransitionTable::~FunctionProtoTransitionTable()
466 {
467     fakeParentMap_.clear();
468 }
469 
Iterate(const RootVisitor &v)470 void FunctionProtoTransitionTable::Iterate(const RootVisitor &v)
471 {
472     v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&protoTransitionTable_)));
473     for (auto &iter : fakeParentMap_) {
474         v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&iter.first)));
475         v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&iter.second)));
476     }
477     for (auto &iter : fakeChildMap_) {
478         v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&iter.first)));
479         v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&iter.second)));
480     }
481 }
482 
UpdateProtoTransitionTable(const JSThread *thread, JSHandle<PointerToIndexDictionary> map)483 void FunctionProtoTransitionTable::UpdateProtoTransitionTable(const JSThread *thread,
484                                                               JSHandle<PointerToIndexDictionary> map)
485 {
486     // PointerToIndexDictionary's hash value is a hclass pointer,
487     // and the hclass pointer could change during deserialized,
488     // so rehash is needed after deserialized.
489     auto newMap = PointerToIndexDictionary::Create(thread, map->Size());
490     map->Rehash(thread, *newMap);
491     protoTransitionTable_ = newMap.GetTaggedValue();
492 }
493 
InsertTransitionItem(const JSThread *thread, JSHandle<JSTaggedValue> ihc, JSHandle<JSTaggedValue> baseIhc, JSHandle<JSTaggedValue> transIhc, JSHandle<JSTaggedValue> transPhc)494 void FunctionProtoTransitionTable::InsertTransitionItem(const JSThread *thread,
495                                                         JSHandle<JSTaggedValue> ihc,
496                                                         JSHandle<JSTaggedValue> baseIhc,
497                                                         JSHandle<JSTaggedValue> transIhc,
498                                                         JSHandle<JSTaggedValue> transPhc)
499 {
500     JSHandle<PointerToIndexDictionary> protoTransitionHandle(thread, protoTransitionTable_);
501     int32_t entry1 = protoTransitionHandle->FindEntry(ihc.GetTaggedValue());
502     if (entry1 != -1) {
503         JSHandle<ProtoArray> protoArray(thread, protoTransitionHandle->GetValue(entry1));
504         auto entry2 = protoArray->FindEntry(baseIhc.GetTaggedValue());
505         if (entry2 != -1) {
506             LOG_ECMA(DEBUG) << "Record prototype for current function already!";
507             return;
508         }
509         uint32_t insertEntry = ProtoArray::GetEntry(JSHandle<JSHClass>(baseIhc));
510         protoArray->SetEntry(
511             thread, insertEntry, baseIhc.GetTaggedValue(), transIhc.GetTaggedValue(), transPhc.GetTaggedValue());
512         return;
513     }
514     JSHandle<ProtoArray> protoArray = ProtoArray::Create(thread);
515     uint32_t insertEntry = ProtoArray::GetEntry(JSHandle<JSHClass>(baseIhc));
516     protoArray->SetEntry(
517         thread, insertEntry, baseIhc.GetTaggedValue(), transIhc.GetTaggedValue(), transPhc.GetTaggedValue());
518     JSHandle<PointerToIndexDictionary> newTransitionTable =
519         PointerToIndexDictionary::PutIfAbsent(thread, protoTransitionHandle, ihc, JSHandle<JSTaggedValue>(protoArray));
520     protoTransitionTable_ = newTransitionTable.GetTaggedValue();
521 }
522 
FindTransitionByHClass( const JSThread *thread, JSHandle<JSTaggedValue> ihc, JSHandle<JSTaggedValue> baseIhc) const523 std::pair<JSTaggedValue, JSTaggedValue> FunctionProtoTransitionTable::FindTransitionByHClass(
524     const JSThread *thread, JSHandle<JSTaggedValue> ihc, JSHandle<JSTaggedValue> baseIhc) const
525 {
526     ASSERT(ihc->IsJSHClass());
527     ASSERT(baseIhc->IsJSHClass());
528     JSHandle<PointerToIndexDictionary> protoTransitionHandle(thread, protoTransitionTable_);
529     int32_t entry1 = protoTransitionHandle->FindEntry(ihc.GetTaggedValue());
530     if (entry1 == -1) {
531         LOG_ECMA(DEBUG) << "Selected ihc not found2!";
532         return std::make_pair(JSTaggedValue::Undefined(), JSTaggedValue::Undefined());
533     }
534     JSHandle<ProtoArray> protoArray(thread, protoTransitionHandle->GetValue(entry1));
535     int32_t entry2 = protoArray->FindEntry(baseIhc.GetTaggedValue());
536     if (entry2 == -1) {
537         LOG_ECMA(ERROR) << "Selected baseIhc not found!";
538         return std::make_pair(JSTaggedValue::Undefined(), JSTaggedValue::Undefined());
539     }
540     return protoArray->GetTransition(entry2);
541 }
542 
TryInsertFakeParentItem(JSTaggedType child, JSTaggedType parent)543 bool FunctionProtoTransitionTable::TryInsertFakeParentItem(JSTaggedType child, JSTaggedType parent)
544 {
545     LockHolder lock(fakeParentMutex_);
546     // Situation:
547     // 1: d1.prototype = p
548     // 2: d2.prototype = p
549     // this check is true for step 2
550     auto iter1 = fakeParentMap_.find(child);
551     if (child == parent && iter1 != fakeParentMap_.end()) {
552         return true;
553     }
554     auto iter2 = fakeChildMap_.find(parent);
555     if (iter2 != fakeChildMap_.end()) {
556         if (iter2->second != child) {
557             LOG_ECMA(DEBUG) << "Unsupported mapping for a parent to more than 1 childs!";
558             return false;
559         }
560     }
561 
562     if (iter1 == fakeParentMap_.end()) {
563         fakeParentMap_[child] = parent;
564         fakeChildMap_[parent] = child;
565         return true;
566     }
567     if (iter1->second == parent) {
568         return true;
569     }
570     LOG_ECMA(ERROR) << "Conflict mapping for the same child";
571     return false;
572 }
573 
GetFakeParent(JSTaggedType child)574 JSTaggedType FunctionProtoTransitionTable::GetFakeParent(JSTaggedType child)
575 {
576     LockHolder lock(fakeParentMutex_);
577     auto iter = fakeParentMap_.find(child);
578     if (iter != fakeParentMap_.end()) {
579         return iter->second;
580     }
581     return 0;
582 }
583 
584 // static
Create(const JSThread *thread)585 JSHandle<ProtoArray> ProtoArray::Create(const JSThread *thread)
586 {
587     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
588     JSHandle<TaggedArray> result = factory->NewTaggedArray(ENTRY_NUMBER * ENTRY_SIZE);
589     return JSHandle<ProtoArray>(result);
590 }
591 
592 // static
GetEntry(JSHandle<JSHClass> hclass)593 bool ProtoArray::GetEntry(JSHandle<JSHClass> hclass)
594 {
595     return JSHandle<JSHClass>(hclass)->IsPrototype() ? ProtoArray::CLONED_ENTRY_INDEX
596                                                      : ProtoArray::INIT_ENTRY_INDEX;
597 }
598 
SetEntry(const JSThread *thread, uint32_t index, JSTaggedValue key, JSTaggedValue transIhc, JSTaggedValue transPhc)599 void ProtoArray::SetEntry(const JSThread *thread, uint32_t index, JSTaggedValue key, JSTaggedValue transIhc,
600                           JSTaggedValue transPhc)
601 {
602     uint32_t entryIndex = index * ENTRY_SIZE;
603     uint32_t keyIndex = entryIndex + KEY_INDEX;
604     uint32_t ihcIndex = entryIndex + TRANS_IHC_INDEX;
605     uint32_t phcIndex = entryIndex + TRANS_PHC_INDEX;
606     Set(thread, keyIndex, key);
607     Set(thread, ihcIndex, transIhc);
608     Set(thread, phcIndex, transPhc);
609 }
610 
GetKey(uint32_t index) const611 JSTaggedValue ProtoArray::GetKey(uint32_t index) const
612 {
613     uint32_t entryIndex = index * ENTRY_SIZE;
614     uint32_t keyIndex = entryIndex + KEY_INDEX;
615     return Get(keyIndex);
616 }
617 
GetTransition(uint32_t index) const618 std::pair<JSTaggedValue, JSTaggedValue> ProtoArray::GetTransition(uint32_t index) const
619 {
620     uint32_t entryIndex = index * ENTRY_SIZE + KEY_INDEX;
621     JSTaggedValue transIhc = Get(entryIndex + TRANS_IHC_INDEX);
622     JSTaggedValue transPhc = Get(entryIndex + TRANS_PHC_INDEX);
623     return std::make_pair(transIhc, transPhc);
624 }
625 
FindTransition(JSTaggedValue key) const626 std::pair<JSTaggedValue, JSTaggedValue> ProtoArray::FindTransition(JSTaggedValue key) const
627 {
628     for (uint32_t i = 0; i < ENTRY_NUMBER; i++) {
629         uint32_t index = i * ENTRY_SIZE + KEY_INDEX;
630         JSTaggedValue keyValue = Get(index);
631         if (keyValue == key) {
632             JSTaggedValue transIhc = Get(index + TRANS_IHC_INDEX);
633             JSTaggedValue transPhc = Get(index + TRANS_PHC_INDEX);
634             return std::make_pair(transIhc, transPhc);
635         }
636     }
637     return std::make_pair(JSTaggedValue::Undefined(), JSTaggedValue::Undefined());
638 }
639 
FindEntry(JSTaggedValue key) const640 int32_t ProtoArray::FindEntry(JSTaggedValue key) const
641 {
642     for (uint32_t i = 0; i < ENTRY_NUMBER; i++) {
643         uint32_t index = i * ENTRY_SIZE + KEY_INDEX;
644         JSTaggedValue keyValue = Get(index);
645         if (keyValue == key) {
646             return i;
647         }
648     }
649     return -1;
650 }
651 }  // namespace panda::ecmascript
652