1 /*
2 * Copyright (c) 2022-2024 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_deque.h"
17
18 #include "ecmascript/containers/containers_errors.h"
19 #include "ecmascript/interpreter/interpreter.h"
20 #include "ecmascript/js_api/js_api_deque.h"
21 #include "ecmascript/js_array.h"
22 #include "ecmascript/js_function.h"
23 #include "ecmascript/object_factory.h"
24 #include "ecmascript/tagged_array-inl.h"
25
26 namespace panda::ecmascript::containers {
DequeConstructor(EcmaRuntimeCallInfo * argv)27 JSTaggedValue ContainersDeque::DequeConstructor(EcmaRuntimeCallInfo *argv)
28 {
29 ASSERT(argv != nullptr);
30 BUILTINS_API_TRACE(argv->GetThread(), Deque, Constructor);
31 JSThread *thread = argv->GetThread();
32 [[maybe_unused]] EcmaHandleScope handleScope(thread);
33 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
34 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
35 if (newTarget->IsUndefined()) {
36 JSTaggedValue error =
37 ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR,
38 "The Deque's constructor cannot be directly invoked");
39 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
40 }
41 JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
42 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
43 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
44 JSHandle<TaggedArray> newElements = factory->NewTaggedArray(JSAPIDeque::DEFAULT_CAPACITY_LENGTH);
45 obj->SetElements(thread, newElements);
46 return obj.GetTaggedValue();
47 }
48
InsertFront(EcmaRuntimeCallInfo * argv)49 JSTaggedValue ContainersDeque::InsertFront(EcmaRuntimeCallInfo *argv)
50 {
51 ASSERT(argv != nullptr);
52 BUILTINS_API_TRACE(argv->GetThread(), Deque, InsertFront);
53 JSThread *thread = argv->GetThread();
54 [[maybe_unused]] EcmaHandleScope handleScope(thread);
55 JSHandle<JSTaggedValue> self = GetThis(argv);
56
57 if (!self->IsJSAPIDeque()) {
58 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) {
59 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
60 } else {
61 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
62 "The insertFront method cannot be bound");
63 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
64 }
65 }
66
67 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
68 JSAPIDeque::InsertFront(thread, JSHandle<JSAPIDeque>::Cast(self), value);
69
70 return JSTaggedValue::True();
71 }
72
73
InsertEnd(EcmaRuntimeCallInfo * argv)74 JSTaggedValue ContainersDeque::InsertEnd(EcmaRuntimeCallInfo *argv)
75 {
76 ASSERT(argv != nullptr);
77 BUILTINS_API_TRACE(argv->GetThread(), Deque, InsertEnd);
78 JSThread *thread = argv->GetThread();
79 [[maybe_unused]] EcmaHandleScope handleScope(thread);
80 JSHandle<JSTaggedValue> self = GetThis(argv);
81
82 if (!self->IsJSAPIDeque()) {
83 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) {
84 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
85 } else {
86 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
87 "The insertEnd method cannot be bound");
88 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
89 }
90 }
91
92 JSHandle<JSTaggedValue> value(GetCallArg(argv, 0));
93 JSAPIDeque::InsertEnd(thread, JSHandle<JSAPIDeque>::Cast(self), value);
94
95 return JSTaggedValue::True();
96 }
97
GetFirst(EcmaRuntimeCallInfo * argv)98 JSTaggedValue ContainersDeque::GetFirst(EcmaRuntimeCallInfo *argv)
99 {
100 ASSERT(argv != nullptr);
101 BUILTINS_API_TRACE(argv->GetThread(), Deque, GetFirst);
102 JSThread *thread = argv->GetThread();
103 [[maybe_unused]] EcmaHandleScope handleScope(thread);
104 JSHandle<JSTaggedValue> self = GetThis(argv);
105
106 if (!self->IsJSAPIDeque()) {
107 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) {
108 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
109 } else {
110 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
111 "The getFirst method cannot be bound");
112 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
113 }
114 }
115
116 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self);
117 JSTaggedValue firstElement = deque->GetFront();
118 return firstElement;
119 }
120
GetLast(EcmaRuntimeCallInfo * argv)121 JSTaggedValue ContainersDeque::GetLast(EcmaRuntimeCallInfo *argv)
122 {
123 ASSERT(argv != nullptr);
124 BUILTINS_API_TRACE(argv->GetThread(), Deque, GetLast);
125 JSThread *thread = argv->GetThread();
126 [[maybe_unused]] EcmaHandleScope handleScope(thread);
127 JSHandle<JSTaggedValue> self = GetThis(argv);
128
129 if (!self->IsJSAPIDeque()) {
130 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) {
131 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
132 } else {
133 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
134 "The getLast method cannot be bound");
135 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
136 }
137 }
138
139 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self);
140 JSTaggedValue lastElement = deque->GetTail();
141 return lastElement;
142 }
143
Has(EcmaRuntimeCallInfo * argv)144 JSTaggedValue ContainersDeque::Has(EcmaRuntimeCallInfo *argv)
145 {
146 ASSERT(argv != nullptr);
147 BUILTINS_API_TRACE(argv->GetThread(), Deque, Has);
148 JSThread *thread = argv->GetThread();
149 [[maybe_unused]] EcmaHandleScope handleScope(thread);
150 JSHandle<JSTaggedValue> self = GetThis(argv);
151
152 if (!self->IsJSAPIDeque()) {
153 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) {
154 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
155 } else {
156 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
157 "The has method cannot be bound");
158 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
159 }
160 }
161
162 JSHandle<JSTaggedValue> value(GetCallArg(argv, 0));
163
164 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self);
165 bool isHas = deque->Has(value.GetTaggedValue());
166 return GetTaggedBoolean(isHas);
167 }
168
PopFirst(EcmaRuntimeCallInfo * argv)169 JSTaggedValue ContainersDeque::PopFirst(EcmaRuntimeCallInfo *argv)
170 {
171 ASSERT(argv != nullptr);
172 BUILTINS_API_TRACE(argv->GetThread(), Deque, PopFirst);
173 JSThread *thread = argv->GetThread();
174 [[maybe_unused]] EcmaHandleScope handleScope(thread);
175 JSHandle<JSTaggedValue> self = GetThis(argv);
176
177 if (!self->IsJSAPIDeque()) {
178 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) {
179 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
180 } else {
181 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
182 "The popFirst method cannot be bound");
183 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
184 }
185 }
186
187 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self);
188 JSTaggedValue firstElement = deque->PopFirst();
189 return firstElement;
190 }
191
PopLast(EcmaRuntimeCallInfo * argv)192 JSTaggedValue ContainersDeque::PopLast(EcmaRuntimeCallInfo *argv)
193 {
194 ASSERT(argv != nullptr);
195 BUILTINS_API_TRACE(argv->GetThread(), Deque, PopLast);
196 JSThread *thread = argv->GetThread();
197 [[maybe_unused]] EcmaHandleScope handleScope(thread);
198 JSHandle<JSTaggedValue> self = GetThis(argv);
199
200 if (!self->IsJSAPIDeque()) {
201 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) {
202 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
203 } else {
204 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
205 "The popLast method cannot be bound");
206 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
207 }
208 }
209
210 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self);
211 JSTaggedValue lastElement = deque->PopLast();
212 return lastElement;
213 }
214
ForEach(EcmaRuntimeCallInfo * argv)215 JSTaggedValue ContainersDeque::ForEach(EcmaRuntimeCallInfo *argv)
216 {
217 ASSERT(argv != nullptr);
218 BUILTINS_API_TRACE(argv->GetThread(), Deque, ForEach);
219 JSThread *thread = argv->GetThread();
220 [[maybe_unused]] EcmaHandleScope handleScope(thread);
221
222 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
223 if (!thisHandle->IsJSAPIDeque()) {
224 if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIDeque()) {
225 thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget());
226 } else {
227 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
228 "The forEach method cannot be bound");
229 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
230 }
231 }
232
233 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
234 if (!callbackFnHandle->IsCallable()) {
235 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle);
236 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
237 CString errorMsg =
238 "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result);
239 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
240 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
241 }
242
243 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(thisHandle);
244 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
245
246 uint32_t first = deque->GetFirst();
247 uint32_t last = deque->GetLast();
248
249 JSHandle<TaggedArray> elements(thread, deque->GetElements());
250 uint32_t capacity = elements->GetLength();
251 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
252 uint32_t index = 0;
253 while (first != last) {
254 JSTaggedValue kValue = deque->Get(index);
255 EcmaRuntimeCallInfo *info =
256 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, 3); // 3:three args
257 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
258 info->SetCallArg(kValue, JSTaggedValue(index), thisHandle.GetTaggedValue());
259 JSTaggedValue funcResult = JSFunction::Call(info);
260 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
261 ASSERT(capacity != 0);
262 first = (first + 1) % capacity;
263 index = index + 1;
264 }
265
266 return JSTaggedValue::Undefined();
267 }
268
GetIteratorObj(EcmaRuntimeCallInfo * argv)269 JSTaggedValue ContainersDeque::GetIteratorObj(EcmaRuntimeCallInfo *argv)
270 {
271 ASSERT(argv != nullptr);
272 BUILTINS_API_TRACE(argv->GetThread(), Deque, GetIteratorObj);
273 JSThread *thread = argv->GetThread();
274 [[maybe_unused]] EcmaHandleScope handleScope(thread);
275
276 JSHandle<JSTaggedValue> self = GetThis(argv);
277
278 if (!self->IsJSAPIDeque()) {
279 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) {
280 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
281 } else {
282 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
283 "The Symbol.iterator method cannot be bound");
284 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
285 }
286 }
287
288 JSTaggedValue values = JSAPIDeque::GetIteratorObj(thread, JSHandle<JSAPIDeque>::Cast(self));
289
290 return values;
291 }
292
GetSize(EcmaRuntimeCallInfo * argv)293 JSTaggedValue ContainersDeque::GetSize(EcmaRuntimeCallInfo *argv)
294 {
295 ASSERT(argv != nullptr);
296 BUILTINS_API_TRACE(argv->GetThread(), Deque, GetSize);
297 JSThread *thread = argv->GetThread();
298 JSHandle<JSTaggedValue> self = GetThis(argv);
299
300 if (!self->IsJSAPIDeque()) {
301 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) {
302 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
303 } else {
304 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
305 "The getLength method cannot be bound");
306 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
307 }
308 }
309
310 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self);
311 uint32_t length = deque->GetSize();
312
313 return JSTaggedValue(length);
314 }
315 } // namespace panda::ecmascript::containers
316