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 "containers_hashset.h"
17
18 #include "ecmascript/containers/containers_errors.h"
19 #include "ecmascript/interpreter/interpreter.h"
20 #include "ecmascript/js_api/js_api_hashset.h"
21 #include "ecmascript/js_api/js_api_hashset_iterator.h"
22 #include "ecmascript/js_function.h"
23
24 namespace panda::ecmascript::containers {
HashSetConstructor(EcmaRuntimeCallInfo *argv)25 JSTaggedValue ContainersHashSet::HashSetConstructor(EcmaRuntimeCallInfo *argv)
26 {
27 ASSERT(argv != nullptr);
28 JSThread *thread = argv->GetThread();
29 BUILTINS_API_TRACE(thread, HashSet, Constructor);
30 [[maybe_unused]] EcmaHandleScope handleScope(thread);
31 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
32 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
33 if (newTarget->IsUndefined()) {
34 JSTaggedValue error =
35 ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR,
36 "The HashSet's constructor cannot be directly invoked");
37 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
38 }
39
40 JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
41 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
42 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
43
44 JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(obj);
45 JSTaggedValue hashSetArray = TaggedHashArray::Create(thread);
46 hashSet->SetTable(thread, hashSetArray);
47 hashSet->SetSize(0);
48
49 return hashSet.GetTaggedValue();
50 }
51
GetIteratorObj(EcmaRuntimeCallInfo *argv)52 JSTaggedValue ContainersHashSet::GetIteratorObj(EcmaRuntimeCallInfo *argv)
53 {
54 ASSERT(argv != nullptr);
55 JSThread *thread = argv->GetThread();
56 BUILTINS_API_TRACE(thread, HashSet, GetIteratorObj);
57 [[maybe_unused]] EcmaHandleScope handleScope(thread);
58 JSHandle<JSTaggedValue> self = GetThis(argv);
59 if (!self->IsJSAPIHashSet()) {
60 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) {
61 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
62 } else {
63 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
64 "The Symbol.iterator method cannot be bound");
65 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
66 }
67 }
68 JSHandle<JSTaggedValue> iter = JSAPIHashSetIterator::CreateHashSetIterator(thread, self, IterationKind::VALUE);
69 return iter.GetTaggedValue();
70 }
71
Values(EcmaRuntimeCallInfo *argv)72 JSTaggedValue ContainersHashSet::Values(EcmaRuntimeCallInfo *argv)
73 {
74 ASSERT(argv != nullptr);
75 JSThread *thread = argv->GetThread();
76 BUILTINS_API_TRACE(thread, HashSet, Values);
77 [[maybe_unused]] EcmaHandleScope handleScope(thread);
78 JSHandle<JSTaggedValue> self = GetThis(argv);
79 if (!self->IsJSAPIHashSet()) {
80 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) {
81 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
82 } else {
83 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
84 "The values method cannot be bound");
85 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
86 }
87 }
88 JSHandle<JSTaggedValue> iter = JSAPIHashSetIterator::CreateHashSetIterator(thread, self, IterationKind::VALUE);
89 return iter.GetTaggedValue();
90 }
91
Entries(EcmaRuntimeCallInfo *argv)92 JSTaggedValue ContainersHashSet::Entries(EcmaRuntimeCallInfo *argv)
93 {
94 ASSERT(argv != nullptr);
95 JSThread *thread = argv->GetThread();
96 BUILTINS_API_TRACE(thread, HashSet, Entries);
97 [[maybe_unused]] EcmaHandleScope handleScope(thread);
98 JSHandle<JSTaggedValue> self = GetThis(argv);
99 if (!self->IsJSAPIHashSet()) {
100 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) {
101 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
102 } else {
103 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
104 "The entries method cannot be bound");
105 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
106 }
107 }
108 JSHandle<JSTaggedValue> iter =
109 JSAPIHashSetIterator::CreateHashSetIterator(thread, self, IterationKind::KEY_AND_VALUE);
110 return iter.GetTaggedValue();
111 }
112
Add(EcmaRuntimeCallInfo *argv)113 JSTaggedValue ContainersHashSet::Add(EcmaRuntimeCallInfo *argv)
114 {
115 ASSERT(argv != nullptr);
116 JSThread *thread = argv->GetThread();
117 BUILTINS_API_TRACE(thread, HashSet, Add);
118 [[maybe_unused]] EcmaHandleScope handleScope(thread);
119 JSHandle<JSTaggedValue> self = GetThis(argv);
120
121 if (!self->IsJSAPIHashSet()) {
122 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) {
123 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
124 } else {
125 JSTaggedValue error = ContainerError::BusinessError(thread, BIND_ERROR,
126 "The add method cannot be bound");
127 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
128 }
129 }
130
131 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
132 JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(self);
133 JSAPIHashSet::Add(thread, hashSet, value);
134 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
135 return JSTaggedValue::True();
136 }
137
Remove(EcmaRuntimeCallInfo *argv)138 JSTaggedValue ContainersHashSet::Remove(EcmaRuntimeCallInfo *argv)
139 {
140 ASSERT(argv != nullptr);
141 JSThread *thread = argv->GetThread();
142 BUILTINS_API_TRACE(thread, HashSet, Remove);
143 [[maybe_unused]] EcmaHandleScope handleScope(thread);
144 JSHandle<JSTaggedValue> self = GetThis(argv);
145
146 if (!self->IsJSAPIHashSet()) {
147 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) {
148 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
149 } else {
150 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
151 "The remove method cannot be bound");
152 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
153 }
154 }
155
156 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
157 JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(self);
158 return JSAPIHashSet::Remove(thread, hashSet, key.GetTaggedValue());
159 }
160
Has(EcmaRuntimeCallInfo *argv)161 JSTaggedValue ContainersHashSet::Has(EcmaRuntimeCallInfo *argv)
162 {
163 ASSERT(argv != nullptr);
164 JSThread *thread = argv->GetThread();
165 BUILTINS_API_TRACE(thread, HashSet, Has);
166 [[maybe_unused]] EcmaHandleScope handleScope(thread);
167 JSHandle<JSTaggedValue> self = GetThis(argv);
168
169 if (!self->IsJSAPIHashSet()) {
170 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) {
171 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
172 } else {
173 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
174 "The has method cannot be bound");
175 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
176 }
177 }
178 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
179 JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::Cast(self);
180 return jsHashSet->Has(thread, value.GetTaggedValue());
181 }
182
Clear(EcmaRuntimeCallInfo *argv)183 JSTaggedValue ContainersHashSet::Clear(EcmaRuntimeCallInfo *argv)
184 {
185 ASSERT(argv != nullptr);
186 JSThread *thread = argv->GetThread();
187 BUILTINS_API_TRACE(thread, HashSet, Clear);
188 [[maybe_unused]] EcmaHandleScope handleScope(thread);
189 JSHandle<JSTaggedValue> self = GetThis(argv);
190
191 if (!self->IsJSAPIHashSet()) {
192 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) {
193 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
194 } else {
195 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
196 "The clear method cannot be bound");
197 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
198 }
199 }
200 JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::Cast(self);
201 jsHashSet->Clear(thread);
202 return JSTaggedValue::Undefined();
203 }
204
GetLength(EcmaRuntimeCallInfo *argv)205 JSTaggedValue ContainersHashSet::GetLength(EcmaRuntimeCallInfo *argv)
206 {
207 ASSERT(argv != nullptr);
208 JSThread *thread = argv->GetThread();
209 BUILTINS_API_TRACE(thread, HashSet, GetLength);
210 [[maybe_unused]] EcmaHandleScope handleScope(thread);
211 JSHandle<JSTaggedValue> self = GetThis(argv);
212
213 if (!self->IsJSAPIHashSet()) {
214 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) {
215 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
216 } else {
217 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
218 "The getLength method cannot be bound");
219 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
220 }
221 }
222
223 JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::Cast(self);
224 return jsHashSet->GetLength();
225 }
226
IsEmpty(EcmaRuntimeCallInfo *argv)227 JSTaggedValue ContainersHashSet::IsEmpty(EcmaRuntimeCallInfo *argv)
228 {
229 ASSERT(argv != nullptr);
230 JSThread *thread = argv->GetThread();
231 BUILTINS_API_TRACE(thread, HashSet, IsEmpty);
232 [[maybe_unused]] EcmaHandleScope handleScope(thread);
233 JSHandle<JSTaggedValue> self = GetThis(argv);
234
235 if (!self->IsJSAPIHashSet()) {
236 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) {
237 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget());
238 } else {
239 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
240 "The isEmpty method cannot be bound");
241 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
242 }
243 }
244 JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::Cast(self);
245 return jsHashSet->IsEmpty();
246 }
247
ForEach(EcmaRuntimeCallInfo *argv)248 JSTaggedValue ContainersHashSet::ForEach(EcmaRuntimeCallInfo *argv)
249 {
250 ASSERT(argv != nullptr);
251 JSThread *thread = argv->GetThread();
252 BUILTINS_API_TRACE(thread, HashSet, ForEach);
253 [[maybe_unused]] EcmaHandleScope handleScope(thread);
254 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
255 if (!thisHandle->IsJSAPIHashSet()) {
256 if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIHashSet()) {
257 thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget());
258 } else {
259 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
260 "The forEach method cannot be bound");
261 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
262 }
263 }
264 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
265 if (!callbackFnHandle->IsCallable()) {
266 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle);
267 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
268 CString errorMsg =
269 "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result);
270 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
271 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
272 }
273 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
274 JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(thisHandle);
275 JSHandle<TaggedHashArray> table(thread, hashSet->GetTable());
276 uint32_t len = table->GetLength();
277 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
278 JSMutableHandle<TaggedQueue> queue(thread, factory->NewTaggedQueue(0));
279 JSMutableHandle<TaggedNode> node(thread, JSTaggedValue::Undefined());
280 JSMutableHandle<JSTaggedValue> currentKey(thread, JSTaggedValue::Undefined());
281 uint32_t index = 0;
282 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
283 while (index < len) {
284 node.Update(TaggedHashArray::GetCurrentNode(thread, queue, table, index));
285 if (!node.GetTaggedValue().IsHole()) {
286 currentKey.Update(node->GetKey());
287 EcmaRuntimeCallInfo *info =
288 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle,
289 thisArgHandle, undefined, 3); // 3: three args
290 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
291 info->SetCallArg(currentKey.GetTaggedValue(), currentKey.GetTaggedValue(), thisHandle.GetTaggedValue());
292 JSTaggedValue funcResult = JSFunction::Call(info);
293 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
294 }
295 }
296 return JSTaggedValue::Undefined();
297 }
298 } // namespace panda::ecmascript::containers
299