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