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