1 /*
2 * Copyright (c) 2022-2024 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_tree.h"
17
18 #include "ecmascript/interpreter/interpreter.h"
19 #include "ecmascript/js_function.h"
20
21 namespace panda::ecmascript {
22 template<typename Derived>
Create(const JSThread * thread,int numberOfElements)23 JSHandle<Derived> TaggedTree<Derived>::Create(const JSThread *thread, int numberOfElements)
24 {
25 ASSERT_PRINT(numberOfElements > 0, "size must be a non-negative integer");
26 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
27 int capacity = numberOfElements > MIN_CAPACITY ? numberOfElements : MIN_CAPACITY;
28 int length = ELEMENTS_START_INDEX + capacity * (Derived::ENTRY_SIZE);
29
30 auto tree = JSHandle<Derived>::Cast(factory->NewTaggedArray(length));
31 tree->SetNumberOfElements(thread, 0);
32 tree->SetNumberOfDeletedElements(thread, 0);
33 tree->SetRootEntries(thread, -1);
34 tree->SetCompare(thread, JSTaggedValue::Hole());
35 tree->SetCapacity(thread, capacity);
36 return tree;
37 }
38
39 template<typename Derived>
InsertRebalance(const JSThread * thread,int index)40 void TaggedTree<Derived>::InsertRebalance(const JSThread *thread, int index)
41 {
42 while (IsValidIndex(thread, index) && GetColor(GetParent(index)) == TreeColor::RED) {
43 if (IsLeft(GetParent(index))) {
44 int bro = GetLeftBrother(GetParent(index));
45 if (GetColor(bro)) {
46 SetColor(thread, GetParent(index), TreeColor::BLACK);
47 SetColor(thread, bro, TreeColor::BLACK);
48 SetColor(thread, GetParent(GetParent(index)), TreeColor::RED);
49 index = GetParent(GetParent(index));
50 } else {
51 if (IsRight(index)) {
52 index = GetParent(index);
53 LeftRotate(thread, index);
54 }
55 SetColor(thread, GetParent(index), TreeColor::BLACK);
56 SetColor(thread, GetParent(GetParent(index)), TreeColor::RED);
57 RightRotate(thread, GetParent(GetParent(index)));
58 }
59 } else {
60 int bro = GetRightBrother(GetParent(index));
61 if (GetColor(bro)) {
62 SetColor(thread, GetParent(index), TreeColor::BLACK);
63 SetColor(thread, bro, TreeColor::BLACK);
64 SetColor(thread, GetParent(GetParent(index)), TreeColor::RED);
65 index = GetParent(GetParent(index));
66 } else {
67 if (IsLeft(index)) {
68 index = GetParent(index);
69 RightRotate(thread, index);
70 }
71 SetColor(thread, GetParent(index), TreeColor::BLACK);
72 SetColor(thread, GetParent(GetParent(index)), TreeColor::RED);
73 LeftRotate(thread, GetParent(GetParent(index)));
74 }
75 }
76 }
77 SetColor(thread, GetRootEntries(), TreeColor::BLACK);
78 }
79
80 template<typename Derived>
LeftRotate(const JSThread * thread,int index)81 void TaggedTree<Derived>::LeftRotate(const JSThread *thread, int index)
82 {
83 if (index >= 0) {
84 int right = GetRightChild(index).GetInt();
85 JSTaggedValue leftOfRight = GetLeftChild(right);
86 SetRightChild(thread, index, leftOfRight);
87 if (!leftOfRight.IsHole()) {
88 SetParent(thread, leftOfRight.GetInt(), JSTaggedValue(index));
89 }
90 int parentOfIndex = GetParent(index);
91 SetParent(thread, right, JSTaggedValue(parentOfIndex));
92 if (parentOfIndex < 0) {
93 SetRootEntries(thread, right);
94 } else {
95 JSTaggedValue left = GetLeftChild(parentOfIndex);
96 if (!left.IsHole() && left.GetInt() == index) { // change to isleft
97 SetLeftChild(thread, parentOfIndex, JSTaggedValue(right));
98 } else {
99 SetRightChild(thread, parentOfIndex, JSTaggedValue(right));
100 }
101 }
102 SetLeftChild(thread, right, JSTaggedValue(index));
103 SetParent(thread, index, JSTaggedValue(right));
104 }
105 }
106
107 template<typename Derived>
RightRotate(const JSThread * thread,int index)108 void TaggedTree<Derived>::RightRotate(const JSThread *thread, int index)
109 {
110 if (index >= 0) {
111 int left = GetLeftChild(index).GetInt();
112 JSTaggedValue rightOfLeft = GetRightChild(left);
113 SetLeftChild(thread, index, rightOfLeft);
114 if (!rightOfLeft.IsHole()) {
115 SetParent(thread, rightOfLeft.GetInt(), JSTaggedValue(index));
116 }
117 int parentOfIndex = GetParent(index);
118 SetParent(thread, left, JSTaggedValue(parentOfIndex));
119 if (parentOfIndex < 0) {
120 SetRootEntries(thread, left);
121 } else {
122 JSTaggedValue right = GetRightChild(parentOfIndex);
123 if (!right.IsHole() && right.GetInt() == index) { // change to isright
124 SetRightChild(thread, parentOfIndex, JSTaggedValue(left));
125 } else {
126 SetLeftChild(thread, parentOfIndex, JSTaggedValue(left));
127 }
128 }
129 SetRightChild(thread, left, JSTaggedValue(index));
130 SetParent(thread, index, JSTaggedValue(left));
131 }
132 }
133
134 template<typename Derived>
AdjustTaggedTree(const JSThread * thread,const JSHandle<Derived> & tree,int len)135 JSHandle<Derived> TaggedTree<Derived>::AdjustTaggedTree(const JSThread *thread, const JSHandle<Derived> &tree, int len)
136 {
137 JSMutableHandle<Derived> newTree(thread, JSTaggedValue::Undefined());
138 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
139 if (tree->NumberOfDeletedElements() == 0) {
140 newTree.Update(factory->ExtendArray(JSHandle<TaggedArray>::Cast(tree), len).GetTaggedValue());
141 return newTree;
142 }
143
144 uint32_t elements = tree->NumberOfElements();
145 newTree.Update(factory->NewTaggedArray(len).GetTaggedValue());
146 newTree->SetNumberOfElements(thread, elements);
147 newTree->SetNumberOfDeletedElements(thread, 0);
148
149 newTree->SetRootEntries(thread, 0);
150 CQueue<int> entries;
151 entries.push(tree->GetRootEntries());
152 int index = 0;
153 newTree->SetParent(thread, index, JSTaggedValue(-1));
154 int child = 1;
155 while (!entries.empty()) {
156 int parent = entries.front();
157 JSTaggedValue left = tree->GetLeftChild(parent);
158 if (!left.IsHole()) {
159 entries.push(left.GetInt());
160 newTree->SetLeftChild(thread, index, JSTaggedValue(child));
161 newTree->SetParent(thread, child++, JSTaggedValue(index));
162 }
163 JSTaggedValue right = tree->GetRightChild(parent);
164 if (!right.IsHole()) {
165 entries.push(right.GetInt());
166 newTree->SetRightChild(thread, index, JSTaggedValue(child));
167 newTree->SetParent(thread, child++, JSTaggedValue(index));
168 }
169 tree->CopyEntry(thread, parent, newTree, index);
170 entries.pop();
171 index++;
172 }
173 return newTree;
174 }
175
176 template<typename Derived>
Transplant(const JSThread * thread,int dst,int src)177 void TaggedTree<Derived>::Transplant(const JSThread *thread, int dst, int src)
178 {
179 int parent = GetParent(dst);
180 if (parent < 0) {
181 SetRootEntries(thread, src);
182 } else if (IsLeft(dst)) {
183 JSTaggedValue child = src < 0 ? JSTaggedValue::Hole() : JSTaggedValue(src);
184 SetLeftChild(thread, parent, child);
185 } else {
186 JSTaggedValue child = src < 0 ? JSTaggedValue::Hole() : JSTaggedValue(src);
187 SetRightChild(thread, parent, child);
188 }
189 SetParent(thread, src, JSTaggedValue(parent));
190 }
191
192 template<typename Derived>
Remove(const JSThread * thread,const JSHandle<Derived> & tree,int entry)193 void TaggedTree<Derived>::Remove(const JSThread *thread, const JSHandle<Derived> &tree, int entry)
194 {
195 int successor = entry;
196 if (!tree->GetLeftChild(entry).IsHole() && !tree->GetRightChild(entry).IsHole()) {
197 successor = tree->GetSuccessor(entry);
198 tree->CopyData(thread, entry, successor);
199 }
200 JSTaggedValue left = tree->GetLeftChild(successor);
201 JSTaggedValue right = tree->GetRightChild(successor);
202 int child = left.IsHole() ? (right.IsHole() ? -1 : right.GetInt()) : left.GetInt();
203 if (child < 0) {
204 if (tree->GetColor(successor) == TreeColor::BLACK) {
205 tree->DeleteRebalance(thread, successor);
206 }
207 }
208 tree->Transplant(thread, successor, child);
209
210 if (child >= 0) {
211 if (tree->GetColor(successor) == TreeColor::BLACK) {
212 tree->DeleteRebalance(thread, child);
213 }
214 }
215 tree->RemoveEntry(thread, successor);
216 ASSERT(tree->NumberOfElements() > 0);
217 tree->SetNumberOfElements(thread, tree->NumberOfElements() - 1);
218 tree->SetNumberOfDeletedElements(thread, tree->NumberOfDeletedElements() + 1);
219 }
220
221 template<typename Derived>
DeleteRebalance(const JSThread * thread,int index)222 void TaggedTree<Derived>::DeleteRebalance(const JSThread *thread, int index)
223 {
224 while (index != GetRootEntries() && GetColor(index) == TreeColor::BLACK) {
225 if (IsLeft(index)) {
226 int bro = GetLeftBrother(index);
227 if (GetColor(bro)) {
228 SetColor(thread, bro, TreeColor::BLACK);
229 SetColor(thread, GetParent(index), TreeColor::RED);
230 LeftRotate(thread, GetParent(index));
231 bro = GetLeftBrother(index);
232 }
233 if (GetColor(GetLeftChildIndex(bro)) == TreeColor::BLACK &&
234 GetColor(GetRightChildIndex(bro)) == TreeColor::BLACK) {
235 SetColor(thread, bro, TreeColor::RED);
236 index = GetParent(index);
237 } else {
238 if (GetColor(GetRightChildIndex(bro)) == TreeColor::BLACK) {
239 SetColor(thread, GetLeftChildIndex(bro), TreeColor::BLACK);
240 SetColor(thread, bro, TreeColor::RED);
241 RightRotate(thread, bro);
242 bro = GetLeftBrother(index);
243 }
244 SetColor(thread, bro, GetColor(GetParent(index)));
245 SetColor(thread, GetParent(index), TreeColor::BLACK);
246 SetColor(thread, GetRightChildIndex(bro), TreeColor::BLACK);
247 LeftRotate(thread, GetParent(index));
248 index = GetRootEntries();
249 }
250 } else {
251 int bro = GetRightBrother(index);
252 if (GetColor(bro)) {
253 SetColor(thread, bro, TreeColor::BLACK);
254 SetColor(thread, GetParent(index), TreeColor::RED);
255 RightRotate(thread, GetParent(index));
256 bro = GetRightBrother(index);
257 }
258 if (GetColor(GetRightChildIndex(bro)) == TreeColor::BLACK &&
259 GetColor(GetLeftChildIndex(bro)) == TreeColor::BLACK) {
260 SetColor(thread, bro, TreeColor::RED);
261 index = GetParent(index);
262 } else {
263 if (GetColor(GetLeftChildIndex(bro)) == TreeColor::BLACK) {
264 SetColor(thread, GetRightChildIndex(bro), TreeColor::BLACK);
265 SetColor(thread, bro, TreeColor::RED);
266 LeftRotate(thread, bro);
267 bro = GetRightBrother(index);
268 }
269 SetColor(thread, bro, GetColor(GetParent(index)));
270 SetColor(thread, GetParent(index), TreeColor::BLACK);
271 SetColor(thread, GetLeftChildIndex(bro), TreeColor::BLACK);
272 RightRotate(thread, GetParent(index));
273 index = GetRootEntries();
274 }
275 }
276 }
277 SetColor(thread, index, TreeColor::BLACK);
278 }
279
280 template<typename Derived>
GetPreDecessor(int entry) const281 int TaggedTree<Derived>::GetPreDecessor(int entry) const
282 {
283 int child = GetLeftChildIndex(entry);
284 if (child >= 0) {
285 return GetMaximum(child);
286 }
287 int parent = GetParent(entry);
288 while (parent >= 0 && (GetLeftChildIndex(parent) == entry)) {
289 entry = parent;
290 parent = GetParent(entry);
291 }
292 return parent;
293 }
294
295 template<typename Derived>
GetSuccessor(int entry) const296 int TaggedTree<Derived>::GetSuccessor(int entry) const
297 {
298 int child = GetRightChildIndex(entry);
299 if (child >= 0) {
300 return GetMinimum(child);
301 }
302 int parent = GetParent(entry);
303 while (parent >= 0 && (GetRightChildIndex(parent) == entry)) {
304 entry = parent;
305 parent = GetParent(entry);
306 }
307 return parent;
308 }
309
310 template<typename Derived>
FindEntry(JSThread * thread,const JSHandle<Derived> & tree,const JSHandle<JSTaggedValue> & key)311 int TaggedTree<Derived>::FindEntry(JSThread *thread, const JSHandle<Derived> &tree, const JSHandle<JSTaggedValue> &key)
312 {
313 int parentIndex = tree->GetRootEntries();
314 JSMutableHandle<JSTaggedValue> parentKey(thread, tree->GetKey(thread, parentIndex));
315 ComparisonResult res;
316 while (!parentKey->IsHole()) {
317 res = EntryCompare(thread, key, parentKey, tree);
318 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
319 if (res == ComparisonResult::EQUAL) {
320 return parentIndex;
321 } else if (res == ComparisonResult::LESS) {
322 JSTaggedValue child = tree->GetLeftChild(parentIndex);
323 if (child.IsHole()) {
324 break;
325 }
326 parentIndex = child.GetInt();
327 } else {
328 JSTaggedValue child = tree->GetRightChild(parentIndex);
329 if (child.IsHole()) {
330 break;
331 }
332 parentIndex = child.GetInt();
333 }
334 parentKey.Update(tree->GetKey(thread, parentIndex));
335 }
336 return -1;
337 }
338
339 template<typename Derived>
EntryCompare(JSThread * thread,const JSHandle<JSTaggedValue> valueX,const JSHandle<JSTaggedValue> valueY,JSHandle<Derived> tree)340 ComparisonResult TaggedTree<Derived>::EntryCompare(JSThread *thread, const JSHandle<JSTaggedValue> valueX,
341 const JSHandle<JSTaggedValue> valueY, JSHandle<Derived> tree)
342 {
343 JSTaggedValue fn = tree->GetCompare(thread);
344 if (fn.IsHole()) {
345 return OrdinayEntryCompare(thread, valueX, valueY);
346 }
347 if (valueX->IsUndefined()) {
348 return valueY->IsUndefined() ? ComparisonResult::EQUAL : ComparisonResult::GREAT;
349 }
350 if (valueY->IsUndefined()) {
351 return ComparisonResult::LESS;
352 }
353 if (valueX->IsNull()) {
354 return valueY->IsNull() ? ComparisonResult::EQUAL : ComparisonResult::GREAT;
355 }
356 if (valueY->IsNull()) {
357 return ComparisonResult::LESS;
358 }
359 JSHandle<JSTaggedValue> compareFn(thread, fn);
360 JSHandle<JSTaggedValue> thisArgHandle = thread->GlobalConstants()->GetHandledUndefined();
361 const uint32_t argsLength = 2;
362 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
363 EcmaRuntimeCallInfo *info =
364 EcmaInterpreter::NewRuntimeCallInfo(thread, compareFn, thisArgHandle, undefined, argsLength);
365 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
366 info->SetCallArg(valueX.GetTaggedValue(), valueY.GetTaggedValue());
367 JSTaggedValue callResult = JSFunction::Call(info);
368 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
369 int compareResult = -1;
370 if (callResult.IsBoolean()) {
371 // if callResult is true, compareResult = -1.
372 if (callResult.IsFalse()) {
373 info = EcmaInterpreter::NewRuntimeCallInfo(thread, compareFn, thisArgHandle, undefined, argsLength);
374 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
375 info->SetCallArg(valueY.GetTaggedValue(), valueX.GetTaggedValue());
376 callResult = JSFunction::Call(info);
377 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
378 compareResult = callResult.IsTrue() ? 1 : 0;
379 }
380 } else if (callResult.IsInt()) {
381 compareResult = callResult.GetInt();
382 } else {
383 JSTaggedNumber v = JSTaggedValue::ToNumber(thread, JSHandle<JSTaggedValue>(thread, callResult));
384 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
385 double value = v.GetNumber();
386 if (std::isnan(value)) {
387 THROW_TYPE_ERROR_AND_RETURN(thread, "CompareFn has illegal return value", ComparisonResult::UNDEFINED);
388 }
389 compareResult = static_cast<int>(value);
390 }
391 return compareResult > 0 ? ComparisonResult::GREAT :
392 (compareResult < 0 ? ComparisonResult::LESS : ComparisonResult::EQUAL);
393 }
394
395 template<typename Derived>
GetSortArray(const JSThread * thread,const JSHandle<Derived> & tree)396 JSHandle<TaggedArray> TaggedTree<Derived>::GetSortArray(const JSThread *thread, const JSHandle<Derived> &tree)
397 {
398 JSHandle<TaggedArray> sortArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(tree->NumberOfElements());
399 CStack<int> entries;
400 int index = tree->GetRootEntries();
401 int aid = 0;
402 while (index >= 0 || !entries.empty()) {
403 while (index >= 0) {
404 entries.emplace(index);
405 index = tree->GetLeftChildIndex(index);
406 }
407 if (!entries.empty()) {
408 sortArray->Set(thread, aid++, JSTaggedValue(entries.top()));
409 index = tree->GetRightChildIndex(entries.top());
410 entries.pop();
411 }
412 }
413 return sortArray;
414 }
415
416 template<typename Derived>
Insert(JSThread * thread,JSHandle<Derived> & tree,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)417 JSHandle<Derived> TaggedTree<Derived>::Insert(JSThread *thread, JSHandle<Derived> &tree,
418 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
419 {
420 ASSERT(IsKey(key.GetTaggedValue()));
421 JSMutableHandle<JSTaggedValue> parentKey(thread, tree->GetRootKey(thread));
422 if (parentKey->IsHole()) {
423 tree->SetRoot(thread, 0, key.GetTaggedValue(), value.GetTaggedValue());
424 tree->SetNumberOfElements(thread, tree->NumberOfElements() + 1);
425 return tree;
426 }
427
428 JSHandle<Derived> newTree = GrowCapacity(thread, tree);
429 int parentIndex = newTree->GetRootEntries();
430 ComparisonResult res;
431 while (!parentKey->IsHole()) {
432 res = EntryCompare(thread, key, parentKey, tree);
433 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<Derived>(thread, JSTaggedValue::Exception()));
434 if (res == ComparisonResult::EQUAL) {
435 newTree->SetValue(thread, parentIndex, value.GetTaggedValue());
436 return newTree;
437 } else if (res == ComparisonResult::LESS) {
438 JSTaggedValue child = newTree->GetLeftChild(parentIndex);
439 if (child.IsHole()) {
440 break;
441 }
442 parentIndex = child.GetInt();
443 } else {
444 JSTaggedValue child = newTree->GetRightChild(parentIndex);
445 if (child.IsHole()) {
446 break;
447 }
448 parentIndex = child.GetInt();
449 }
450 parentKey.Update(newTree->GetKey(thread, parentIndex));
451 }
452
453 uint32_t entry = newTree->NumberOfElements() + newTree->NumberOfDeletedElements();
454 if (res != ComparisonResult::LESS) {
455 newTree->InsertRightEntry(thread, parentIndex, entry, key.GetTaggedValue(), value.GetTaggedValue());
456 } else {
457 newTree->InsertLeftEntry(thread, parentIndex, entry, key.GetTaggedValue(), value.GetTaggedValue());
458 }
459 newTree->SetNumberOfElements(thread, newTree->NumberOfElements() + 1);
460 newTree->InsertRebalance(thread, entry);
461 return newTree;
462 }
463
464 template<typename Derived>
GrowCapacity(const JSThread * thread,JSHandle<Derived> & tree)465 JSHandle<Derived> TaggedTree<Derived>::GrowCapacity(const JSThread *thread, JSHandle<Derived> &tree)
466 {
467 uint32_t nof = tree->NumberOfElements() + tree->NumberOfDeletedElements();
468 int oldCapacity = tree->Capacity();
469 if (static_cast<int>(nof + 1) <= oldCapacity) {
470 return tree;
471 }
472
473 int newCapacity = ComputeCapacity(oldCapacity);
474 int length = ELEMENTS_START_INDEX + newCapacity * (Derived::ENTRY_SIZE);
475 JSHandle<Derived> newTree = AdjustTaggedTree(thread, tree, length);
476 // 20 : version isolation at api20
477 if (thread->GetEcmaVM()->GetVMAPIVersion() >= 20) {
478 JSTaggedValue fn = tree->GetCompare(thread);
479 if (!fn.IsUndefined() && !fn.IsNull()) {
480 newTree->SetCompare(thread, fn);
481 }
482 }
483 newTree->SetCapacity(thread, newCapacity);
484 return newTree;
485 }
486
487 template<typename Derived>
GetLowerKey(JSThread * thread,const JSHandle<Derived> & tree,const JSHandle<JSTaggedValue> & key)488 JSTaggedValue TaggedTree<Derived>::GetLowerKey(JSThread *thread, const JSHandle<Derived> &tree,
489 const JSHandle<JSTaggedValue> &key)
490 {
491 int parentIndex = tree->GetRootEntries();
492 JSMutableHandle<JSTaggedValue> parentKey(thread, tree->GetKey(thread, parentIndex));
493 int resultIndex = -1;
494 ComparisonResult res;
495 while (parentIndex >= 0) {
496 res = EntryCompare(thread, key, parentKey, tree);
497 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
498 if (res == ComparisonResult::GREAT) {
499 resultIndex = parentIndex;
500 parentIndex = tree->GetRightChildIndex(parentIndex);
501 } else {
502 parentIndex = tree->GetLeftChildIndex(parentIndex);
503 }
504 parentKey.Update(tree->GetKey(thread, parentIndex));
505 }
506 JSTaggedValue lowerKey = tree->GetKey(thread, resultIndex);
507 return tree->Transform(lowerKey);
508 }
509
510 template<typename Derived>
GetHigherKey(JSThread * thread,const JSHandle<Derived> & tree,const JSHandle<JSTaggedValue> & key)511 JSTaggedValue TaggedTree<Derived>::GetHigherKey(JSThread *thread, const JSHandle<Derived> &tree,
512 const JSHandle<JSTaggedValue> &key)
513 {
514 int parentIndex = tree->GetRootEntries();
515 JSMutableHandle<JSTaggedValue> parentKey(thread, tree->GetKey(thread, parentIndex));
516 int resultIndex = -1;
517 ComparisonResult res;
518 while (parentIndex >= 0) {
519 res = EntryCompare(thread, key, parentKey, tree);
520 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
521 if (res == ComparisonResult::LESS) {
522 resultIndex = parentIndex;
523 parentIndex = tree->GetLeftChildIndex(parentIndex);
524 } else {
525 parentIndex = tree->GetRightChildIndex(parentIndex);
526 }
527 parentKey.Update(tree->GetKey(thread, parentIndex));
528 }
529 JSTaggedValue lowerKey = tree->GetKey(thread, resultIndex);
530 return tree->Transform(lowerKey);
531 }
532
533 template<typename Derived>
Shrink(const JSThread * thread,const JSHandle<Derived> & tree)534 JSHandle<Derived> TaggedTree<Derived>::Shrink(const JSThread *thread, const JSHandle<Derived> &tree)
535 {
536 int oldCapacity = static_cast<int>(tree->Capacity());
537 if (static_cast<int>(tree->NumberOfElements()) >= (oldCapacity + 1) / 4) { // 4: quarter
538 return tree;
539 }
540 uint32_t newCapacity = static_cast<uint32_t>(oldCapacity - 1) >> 1;
541 if (newCapacity < static_cast<uint32_t>(Derived::MIN_SHRINK_CAPACITY)) {
542 return tree;
543 }
544
545 int length = ELEMENTS_START_INDEX + static_cast<int>(newCapacity) * (Derived::ENTRY_SIZE);
546 JSHandle<Derived> newTree = AdjustTaggedTree(thread, tree, length);
547 JSTaggedValue fn = tree->GetCompare(thread);
548 JSHandle<JSTaggedValue> compareFn = JSHandle<JSTaggedValue>(thread, fn);
549 if (!compareFn->IsUndefined() && !compareFn->IsNull()) {
550 newTree->SetCompare(thread, compareFn.GetTaggedValue());
551 }
552 newTree->SetCapacity(thread, newCapacity);
553 return newTree;
554 }
555
556 // TaggedTreeMap
Create(const JSThread * thread,int numberOfElements)557 JSTaggedValue TaggedTreeMap::Create(const JSThread *thread, int numberOfElements)
558 {
559 return RBTree::Create(thread, numberOfElements).GetTaggedValue();
560 }
561
GetArrayFromMap(const JSThread * thread,const JSHandle<TaggedTreeMap> & map)562 JSHandle<TaggedArray> TaggedTreeMap::GetArrayFromMap(const JSThread *thread, const JSHandle<TaggedTreeMap> &map)
563 {
564 return RBTree::GetSortArray(thread, map);
565 }
566
Set(JSThread * thread,JSHandle<TaggedTreeMap> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)567 JSTaggedValue TaggedTreeMap::Set(JSThread *thread, JSHandle<TaggedTreeMap> &obj,
568 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
569 {
570 return RBTree::Insert(thread, obj, key, value).GetTaggedValue();
571 }
572
Delete(JSThread * thread,const JSHandle<TaggedTreeMap> & map,int entry)573 JSTaggedValue TaggedTreeMap::Delete(JSThread *thread, const JSHandle<TaggedTreeMap> &map, int entry)
574 {
575 RBTree::Remove(thread, map, entry);
576 return RBTree::Shrink(thread, map).GetTaggedValue();
577 }
578
HasValue(const JSThread * thread,JSTaggedValue value) const579 bool TaggedTreeMap::HasValue([[maybe_unused]] const JSThread *thread, JSTaggedValue value) const
580 {
581 int root = GetRootEntries();
582 if (root < 0) {
583 return false;
584 }
585
586 CQueue<int> entries;
587 entries.push(root);
588 while (!entries.empty()) {
589 int parent = entries.front();
590 if (JSTaggedValue::SameValue(thread, GetValue(thread, parent), value)) {
591 return true;
592 }
593 int left = GetLeftChildIndex(parent);
594 if (left >= 0) {
595 entries.push(left);
596 }
597 int right = GetRightChildIndex(parent);
598 if (right >= 0) {
599 entries.push(right);
600 }
601 entries.pop();
602 }
603 return false;
604 }
605
SetAll(JSThread * thread,JSHandle<TaggedTreeMap> & dst,const JSHandle<TaggedTreeMap> & src)606 JSTaggedValue TaggedTreeMap::SetAll(JSThread *thread, JSHandle<TaggedTreeMap> &dst, const JSHandle<TaggedTreeMap> &src)
607 {
608 CQueue<int> entries;
609 entries.push(src->GetRootEntries());
610 JSHandle<TaggedTreeMap> map = dst;
611 while (!entries.empty()) {
612 int parent = entries.front();
613 map = Insert(thread, map, JSHandle<JSTaggedValue>(thread, src->GetKey(thread, parent)),
614 JSHandle<JSTaggedValue>(thread, src->GetValue(thread, parent)));
615 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
616 int left = src->GetLeftChildIndex(parent);
617 if (left >= 0) {
618 entries.push(left);
619 }
620 int right = src->GetRightChildIndex(parent);
621 if (right >= 0) {
622 entries.push(right);
623 }
624 entries.pop();
625 }
626 return map.GetTaggedValue();
627 }
628
GetLowerKey(JSThread * thread,const JSHandle<TaggedTreeMap> & map,const JSHandle<JSTaggedValue> & key)629 JSTaggedValue TaggedTreeMap::GetLowerKey(JSThread *thread, const JSHandle<TaggedTreeMap> &map,
630 const JSHandle<JSTaggedValue> &key)
631 {
632 return RBTree::GetLowerKey(thread, map, key);
633 }
634
GetHigherKey(JSThread * thread,const JSHandle<TaggedTreeMap> & map,const JSHandle<JSTaggedValue> & key)635 JSTaggedValue TaggedTreeMap::GetHigherKey(JSThread *thread, const JSHandle<TaggedTreeMap> &map,
636 const JSHandle<JSTaggedValue> &key)
637 {
638 return RBTree::GetHigherKey(thread, map, key);
639 }
640
FindEntry(JSThread * thread,const JSHandle<TaggedTreeMap> & map,const JSHandle<JSTaggedValue> & key)641 int TaggedTreeMap::FindEntry(JSThread *thread, const JSHandle<TaggedTreeMap> &map, const JSHandle<JSTaggedValue> &key)
642 {
643 return RBTree::FindEntry(thread, map, key);
644 }
645
646 // TaggedTreeSet
Create(const JSThread * thread,int numberOfElements)647 JSTaggedValue TaggedTreeSet::Create(const JSThread *thread, int numberOfElements)
648 {
649 return RBTree::Create(thread, numberOfElements).GetTaggedValue();
650 }
651
GetArrayFromSet(const JSThread * thread,const JSHandle<TaggedTreeSet> & set)652 JSHandle<TaggedArray> TaggedTreeSet::GetArrayFromSet(const JSThread *thread, const JSHandle<TaggedTreeSet> &set)
653 {
654 return RBTree::GetSortArray(thread, set);
655 }
656
Add(JSThread * thread,JSHandle<TaggedTreeSet> & obj,const JSHandle<JSTaggedValue> & value)657 JSTaggedValue TaggedTreeSet::Add(JSThread *thread, JSHandle<TaggedTreeSet> &obj, const JSHandle<JSTaggedValue> &value)
658 {
659 return RBTree::Insert(thread, obj, value, value).GetTaggedValue();
660 }
661
Delete(JSThread * thread,const JSHandle<TaggedTreeSet> & set,int entry)662 JSTaggedValue TaggedTreeSet::Delete(JSThread *thread, const JSHandle<TaggedTreeSet> &set, int entry)
663 {
664 RBTree::Remove(thread, set, entry);
665 return RBTree::Shrink(thread, set).GetTaggedValue();
666 }
667
GetLowerKey(JSThread * thread,const JSHandle<TaggedTreeSet> & set,const JSHandle<JSTaggedValue> & key)668 JSTaggedValue TaggedTreeSet::GetLowerKey(JSThread *thread, const JSHandle<TaggedTreeSet> &set,
669 const JSHandle<JSTaggedValue> &key)
670 {
671 return RBTree::GetLowerKey(thread, set, key);
672 }
673
GetHigherKey(JSThread * thread,const JSHandle<TaggedTreeSet> & set,const JSHandle<JSTaggedValue> & key)674 JSTaggedValue TaggedTreeSet::GetHigherKey(JSThread *thread, const JSHandle<TaggedTreeSet> &set,
675 const JSHandle<JSTaggedValue> &key)
676 {
677 return RBTree::GetHigherKey(thread, set, key);
678 }
679
FindEntry(JSThread * thread,const JSHandle<TaggedTreeSet> & set,const JSHandle<JSTaggedValue> & key)680 int TaggedTreeSet::FindEntry(JSThread *thread, const JSHandle<TaggedTreeSet> &set, const JSHandle<JSTaggedValue> &key)
681 {
682 return RBTree::FindEntry(thread, set, key);
683 }
684 } // namespace panda::ecmascript
685