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