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