• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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