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_queue.h"
17 
18 #include "ecmascript/containers/containers_errors.h"
19 #include "ecmascript/interpreter/interpreter.h"
20 #include "ecmascript/js_api/js_api_queue.h"
21 #include "ecmascript/js_function.h"
22 
23 namespace panda::ecmascript::containers {
QueueConstructor(EcmaRuntimeCallInfo *argv)24 JSTaggedValue ContainersQueue::QueueConstructor(EcmaRuntimeCallInfo *argv)
25 {
26     ASSERT(argv);
27     BUILTINS_API_TRACE(argv->GetThread(), Queue, 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 Queue'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     JSHandle<TaggedArray> newTaggedArray = factory->NewTaggedArray(JSAPIQueue::DEFAULT_CAPACITY_LENGTH);
42     obj->SetElements(thread, newTaggedArray);
43 
44     return obj.GetTaggedValue();
45 }
46 
Add(EcmaRuntimeCallInfo *argv)47 JSTaggedValue ContainersQueue::Add(EcmaRuntimeCallInfo *argv)
48 {
49     ASSERT(argv);
50     BUILTINS_API_TRACE(argv->GetThread(), Queue, Add);
51     JSThread *thread = argv->GetThread();
52     [[maybe_unused]] EcmaHandleScope handleScope(thread);
53     JSHandle<JSTaggedValue> self = GetThis(argv);
54 
55     if (!self->IsJSAPIQueue()) {
56         if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIQueue()) {
57             self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
58         } else {
59             JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
60                                                                 "The add method cannot be bound");
61             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
62         }
63     }
64 
65     JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
66     JSAPIQueue::Add(thread, JSHandle<JSAPIQueue>::Cast(self), value);
67     return JSTaggedValue::True();
68 }
69 
GetFirst(EcmaRuntimeCallInfo *argv)70 JSTaggedValue ContainersQueue::GetFirst(EcmaRuntimeCallInfo *argv)
71 {
72     ASSERT(argv);
73     BUILTINS_API_TRACE(argv->GetThread(), Queue, Add);
74     JSThread *thread = argv->GetThread();
75     [[maybe_unused]] EcmaHandleScope handleScope(thread);
76     JSHandle<JSTaggedValue> self = GetThis(argv);
77 
78     if (!self->IsJSAPIQueue()) {
79         if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIQueue()) {
80             self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
81         } else {
82             JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
83                                                                 "The getFirst method cannot be bound");
84             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
85         }
86     }
87 
88     JSTaggedValue value = JSAPIQueue::GetFirst(thread, JSHandle<JSAPIQueue>::Cast(self));
89     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False());
90     return value;
91 }
92 
Pop(EcmaRuntimeCallInfo *argv)93 JSTaggedValue ContainersQueue::Pop(EcmaRuntimeCallInfo *argv)
94 {
95     ASSERT(argv);
96     BUILTINS_API_TRACE(argv->GetThread(), Queue, Add);
97     JSThread *thread = argv->GetThread();
98     [[maybe_unused]] EcmaHandleScope handleScope(thread);
99     JSHandle<JSTaggedValue> self = GetThis(argv);
100 
101     if (!self->IsJSAPIQueue()) {
102         if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIQueue()) {
103             self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
104         } else {
105             JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
106                                                                 "The pop method cannot be bound");
107             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
108         }
109     }
110 
111     JSTaggedValue value = JSAPIQueue::Pop(thread, JSHandle<JSAPIQueue>::Cast(self));
112     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False());
113     return value;
114 }
115 
ForEach(EcmaRuntimeCallInfo *argv)116 JSTaggedValue ContainersQueue::ForEach(EcmaRuntimeCallInfo *argv)
117 {
118     ASSERT(argv);
119     BUILTINS_API_TRACE(argv->GetThread(), Queue, ForEach);
120     JSThread *thread = argv->GetThread();
121     [[maybe_unused]] EcmaHandleScope handleScope(thread);
122 
123     // Let O be ToObject(this value).
124     JSHandle<JSTaggedValue> thisHandle = GetThis(argv); // JSAPIQueue
125     if (!thisHandle->IsJSAPIQueue()) {
126         if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIQueue()) {
127             thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget());
128         } else {
129             JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
130                                                                 "The forEach method cannot be bound");
131             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
132         }
133     }
134 
135     JSHandle<JSAPIQueue> queue = JSHandle<JSAPIQueue>::Cast(thisHandle);
136     // Let len be ToLength(Get(O, "length")).
137     uint32_t len = JSAPIQueue::GetArrayLength(thread, queue);
138     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
139 
140     JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
141     // If IsCallable(callbackfn) is false, throw a TypeError exception.
142     if (!callbackFnHandle->IsCallable()) {
143         JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue());
144         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
145         CString errorMsg =
146             "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result);
147         JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
148         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
149     }
150     // If thisArg was supplied, let T be thisArg; else let T be undefined.
151     JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
152 
153     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
154     uint32_t index = 0;
155     uint32_t k = 0;
156     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
157     while (k < len) {
158         JSHandle<JSTaggedValue> kValue =
159             JSHandle<JSTaggedValue>(thread, queue->Get(thread, index));
160         index = queue->GetNextPosition(index);
161         key.Update(JSTaggedValue(k));
162         const uint32_t argsLength = 3;
163         EcmaRuntimeCallInfo *info =
164             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
165         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
166         info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisHandle.GetTaggedValue());
167         JSTaggedValue funcResult = JSFunction::Call(info);
168         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
169         k++;
170     }
171     return JSTaggedValue::Undefined();
172 }
173 
GetIteratorObj(EcmaRuntimeCallInfo *argv)174 JSTaggedValue ContainersQueue::GetIteratorObj(EcmaRuntimeCallInfo *argv)
175 {
176     ASSERT(argv);
177     BUILTINS_API_TRACE(argv->GetThread(), Queue, GetIteratorObj);
178     JSThread *thread = argv->GetThread();
179     [[maybe_unused]] EcmaHandleScope handleScope(thread);
180     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
181     JSHandle<JSTaggedValue> self = GetThis(argv);
182     if (!self->IsJSAPIQueue()) {
183         if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIQueue()) {
184             self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
185         } else {
186             JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
187                                                                 "The Symbol.iterator method cannot be bound");
188             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
189         }
190     }
191     JSHandle<JSAPIQueueIterator> iter(factory->NewJSAPIQueueIterator(JSHandle<JSAPIQueue>::Cast(self)));
192     return iter.GetTaggedValue();
193 }
194 
GetSize(EcmaRuntimeCallInfo *argv)195 JSTaggedValue ContainersQueue::GetSize(EcmaRuntimeCallInfo *argv)
196 {
197     ASSERT(argv);
198     BUILTINS_API_TRACE(argv->GetThread(), Queue, GetSize);
199     JSThread *thread = argv->GetThread();
200     [[maybe_unused]] EcmaHandleScope handleScope(thread);
201     JSHandle<JSTaggedValue> self = GetThis(argv);
202 
203     if (!self->IsJSAPIQueue()) {
204         if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIQueue()) {
205             self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
206         } else {
207             JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
208                                                                 "The getLength method cannot be bound");
209             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
210         }
211     }
212 
213     uint32_t length = JSHandle<JSAPIQueue>::Cast(self)->GetSize();
214     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
215 
216     return JSTaggedValue(length);
217 }
218 } // namespace panda::ecmascript::containers
219