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