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_map.h"
17
18 #include "ecmascript/interpreter/interpreter.h"
19 #include "ecmascript/js_function.h"
20 #include "ecmascript/linked_hash_table.h"
21 #include "ecmascript/shared_objects/concurrent_api_scope.h"
22 #include "ecmascript/shared_objects/js_shared_map.h"
23 #include "ecmascript/shared_objects/js_shared_map_iterator.h"
24
25 namespace panda::ecmascript::builtins {
Constructor(EcmaRuntimeCallInfo *argv)26 JSTaggedValue BuiltinsSharedMap::Constructor(EcmaRuntimeCallInfo *argv)
27 {
28 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Constructor);
29 JSThread *thread = argv->GetThread();
30 [[maybe_unused]] EcmaHandleScope handleScope(thread);
31 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
32 // 1. If NewTarget is undefined, throw exception
33 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
34 if (newTarget->IsUndefined()) {
35 JSTaggedValue error = containers::ContainerError::BusinessError(
36 thread, containers::ErrorFlag::IS_NULL_ERROR, "The ArkTS Map's constructor cannot be directly invoked.");
37 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
38 }
39 // 2.Let Map be OrdinaryCreateFromConstructor(NewTarget, "%MapPrototype%", «[[MapData]]» ).
40 JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
41 ASSERT(constructor->IsJSSharedFunction() && constructor.GetTaggedValue().IsInSharedHeap());
42 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
43 // 3.returnIfAbrupt()
44 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
45 ASSERT(obj.GetTaggedValue().IsInSharedHeap());
46 JSHandle<JSSharedMap> map = JSHandle<JSSharedMap>::Cast(obj);
47
48 // 4.Set map’s [[MapData]] internal slot to a new empty List.
49 JSHandle<LinkedHashMap> linkedMap = LinkedHashMap::Create(thread,
50 LinkedHashMap::MIN_CAPACITY, MemSpaceKind::SHARED);
51 map->SetLinkedMap(thread, linkedMap);
52 // add data into set from iterable
53 // 5.If iterable is not present, let iterable be undefined.
54 // 6.If iterable is either undefined or null, let iter be undefined.
55 JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
56 if (iterable->IsUndefined() || iterable->IsNull()) {
57 return map.GetTaggedValue();
58 }
59 if (!iterable->IsECMAObject()) {
60 THROW_TYPE_ERROR_AND_RETURN(thread, "iterable is not object", JSTaggedValue::Exception());
61 }
62 // Let adder be Get(map, "set").
63 JSHandle<JSTaggedValue> adderKey = thread->GlobalConstants()->GetHandledSetString();
64 JSHandle<JSTaggedValue> adder = JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(map), adderKey).GetValue();
65 // ReturnIfAbrupt(adder).
66 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue());
67 return AddEntriesFromIterable(thread, obj, iterable, adder, factory);
68 }
69
Set(EcmaRuntimeCallInfo *argv)70 JSTaggedValue BuiltinsSharedMap::Set(EcmaRuntimeCallInfo *argv)
71 {
72 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Set);
73 JSThread *thread = argv->GetThread();
74 [[maybe_unused]] EcmaHandleScope handleScope(thread);
75 JSHandle<JSTaggedValue> self = GetThis(argv);
76 if (!self->IsJSSharedMap()) {
77 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
78 "The set method cannot be bound.");
79 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
80 }
81 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
82 JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
83 JSHandle<JSSharedMap> map(self);
84 JSSharedMap::Set(thread, map, key, value);
85 return map.GetTaggedValue();
86 }
87
Clear(EcmaRuntimeCallInfo *argv)88 JSTaggedValue BuiltinsSharedMap::Clear(EcmaRuntimeCallInfo *argv)
89 {
90 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Clear);
91 JSThread *thread = argv->GetThread();
92 [[maybe_unused]] EcmaHandleScope handleScope(thread);
93 JSHandle<JSTaggedValue> self = GetThis(argv);
94 if (!self->IsJSSharedMap()) {
95 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
96 "The clear method cannot be bound.");
97 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
98 }
99 JSHandle<JSSharedMap> map(self);
100 JSSharedMap::Clear(thread, map);
101 return JSTaggedValue::Undefined();
102 }
103
Delete(EcmaRuntimeCallInfo *argv)104 JSTaggedValue BuiltinsSharedMap::Delete(EcmaRuntimeCallInfo *argv)
105 {
106 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Delete);
107 JSThread *thread = argv->GetThread();
108 [[maybe_unused]] EcmaHandleScope handleScope(thread);
109 JSHandle<JSTaggedValue> self = GetThis(argv);
110 if (!self->IsJSSharedMap()) {
111 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
112 "The delete method cannot be bound.");
113 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
114 }
115 JSHandle<JSSharedMap> map(self);
116 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
117 bool flag = JSSharedMap::Delete(thread, map, key);
118 return GetTaggedBoolean(flag);
119 }
120
Has(EcmaRuntimeCallInfo *argv)121 JSTaggedValue BuiltinsSharedMap::Has(EcmaRuntimeCallInfo *argv)
122 {
123 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Has);
124 JSThread *thread = argv->GetThread();
125 [[maybe_unused]] EcmaHandleScope handleScope(thread);
126 JSHandle<JSTaggedValue> self(GetThis(argv));
127 if (!self->IsJSSharedMap()) {
128 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
129 "The has method cannot be bound.");
130 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
131 }
132 JSHandle<JSSharedMap> map(self);
133 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
134 bool flag = JSSharedMap::Has(thread, map, key.GetTaggedValue());
135 return GetTaggedBoolean(flag);
136 }
137
Get(EcmaRuntimeCallInfo *argv)138 JSTaggedValue BuiltinsSharedMap::Get(EcmaRuntimeCallInfo *argv)
139 {
140 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Get);
141 JSThread *thread = argv->GetThread();
142 [[maybe_unused]] EcmaHandleScope handleScope(thread);
143 JSHandle<JSTaggedValue> self(GetThis(argv));
144 if (!self->IsJSSharedMap()) {
145 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
146 "The get method cannot be bound.");
147 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
148 }
149 JSHandle<JSSharedMap> map(self);
150 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
151 JSTaggedValue value = JSSharedMap::Get(thread, map, key.GetTaggedValue());
152 return value;
153 }
154
ForEach(EcmaRuntimeCallInfo *argv)155 JSTaggedValue BuiltinsSharedMap::ForEach(EcmaRuntimeCallInfo *argv)
156 {
157 JSThread *thread = argv->GetThread();
158 BUILTINS_API_TRACE(thread, SharedMap, ForEach);
159 [[maybe_unused]] EcmaHandleScope handleScope(thread);
160 JSHandle<JSTaggedValue> self = GetThis(argv);
161 if (!self->IsJSSharedMap()) {
162 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
163 "The forEach method cannot be bound.");
164 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
165 }
166 [[maybe_unused]] ConcurrentApiScope<JSSharedMap> scope(thread, self);
167 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
168 JSHandle<JSSharedMap> map(self);
169 JSHandle<JSTaggedValue> func(GetCallArg(argv, 0));
170 if (!func->IsCallable()) {
171 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not Callable", JSTaggedValue::Exception());
172 }
173 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 1);
174 JSMutableHandle<LinkedHashMap> hashMap(thread, map->GetLinkedMap());
175 const uint32_t argsLength = 3;
176 int index = 0;
177 int totalElements = hashMap->NumberOfElements() + hashMap->NumberOfDeletedElements();
178 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
179 // Repeat for each e that is an element of entries, in original insertion order
180 while (index < totalElements) {
181 JSHandle<JSTaggedValue> key(thread, hashMap->GetKey(index++));
182 if (!key->IsHole()) {
183 JSHandle<JSTaggedValue> value(thread, hashMap->GetValue(index - 1));
184 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(
185 thread, func, thisArg, undefined, argsLength);
186 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
187 info->SetCallArg(value.GetTaggedValue(), key.GetTaggedValue(), map.GetTaggedValue());
188 JSTaggedValue ret = JSFunction::Call(info);
189 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret);
190 }
191 }
192
193 return JSTaggedValue::Undefined();
194 }
195
Species(EcmaRuntimeCallInfo *argv)196 JSTaggedValue BuiltinsSharedMap::Species(EcmaRuntimeCallInfo *argv)
197 {
198 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Species);
199 return GetThis(argv).GetTaggedValue();
200 }
201
GetSize(EcmaRuntimeCallInfo *argv)202 JSTaggedValue BuiltinsSharedMap::GetSize(EcmaRuntimeCallInfo *argv)
203 {
204 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, GetSize);
205 JSThread *thread = argv->GetThread();
206 [[maybe_unused]] EcmaHandleScope handleScope(thread);
207 JSHandle<JSTaggedValue> self(GetThis(argv));
208 if (!self->IsJSSharedMap()) {
209 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not SharedMap", JSTaggedValue::Exception());
210 }
211 JSHandle<JSSharedMap> map(self);
212 uint32_t size = JSSharedMap::GetSize(thread, map);
213 return JSTaggedValue(size);
214 }
215
Entries(EcmaRuntimeCallInfo *argv)216 JSTaggedValue BuiltinsSharedMap::Entries(EcmaRuntimeCallInfo *argv)
217 {
218 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Entries);
219 JSThread *thread = argv->GetThread();
220 [[maybe_unused]] EcmaHandleScope handleScope(thread);
221 JSHandle<JSTaggedValue> self = GetThis(argv);
222 if (!self->IsJSSharedMap()) {
223 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
224 "The entries method cannot be bound.");
225 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
226 }
227 JSHandle<JSTaggedValue> iter = JSSharedMapIterator::CreateMapIterator(thread, self, IterationKind::KEY_AND_VALUE);
228 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
229 return iter.GetTaggedValue();
230 }
231
Keys(EcmaRuntimeCallInfo *argv)232 JSTaggedValue BuiltinsSharedMap::Keys(EcmaRuntimeCallInfo *argv)
233 {
234 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Keys);
235 JSThread *thread = argv->GetThread();
236 [[maybe_unused]] EcmaHandleScope handleScope(thread);
237 JSHandle<JSTaggedValue> self = GetThis(argv);
238 if (!self->IsJSSharedMap()) {
239 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
240 "The keys method cannot be bound.");
241 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
242 }
243 JSHandle<JSTaggedValue> iter = JSSharedMapIterator::CreateMapIterator(thread, self, IterationKind::KEY);
244 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
245 return iter.GetTaggedValue();
246 }
247
Values(EcmaRuntimeCallInfo *argv)248 JSTaggedValue BuiltinsSharedMap::Values(EcmaRuntimeCallInfo *argv)
249 {
250 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Values);
251 JSThread *thread = argv->GetThread();
252 [[maybe_unused]] EcmaHandleScope handleScope(thread);
253 JSHandle<JSTaggedValue> self = GetThis(argv);
254 if (!self->IsJSSharedMap()) {
255 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR,
256 "The values method cannot be bound.");
257 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
258 }
259 JSHandle<JSTaggedValue> iter = JSSharedMapIterator::CreateMapIterator(thread, self, IterationKind::VALUE);
260 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
261 return iter.GetTaggedValue();
262 }
263
AddEntriesFromIterable(JSThread *thread, const JSHandle<JSObject> &target, const JSHandle<JSTaggedValue> &iterable, const JSHandle<JSTaggedValue> &adder, ObjectFactory *factory)264 JSTaggedValue BuiltinsSharedMap::AddEntriesFromIterable(JSThread *thread, const JSHandle<JSObject> &target,
265 const JSHandle<JSTaggedValue> &iterable, const JSHandle<JSTaggedValue> &adder, ObjectFactory *factory)
266 {
267 BUILTINS_API_TRACE(thread, SharedMap, AddEntriesFromIterable);
268 // If IsCallable(adder) is false, throw a TypeError exception
269 if (!adder->IsCallable()) {
270 THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue());
271 }
272 // Let iter be GetIterator(iterable).
273 JSHandle<JSTaggedValue> iter(JSIterator::GetIterator(thread, iterable));
274 // ReturnIfAbrupt(iter).
275 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue());
276 JSHandle<JSTaggedValue> keyIndex(thread, JSTaggedValue(0));
277 JSHandle<JSTaggedValue> valueIndex(thread, JSTaggedValue(1));
278 JSHandle<JSTaggedValue> next = JSIterator::IteratorStep(thread, iter);
279 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue());
280 while (!next->IsFalse()) {
281 // Let nextValue be IteratorValue(next).
282 JSHandle<JSTaggedValue> nextValue(JSIterator::IteratorValue(thread, next));
283 // ReturnIfAbrupt(nextValue).
284 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue());
285
286 // If Type(nextItem) is not Object
287 if (!nextValue->IsECMAObject()) {
288 JSHandle<JSObject> typeError = factory->GetJSError(ErrorType::TYPE_ERROR,
289 "nextItem is not Object", StackCheck::NO);
290 JSHandle<JSTaggedValue> record(
291 factory->NewCompletionRecord(CompletionRecordType::THROW, JSHandle<JSTaggedValue>(typeError)));
292 JSTaggedValue ret = JSIterator::IteratorClose(thread, iter, record).GetTaggedValue();
293 if (!thread->HasPendingException()) {
294 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, typeError.GetTaggedValue(), ret);
295 }
296 return ret;
297 }
298 // Let k be Get(nextItem, "0").
299 JSHandle<JSTaggedValue> key = JSTaggedValue::GetProperty(thread, nextValue, keyIndex).GetValue();
300 // If k is an abrupt completion, return IteratorClose(iter, k).
301 if (thread->HasPendingException()) {
302 return JSIterator::IteratorCloseAndReturn(thread, iter);
303 }
304 // Let v be Get(nextItem, "1").
305 JSHandle<JSTaggedValue> value = JSTaggedValue::GetProperty(thread, nextValue, valueIndex).GetValue();
306 // If v is an abrupt completion, return IteratorClose(iter, v).
307 if (thread->HasPendingException()) {
308 return JSIterator::IteratorCloseAndReturn(thread, iter);
309 }
310 const uint32_t argsLength = 2; // 2: key and value pair
311 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
312 EcmaRuntimeCallInfo *info =
313 EcmaInterpreter::NewRuntimeCallInfo(thread, adder, JSHandle<JSTaggedValue>(target), undefined, argsLength);
314 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue());
315 info->SetCallArg(key.GetTaggedValue(), value.GetTaggedValue());
316 JSFunction::Call(info);
317 // If status is an abrupt completion, return IteratorClose(iter, status).
318 if (thread->HasPendingException()) {
319 return JSIterator::IteratorCloseAndReturn(thread, iter);
320 }
321 // Let next be IteratorStep(iter).
322 next = JSIterator::IteratorStep(thread, iter);
323 // ReturnIfAbrupt(next).
324 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue());
325 }
326 return target.GetTaggedValue();
327 }
328 } // namespace panda::ecmascript::builtins
329