• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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