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