1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/containers/containers_arraylist.h"
17
18#include "ecmascript/containers/containers_errors.h"
19#include "ecmascript/base/array_helper.h"
20#include "ecmascript/js_api/js_api_arraylist.h"
21#include "ecmascript/js_array.h"
22#include "ecmascript/tagged_array-inl.h"
23
24namespace panda::ecmascript::containers {
25JSTaggedValue ContainersArrayList::ArrayListConstructor(EcmaRuntimeCallInfo *argv)
26{
27    ASSERT(argv);
28    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Constructor);
29    JSThread *thread = argv->GetThread();
30    [[maybe_unused]] EcmaHandleScope handleScope(thread);
31    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
32    JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
33    if (newTarget->IsUndefined()) {
34        JSTaggedValue error =
35            ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR,
36                                          "The ArrayList's constructor cannot be directly invoked.");
37        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
38    }
39    JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
40    JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
41    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
42    JSHandle<TaggedArray> newTaggedArray = factory->NewTaggedArray(JSAPIArrayList::DEFAULT_CAPACITY_LENGTH);
43    obj->SetElements(thread, newTaggedArray);
44    return obj.GetTaggedValue();
45}
46
47JSTaggedValue ContainersArrayList::Add(EcmaRuntimeCallInfo *argv)
48{
49    ASSERT(argv);
50    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Add);
51    JSThread *thread = argv->GetThread();
52    [[maybe_unused]] EcmaHandleScope handleScope(thread);
53    JSHandle<JSTaggedValue> self = GetThis(argv);
54
55    if (!self->IsJSAPIArrayList()) {
56        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
57            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
58        } else {
59            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
60                                                                "The add method cannot be bound");
61            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
62        }
63    }
64
65    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
66    return GetTaggedBoolean(JSAPIArrayList::Add(thread, JSHandle<JSAPIArrayList>::Cast(self), value));
67}
68
69JSTaggedValue ContainersArrayList::Insert(EcmaRuntimeCallInfo *argv)
70{
71    ASSERT(argv);
72    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Insert);
73    JSThread *thread = argv->GetThread();
74    [[maybe_unused]] EcmaHandleScope handleScope(thread);
75    JSHandle<JSTaggedValue> self = GetThis(argv);
76
77    if (!self->IsJSAPIArrayList()) {
78        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
79            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
80        } else {
81            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
82                                                                "The insert method cannot be bound");
83            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
84        }
85    }
86
87    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
88    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
89    if (index->IsDouble()) {
90        // Math.floor(1) will produce TaggedDouble, we need to cast into TaggedInt
91        // For integer which is greater than INT32_MAX, it will remain TaggedDouble
92        index = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(index->GetDouble()));
93    }
94    if (!index->IsInt()) {
95        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, index);
96        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
97        CString errorMsg =
98            "The type of \"index\" must be small integer. Received value is: " + ConvertToString(*result);
99        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
100        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
101    }
102    JSAPIArrayList::Insert(thread, JSHandle<JSAPIArrayList>::Cast(self), value, JSTaggedValue::ToUint32(thread, index));
103    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
104
105    return JSTaggedValue::Undefined();
106}
107
108JSTaggedValue ContainersArrayList::Clear(EcmaRuntimeCallInfo *argv)
109{
110    ASSERT(argv);
111    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Clear);
112    JSThread *thread = argv->GetThread();
113    [[maybe_unused]] EcmaHandleScope handleScope(thread);
114    JSHandle<JSTaggedValue> self = GetThis(argv);
115
116    if (!self->IsJSAPIArrayList()) {
117        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
118            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
119        } else {
120            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
121                                                                "The clear method cannot be bound");
122            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
123        }
124    }
125
126    JSAPIArrayList::Clear(thread, JSHandle<JSAPIArrayList>::Cast(self));
127
128    return JSTaggedValue::True();
129}
130
131JSTaggedValue ContainersArrayList::Clone(EcmaRuntimeCallInfo *argv)
132{
133    ASSERT(argv);
134    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Clone);
135    JSThread *thread = argv->GetThread();
136    [[maybe_unused]] EcmaHandleScope handleScope(thread);
137    JSHandle<JSTaggedValue> self = GetThis(argv);
138
139    if (!self->IsJSAPIArrayList()) {
140        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
141            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
142        } else {
143            JSTaggedValue error = ContainerError::BusinessError(thread, BIND_ERROR,
144                                                                "The clone method cannot be bound");
145            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
146        }
147    }
148
149    JSHandle<JSAPIArrayList> newArrayList = JSAPIArrayList::Clone(thread, JSHandle<JSAPIArrayList>::Cast(self));
150
151    return newArrayList.GetTaggedValue();
152}
153
154JSTaggedValue ContainersArrayList::Has(EcmaRuntimeCallInfo *argv)
155{
156    ASSERT(argv);
157    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Has);
158    JSThread *thread = argv->GetThread();
159    [[maybe_unused]] EcmaHandleScope handleScope(thread);
160    JSHandle<JSTaggedValue> self = GetThis(argv);
161
162    if (!self->IsJSAPIArrayList()) {
163        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
164            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
165        } else {
166            JSTaggedValue error = ContainerError::BusinessError(thread, BIND_ERROR,
167                                                                "The has method cannot be bound");
168            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
169        }
170    }
171
172    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
173    bool isHas = JSHandle<JSAPIArrayList>::Cast(self)->Has(value.GetTaggedValue());
174
175    return GetTaggedBoolean(isHas);
176}
177
178JSTaggedValue ContainersArrayList::GetCapacity(EcmaRuntimeCallInfo *argv)
179{
180    ASSERT(argv);
181
182    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, GetCapacity);
183    JSThread *thread = argv->GetThread();
184
185    JSHandle<JSTaggedValue> self = GetThis(argv);
186    if (!self->IsJSAPIArrayList()) {
187        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
188            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
189        } else {
190            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
191                                                                "The getCapacity method cannot be bound");
192            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
193        }
194    }
195
196    uint32_t capacity = JSAPIArrayList::GetCapacity(thread, JSHandle<JSAPIArrayList>::Cast(self));
197
198    return JSTaggedValue(capacity);
199}
200
201JSTaggedValue ContainersArrayList::IncreaseCapacityTo(EcmaRuntimeCallInfo *argv)
202{
203    ASSERT(argv);
204    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, IncreaseCapacityTo);
205    JSThread *thread = argv->GetThread();
206    [[maybe_unused]] EcmaHandleScope handleScope(thread);
207    JSHandle<JSTaggedValue> self = GetThis(argv);
208
209    if (!self->IsJSAPIArrayList()) {
210        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
211            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
212        } else {
213            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
214                                                                "The increaseCapacityTo method cannot be bound");
215            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
216        }
217    }
218
219    JSHandle<JSTaggedValue> newCapacity = GetCallArg(argv, 0);
220    if (!newCapacity->IsInteger()) {
221        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, newCapacity);
222        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
223        CString errorMsg =
224            "The type of \"newCapacity\" must be number. Received value is: " + ConvertToString(*result);
225        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
226        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
227    }
228
229    JSAPIArrayList::IncreaseCapacityTo(thread, JSHandle<JSAPIArrayList>::Cast(self),
230                                       JSTaggedValue::ToUint32(thread, newCapacity));
231
232    return JSTaggedValue::True();
233}
234
235JSTaggedValue ContainersArrayList::TrimToCurrentLength(EcmaRuntimeCallInfo *argv)
236{
237    ASSERT(argv);
238    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, TrimToCurrentLength);
239    JSThread *thread = argv->GetThread();
240    [[maybe_unused]] EcmaHandleScope handleScope(thread);
241    JSHandle<JSTaggedValue> self = GetThis(argv);
242
243    if (!self->IsJSAPIArrayList()) {
244        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
245            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
246        } else {
247            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
248                                                                "The trimToCurrentLength method cannot be bound");
249            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
250        }
251    }
252
253    JSAPIArrayList::TrimToCurrentLength(thread, JSHandle<JSAPIArrayList>::Cast(self));
254
255    return JSTaggedValue::True();
256}
257
258JSTaggedValue ContainersArrayList::Get(EcmaRuntimeCallInfo *argv)
259{
260    ASSERT(argv);
261    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Get);
262    JSThread *thread = argv->GetThread();
263    [[maybe_unused]] EcmaHandleScope handleScope(thread);
264    JSHandle<JSTaggedValue> self = GetThis(argv);
265
266    if (!self->IsJSAPIArrayList()) {
267        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
268            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
269        } else {
270            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
271                                                                "The get method cannot be bound");
272            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
273        }
274    }
275
276    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
277
278    JSTaggedValue element = JSHandle<JSAPIArrayList>::Cast(self)->Get(thread, JSTaggedValue::ToUint32(thread, value));
279
280    return element;
281}
282
283JSTaggedValue ContainersArrayList::GetIndexOf(EcmaRuntimeCallInfo *argv)
284{
285    ASSERT(argv);
286    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, GetIndexOf);
287    JSThread *thread = argv->GetThread();
288    [[maybe_unused]] EcmaHandleScope handleScope(thread);
289    JSHandle<JSTaggedValue> self = GetThis(argv);
290
291    if (!self->IsJSAPIArrayList()) {
292        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
293            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
294        } else {
295            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
296                                                                "The getIndexOf method cannot be bound");
297            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
298        }
299    }
300
301    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
302
303    return JSTaggedValue(JSAPIArrayList::GetIndexOf(thread, JSHandle<JSAPIArrayList>::Cast(self), value));
304}
305
306JSTaggedValue ContainersArrayList::IsEmpty(EcmaRuntimeCallInfo *argv)
307{
308    ASSERT(argv);
309    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, IsEmpty);
310    JSThread *thread = argv->GetThread();
311    [[maybe_unused]] EcmaHandleScope handleScope(thread);
312    JSHandle<JSTaggedValue> self = GetThis(argv);
313
314    if (!self->IsJSAPIArrayList()) {
315        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
316            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
317        } else {
318            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
319                                                                "The isEmpty method cannot be bound");
320            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
321        }
322    }
323
324    return JSTaggedValue(JSAPIArrayList::IsEmpty(JSHandle<JSAPIArrayList>::Cast(self)));
325}
326
327JSTaggedValue ContainersArrayList::GetLastIndexOf(EcmaRuntimeCallInfo *argv)
328{
329    ASSERT(argv);
330    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, GetLastIndexOf);
331    JSThread *thread = argv->GetThread();
332    [[maybe_unused]] EcmaHandleScope handleScope(thread);
333    JSHandle<JSTaggedValue> self = GetThis(argv);
334
335    if (!self->IsJSAPIArrayList()) {
336        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
337            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
338        } else {
339            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
340                                                                "The getLastIndexOf method cannot be bound");
341            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
342        }
343    }
344
345    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
346
347    return JSTaggedValue(JSAPIArrayList::GetLastIndexOf(thread, JSHandle<JSAPIArrayList>::Cast(self), value));
348}
349
350JSTaggedValue ContainersArrayList::RemoveByIndex(EcmaRuntimeCallInfo *argv)
351{
352    ASSERT(argv);
353    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, RemoveByIndex);
354    JSThread *thread = argv->GetThread();
355    [[maybe_unused]] EcmaHandleScope handleScope(thread);
356    JSHandle<JSTaggedValue> self = GetThis(argv);
357
358    if (!self->IsJSAPIArrayList()) {
359        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
360            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
361        } else {
362            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
363                                                                "The removeByIndex method cannot be bound");
364            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
365        }
366    }
367
368    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
369    if (value->IsDouble()) {
370        value = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(value->GetDouble()));
371    }
372    if (!value->IsInt()) {
373        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, value.GetTaggedValue());
374        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
375        CString errorMsg =
376            "The type of \"index\" must be small integer. Received value is: " + ConvertToString(*result);
377        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
378        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
379    }
380
381    JSTaggedValue result =
382        JSAPIArrayList::RemoveByIndex(thread,
383                                      JSHandle<JSAPIArrayList>::Cast(self), JSTaggedValue::ToUint32(thread, value));
384
385    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
386    return result;
387}
388
389JSTaggedValue ContainersArrayList::Remove(EcmaRuntimeCallInfo *argv)
390{
391    ASSERT(argv);
392    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Remove);
393    JSThread *thread = argv->GetThread();
394    [[maybe_unused]] EcmaHandleScope handleScope(thread);
395    JSHandle<JSTaggedValue> self = GetThis(argv);
396
397    if (!self->IsJSAPIArrayList()) {
398        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
399            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
400        } else {
401            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
402                                                                "The remove method cannot be bound");
403            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
404        }
405    }
406
407    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
408
409    bool isRemove = JSAPIArrayList::Remove(thread, JSHandle<JSAPIArrayList>::Cast(self), value);
410    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
411
412    return GetTaggedBoolean(isRemove);
413}
414
415JSTaggedValue ContainersArrayList::RemoveByRange(EcmaRuntimeCallInfo *argv)
416{
417    ASSERT(argv);
418    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, RemoveByRange);
419    JSThread *thread = argv->GetThread();
420    [[maybe_unused]] EcmaHandleScope handleScope(thread);
421    JSHandle<JSTaggedValue> self = GetThis(argv);
422
423    if (!self->IsJSAPIArrayList()) {
424        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
425            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
426        } else {
427            JSTaggedValue error = ContainerError::BusinessError(thread, BIND_ERROR,
428                                                                "The removeByRange method cannot be bound");
429            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
430        }
431    }
432
433    JSHandle<JSTaggedValue> startIndex = GetCallArg(argv, 0);
434    JSHandle<JSTaggedValue> endIndex = GetCallArg(argv, 1);
435    if (!startIndex->IsInteger()) {
436        std::ostringstream oss;
437        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, startIndex);
438        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
439        CString errorMsg =
440            "The type of \"fromIndex\" must be number. Received value is: " + ConvertToString(*result);
441        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
442        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
443    }
444    if (!endIndex->IsInteger()) {
445        std::ostringstream oss;
446        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, endIndex);
447        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
448        CString errorMsg =
449            "The type of \"toIndex\" must be number. Received value is: " + ConvertToString(*result);
450        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
451        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
452    }
453    JSTaggedValue result =
454        JSAPIArrayList::RemoveByRange(thread, JSHandle<JSAPIArrayList>::Cast(self), startIndex, endIndex);
455
456    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
457    return result;
458}
459
460JSTaggedValue ContainersArrayList::ReplaceAllElements(EcmaRuntimeCallInfo *argv)
461{
462    ASSERT(argv);
463    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, ReplaceAllElements);
464    JSThread *thread = argv->GetThread();
465    [[maybe_unused]] EcmaHandleScope handleScope(thread);
466    JSHandle<JSTaggedValue> self = GetThis(argv);
467
468    if (!self->IsJSAPIArrayList()) {
469        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
470            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
471        } else {
472            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
473                                                                "The replaceAllElements method cannot be bound");
474            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
475        }
476    }
477
478    JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
479    if (!callbackFnHandle->IsCallable()) {
480        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle);
481        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
482        CString errorMsg =
483            "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result);
484        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
485        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
486    }
487    JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
488
489    return JSAPIArrayList::ReplaceAllElements(thread, self, callbackFnHandle, thisArgHandle);
490}
491
492JSTaggedValue ContainersArrayList::Set(EcmaRuntimeCallInfo *argv)
493{
494    ASSERT(argv);
495    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Set);
496    JSThread *thread = argv->GetThread();
497    [[maybe_unused]] EcmaHandleScope handleScope(thread);
498    JSHandle<JSTaggedValue> self = GetThis(argv);
499
500    if (!self->IsJSAPIArrayList()) {
501        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
502            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
503        } else {
504            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
505                                                                "The set method cannot be bound");
506            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
507        }
508    }
509
510    JSHandle<JSTaggedValue> index = GetCallArg(argv, 0);
511    JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
512    JSHandle<JSAPIArrayList>::Cast(self)->Set(thread, JSTaggedValue::ToUint32(thread, index), value.GetTaggedValue());
513
514    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
515    return JSTaggedValue::Undefined();
516}
517
518JSTaggedValue ContainersArrayList::SubArrayList(EcmaRuntimeCallInfo *argv)
519{
520    ASSERT(argv);
521    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, SubArrayList);
522    JSThread *thread = argv->GetThread();
523    [[maybe_unused]] EcmaHandleScope handleScope(thread);
524    JSHandle<JSTaggedValue> self = GetThis(argv);
525
526    if (!self->IsJSAPIArrayList()) {
527        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
528            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
529        } else {
530            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
531                                                                "The subArrayList method cannot be bound");
532            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
533        }
534    }
535    JSHandle<JSTaggedValue> value1 = GetCallArg(argv, 0);
536    JSHandle<JSTaggedValue> value2 = GetCallArg(argv, 1);
537    if (!value1->IsInteger()) {
538        std::ostringstream oss;
539        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, value1);
540        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
541        CString errorMsg =
542            "The type of \"fromIndex\" must be number. Received value is: " + ConvertToString(*result);
543        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
544        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
545    }
546    if (!value2->IsInteger()) {
547        std::ostringstream oss;
548        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, value2);
549        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
550        CString errorMsg =
551            "The type of \"toIndex\" must be number. Received value is: " + ConvertToString(*result);
552        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
553        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
554    }
555    JSTaggedValue newArrayList =
556        JSAPIArrayList::SubArrayList(thread, JSHandle<JSAPIArrayList>::Cast(self), value1, value2);
557
558    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
559    return newArrayList;
560}
561
562JSTaggedValue ContainersArrayList::Sort(EcmaRuntimeCallInfo *argv)
563{
564    ASSERT(argv);
565    BUILTINS_API_TRACE(argv->GetThread(), Array, Sort);
566    JSThread *thread = argv->GetThread();
567    [[maybe_unused]] EcmaHandleScope handleScope(thread);
568    JSHandle<JSTaggedValue> self = GetThis(argv);
569    if (!self->IsJSAPIArrayList()) {
570        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
571            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
572        } else {
573            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
574                                                                "The sort method cannot be bound");
575            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
576        }
577    }
578    JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
579    if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable() && !callbackFnHandle->IsNull()) {
580        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle);
581        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
582        CString errorMsg =
583            "The type of \"comparator\" must be callable. Received value is: " + ConvertToString(*result);
584        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
585        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
586    }
587    JSHandle<TaggedArray> elements(thread, JSHandle<JSAPIArrayList>::Cast(self)->GetElements());
588    JSMutableHandle<JSTaggedValue> presentValue(thread, JSTaggedValue::Undefined());
589    JSMutableHandle<JSTaggedValue> middleValue(thread, JSTaggedValue::Undefined());
590    JSMutableHandle<JSTaggedValue> previousValue(thread, JSTaggedValue::Undefined());
591    uint32_t length = JSHandle<JSAPIArrayList>::Cast(self)->GetLength().GetArrayLength();
592    for (uint32_t i = 1; i < length; i++) {
593        uint32_t beginIndex = 0;
594        uint32_t endIndex = i;
595        presentValue.Update(elements->Get(i));
596        while (beginIndex < endIndex) {
597            uint32_t middleIndex = (beginIndex + endIndex) / 2; // 2 : half
598            middleValue.Update(elements->Get(middleIndex));
599            double compareResult = base::ArrayHelper::SortCompare(thread, callbackFnHandle,
600                                                                  middleValue, presentValue);
601            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
602            if (compareResult > 0) {
603                endIndex = middleIndex;
604            } else {
605                beginIndex = middleIndex + 1;
606            }
607        }
608        if (endIndex >= 0 && endIndex < i) {
609            for (uint32_t j = i; j > endIndex; j--) {
610                previousValue.Update(elements->Get(j - 1));
611                elements->Set(thread, j, previousValue.GetTaggedValue());
612            }
613            elements->Set(thread, endIndex, presentValue.GetTaggedValue());
614        }
615    }
616    return JSTaggedValue::Undefined();
617}
618
619JSTaggedValue ContainersArrayList::GetSize(EcmaRuntimeCallInfo *argv)
620{
621    ASSERT(argv);
622    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, GetSize);
623    JSThread *thread = argv->GetThread();
624    [[maybe_unused]] EcmaHandleScope handleScope(thread);
625    JSHandle<JSTaggedValue> self = GetThis(argv);
626
627    if (!self->IsJSAPIArrayList()) {
628        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
629            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
630        } else {
631            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
632                                                                "The getLength method cannot be bound");
633            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
634        }
635    }
636
637    return JSTaggedValue(JSHandle<JSAPIArrayList>::Cast(self)->GetSize());
638}
639
640JSTaggedValue ContainersArrayList::ConvertToArray(EcmaRuntimeCallInfo *argv)
641{
642    ASSERT(argv);
643    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, ConvertToArray);
644    JSThread *thread = argv->GetThread();
645    [[maybe_unused]] EcmaHandleScope handleScope(thread);
646    JSHandle<JSTaggedValue> self = GetThis(argv);
647
648    if (!self->IsJSAPIArrayList()) {
649        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
650            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
651        } else {
652            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
653                                                                "The convertToArray method cannot be bound");
654            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
655        }
656    }
657
658    JSHandle<JSAPIArrayList> arrayList = JSHandle<JSAPIArrayList>::Cast(self);
659    auto factory = thread->GetEcmaVM()->GetFactory();
660    JSHandle<JSArray> array = factory->NewJSArray();
661
662    uint32_t length = arrayList->GetLength().GetArrayLength();
663    array->SetArrayLength(thread, length);
664
665    JSHandle<TaggedArray> srcElements(thread, arrayList->GetElements());
666    JSHandle<TaggedArray> dstElements = factory->NewAndCopyTaggedArray(srcElements, length, length);
667    array->SetElements(thread, dstElements);
668    return array.GetTaggedValue();
669}
670
671JSTaggedValue ContainersArrayList::ForEach(EcmaRuntimeCallInfo *argv)
672{
673    ASSERT(argv);
674    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, ForEach);
675    JSThread *thread = argv->GetThread();
676    [[maybe_unused]] EcmaHandleScope handleScope(thread);
677    JSHandle<JSTaggedValue> self = GetThis(argv);
678
679    if (!self->IsJSAPIArrayList()) {
680        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
681            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
682        } else {
683            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
684                                                                "The forEach method cannot be bound");
685            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
686        }
687    }
688
689    JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
690    if (!callbackFnHandle->IsCallable()) {
691        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle);
692        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
693        CString errorMsg =
694            "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result);
695        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
696        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
697    }
698
699    JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
700
701    return JSAPIArrayList::ForEach(thread, self, callbackFnHandle, thisArgHandle);
702}
703
704JSTaggedValue ContainersArrayList::GetIteratorObj(EcmaRuntimeCallInfo *argv)
705{
706    ASSERT(argv);
707    BUILTINS_API_TRACE(argv->GetThread(), ArrayList, GetIteratorObj);
708    JSThread *thread = argv->GetThread();
709    [[maybe_unused]] EcmaHandleScope handleScope(thread);
710
711    JSHandle<JSTaggedValue> self = GetThis(argv);
712
713    if (!self->IsJSAPIArrayList()) {
714        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIArrayList()) {
715            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
716        } else {
717            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
718                                                                "The Symbol.iterator method cannot be bound");
719            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
720        }
721    }
722
723    JSTaggedValue values = JSAPIArrayList::GetIteratorObj(thread, JSHandle<JSAPIArrayList>::Cast(self));
724
725    return values;
726}
727}  // namespace panda::ecmascript::containers
728