1 /*
2  * Copyright (c) 2024 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/builtins/builtins_shared_set.h"
17 
18 #include "ecmascript/js_function.h"
19 #include "ecmascript/interpreter/interpreter.h"
20 
21 #include "ecmascript/linked_hash_table.h"
22 #include "ecmascript/shared_objects/concurrent_api_scope.h"
23 #include "ecmascript/shared_objects/js_shared_set_iterator.h"
24 
25 namespace panda::ecmascript::builtins {
Constructor(EcmaRuntimeCallInfo *argv)26 JSTaggedValue BuiltinsSharedSet::Constructor(EcmaRuntimeCallInfo *argv)
27 {
28     ASSERT(argv);
29     BUILTINS_API_TRACE(argv->GetThread(), SharedSet, Constructor);
30     JSThread *thread = argv->GetThread();
31     [[maybe_unused]] EcmaHandleScope handleScope(thread);
32     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
33     // 1. If NewTarget is undefined, throw exception
34     JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
35     if (newTarget->IsUndefined()) {
36         JSTaggedValue error = containers::ContainerError::BusinessError(
37             thread, containers::ErrorFlag::IS_NULL_ERROR, "The ArkTS Set's constructor cannot be directly invoked.");
38         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
39     }
40     // 2.Let set be OrdinaryCreateFromConstructor(NewTarget, "%SetPrototype%", «‍[[SetData]]» ).
41     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
42     ASSERT(constructor->IsJSSharedFunction() && constructor.GetTaggedValue().IsInSharedHeap());
43     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
44     // 3.returnIfAbrupt()
45     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
46     ASSERT(obj.GetTaggedValue().IsInSharedHeap());
47     JSHandle<JSSharedSet> set = JSHandle<JSSharedSet>::Cast(obj);
48     // 3.ReturnIfAbrupt(set).
49     // 4.Set set’s [[SetData]] internal slot to a new empty List.
50     JSHandle<LinkedHashSet> linkedSet = LinkedHashSet::Create(thread,
51         LinkedHashSet::MIN_CAPACITY, MemSpaceKind::SHARED);
52     set->SetLinkedSet(thread, linkedSet);
53     // add data into set from iterable
54     // 5.If iterable is not present, let iterable be undefined.
55     // 6.If iterable is either undefined or null, let iter be undefined.
56     JSHandle<JSTaggedValue> iterable(GetCallArg(argv, 0));
57     // 8.If iter is undefined, return set
58     if (iterable->IsUndefined() || iterable->IsNull()) {
59         return set.GetTaggedValue();
60     }
61     // Let adder be Get(set, "add").
62     JSHandle<JSTaggedValue> adderKey(thread->GlobalConstants()->GetHandledAddString());
63     JSHandle<JSTaggedValue> setHandle(set);
64     JSHandle<JSTaggedValue> adder = JSObject::GetProperty(thread, setHandle, adderKey).GetValue();
65     // ReturnIfAbrupt(adder).
66     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue());
67     // If IsCallable(adder) is false, throw a TypeError exception
68     if (!adder->IsCallable()) {
69         THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue());
70     }
71     // Let iter be GetIterator(iterable).
72     JSHandle<JSTaggedValue> iter(JSIterator::GetIterator(thread, iterable));
73     // ReturnIfAbrupt(iter).
74     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue());
75     // values in iterator_result may be a JSArray, values[0] = key values[1]=value, used valueIndex to get value from
76     // jsarray
77     JSHandle<JSTaggedValue> valueIndex(thread, JSTaggedValue(1));
78     JSHandle<JSTaggedValue> next = JSIterator::IteratorStep(thread, iter);
79     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue());
80     while (!next->IsFalse()) {
81         // Let nextValue be IteratorValue(next).
82         JSHandle<JSTaggedValue> nextValue(JSIterator::IteratorValue(thread, next));
83         // ReturnIfAbrupt(nextValue).
84         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue());
85         JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
86         EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, adder, setHandle, undefined, 1);
87         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue());
88         info->SetCallArg(nextValue.GetTaggedValue());
89         if (nextValue->IsArray(thread)) {
90             auto prop = JSTaggedValue::GetProperty(thread, nextValue, valueIndex).GetValue();
91             info->SetCallArg(prop.GetTaggedValue());
92         }
93         JSFunction::Call(info);
94         // Let status be Call(adder, set, «nextValue.[[value]]»).
95         if (thread->HasPendingException()) {
96             return JSIterator::IteratorCloseAndReturn(thread, iter);
97         }
98         // Let next be IteratorStep(iter).
99         next = JSIterator::IteratorStep(thread, iter);
100         // ReturnIfAbrupt(next).
101         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue());
102     }
103     return set.GetTaggedValue();
104 }
105 
Add(EcmaRuntimeCallInfo *argv)106 JSTaggedValue BuiltinsSharedSet::Add(EcmaRuntimeCallInfo *argv)
107 {
108     ASSERT(argv);
109     BUILTINS_API_TRACE(argv->GetThread(), SharedSet, Add);
110     JSThread *thread = argv->GetThread();
111     [[maybe_unused]] EcmaHandleScope handleScope(thread);
112     JSHandle<JSTaggedValue> self = GetThis(argv);
113     if (!self->IsJSSharedSet()) {
114         auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
115                                                                "The add method cannot be bound.");
116         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
117     }
118     JSHandle<JSTaggedValue> value(GetCallArg(argv, 0));
119     JSHandle<JSSharedSet> set(self);
120     JSSharedSet::Add(thread, set, value);
121     return set.GetTaggedValue();
122 }
123 
Clear(EcmaRuntimeCallInfo *argv)124 JSTaggedValue BuiltinsSharedSet::Clear(EcmaRuntimeCallInfo *argv)
125 {
126     ASSERT(argv);
127     BUILTINS_API_TRACE(argv->GetThread(), SharedSet, Clear);
128     JSThread *thread = argv->GetThread();
129     [[maybe_unused]] EcmaHandleScope handleScope(thread);
130     JSHandle<JSTaggedValue> self = GetThis(argv);
131     if (!self->IsJSSharedSet()) {
132         auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
133                                                                "The clear method cannot be bound.");
134         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
135     }
136     JSHandle<JSSharedSet> set(self);
137     JSSharedSet::Clear(thread, set);
138     return JSTaggedValue::Undefined();
139 }
140 
Delete(EcmaRuntimeCallInfo *argv)141 JSTaggedValue BuiltinsSharedSet::Delete(EcmaRuntimeCallInfo *argv)
142 {
143     ASSERT(argv);
144     BUILTINS_API_TRACE(argv->GetThread(), SharedSet, Delete);
145     JSThread *thread = argv->GetThread();
146     [[maybe_unused]] EcmaHandleScope handleScope(thread);
147     JSHandle<JSTaggedValue> self = GetThis(argv);
148     if (!self->IsJSSharedSet()) {
149         auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
150                                                                "The delete method cannot be bound.");
151         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
152     }
153     JSHandle<JSSharedSet> set(self);
154     JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
155     bool flag = JSSharedSet::Delete(thread, set, value);
156     return GetTaggedBoolean(flag);
157 }
158 
Has(EcmaRuntimeCallInfo *argv)159 JSTaggedValue BuiltinsSharedSet::Has(EcmaRuntimeCallInfo *argv)
160 {
161     ASSERT(argv);
162     BUILTINS_API_TRACE(argv->GetThread(), SharedSet, Has);
163     JSThread *thread = argv->GetThread();
164     [[maybe_unused]] EcmaHandleScope handleScope(thread);
165     JSHandle<JSTaggedValue> self = GetThis(argv);
166     if (!self->IsJSSharedSet()) {
167         auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
168                                                                "The has method cannot be bound.");
169         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
170     }
171     JSHandle<JSSharedSet> set(self);
172     JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
173     bool flag = JSSharedSet::Has(thread, set, value.GetTaggedValue());
174     return GetTaggedBoolean(flag);
175 }
176 
ForEach(EcmaRuntimeCallInfo *argv)177 JSTaggedValue BuiltinsSharedSet::ForEach(EcmaRuntimeCallInfo *argv)
178 {
179     JSThread *thread = argv->GetThread();
180     BUILTINS_API_TRACE(thread, SharedSet, ForEach);
181     [[maybe_unused]] EcmaHandleScope handleScope(thread);
182     JSHandle<JSTaggedValue> self = GetThis(argv);
183     if (!self->IsJSSharedSet()) {
184         auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
185                                                                "The forEach method cannot be bound.");
186         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
187     }
188     [[maybe_unused]] ConcurrentApiScope<JSSharedSet> scope(thread, self);
189     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
190 
191     JSHandle<JSSharedSet> set(self);
192     JSHandle<JSTaggedValue> func(GetCallArg(argv, 0));
193     if (!func->IsCallable()) {
194         THROW_TYPE_ERROR_AND_RETURN(thread, "callback is not callable", JSTaggedValue::Exception());
195     }
196     JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 1);
197     JSMutableHandle<LinkedHashSet> hashSet(thread, set->GetLinkedSet());
198     const uint32_t argsLength = 3;
199     int index = 0;
200     int totalElements = hashSet->NumberOfElements() + hashSet->NumberOfDeletedElements();
201     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
202     // Repeat for each e that is an element of entries, in original insertion order
203     while (index < totalElements) {
204         JSHandle<JSTaggedValue> key(thread, hashSet->GetKey(index++));
205         if (!key->IsHole()) {
206             EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(
207                 thread, func, thisArg, undefined, argsLength);
208             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
209             info->SetCallArg(key.GetTaggedValue(), key.GetTaggedValue(), set.GetTaggedValue());
210             JSTaggedValue ret = JSFunction::Call(info);
211             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret);
212         }
213     }
214     return JSTaggedValue::Undefined();
215 }
216 
Species(EcmaRuntimeCallInfo *argv)217 JSTaggedValue BuiltinsSharedSet::Species(EcmaRuntimeCallInfo *argv)
218 {
219     return GetThis(argv).GetTaggedValue();
220 }
221 
GetSize(EcmaRuntimeCallInfo *argv)222 JSTaggedValue BuiltinsSharedSet::GetSize(EcmaRuntimeCallInfo *argv)
223 {
224     ASSERT(argv);
225     BUILTINS_API_TRACE(argv->GetThread(), SharedSet, GetSize);
226     JSThread *thread = argv->GetThread();
227     [[maybe_unused]] EcmaHandleScope handleScope(thread);
228     JSHandle<JSTaggedValue> self(GetThis(argv));
229     if (!self->IsJSSharedSet()) {
230         THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not SharedSet", JSTaggedValue::Exception());
231     }
232     JSHandle<JSSharedSet> set(self);
233     uint32_t size = JSSharedSet::GetSize(thread, set);
234     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(0));
235     return JSTaggedValue(size);
236 }
237 
Entries(EcmaRuntimeCallInfo *argv)238 JSTaggedValue BuiltinsSharedSet::Entries(EcmaRuntimeCallInfo *argv)
239 {
240     ASSERT(argv);
241     BUILTINS_API_TRACE(argv->GetThread(), SharedSet, Entries);
242     JSThread *thread = argv->GetThread();
243     [[maybe_unused]] EcmaHandleScope handleScope(thread);
244     JSHandle<JSTaggedValue> self = GetThis(argv);
245     if (!self->IsJSSharedSet()) {
246         auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
247                                                                "The entries method cannot be bound.");
248         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
249     }
250     JSHandle<JSTaggedValue> iter = JSSharedSetIterator::CreateSetIterator(thread, self, IterationKind::KEY_AND_VALUE);
251     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Undefined());
252     return iter.GetTaggedValue();
253 }
254 
Values(EcmaRuntimeCallInfo *argv)255 JSTaggedValue BuiltinsSharedSet::Values(EcmaRuntimeCallInfo *argv)
256 {
257     ASSERT(argv);
258     BUILTINS_API_TRACE(argv->GetThread(), SharedSet, Values);
259     JSThread *thread = argv->GetThread();
260     [[maybe_unused]] EcmaHandleScope handleScope(thread);
261     JSHandle<JSTaggedValue> self = GetThis(argv);
262     if (!self->IsJSSharedSet()) {
263         auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
264                                                                "The values method cannot be bound.");
265         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
266     }
267     JSHandle<JSTaggedValue> iter = JSSharedSetIterator::CreateSetIterator(thread, self, IterationKind::VALUE);
268     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Undefined());
269     return iter.GetTaggedValue();
270 }
271 }  // namespace panda::ecmascript::builtins
272