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_list.h"
17
18#include "ecmascript/containers/containers_errors.h"
19#include "ecmascript/interpreter/interpreter.h"
20#include "ecmascript/js_api/js_api_list.h"
21#include "ecmascript/js_api/js_api_list_iterator.h"
22#include "ecmascript/js_function.h"
23
24namespace panda::ecmascript::containers {
25JSTaggedValue ContainersList::ListConstructor(EcmaRuntimeCallInfo *argv)
26{
27    ASSERT(argv != nullptr);
28    JSThread *thread = argv->GetThread();
29    BUILTINS_API_TRACE(thread, List, Constructor);
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 List'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<JSAPIList> list = JSHandle<JSAPIList>::Cast(obj);
43    list->SetIsOrderedList(true);
44
45    JSTaggedValue singleList = TaggedSingleList::Create(thread);
46    list->SetSingleList(thread, singleList);
47
48    return list.GetTaggedValue();
49}
50
51JSTaggedValue ContainersList::Add(EcmaRuntimeCallInfo *argv)
52{
53    ASSERT(argv != nullptr);
54    JSThread *thread = argv->GetThread();
55    BUILTINS_API_TRACE(thread, List, Add);
56    [[maybe_unused]] EcmaHandleScope handleScope(thread);
57    JSHandle<JSTaggedValue> self = GetThis(argv);
58    if (!self->IsJSAPIList()) {
59        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
60            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
61        } else {
62            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
63                                                                "The add method cannot be bound");
64            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
65        }
66    }
67
68    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
69    JSHandle<JSAPIList> jSAPIList = JSHandle<JSAPIList>::Cast(self);
70    JSAPIList::Add(thread, jSAPIList, value);
71    return JSTaggedValue::True();
72}
73
74JSTaggedValue ContainersList::Insert(EcmaRuntimeCallInfo *argv)
75{
76    ASSERT(argv != nullptr);
77    JSThread *thread = argv->GetThread();
78    BUILTINS_API_TRACE(thread, List, Insert);
79    [[maybe_unused]] EcmaHandleScope handleScope(thread);
80    JSHandle<JSTaggedValue> self = GetThis(argv);
81    if (!self->IsJSAPIList()) {
82        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
83            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
84        } else {
85            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
86                                                                "The insert method cannot be bound");
87            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
88        }
89    }
90    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
91    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
92    if (index->IsDouble()) {
93        index = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(index->GetDouble()));
94    }
95    if (!index->IsInt()) {
96        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, index.GetTaggedValue());
97        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
98        CString errorMsg =
99            "The type of \"index\" must be small integer. Received value is: " + ConvertToString(*result);
100        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
101        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
102    }
103    JSHandle<JSAPIList> jSAPIList = JSHandle<JSAPIList>::Cast(self);
104    JSAPIList::Insert(thread, jSAPIList, value, index->GetInt());
105    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
106    return JSTaggedValue::True();
107}
108
109JSTaggedValue ContainersList::GetFirst(EcmaRuntimeCallInfo *argv)
110{
111    ASSERT(argv != nullptr);
112    JSThread *thread = argv->GetThread();
113    BUILTINS_API_TRACE(thread, List, GetFirst);
114    [[maybe_unused]] EcmaHandleScope handleScope(thread);
115    JSHandle<JSTaggedValue> self = GetThis(argv);
116    if (!self->IsJSAPIList()) {
117        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
118            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
119        } else {
120            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
121                                                                "The getFirst method cannot be bound");
122            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
123        }
124    }
125    JSHandle<JSAPIList> jSAPIList = JSHandle<JSAPIList>::Cast(self);
126    return jSAPIList->GetFirst();
127}
128
129JSTaggedValue ContainersList::GetLast(EcmaRuntimeCallInfo *argv)
130{
131    ASSERT(argv != nullptr);
132    JSThread *thread = argv->GetThread();
133    BUILTINS_API_TRACE(thread, List, GetLast);
134    [[maybe_unused]] EcmaHandleScope handleScope(thread);
135    JSHandle<JSTaggedValue> self = GetThis(argv);
136    if (!self->IsJSAPIList()) {
137        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
138            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
139        } else {
140            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
141                                                                "The getLast method cannot be bound");
142            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
143        }
144    }
145    JSHandle<JSAPIList> jSAPIList = JSHandle<JSAPIList>::Cast(self);
146    return jSAPIList->GetLast();
147}
148
149JSTaggedValue ContainersList::Has(EcmaRuntimeCallInfo *argv)
150{
151    ASSERT(argv != nullptr);
152    JSThread *thread = argv->GetThread();
153    BUILTINS_API_TRACE(thread, List, Has);
154    [[maybe_unused]] EcmaHandleScope handleScope(thread);
155    JSHandle<JSTaggedValue> self = GetThis(argv);
156    if (!self->IsJSAPIList()) {
157        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
158            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
159        } else {
160            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
161                                                                "The has method cannot be bound");
162            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
163        }
164    }
165    JSHandle<JSAPIList> jSAPIList = JSHandle<JSAPIList>::Cast(self);
166    JSHandle<JSTaggedValue> element = GetCallArg(argv, 0);
167    return GetTaggedBoolean(jSAPIList->Has(element.GetTaggedValue()));
168}
169
170JSTaggedValue ContainersList::IsEmpty(EcmaRuntimeCallInfo *argv)
171{
172    ASSERT(argv != nullptr);
173    JSThread *thread = argv->GetThread();
174    BUILTINS_API_TRACE(thread, List, IsEmpty);
175    [[maybe_unused]] EcmaHandleScope handleScope(thread);
176    JSHandle<JSTaggedValue> self = GetThis(argv);
177    if (!self->IsJSAPIList()) {
178        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
179            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
180        } else {
181            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
182                                                                "The isEmpty method cannot be bound");
183            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
184        }
185    }
186    JSHandle<JSAPIList> jSAPIList = JSHandle<JSAPIList>::Cast(self);
187    return GetTaggedBoolean(jSAPIList->IsEmpty());
188}
189
190JSTaggedValue ContainersList::Get(EcmaRuntimeCallInfo *argv)
191{
192    ASSERT(argv != nullptr);
193    JSThread *thread = argv->GetThread();
194    BUILTINS_API_TRACE(thread, List, Get);
195    [[maybe_unused]] EcmaHandleScope handleScope(thread);
196    JSHandle<JSTaggedValue> self = GetThis(argv);
197    if (!self->IsJSAPIList()) {
198        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
199            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
200        } else {
201            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
202                                                                "The get method cannot be bound");
203            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
204        }
205    }
206    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
207    JSHandle<JSTaggedValue> index = GetCallArg(argv, 0);
208    if (index->IsDouble()) {
209        index = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(index->GetDouble()));
210    }
211    if (!index->IsInt()) {
212        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, index.GetTaggedValue());
213        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
214        CString errorMsg =
215            "The type of \"index\" must be small integer. Received value is: " + ConvertToString(*result);
216        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
217        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
218    }
219    return jsAPIList->Get(index->GetInt());
220}
221
222JSTaggedValue ContainersList::GetIndexOf(EcmaRuntimeCallInfo *argv)
223{
224    ASSERT(argv != nullptr);
225    JSThread *thread = argv->GetThread();
226    BUILTINS_API_TRACE(thread, List, GetIndexOf);
227    [[maybe_unused]] EcmaHandleScope handleScope(thread);
228    JSHandle<JSTaggedValue> self = GetThis(argv);
229    if (!self->IsJSAPIList()) {
230        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
231            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
232        } else {
233            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
234                                                                "The getIndexOf method cannot be bound");
235            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
236        }
237    }
238    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
239    JSHandle<JSTaggedValue> element = GetCallArg(argv, 0);
240    return jsAPIList->GetIndexOf(element.GetTaggedValue());
241}
242
243JSTaggedValue ContainersList::GetLastIndexOf(EcmaRuntimeCallInfo *argv)
244{
245    ASSERT(argv != nullptr);
246    JSThread *thread = argv->GetThread();
247    BUILTINS_API_TRACE(thread, List, GetLastIndexOf);
248    [[maybe_unused]] EcmaHandleScope handleScope(thread);
249    JSHandle<JSTaggedValue> self = GetThis(argv);
250    if (!self->IsJSAPIList()) {
251        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
252            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
253        } else {
254            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
255                                                                "The getLastIndexOf method cannot be bound");
256            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
257        }
258    }
259    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
260    JSHandle<JSTaggedValue> element = GetCallArg(argv, 0);
261    return jsAPIList->GetLastIndexOf(element.GetTaggedValue());
262}
263
264JSTaggedValue ContainersList::Set(EcmaRuntimeCallInfo *argv)
265{
266    ASSERT(argv != nullptr);
267    JSThread *thread = argv->GetThread();
268    BUILTINS_API_TRACE(thread, List, Set);
269    [[maybe_unused]] EcmaHandleScope handleScope(thread);
270    JSHandle<JSTaggedValue> self = GetThis(argv);
271    if (!self->IsJSAPIList()) {
272        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
273            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
274        } else {
275            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
276                                                                "The set method cannot be bound");
277            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
278        }
279    }
280    JSHandle<JSTaggedValue> index = GetCallArg(argv, 0);
281    JSHandle<JSTaggedValue> element = GetCallArg(argv, 1);
282    if (index->IsDouble()) {
283        index = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(index->GetDouble()));
284    }
285    if (!index->IsInt()) {
286        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, index.GetTaggedValue());
287        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
288        CString errorMsg =
289            "The type of \"index\" must be small integer. Received value is: " + ConvertToString(*result);
290        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
291        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
292    }
293    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
294    JSTaggedValue oldValue = JSAPIList::Set(thread, jsAPIList, index->GetInt(), element);
295    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
296    return oldValue;
297}
298
299JSTaggedValue ContainersList::ForEach(EcmaRuntimeCallInfo *argv)
300{
301    ASSERT(argv != nullptr);
302    JSThread *thread = argv->GetThread();
303    BUILTINS_API_TRACE(thread, List, ForEach);
304    [[maybe_unused]] EcmaHandleScope handleScope(thread);
305    // get and check List object
306    JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
307    if (!thisHandle->IsJSAPIList()) {
308        if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIList()) {
309            thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget());
310        } else {
311            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
312                                                                "The forEach method cannot be bound");
313            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
314        }
315    }
316
317    // get and check callback function
318    JSHandle<JSTaggedValue> callbackFnHandle(GetCallArg(argv, 0));
319    if (!callbackFnHandle->IsCallable()) {
320        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue());
321        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
322        CString errorMsg =
323            "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result);
324        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
325        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
326    }
327
328    // If thisArgHandle was supplied, let T be thisArgHandle; else let T be undefined.
329    JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
330    JSHandle<JSAPIList> list = JSHandle<JSAPIList>::Cast(thisHandle);
331    JSHandle<TaggedSingleList> singleList(thread, list->GetSingleList());
332    uint32_t length = list->Length();
333
334    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
335    uint32_t index = 0;
336    const uint32_t argsLength = 3; // 3: «kValue, k, O»
337    int valueNode = TaggedSingleList::ELEMENTS_START_INDEX;
338    while (index < length) {
339        valueNode = singleList->GetNextDataIndex(valueNode);
340        JSTaggedValue value = singleList->GetElement(valueNode);
341        EcmaRuntimeCallInfo *info =
342            EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
343        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
344        info->SetCallArg(value, JSTaggedValue(index), thisHandle.GetTaggedValue());
345        JSTaggedValue funcResult = JSFunction::Call(info);
346        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
347        index++;
348    }
349    return JSTaggedValue::Undefined();
350}
351
352JSTaggedValue ContainersList::Clear(EcmaRuntimeCallInfo *argv)
353{
354    ASSERT(argv != nullptr);
355    JSThread *thread = argv->GetThread();
356    BUILTINS_API_TRACE(thread, List, Clear);
357    [[maybe_unused]] EcmaHandleScope handleScope(thread);
358    JSHandle<JSTaggedValue> self = GetThis(argv);
359    if (!self->IsJSAPIList()) {
360        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
361            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
362        } else {
363            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
364                                                                "The clear method cannot be bound");
365            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
366        }
367    }
368    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
369    jsAPIList->Clear(thread);
370    return JSTaggedValue::Undefined();
371}
372
373JSTaggedValue ContainersList::RemoveByIndex(EcmaRuntimeCallInfo *argv)
374{
375    ASSERT(argv != nullptr);
376    JSThread *thread = argv->GetThread();
377    BUILTINS_API_TRACE(thread, List, RemoveByIndex);
378    [[maybe_unused]] EcmaHandleScope handleScope(thread);
379    JSHandle<JSTaggedValue> self = GetThis(argv);
380    if (!self->IsJSAPIList()) {
381        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
382            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
383        } else {
384            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
385                                                                "The removeByIndex method cannot be bound");
386            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
387        }
388    }
389    JSHandle<JSTaggedValue> index = GetCallArg(argv, 0);
390    if (index->IsDouble()) {
391        index = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(index->GetDouble()));
392    }
393    if (!index->IsInt()) {
394        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, index.GetTaggedValue());
395        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
396        CString errorMsg =
397            "The type of \"index\" must be small integer. Received value is: " + ConvertToString(*result);
398        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
399        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
400    }
401    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
402    JSTaggedValue result = JSAPIList::RemoveByIndex(thread, jsAPIList, index->GetInt());
403    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
404    return result;
405}
406
407JSTaggedValue ContainersList::Remove(EcmaRuntimeCallInfo *argv)
408{
409    ASSERT(argv != nullptr);
410    JSThread *thread = argv->GetThread();
411    BUILTINS_API_TRACE(thread, List, Remove);
412    [[maybe_unused]] EcmaHandleScope handleScope(thread);
413    JSHandle<JSTaggedValue> self = GetThis(argv);
414    if (!self->IsJSAPIList()) {
415        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
416            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
417        } else {
418            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
419                                                                "The remove method cannot be bound");
420            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
421        }
422    }
423    JSHandle<JSTaggedValue> element = GetCallArg(argv, 0);
424    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
425    return jsAPIList->Remove(thread, element.GetTaggedValue());
426}
427
428JSTaggedValue ContainersList::ReplaceAllElements(EcmaRuntimeCallInfo *argv)
429{
430    ASSERT(argv != nullptr);
431    JSThread *thread = argv->GetThread();
432    BUILTINS_API_TRACE(thread, List, ReplaceAllElements);
433    JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
434    if (!thisHandle->IsJSAPIList()) {
435        if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIList()) {
436            thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget());
437        } else {
438            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
439                                                                "The replaceAllElements method cannot be bound");
440            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
441        }
442    }
443    JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
444    if (!callbackFnHandle->IsCallable()) {
445        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue());
446        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
447        CString errorMsg =
448            "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result);
449        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
450        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
451    }
452    JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
453    return JSAPIList::ReplaceAllElements(thread, thisHandle, callbackFnHandle, thisArgHandle);
454}
455
456JSTaggedValue ContainersList::Equal(EcmaRuntimeCallInfo *argv)
457{
458    ASSERT(argv != nullptr);
459    JSThread *thread = argv->GetThread();
460    BUILTINS_API_TRACE(thread, List, Equal);
461    [[maybe_unused]] EcmaHandleScope handleScope(thread);
462    JSHandle<JSTaggedValue> self = GetThis(argv);
463    if (!self->IsJSAPIList()) {
464        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
465            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
466        } else {
467            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
468                                                                "The equal method cannot be bound");
469            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
470        }
471    }
472    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
473    JSHandle<JSTaggedValue> obj = GetCallArg(argv, 0);
474    if (!obj->IsJSAPIList()) {
475        if (obj->IsJSProxy() && JSHandle<JSProxy>::Cast(obj)->GetTarget().IsJSAPIList()) {
476            obj = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(obj)->GetTarget());
477        } else {
478            return JSTaggedValue::False();
479        }
480    }
481    JSHandle<JSAPIList> handleObj = JSHandle<JSAPIList>::Cast(obj);
482    return jsAPIList->Equal(thread, handleObj);
483}
484
485JSTaggedValue ContainersList::Sort(EcmaRuntimeCallInfo *argv)
486{
487    ASSERT(argv != nullptr);
488    JSThread *thread = argv->GetThread();
489    BUILTINS_API_TRACE(thread, List, Sort);
490    [[maybe_unused]] EcmaHandleScope handleScope(thread);
491    JSHandle<JSTaggedValue> self = GetThis(argv);
492    if (!self->IsJSAPIList()) {
493        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
494            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
495        } else {
496            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
497                                                                "The sort method cannot be bound");
498            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
499        }
500    }
501    JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
502    if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) {
503        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue());
504        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
505        CString errorMsg =
506            "The type of \"comparator\" must be callable. Received value is: " + ConvertToString(*result);
507        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
508        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
509    }
510    return JSAPIList::Sort(thread, self, callbackFnHandle);
511}
512
513JSTaggedValue ContainersList::GetIteratorObj(EcmaRuntimeCallInfo *argv)
514{
515    ASSERT(argv != nullptr);
516    JSThread *thread = argv->GetThread();
517    BUILTINS_API_TRACE(thread, List, GetIteratorObj);
518    [[maybe_unused]] EcmaHandleScope handleScope(thread);
519    JSHandle<JSTaggedValue> self = GetThis(argv);
520    JSHandle<JSTaggedValue> iter = JSAPIListIterator::CreateListIterator(thread, self);
521    return iter.GetTaggedValue();
522}
523
524JSTaggedValue ContainersList::ConvertToArray(EcmaRuntimeCallInfo *argv)
525{
526    ASSERT(argv != nullptr);
527    JSThread *thread = argv->GetThread();
528    BUILTINS_API_TRACE(thread, List, ConvertToArray);
529    [[maybe_unused]] EcmaHandleScope handleScope(thread);
530    JSHandle<JSTaggedValue> self = GetThis(argv);
531    if (!self->IsJSAPIList()) {
532        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
533            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
534        } else {
535            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
536                                                                "The convertToArray method cannot be bound");
537            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
538        }
539    }
540    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
541    return JSAPIList::ConvertToArray(thread, jsAPIList);
542}
543
544JSTaggedValue ContainersList::GetSubList(EcmaRuntimeCallInfo *argv)
545{
546    ASSERT(argv != nullptr);
547    JSThread *thread = argv->GetThread();
548    BUILTINS_API_TRACE(thread, List, GetSubList);
549    [[maybe_unused]] EcmaHandleScope handleScope(thread);
550    JSHandle<JSTaggedValue> self = GetThis(argv);
551    if (!self->IsJSAPIList()) {
552        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
553            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
554        } else {
555            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
556                                                                "The getSubList method cannot be bound");
557            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
558        }
559    }
560    JSHandle<JSTaggedValue> fromIndex = GetCallArg(argv, 0);
561
562    if (fromIndex->IsDouble()) {
563        fromIndex = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(fromIndex->GetDouble()));
564    }
565
566    if (!fromIndex->IsInt()) {
567        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, fromIndex.GetTaggedValue());
568        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
569        CString errorMsg =
570            "The type of \"fromIndex\" must be number. Received value is: " + ConvertToString(*result);
571        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
572        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
573    }
574
575    JSHandle<JSTaggedValue> toIndex = GetCallArg(argv, 1);
576
577    // for case like Math.foor(1.3), it gives double 1.0;
578    if (toIndex->IsDouble()) {
579        toIndex = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(toIndex->GetDouble()));
580    }
581
582    if (!toIndex->IsInt()) {
583        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, toIndex.GetTaggedValue());
584        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
585        CString errorMsg =
586            "The type of \"toIndex\" must be number. Received value is: " + ConvertToString(*result);
587        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
588        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
589    }
590
591    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
592    JSTaggedValue newList = JSAPIList::GetSubList(thread, jsAPIList, fromIndex->GetInt(), toIndex->GetInt());
593    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
594    return newList;
595}
596
597JSTaggedValue ContainersList::Length(EcmaRuntimeCallInfo *argv)
598{
599    ASSERT(argv != nullptr);
600    JSThread *thread = argv->GetThread();
601    BUILTINS_API_TRACE(thread, List, Length);
602    [[maybe_unused]] EcmaHandleScope handleScope(thread);
603    JSHandle<JSTaggedValue> self = GetThis(argv);
604    if (!self->IsJSAPIList()) {
605        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIList()) {
606            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
607        } else {
608            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
609                                                                "The getLength method cannot be bound");
610            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
611        }
612    }
613    JSHandle<JSAPIList> jsAPIList = JSHandle<JSAPIList>::Cast(self);
614    return JSTaggedValue(jsAPIList->Length());
615}
616}  // namespace panda::ecmascript::containers
617