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
23 namespace panda::ecmascript::containers {
StackConstructor(EcmaRuntimeCallInfo * argv)24 JSTaggedValue 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
IsEmpty(EcmaRuntimeCallInfo * argv)48 JSTaggedValue 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
Push(EcmaRuntimeCallInfo * argv)71 JSTaggedValue 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
Peek(EcmaRuntimeCallInfo * argv)94 JSTaggedValue 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
Locate(EcmaRuntimeCallInfo * argv)118 JSTaggedValue 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
Pop(EcmaRuntimeCallInfo * argv)142 JSTaggedValue 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(thread);
162 return jsValue;
163 }
164
ForEach(EcmaRuntimeCallInfo * argv)165 JSTaggedValue 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
Iterator(EcmaRuntimeCallInfo * argv)214 JSTaggedValue 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
GetLength(EcmaRuntimeCallInfo * argv)235 JSTaggedValue 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