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/containers/containers_treemap.h"
17
18#include "ecmascript/containers/containers_errors.h"
19#include "ecmascript/interpreter/interpreter.h"
20#include "ecmascript/js_api/js_api_tree_map.h"
21#include "ecmascript/js_api/js_api_tree_map_iterator.h"
22#include "ecmascript/js_function.h"
23#include "ecmascript/tagged_tree.h"
24
25namespace panda::ecmascript::containers {
26JSTaggedValue ContainersTreeMap::TreeMapConstructor(EcmaRuntimeCallInfo *argv)
27{
28    ASSERT(argv);
29    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Constructor);
30    JSThread *thread = argv->GetThread();
31    [[maybe_unused]] EcmaHandleScope handleScope(thread);
32    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
33
34    JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
35    if (newTarget->IsUndefined()) {
36        JSTaggedValue error =
37            ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR,
38                                          "The TreeMap's constructor cannot be directly invoked");
39        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
40    }
41
42    // new TreeMap
43    JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
44    JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
45    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
46
47    // Set map’s internal slot with a new empty List.
48    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(obj);
49    JSTaggedValue internal = TaggedTreeMap::Create(thread);
50    map->SetTreeMap(thread, internal);
51
52    // If comparefn was supplied, let compare be comparefn; else let compare be hole.
53    JSHandle<JSTaggedValue> compareFn(GetCallArg(argv, 0));
54    if (compareFn->IsUndefined() || compareFn->IsNull()) {
55        return map.GetTaggedValue();
56    }
57    if (!compareFn->IsCallable()) {
58        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, compareFn.GetTaggedValue());
59        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
60        CString errorMsg =
61            "The type of \"comparefn\" must be callable. Received value is: " + ConvertToString(*result);
62        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
63        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
64    }
65
66    TaggedTreeMap::Cast(internal.GetTaggedObject())->SetCompare(thread, compareFn.GetTaggedValue());
67    return map.GetTaggedValue();
68}
69
70JSTaggedValue ContainersTreeMap::Set(EcmaRuntimeCallInfo *argv)
71{
72    ASSERT(argv);
73    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Set);
74    JSThread *thread = argv->GetThread();
75    [[maybe_unused]] EcmaHandleScope handleScope(thread);
76    JSHandle<JSTaggedValue> self = GetThis(argv);
77    // get and check this map
78    if (!self->IsJSAPITreeMap()) {
79        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
80            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
81        } else {
82            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
83                                                                "The set method cannot be bound");
84            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
85        }
86    }
87
88    JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
89    JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
90
91    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
92    JSAPITreeMap::Set(thread, map, key, value);
93    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
94    return map.GetTaggedValue();
95}
96
97JSTaggedValue ContainersTreeMap::Get(EcmaRuntimeCallInfo *argv)
98{
99    ASSERT(argv);
100    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Get);
101    JSThread *thread = argv->GetThread();
102    [[maybe_unused]] EcmaHandleScope handleScope(thread);
103    // get and check this map
104    JSHandle<JSTaggedValue> self(GetThis(argv));
105    if (!self->IsJSAPITreeMap()) {
106        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
107            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
108        } else {
109            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
110                                                                "The get method cannot be bound");
111            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
112        }
113    }
114
115    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
116    JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
117    return JSAPITreeMap::Get(thread, map, key);
118}
119
120JSTaggedValue ContainersTreeMap::Remove(EcmaRuntimeCallInfo *argv)
121{
122    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Remove);
123    JSThread *thread = argv->GetThread();
124    [[maybe_unused]] EcmaHandleScope handleScope(thread);
125    JSHandle<JSTaggedValue> self = GetThis(argv);
126    // get and check this map
127    if (!self->IsJSAPITreeMap()) {
128        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
129            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
130        } else {
131            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
132                                                                "The remove method cannot be bound");
133            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
134        }
135    }
136
137    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
138    JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
139    return JSAPITreeMap::Delete(thread, map, key);
140}
141
142JSTaggedValue ContainersTreeMap::HasKey(EcmaRuntimeCallInfo *argv)
143{
144    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, HasKey);
145    JSThread *thread = argv->GetThread();
146    [[maybe_unused]] EcmaHandleScope handleScope(thread);
147    // get and check this map
148    JSHandle<JSTaggedValue> self(GetThis(argv));
149    if (!self->IsJSAPITreeMap()) {
150        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
151            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
152        } else {
153            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
154                                                                "The hasKey method cannot be bound");
155            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
156        }
157    }
158
159    JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
160    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
161
162    bool flag = JSAPITreeMap::HasKey(thread, JSHandle<JSAPITreeMap>::Cast(map), key);
163    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
164    return GetTaggedBoolean(flag);
165}
166
167JSTaggedValue ContainersTreeMap::HasValue(EcmaRuntimeCallInfo *argv)
168{
169    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, HasValue);
170    JSThread *thread = argv->GetThread();
171    [[maybe_unused]] EcmaHandleScope handleScope(thread);
172    // get and check this map
173    JSHandle<JSTaggedValue> self(GetThis(argv));
174    if (!self->IsJSAPITreeMap()) {
175        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
176            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
177        } else {
178            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
179                                                                "The hasValue method cannot be bound");
180            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
181        }
182    }
183
184    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
185    bool flag = map->HasValue(thread, GetCallArg(argv, 0));
186    return GetTaggedBoolean(flag);
187}
188
189JSTaggedValue ContainersTreeMap::GetFirstKey(EcmaRuntimeCallInfo *argv)
190{
191    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, GetFirstKey);
192    JSThread *thread = argv->GetThread();
193    [[maybe_unused]] EcmaHandleScope handleScope(thread);
194    // get and check this map
195    JSHandle<JSTaggedValue> self(GetThis(argv));
196    if (!self->IsJSAPITreeMap()) {
197        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
198            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
199        } else {
200            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
201                                                                "The getFirstKey method cannot be bound");
202            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
203        }
204    }
205
206    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
207    return TaggedTreeMap::Cast(map->GetTreeMap().GetTaggedObject())->GetFirstKey();
208}
209
210JSTaggedValue ContainersTreeMap::GetLastKey(EcmaRuntimeCallInfo *argv)
211{
212    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, GetLastKey);
213    JSThread *thread = argv->GetThread();
214    [[maybe_unused]] EcmaHandleScope handleScope(thread);
215    // get and check this map
216    JSHandle<JSTaggedValue> self(GetThis(argv));
217    if (!self->IsJSAPITreeMap()) {
218        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
219            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
220        } else {
221            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
222                                                                "The getLastKey method cannot be bound");
223            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
224        }
225    }
226
227    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
228    return TaggedTreeMap::Cast(map->GetTreeMap().GetTaggedObject())->GetLastKey();
229}
230
231JSTaggedValue ContainersTreeMap::SetAll(EcmaRuntimeCallInfo *argv)
232{
233    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, SetAll);
234    JSThread *thread = argv->GetThread();
235    [[maybe_unused]] EcmaHandleScope handleScope(thread);
236    // get and check this map
237    JSHandle<JSTaggedValue> self(GetThis(argv));
238    if (!self->IsJSAPITreeMap()) {
239        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
240            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
241        } else {
242            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
243                                                                "The setAll method cannot be bound");
244            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
245        }
246    }
247
248    JSHandle<JSTaggedValue> obj = GetCallArg(argv, 0);
249    if (!obj->IsJSAPITreeMap()) {
250        if (obj->IsJSProxy() && JSHandle<JSProxy>::Cast(obj)->GetTarget().IsJSAPITreeMap()) {
251            obj = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(obj)->GetTarget());
252        } else {
253            JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, obj.GetTaggedValue());
254            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
255            CString errorMsg =
256                "The type of \"map\" must be TreeMap. Received value is: " + ConvertToString(*result);
257            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
258            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
259        }
260    }
261
262    JSHandle<JSAPITreeMap> dst = JSHandle<JSAPITreeMap>::Cast(self);
263    JSHandle<TaggedTreeMap> dmap(thread, dst->GetTreeMap());
264    JSHandle<TaggedTreeMap> smap(thread, JSHandle<JSAPITreeMap>::Cast(obj)->GetTreeMap());
265
266    if (JSHandle<JSAPITreeMap>::Cast(obj)->GetSize() > 0) {
267        JSTaggedValue tmap = TaggedTreeMap::SetAll(thread, dmap, smap);
268        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
269        dst->SetTreeMap(thread, tmap);
270    }
271    return JSTaggedValue::Undefined();
272}
273
274JSTaggedValue ContainersTreeMap::Clear(EcmaRuntimeCallInfo *argv)
275{
276    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Clear);
277    JSThread *thread = argv->GetThread();
278    [[maybe_unused]] EcmaHandleScope handleScope(thread);
279    // get and check this map
280    JSHandle<JSTaggedValue> self(GetThis(argv));
281    if (!self->IsJSAPITreeMap()) {
282        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
283            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
284        } else {
285            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
286                                                                "The clear method cannot be bound");
287            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
288        }
289    }
290
291    JSAPITreeMap::Clear(thread, JSHandle<JSAPITreeMap>::Cast(self));
292    return JSTaggedValue::Undefined();
293}
294
295JSTaggedValue ContainersTreeMap::GetLowerKey(EcmaRuntimeCallInfo *argv)
296{
297    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, GetLowerKey);
298    JSThread *thread = argv->GetThread();
299    [[maybe_unused]] EcmaHandleScope handleScope(thread);
300    // get and check this map
301    JSHandle<JSTaggedValue> self(GetThis(argv));
302    if (!self->IsJSAPITreeMap()) {
303        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
304            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
305        } else {
306            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
307                                                                "The getLowerKey method cannot be bound");
308            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
309        }
310    }
311
312    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
313    JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
314
315    JSHandle<TaggedTreeMap> tmap(thread, map->GetTreeMap());
316    return TaggedTreeMap::GetLowerKey(thread, tmap, key);
317}
318
319JSTaggedValue ContainersTreeMap::GetHigherKey(EcmaRuntimeCallInfo *argv)
320{
321    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, GetHigherKey);
322    JSThread *thread = argv->GetThread();
323    [[maybe_unused]] EcmaHandleScope handleScope(thread);
324    // get and check this map
325    JSHandle<JSTaggedValue> self(GetThis(argv));
326    if (!self->IsJSAPITreeMap()) {
327        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
328            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
329        } else {
330            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
331                                                                "The getHigherKey method cannot be bound");
332            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
333        }
334    }
335
336    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
337    JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
338
339    JSHandle<TaggedTreeMap> tmap(thread, map->GetTreeMap());
340    return TaggedTreeMap::GetHigherKey(thread, tmap, key);
341}
342
343JSTaggedValue ContainersTreeMap::Replace(EcmaRuntimeCallInfo *argv)
344{
345    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Replace);
346    JSThread *thread = argv->GetThread();
347    [[maybe_unused]] EcmaHandleScope handleScope(thread);
348    // get and check this map
349    JSHandle<JSTaggedValue> self(GetThis(argv));
350    if (!self->IsJSAPITreeMap()) {
351        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
352            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
353        } else {
354            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
355                                                                "The replace method cannot be bound");
356            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
357        }
358    }
359
360    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
361    JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
362    JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
363
364    bool success = JSAPITreeMap::Replace(thread, map, key, value);
365    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
366    return GetTaggedBoolean(success);
367}
368
369JSTaggedValue ContainersTreeMap::Keys(EcmaRuntimeCallInfo *argv)
370{
371    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Keys);
372    JSThread *thread = argv->GetThread();
373    [[maybe_unused]] EcmaHandleScope handleScope(thread);
374    JSHandle<JSTaggedValue> self = GetThis(argv);
375    JSHandle<JSTaggedValue> iter = JSAPITreeMapIterator::CreateTreeMapIterator(thread, self, IterationKind::KEY);
376    return iter.GetTaggedValue();
377}
378
379JSTaggedValue ContainersTreeMap::Values(EcmaRuntimeCallInfo *argv)
380{
381    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Values);
382    JSThread *thread = argv->GetThread();
383    [[maybe_unused]] EcmaHandleScope handleScope(thread);
384    JSHandle<JSTaggedValue> self = GetThis(argv);
385    JSHandle<JSTaggedValue> iter = JSAPITreeMapIterator::CreateTreeMapIterator(thread, self, IterationKind::VALUE);
386    return iter.GetTaggedValue();
387}
388
389JSTaggedValue ContainersTreeMap::Entries(EcmaRuntimeCallInfo *argv)
390{
391    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Entries);
392    JSThread *thread = argv->GetThread();
393    [[maybe_unused]] EcmaHandleScope handleScope(thread);
394    JSHandle<JSTaggedValue> self = GetThis(argv);
395    JSHandle<JSTaggedValue> iter =
396        JSAPITreeMapIterator::CreateTreeMapIterator(thread, self, IterationKind::KEY_AND_VALUE);
397    return iter.GetTaggedValue();
398}
399
400JSTaggedValue ContainersTreeMap::ForEach(EcmaRuntimeCallInfo *argv)
401{
402    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, ForEach);
403    JSThread *thread = argv->GetThread();
404    [[maybe_unused]] EcmaHandleScope handleScope(thread);
405    // get and check TreeMap object
406    JSHandle<JSTaggedValue> self = GetThis(argv);
407    if (!self->IsJSAPITreeMap()) {
408        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
409            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
410        } else {
411            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
412                                                                "The forEach method cannot be bound");
413            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
414        }
415    }
416    // get and check callback function
417    JSHandle<JSTaggedValue> func(GetCallArg(argv, 0));
418    if (!func->IsCallable()) {
419        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, func.GetTaggedValue());
420        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
421        CString errorMsg =
422            "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result);
423        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
424        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
425    }
426    // If thisArg was supplied, let T be thisArg; else let T be undefined.
427    JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 1);
428    JSHandle<JSAPITreeMap> tmap = JSHandle<JSAPITreeMap>::Cast(self);
429    JSMutableHandle<TaggedTreeMap> iteratedMap(thread, tmap->GetTreeMap());
430    uint32_t elements = iteratedMap->NumberOfElements();
431    JSHandle<TaggedArray> entries = TaggedTreeMap::GetArrayFromMap(thread, iteratedMap);
432    uint32_t index = 0;
433    size_t length = entries->GetLength();
434    const uint32_t argsLength = 3;
435    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
436    JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
437    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
438    while (index < elements) {
439        int entriesIndex = entries->Get(index).GetInt();
440        key.Update(iteratedMap->GetKey(entriesIndex));
441        value.Update(iteratedMap->GetValue(entriesIndex));
442        // Let funcResult be Call(callbackfn, T, «e, e, S»).
443        EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength);
444        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
445        info->SetCallArg(value.GetTaggedValue(), key.GetTaggedValue(), self.GetTaggedValue());
446        JSTaggedValue ret = JSFunction::Call(info);
447        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret);
448        // check entries should be update, size will be update in tmap set or remove.
449        if (tmap->GetSize() != static_cast<int>(length)) {
450            iteratedMap.Update(tmap->GetTreeMap());
451            entries = TaggedTreeMap::GetArrayFromMap(thread, iteratedMap);
452            elements = iteratedMap->NumberOfElements();
453            length = entries->GetLength();
454        }
455        index++;
456    }
457    return JSTaggedValue::Undefined();
458}
459
460JSTaggedValue ContainersTreeMap::GetLength(EcmaRuntimeCallInfo *argv)
461{
462    ASSERT(argv);
463    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, GetLength);
464    JSThread *thread = argv->GetThread();
465    [[maybe_unused]] EcmaHandleScope handleScope(thread);
466    // get and check this map
467    JSHandle<JSTaggedValue> self(GetThis(argv));
468    if (!self->IsJSAPITreeMap()) {
469        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
470            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
471        } else {
472            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
473                                                                "The getLength method cannot be bound");
474            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
475        }
476    }
477    int count = JSHandle<JSAPITreeMap>::Cast(self)->GetSize();
478    return JSTaggedValue(count);
479}
480
481JSTaggedValue ContainersTreeMap::IsEmpty(EcmaRuntimeCallInfo *argv)
482{
483    BUILTINS_API_TRACE(argv->GetThread(), TreeMap, IsEmpty);
484    JSThread *thread = argv->GetThread();
485    [[maybe_unused]] EcmaHandleScope handleScope(thread);
486    // get and check this map
487    JSHandle<JSTaggedValue> self = GetThis(argv);
488    if (!self->IsJSAPITreeMap()) {
489        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeMap()) {
490            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
491        } else {
492            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
493                                                                "The isEmpty method cannot be bound");
494            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
495        }
496    }
497    JSHandle<JSAPITreeMap> map = JSHandle<JSAPITreeMap>::Cast(self);
498    return GetTaggedBoolean(map->GetSize() == 0);
499}
500}  // namespace panda::ecmascript::containers
501