1 /*
2 * Copyright (c) 2021 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_treeset.h"
17 #include "ecmascript/ecma_vm.h"
18 #include "ecmascript/internal_call_params.h"
19 #include "ecmascript/js_api_tree_set.h"
20 #include "ecmascript/js_api_tree_set_iterator.h"
21 #include "ecmascript/object_factory.h"
22 #include "ecmascript/tagged_array-inl.h"
23 #include "ecmascript/tagged_tree-inl.h"
24
25 namespace panda::ecmascript::containers {
TreeSetConstructor(EcmaRuntimeCallInfo * argv)26 JSTaggedValue ContainersTreeSet::TreeSetConstructor(EcmaRuntimeCallInfo *argv)
27 {
28 ASSERT(argv);
29 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Constructor);
30 JSThread *thread = argv->GetThread();
31 [[maybe_unused]] EcmaHandleScope handleScope(thread);
32 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
33
34 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
35 if (newTarget->IsUndefined()) {
36 THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception());
37 }
38 // new TreeSet
39 JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
40 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
41 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
42
43 // Set set’s internal slot with a new empty List.
44 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(obj);
45 JSTaggedValue internal = TaggedTreeSet::Create(thread);
46 set->SetTreeSet(thread, internal);
47
48 // If comparefn was supplied, let compare be comparefn; else let compare be hole.
49 JSHandle<JSTaggedValue> compareFn(GetCallArg(argv, 0));
50 if (compareFn->IsUndefined() || compareFn->IsNull()) {
51 return set.GetTaggedValue();
52 }
53 if (!compareFn->IsCallable()) {
54 THROW_TYPE_ERROR_AND_RETURN(thread, "comparefn is not Callable", JSTaggedValue::Exception());
55 }
56
57 TaggedTreeSet::Cast(internal.GetTaggedObject())->SetCompare(thread, compareFn.GetTaggedValue());
58 return set.GetTaggedValue();
59 }
60
Add(EcmaRuntimeCallInfo * argv)61 JSTaggedValue ContainersTreeSet::Add(EcmaRuntimeCallInfo *argv)
62 {
63 ASSERT(argv);
64 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Add);
65 JSThread *thread = argv->GetThread();
66 [[maybe_unused]] EcmaHandleScope handleScope(thread);
67 // get and check this set
68 JSHandle<JSTaggedValue> self = GetThis(argv);
69 if (!self->IsJSAPITreeSet()) {
70 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
71 }
72
73 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
74 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self);
75 JSAPITreeSet::Add(thread, set, value);
76 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
77 return JSTaggedValue::True();
78 }
79
Remove(EcmaRuntimeCallInfo * argv)80 JSTaggedValue ContainersTreeSet::Remove(EcmaRuntimeCallInfo *argv)
81 {
82 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Remove);
83 JSThread *thread = argv->GetThread();
84 [[maybe_unused]] EcmaHandleScope handleScope(thread);
85 // get and check this set
86 JSHandle<JSTaggedValue> self = GetThis(argv);
87 if (!self->IsJSAPITreeSet()) {
88 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
89 }
90
91 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self);
92 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
93 return GetTaggedBoolean(JSAPITreeSet::Delete(thread, set, key));
94 }
95
Has(EcmaRuntimeCallInfo * argv)96 JSTaggedValue ContainersTreeSet::Has(EcmaRuntimeCallInfo *argv)
97 {
98 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Has);
99 JSThread *thread = argv->GetThread();
100 [[maybe_unused]] EcmaHandleScope handleScope(thread);
101 // get and check this set
102 JSHandle<JSTaggedValue> self(GetThis(argv));
103 if (!self->IsJSAPITreeSet()) {
104 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
105 }
106
107 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
108 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self);
109
110 bool flag = JSAPITreeSet::Has(thread, JSHandle<JSAPITreeSet>::Cast(set), key);
111 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
112 return GetTaggedBoolean(flag);
113 }
114
GetFirstValue(EcmaRuntimeCallInfo * argv)115 JSTaggedValue ContainersTreeSet::GetFirstValue(EcmaRuntimeCallInfo *argv)
116 {
117 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetFirstValue);
118 JSThread *thread = argv->GetThread();
119 [[maybe_unused]] EcmaHandleScope handleScope(thread);
120 // get and check this set
121 JSHandle<JSTaggedValue> self(GetThis(argv));
122 if (!self->IsJSAPITreeSet()) {
123 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
124 }
125
126 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self);
127 return TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())->GetFirstKey();
128 }
129
GetLastValue(EcmaRuntimeCallInfo * argv)130 JSTaggedValue ContainersTreeSet::GetLastValue(EcmaRuntimeCallInfo *argv)
131 {
132 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetLastValue);
133 JSThread *thread = argv->GetThread();
134 [[maybe_unused]] EcmaHandleScope handleScope(thread);
135 // get and check this set
136 JSHandle<JSTaggedValue> self(GetThis(argv));
137 if (!self->IsJSAPITreeSet()) {
138 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
139 }
140
141 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self);
142 return TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())->GetLastKey();
143 }
144
Clear(EcmaRuntimeCallInfo * argv)145 JSTaggedValue ContainersTreeSet::Clear(EcmaRuntimeCallInfo *argv)
146 {
147 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Clear);
148 JSThread *thread = argv->GetThread();
149 [[maybe_unused]] EcmaHandleScope handleScope(thread);
150 // get and check this set
151 JSHandle<JSTaggedValue> self(GetThis(argv));
152 if (!self->IsJSAPITreeSet()) {
153 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
154 }
155
156 JSAPITreeSet::Clear(thread, JSHandle<JSAPITreeSet>::Cast(self));
157 return JSTaggedValue::Undefined();
158 }
159
GetLowerValue(EcmaRuntimeCallInfo * argv)160 JSTaggedValue ContainersTreeSet::GetLowerValue(EcmaRuntimeCallInfo *argv)
161 {
162 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetLowerValue);
163 JSThread *thread = argv->GetThread();
164 [[maybe_unused]] EcmaHandleScope handleScope(thread);
165 // get and check this set
166 JSHandle<JSTaggedValue> self(GetThis(argv));
167 if (!self->IsJSAPITreeSet()) {
168 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
169 }
170
171 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self);
172 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
173
174 JSHandle<TaggedTreeSet> tset(thread, set->GetTreeSet());
175 return TaggedTreeSet::GetLowerKey(thread, tset, key);
176 }
177
GetHigherValue(EcmaRuntimeCallInfo * argv)178 JSTaggedValue ContainersTreeSet::GetHigherValue(EcmaRuntimeCallInfo *argv)
179 {
180 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetHigherValue);
181 JSThread *thread = argv->GetThread();
182 [[maybe_unused]] EcmaHandleScope handleScope(thread);
183 // get and check this set
184 JSHandle<JSTaggedValue> self(GetThis(argv));
185 if (!self->IsJSAPITreeSet()) {
186 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
187 }
188
189 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self);
190 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
191 if (!key->IsString() && !key->IsNumber()) {
192 THROW_TYPE_ERROR_AND_RETURN(thread, "Incorrect key parameters", JSTaggedValue::Exception());
193 }
194 JSHandle<TaggedTreeSet> tset(thread, set->GetTreeSet());
195 return TaggedTreeSet::GetHigherKey(thread, tset, key);
196 }
197
PopFirst(EcmaRuntimeCallInfo * argv)198 JSTaggedValue ContainersTreeSet::PopFirst(EcmaRuntimeCallInfo *argv)
199 {
200 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, PopFirst);
201 JSThread *thread = argv->GetThread();
202 [[maybe_unused]] EcmaHandleScope handleScope(thread);
203 // get and check this set
204 JSHandle<JSTaggedValue> self(GetThis(argv));
205 if (!self->IsJSAPITreeSet()) {
206 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
207 }
208
209 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self);
210 return JSAPITreeSet::PopFirst(thread, set);
211 }
212
PopLast(EcmaRuntimeCallInfo * argv)213 JSTaggedValue ContainersTreeSet::PopLast(EcmaRuntimeCallInfo *argv)
214 {
215 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, PopLast);
216 JSThread *thread = argv->GetThread();
217 [[maybe_unused]] EcmaHandleScope handleScope(thread);
218 // get and check this set
219 JSHandle<JSTaggedValue> self(GetThis(argv));
220 if (!self->IsJSAPITreeSet()) {
221 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
222 }
223
224 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self);
225 return JSAPITreeSet::PopLast(thread, set);
226 }
227
IsEmpty(EcmaRuntimeCallInfo * argv)228 JSTaggedValue ContainersTreeSet::IsEmpty(EcmaRuntimeCallInfo *argv)
229 {
230 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, IsEmpty);
231 JSThread *thread = argv->GetThread();
232 [[maybe_unused]] EcmaHandleScope handleScope(thread);
233 // get and check this set
234 JSHandle<JSTaggedValue> self = GetThis(argv);
235 if (!self->IsJSAPITreeSet()) {
236 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
237 }
238 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self);
239 return GetTaggedBoolean(set->GetSize() == 0);
240 }
241
Values(EcmaRuntimeCallInfo * argv)242 JSTaggedValue ContainersTreeSet::Values(EcmaRuntimeCallInfo *argv)
243 {
244 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Values);
245 JSThread *thread = argv->GetThread();
246 [[maybe_unused]] EcmaHandleScope handleScope(thread);
247 JSHandle<JSTaggedValue> self = GetThis(argv);
248 JSHandle<JSTaggedValue> iter = JSAPITreeSetIterator::CreateTreeSetIterator(thread, self, IterationKind::KEY);
249 return iter.GetTaggedValue();
250 }
251
Entries(EcmaRuntimeCallInfo * argv)252 JSTaggedValue ContainersTreeSet::Entries(EcmaRuntimeCallInfo *argv)
253 {
254 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Entries);
255 JSThread *thread = argv->GetThread();
256 [[maybe_unused]] EcmaHandleScope handleScope(thread);
257 JSHandle<JSTaggedValue> self = GetThis(argv);
258 JSHandle<JSTaggedValue> iter =
259 JSAPITreeSetIterator::CreateTreeSetIterator(thread, self, IterationKind::KEY_AND_VALUE);
260 return iter.GetTaggedValue();
261 }
262
ForEach(EcmaRuntimeCallInfo * argv)263 JSTaggedValue ContainersTreeSet::ForEach(EcmaRuntimeCallInfo *argv)
264 {
265 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, ForEach);
266 JSThread *thread = argv->GetThread();
267 [[maybe_unused]] EcmaHandleScope handleScope(thread);
268
269 // get and check TreeSet object
270 JSHandle<JSTaggedValue> self = GetThis(argv);
271 if (!self->IsJSAPITreeSet()) {
272 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
273 }
274
275 // get and check callback function
276 JSHandle<JSTaggedValue> func(GetCallArg(argv, 0));
277 if (!func->IsCallable()) {
278 THROW_TYPE_ERROR_AND_RETURN(thread, "The first arg is not Callable", JSTaggedValue::Exception());
279 }
280
281 // If thisArg was supplied, let T be thisArg; else let T be undefined.
282 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 1);
283 JSHandle<JSAPITreeSet> tset = JSHandle<JSAPITreeSet>::Cast(self);
284 JSMutableHandle<TaggedTreeSet> iteratedSet(thread, tset->GetTreeSet());
285 int elements = iteratedSet->NumberOfElements();
286 JSMutableHandle<TaggedArray> entries(TaggedTreeSet::GetArrayFromSet(thread, iteratedSet));
287
288 int index = 0;
289 int length = entries->GetLength();
290 InternalCallParams *arguments = thread->GetInternalCallParams();
291 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
292 while (index < elements) {
293 int entriesIndex = entries->Get(index).GetInt();
294 key.Update(iteratedSet->GetKey(entriesIndex));
295
296 // Let funcResult be Call(callbackfn, T, «e, e, S»).
297 arguments->MakeArgv(key, key, self);
298 JSTaggedValue ret = JSFunction::Call(thread, func, thisArg, 3, arguments->GetArgv()); // 3: three args
299 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret);
300
301 // check entries should be update, size will be update by set add and remove.
302 if (tset->GetSize() != length) {
303 iteratedSet.Update(tset->GetTreeSet());
304 entries.Update(TaggedTreeSet::GetArrayFromSet(thread, iteratedSet).GetTaggedValue());
305 elements = iteratedSet->NumberOfElements();
306 length = entries->GetLength();
307 }
308 index++;
309 }
310 return JSTaggedValue::Undefined();
311 }
312
GetLength(EcmaRuntimeCallInfo * argv)313 JSTaggedValue ContainersTreeSet::GetLength(EcmaRuntimeCallInfo *argv)
314 {
315 ASSERT(argv);
316 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetLength);
317 JSThread *thread = argv->GetThread();
318 [[maybe_unused]] EcmaHandleScope handleScope(thread);
319 // get and check this set
320 JSHandle<JSTaggedValue> self(GetThis(argv));
321 if (!self->IsJSAPITreeSet()) {
322 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception());
323 }
324
325 int count = JSHandle<JSAPITreeSet>::Cast(self)->GetSize();
326 return JSTaggedValue(count);
327 }
328 } // namespace panda::ecmascript::containers
329