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