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_stack.h"
17
18 #include "ecmascript/containers/containers_errors.h"
19 #include "ecmascript/ecma_vm.h"
20 #include "ecmascript/interpreter/interpreter.h"
21 #include "ecmascript/js_api/js_api_stack.h"
22 #include "ecmascript/js_api/js_api_stack_iterator.h"
23 #include "ecmascript/js_function.h"
24 #include "ecmascript/js_iterator.h"
25 #include "ecmascript/object_factory.h"
26 #include "ecmascript/tagged_array-inl.h"
27
28 namespace panda::ecmascript::containers {
StackConstructor(EcmaRuntimeCallInfo * argv)29 JSTaggedValue ContainersStack::StackConstructor(EcmaRuntimeCallInfo *argv)
30 {
31 ASSERT(argv != nullptr);
32 BUILTINS_API_TRACE(argv->GetThread(), Stack, Constructor);
33 JSThread *thread = argv->GetThread();
34 [[maybe_unused]] EcmaHandleScope handleScope(thread);
35 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
36 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
37 if (newTarget->IsUndefined()) {
38 JSTaggedValue error =
39 ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR,
40 "The List's constructor cannot be directly invoked");
41 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
42 }
43 JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
44 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
45 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
46
47 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(obj);
48 stack->SetTop(-1);
49
50 return obj.GetTaggedValue();
51 }
52
IsEmpty(EcmaRuntimeCallInfo * argv)53 JSTaggedValue ContainersStack::IsEmpty(EcmaRuntimeCallInfo *argv)
54 {
55 ASSERT(argv != nullptr);
56 BUILTINS_API_TRACE(argv->GetThread(), Stack, IsEmpty);
57 JSThread *thread = argv->GetThread();
58 [[maybe_unused]] EcmaHandleScope handleScope(thread);
59 JSHandle<JSTaggedValue> self = GetThis(argv);
60
61 if (!self->IsJSAPIStack()) {
62 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
63 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
64 } else {
65 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
66 "The isEmpty method cannot be bound");
67 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
68 }
69 }
70 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self);
71 bool judge = stack->Empty();
72
73 return GetTaggedBoolean(judge);
74 }
75
Push(EcmaRuntimeCallInfo * argv)76 JSTaggedValue ContainersStack::Push(EcmaRuntimeCallInfo *argv)
77 {
78 ASSERT(argv != nullptr);
79 BUILTINS_API_TRACE(argv->GetThread(), Stack, Push);
80 JSThread *thread = argv->GetThread();
81 [[maybe_unused]] EcmaHandleScope handleScope(thread);
82 JSHandle<JSTaggedValue> self = GetThis(argv);
83
84 if (!self->IsJSAPIStack()) {
85 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
86 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
87 } else {
88 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
89 "The push method cannot be bound");
90 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
91 }
92 }
93
94 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
95 JSTaggedValue jsValue = JSAPIStack::Push(thread, JSHandle<JSAPIStack>::Cast(self), value);
96 return jsValue;
97 }
98
Peek(EcmaRuntimeCallInfo * argv)99 JSTaggedValue ContainersStack::Peek(EcmaRuntimeCallInfo *argv)
100 {
101 ASSERT(argv != nullptr);
102 BUILTINS_API_TRACE(argv->GetThread(), Stack, Peek);
103 JSThread *thread = argv->GetThread();
104 [[maybe_unused]] EcmaHandleScope handleScope(thread);
105 JSHandle<JSTaggedValue> self = GetThis(argv);
106
107 if (!self->IsJSAPIStack()) {
108 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
109 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
110 } else {
111 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
112 "The peek method cannot be bound");
113 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
114 }
115 }
116
117 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self);
118 JSTaggedValue jsValue = stack->Peek();
119 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
120 return jsValue;
121 }
122
Locate(EcmaRuntimeCallInfo * argv)123 JSTaggedValue ContainersStack::Locate(EcmaRuntimeCallInfo *argv)
124 {
125 ASSERT(argv != nullptr);
126 BUILTINS_API_TRACE(argv->GetThread(), Stack, Locate);
127 JSThread *thread = argv->GetThread();
128 [[maybe_unused]] EcmaHandleScope handleScope(thread);
129 JSHandle<JSTaggedValue> self = GetThis(argv);
130
131 if (!self->IsJSAPIStack()) {
132 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
133 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
134 } else {
135 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
136 "The locate method cannot be bound");
137 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
138 }
139 }
140
141 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
142 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self);
143 int num = stack->Search(value);
144 return JSTaggedValue(num);
145 }
146
Pop(EcmaRuntimeCallInfo * argv)147 JSTaggedValue ContainersStack::Pop(EcmaRuntimeCallInfo *argv)
148 {
149 ASSERT(argv != nullptr);
150 BUILTINS_API_TRACE(argv->GetThread(), Stack, Pop);
151 JSThread *thread = argv->GetThread();
152 [[maybe_unused]] EcmaHandleScope handleScope(thread);
153 JSHandle<JSTaggedValue> self = GetThis(argv);
154
155 if (!self->IsJSAPIStack()) {
156 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
157 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
158 } else {
159 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
160 "The pop method cannot be bound");
161 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
162 }
163 }
164
165 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self);
166 JSTaggedValue jsValue = stack->Pop();
167 return jsValue;
168 }
169
ForEach(EcmaRuntimeCallInfo * argv)170 JSTaggedValue ContainersStack::ForEach(EcmaRuntimeCallInfo *argv)
171 {
172 ASSERT(argv != nullptr);
173 BUILTINS_API_TRACE(argv->GetThread(), Stack, ForEach);
174 JSThread *thread = argv->GetThread();
175 [[maybe_unused]] EcmaHandleScope handleScope(thread);
176
177 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
178 if (!thisHandle->IsJSAPIStack()) {
179 if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIStack()) {
180 thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget());
181 } else {
182 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
183 "The forEach method cannot be bound");
184 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
185 }
186 }
187
188 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(thisHandle);
189 uint32_t len = stack->GetSize();
190 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
191
192 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
193 if (!callbackFnHandle->IsCallable()) {
194 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue());
195 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
196 CString errorMsg =
197 "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result);
198 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
199 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
200 }
201
202 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
203 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
204 uint32_t k = 0;
205 while (k < len + 1) {
206 JSTaggedValue kValue = stack->Get(k);
207 EcmaRuntimeCallInfo *info =
208 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, 3); // 3:three args
209 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
210 info->SetCallArg(kValue, JSTaggedValue(k), thisHandle.GetTaggedValue());
211 JSTaggedValue funcResult = JSFunction::Call(info);
212 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
213 k++;
214 }
215 return JSTaggedValue::Undefined();
216 }
217
Iterator(EcmaRuntimeCallInfo * argv)218 JSTaggedValue ContainersStack::Iterator(EcmaRuntimeCallInfo *argv)
219 {
220 ASSERT(argv != nullptr);
221 BUILTINS_API_TRACE(argv->GetThread(), Stack, Iterator);
222 JSThread *thread = argv->GetThread();
223 [[maybe_unused]] EcmaHandleScope handleScope(thread);
224 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
225 JSHandle<JSTaggedValue> self = GetThis(argv);
226 if (!self->IsJSAPIStack()) {
227 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
228 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
229 } else {
230 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
231 "The Symbol.iterator method cannot be bound");
232 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
233 }
234 }
235 JSHandle<JSAPIStackIterator> iter(factory->NewJSAPIStackIterator(JSHandle<JSAPIStack>::Cast(self)));
236 return iter.GetTaggedValue();
237 }
238
GetLength(EcmaRuntimeCallInfo * argv)239 JSTaggedValue ContainersStack::GetLength(EcmaRuntimeCallInfo *argv)
240 {
241 ASSERT(argv != nullptr);
242 BUILTINS_API_TRACE(argv->GetThread(), Stack, GetLength);
243 JSThread *thread = argv->GetThread();
244 [[maybe_unused]] EcmaHandleScope handleScope(thread);
245 JSHandle<JSTaggedValue> self = GetThis(argv);
246
247 if (!self->IsJSAPIStack()) {
248 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) {
249 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
250 } else {
251 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
252 "The getLength method cannot be bound");
253 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
254 }
255 }
256
257 uint32_t len = (JSHandle<JSAPIStack>::Cast(self))->GetSize();
258 return JSTaggedValue(len + 1);
259 }
260 } // namespace panda::ecmascript::containers
261