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