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