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