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_stack.h"
17
18#include "ecmascript/containers/containers_errors.h"
19#include "ecmascript/interpreter/interpreter.h"
20#include "ecmascript/js_api/js_api_stack_iterator.h"
21#include "ecmascript/js_function.h"
22
23namespace panda::ecmascript::containers {
24JSTaggedValue ContainersStack::StackConstructor(EcmaRuntimeCallInfo *argv)
25{
26    ASSERT(argv != nullptr);
27    BUILTINS_API_TRACE(argv->GetThread(), Stack, Constructor);
28    JSThread *thread = argv->GetThread();
29    [[maybe_unused]] EcmaHandleScope handleScope(thread);
30    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
31    JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
32    if (newTarget->IsUndefined()) {
33        JSTaggedValue error =
34            ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR,
35                                          "The List's constructor cannot be directly invoked");
36        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
37    }
38    JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
39    JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
40    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
41
42    JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(obj);
43    stack->SetTop(-1);
44
45    return obj.GetTaggedValue();
46}
47
48JSTaggedValue ContainersStack::IsEmpty(EcmaRuntimeCallInfo *argv)
49{
50    ASSERT(argv != nullptr);
51    BUILTINS_API_TRACE(argv->GetThread(), Stack, IsEmpty);
52    JSThread *thread = argv->GetThread();
53    [[maybe_unused]] EcmaHandleScope handleScope(thread);
54    JSHandle<JSTaggedValue> self = GetThis(argv);
55
56    if (!self->IsJSAPIStack()) {
57        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
58            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
59        } else {
60            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
61                                                                "The isEmpty method cannot be bound");
62            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
63        }
64    }
65    JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self);
66    bool judge = stack->Empty();
67
68    return GetTaggedBoolean(judge);
69}
70
71JSTaggedValue ContainersStack::Push(EcmaRuntimeCallInfo *argv)
72{
73    ASSERT(argv != nullptr);
74    BUILTINS_API_TRACE(argv->GetThread(), Stack, Push);
75    JSThread *thread = argv->GetThread();
76    [[maybe_unused]] EcmaHandleScope handleScope(thread);
77    JSHandle<JSTaggedValue> self = GetThis(argv);
78
79    if (!self->IsJSAPIStack()) {
80        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
81            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
82        } else {
83            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
84                                                                "The push method cannot be bound");
85            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
86        }
87    }
88
89    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
90    JSTaggedValue jsValue = JSAPIStack::Push(thread, JSHandle<JSAPIStack>::Cast(self), value);
91    return jsValue;
92}
93
94JSTaggedValue ContainersStack::Peek(EcmaRuntimeCallInfo *argv)
95{
96    ASSERT(argv != nullptr);
97    BUILTINS_API_TRACE(argv->GetThread(), Stack, Peek);
98    JSThread *thread = argv->GetThread();
99    [[maybe_unused]] EcmaHandleScope handleScope(thread);
100    JSHandle<JSTaggedValue> self = GetThis(argv);
101
102    if (!self->IsJSAPIStack()) {
103        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
104            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
105        } else {
106            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
107                                                                "The peek method cannot be bound");
108            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
109        }
110    }
111
112    JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self);
113    JSTaggedValue jsValue = stack->Peek();
114    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
115    return jsValue;
116}
117
118JSTaggedValue ContainersStack::Locate(EcmaRuntimeCallInfo *argv)
119{
120    ASSERT(argv != nullptr);
121    BUILTINS_API_TRACE(argv->GetThread(), Stack, Locate);
122    JSThread *thread = argv->GetThread();
123    [[maybe_unused]] EcmaHandleScope handleScope(thread);
124    JSHandle<JSTaggedValue> self = GetThis(argv);
125
126    if (!self->IsJSAPIStack()) {
127        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
128            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
129        } else {
130            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
131                                                                "The locate method cannot be bound");
132            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
133        }
134    }
135
136    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
137    JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self);
138    int num = stack->Search(value);
139    return JSTaggedValue(num);
140}
141
142JSTaggedValue ContainersStack::Pop(EcmaRuntimeCallInfo *argv)
143{
144    ASSERT(argv != nullptr);
145    BUILTINS_API_TRACE(argv->GetThread(), Stack, Pop);
146    JSThread *thread = argv->GetThread();
147    [[maybe_unused]] EcmaHandleScope handleScope(thread);
148    JSHandle<JSTaggedValue> self = GetThis(argv);
149
150    if (!self->IsJSAPIStack()) {
151        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
152            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
153        } else {
154            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
155                                                                "The pop method cannot be bound");
156            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
157        }
158    }
159
160    JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self);
161    JSTaggedValue jsValue = stack->Pop();
162    return jsValue;
163}
164
165JSTaggedValue ContainersStack::ForEach(EcmaRuntimeCallInfo *argv)
166{
167    ASSERT(argv != nullptr);
168    BUILTINS_API_TRACE(argv->GetThread(), Stack, ForEach);
169    JSThread *thread = argv->GetThread();
170    [[maybe_unused]] EcmaHandleScope handleScope(thread);
171
172    JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
173    if (!thisHandle->IsJSAPIStack()) {
174        if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIStack()) {
175            thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget());
176        } else {
177            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
178                                                                "The forEach method cannot be bound");
179            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
180        }
181    }
182
183    JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(thisHandle);
184    int32_t len = stack->GetSize();
185    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
186
187    JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
188    if (!callbackFnHandle->IsCallable()) {
189        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue());
190        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
191        CString errorMsg =
192            "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result);
193        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
194        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
195    }
196
197    JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
198    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
199    uint32_t k = 0;
200    uint32_t actLen = static_cast<uint32_t>(len + 1);
201    while (k < actLen) {
202        JSTaggedValue kValue = stack->Get(k);
203        EcmaRuntimeCallInfo *info =
204            EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, 3); // 3:three args
205        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
206        info->SetCallArg(kValue, JSTaggedValue(k), thisHandle.GetTaggedValue());
207        JSTaggedValue funcResult = JSFunction::Call(info);
208        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
209        k++;
210    }
211    return JSTaggedValue::Undefined();
212}
213
214JSTaggedValue ContainersStack::Iterator(EcmaRuntimeCallInfo *argv)
215{
216    ASSERT(argv != nullptr);
217    BUILTINS_API_TRACE(argv->GetThread(), Stack, Iterator);
218    JSThread *thread = argv->GetThread();
219    [[maybe_unused]] EcmaHandleScope handleScope(thread);
220    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
221    JSHandle<JSTaggedValue> self = GetThis(argv);
222    if (!self->IsJSAPIStack()) {
223        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
224            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
225        } else {
226            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
227                                                                "The Symbol.iterator method cannot be bound");
228            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
229        }
230    }
231    JSHandle<JSAPIStackIterator> iter(factory->NewJSAPIStackIterator(JSHandle<JSAPIStack>::Cast(self)));
232    return iter.GetTaggedValue();
233}
234
235JSTaggedValue ContainersStack::GetLength(EcmaRuntimeCallInfo *argv)
236{
237    ASSERT(argv != nullptr);
238    BUILTINS_API_TRACE(argv->GetThread(), Stack, GetLength);
239    JSThread *thread = argv->GetThread();
240    [[maybe_unused]] EcmaHandleScope handleScope(thread);
241    JSHandle<JSTaggedValue> self = GetThis(argv);
242
243    if (!self->IsJSAPIStack()) {
244        if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
245            self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
246        } else {
247            JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
248                                                                "The getLength method cannot be bound");
249            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
250        }
251    }
252
253    int32_t len = (JSHandle<JSAPIStack>::Cast(self))->GetSize();
254    return JSTaggedValue(len + 1);
255}
256}  // namespace panda::ecmascript::containers
257