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/base/builtins_base.h"
19 #include "ecmascript/ecma_runtime_call_info.h"
20 #include "ecmascript/ecma_string.h"
21 #include "ecmascript/ecma_vm.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/js_array.h"
24 #include "ecmascript/js_handle.h"
25 #include "ecmascript/js_hclass.h"
26 #include "ecmascript/js_map.h"
27 #include "ecmascript/js_map_iterator.h"
28 #include "ecmascript/js_object-inl.h"
29 #include "ecmascript/js_tagged_value.h"
30 #include "ecmascript/js_thread.h"
31 #include "ecmascript/object_factory.h"
32 #include "ecmascript/tagged_array-inl.h"
33 #include "ecmascript/tests/test_helper.h"
34 #include "ecmascript/shared_objects/js_shared_map_iterator.h"
35
36 using namespace panda::ecmascript;
37 using namespace panda::ecmascript::builtins;
38
39 namespace panda::test {
40 using BuiltinsMap = ecmascript::builtins::BuiltinsMap;
41 using JSMap = ecmascript::JSMap;
42
43 class BuiltinsMapTest : public BaseTestWithScope<false> {
44 public:
45 class TestClass : public base::BuiltinsBase {
46 public:
TestFunc(EcmaRuntimeCallInfo *argv)47 static JSTaggedValue TestFunc(EcmaRuntimeCallInfo *argv)
48 {
49 int num = GetCallArg(argv, 0)->GetInt();
50 JSArray *jsArray = JSArray::Cast(GetThis(argv)->GetTaggedObject());
51 int length = jsArray->GetArrayLength() + num;
52 jsArray->SetArrayLength(argv->GetThread(), length);
53 return JSTaggedValue::Undefined();
54 }
55 };
56 };
57
CreateBuiltinsMap(JSThread *thread)58 JSMap *CreateBuiltinsMap(JSThread *thread)
59 {
60 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
61 JSHandle<JSFunction> newTarget(env->GetBuiltinsMapFunction());
62 // 4 : test case
63 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4);
64 ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
65 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
66
67 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
68 JSTaggedValue result = BuiltinsMap::MapConstructor(ecmaRuntimeCallInfo);
69 TestHelper::TearDownFrame(thread, prev);
70
71 EXPECT_TRUE(result.IsECMAObject());
72 JSMap *jsMap = JSMap::Cast(reinterpret_cast<TaggedObject *>(result.GetRawData()));
73 return jsMap;
74 }
75
76 enum class AlgorithmType {
77 SET,
78 FOR_EACH,
79 HAS,
80 };
81
MapAlgorithm(JSThread *thread, JSTaggedValue thisArg, std::vector<JSTaggedValue>& args, int32_t argLen, AlgorithmType type)82 JSTaggedValue MapAlgorithm(JSThread *thread, JSTaggedValue thisArg, std::vector<JSTaggedValue>& args,
83 int32_t argLen, AlgorithmType type)
84 {
85 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), argLen);
86 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
87 ecmaRuntimeCallInfo->SetThis(thisArg);
88 for (size_t i = 0; i < args.size(); i++) {
89 ecmaRuntimeCallInfo->SetCallArg(i, args[i]);
90 }
91 auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
92 JSTaggedValue result;
93 switch (type) {
94 case AlgorithmType::SET:
95 result = BuiltinsMap::Set(ecmaRuntimeCallInfo);
96 break;
97 case AlgorithmType::FOR_EACH:
98 result = BuiltinsMap::ForEach(ecmaRuntimeCallInfo);
99 break;
100 case AlgorithmType::HAS:
101 result = BuiltinsMap::Has(ecmaRuntimeCallInfo);
102 break;
103 default:
104 break;
105 }
106 TestHelper::TearDownFrame(thread, prev);
107 return result;
108 }
109
110 // new Map("abrupt").toString()
HWTEST_F_L0(BuiltinsMapTest, CreateAndGetSize)111 HWTEST_F_L0(BuiltinsMapTest, CreateAndGetSize)
112 {
113 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
114 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
115 JSHandle<JSFunction> newTarget(env->GetBuiltinsMapFunction());
116 JSHandle<JSMap> map(thread, CreateBuiltinsMap(thread));
117
118 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
119 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
120 ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue());
121 ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined());
122
123 {
124 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
125 JSTaggedValue result = BuiltinsMap::GetSize(ecmaRuntimeCallInfo);
126 TestHelper::TearDownFrame(thread, prev);
127
128 EXPECT_EQ(result.GetRawData(), JSTaggedValue(0).GetRawData());
129 }
130 JSHandle<TaggedArray> array(factory->NewTaggedArray(5));
131 for (int i = 0; i < 5; i++) {
132 JSHandle<TaggedArray> internalArray(factory->NewTaggedArray(2));
133 internalArray->Set(thread, 0, JSTaggedValue(i));
134 internalArray->Set(thread, 1, JSTaggedValue(i));
135 auto arr = JSArray::CreateArrayFromList(thread, internalArray);
136 array->Set(thread, i, arr);
137 }
138 JSHandle<JSArray> values = JSArray::CreateArrayFromList(thread, array);
139 auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
140 ecmaRuntimeCallInfo1->SetFunction(newTarget.GetTaggedValue());
141 ecmaRuntimeCallInfo1->SetThis(map.GetTaggedValue());
142 ecmaRuntimeCallInfo1->SetCallArg(0, values.GetTaggedValue());
143 ecmaRuntimeCallInfo1->SetNewTarget(newTarget.GetTaggedValue());
144 {
145 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
146 JSTaggedValue result1 = BuiltinsMap::MapConstructor(ecmaRuntimeCallInfo1);
147 TestHelper::TearDownFrame(thread, prev);
148
149 EXPECT_EQ(JSMap::Cast(reinterpret_cast<TaggedObject *>(result1.GetRawData()))->GetSize(), 5);
150 }
151 }
152
HWTEST_F_L0(BuiltinsMapTest, SetAndHas)153 HWTEST_F_L0(BuiltinsMapTest, SetAndHas)
154 {
155 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
156 // create jsMap
157 JSHandle<JSMap> map(thread, CreateBuiltinsMap(thread));
158 JSHandle<JSTaggedValue> key(factory->NewFromASCII("key"));
159
160 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
161 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
162 ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue());
163 ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue());
164 ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(1)));
165
166 JSMap *jsMap;
167 {
168 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
169 JSTaggedValue result1 = BuiltinsMap::Has(ecmaRuntimeCallInfo);
170
171 EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData());
172
173 // test Set()
174 JSTaggedValue result2 = BuiltinsMap::Set(ecmaRuntimeCallInfo);
175
176 EXPECT_TRUE(result2.IsECMAObject());
177 jsMap = JSMap::Cast(reinterpret_cast<TaggedObject *>(result2.GetRawData()));
178 EXPECT_EQ(jsMap->GetSize(), 1);
179 }
180
181 // test Has()
182 std::vector<JSTaggedValue> args{key.GetTaggedValue(), JSTaggedValue(static_cast<int32_t>(1))}; // 1:value
183 auto result3 = MapAlgorithm(thread, JSTaggedValue(jsMap), args, 8, AlgorithmType::HAS); // 8: arg len
184 EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData());
185 }
186
HWTEST_F_L0(BuiltinsMapTest, ForEach)187 HWTEST_F_L0(BuiltinsMapTest, ForEach)
188 {
189 // generate a map has 5 entries{key1:0,key2:1,key3:2,key4:3,key5:4}
190 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
191 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
192 JSHandle<JSMap> map(thread, CreateBuiltinsMap(thread));
193 char keyArray[] = "key0";
194 for (uint32_t i = 0; i < 5; i++) {
195 keyArray[3] = '1' + i;
196 JSHandle<JSTaggedValue> key(factory->NewFromASCII(keyArray));
197 std::vector<JSTaggedValue> args{key.GetTaggedValue(), JSTaggedValue(static_cast<int32_t>(i))};
198 auto result1 = MapAlgorithm(thread, map.GetTaggedValue(), args, 8, AlgorithmType::SET);
199
200 EXPECT_TRUE(result1.IsECMAObject());
201 JSMap *jsMap = JSMap::Cast(reinterpret_cast<TaggedObject *>(result1.GetRawData()));
202 EXPECT_EQ(jsMap->GetSize(), static_cast<int>(i) + 1);
203 }
204 JSHandle<JSArray> jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
205 JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestFunc));
206
207 std::vector<JSTaggedValue> args{func.GetTaggedValue(), jsArray.GetTaggedValue()};
208 auto result2 = MapAlgorithm(thread, map.GetTaggedValue(), args, 8, AlgorithmType::FOR_EACH);
209
210 EXPECT_EQ(result2.GetRawData(), JSTaggedValue::VALUE_UNDEFINED);
211 EXPECT_EQ(jsArray->GetArrayLength(), 10U);
212 }
213
HWTEST_F_L0(BuiltinsMapTest, DeleteAndRemove)214 HWTEST_F_L0(BuiltinsMapTest, DeleteAndRemove)
215 {
216 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
217 // create jsMap
218 JSHandle<JSMap> map(thread, CreateBuiltinsMap(thread));
219
220 // add 40 keys
221 char keyArray[] = "key0";
222 for (uint32_t i = 0; i < 40; i++) {
223 keyArray[3] = '1' + i;
224 JSHandle<JSTaggedValue> key(thread, factory->NewFromASCII(keyArray).GetTaggedValue());
225 std::vector<JSTaggedValue> args{key.GetTaggedValue(), JSTaggedValue(static_cast<int32_t>(i))};
226 auto result1 = MapAlgorithm(thread, map.GetTaggedValue(), args, 8, AlgorithmType::SET);
227
228 EXPECT_TRUE(result1.IsECMAObject());
229 JSMap *jsMap = JSMap::Cast(reinterpret_cast<TaggedObject *>(result1.GetRawData()));
230 EXPECT_EQ(jsMap->GetSize(), static_cast<int>(i) + 1);
231 }
232 // whether jsMap has delete key
233 keyArray[3] = '1' + 8;
234 JSHandle<JSTaggedValue> deleteKey(factory->NewFromASCII(keyArray));
235
236 auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
237 ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
238 ecmaRuntimeCallInfo1->SetThis(map.GetTaggedValue());
239 ecmaRuntimeCallInfo1->SetCallArg(0, deleteKey.GetTaggedValue());
240
241 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
242 JSTaggedValue result2 = BuiltinsMap::Has(ecmaRuntimeCallInfo1);
243 TestHelper::TearDownFrame(thread, prev);
244
245 EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData());
246
247 // delete
248 [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
249 JSTaggedValue result3 = BuiltinsMap::Delete(ecmaRuntimeCallInfo1);
250 TestHelper::TearDownFrame(thread, prev1);
251 EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData());
252
253 // check deleteKey is deleted
254 [[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
255 JSTaggedValue result4 = BuiltinsMap::Has(ecmaRuntimeCallInfo1);
256 TestHelper::TearDownFrame(thread, prev2);
257 EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData());
258 [[maybe_unused]] auto prev3 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
259 JSTaggedValue result5 = BuiltinsMap::GetSize(ecmaRuntimeCallInfo1);
260 TestHelper::TearDownFrame(thread, prev3);
261 EXPECT_EQ(result5.GetRawData(), JSTaggedValue(39).GetRawData());
262
263 // clear
264 [[maybe_unused]] auto prev4 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
265 JSTaggedValue result6 = BuiltinsMap::Clear(ecmaRuntimeCallInfo1);
266 TestHelper::TearDownFrame(thread, prev4);
267 EXPECT_EQ(result6.GetRawData(), JSTaggedValue::VALUE_UNDEFINED);
268 EXPECT_EQ(map->GetSize(), 0);
269 }
270
HWTEST_F_L0(BuiltinsMapTest, Species)271 HWTEST_F_L0(BuiltinsMapTest, Species)
272 {
273 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
274 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
275 JSHandle<JSTaggedValue> map(thread, CreateBuiltinsMap(thread));
276
277 // test species
278 JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
279 EXPECT_TRUE(!speciesSymbol.GetTaggedValue().IsUndefined());
280
281 JSHandle<JSFunction> newTarget(env->GetBuiltinsMapFunction());
282
283 JSTaggedValue value =
284 JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(newTarget), speciesSymbol).GetValue().GetTaggedValue();
285 JSHandle<JSTaggedValue> valueHandle(thread, value);
286 EXPECT_EQ(value, newTarget.GetTaggedValue());
287
288 // to string tag
289 JSHandle<JSTaggedValue> toStringTagSymbol = env->GetToStringTagSymbol();
290 JSHandle<EcmaString> stringTag(JSObject::GetProperty(thread, map, toStringTagSymbol).GetValue());
291 JSHandle<EcmaString> str = factory->NewFromASCII("Map");
292 EXPECT_TRUE(!stringTag.GetTaggedValue().IsUndefined());
293 EXPECT_TRUE(EcmaStringAccessor::StringsAreEqual(*str, *stringTag));
294
295 JSHandle<JSFunction> constructor = JSHandle<JSFunction>::Cast(JSTaggedValue::ToObject(thread, valueHandle));
296 EXPECT_EQ(JSTaggedValue::GetPrototype(thread, map), constructor->GetFunctionPrototype());
297
298 JSHandle<JSTaggedValue> key1(factory->NewFromASCII("set"));
299 JSTaggedValue value1 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue();
300 EXPECT_FALSE(value1.IsUndefined());
301
302 JSHandle<JSTaggedValue> key2(factory->NewFromASCII("has"));
303 JSTaggedValue value2 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue();
304 EXPECT_FALSE(value2.IsUndefined());
305
306 JSHandle<JSTaggedValue> key3(factory->NewFromASCII("clear"));
307 JSTaggedValue value3 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue();
308 EXPECT_FALSE(value3.IsUndefined());
309
310 JSHandle<JSTaggedValue> key4(factory->NewFromASCII("size"));
311 JSTaggedValue value4 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue();
312 EXPECT_FALSE(value4.IsUndefined());
313
314 JSHandle<JSTaggedValue> key5(factory->NewFromASCII("delete"));
315 JSTaggedValue value5 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue();
316 EXPECT_FALSE(value5.IsUndefined());
317
318 JSHandle<JSTaggedValue> key6(factory->NewFromASCII("forEach"));
319 JSTaggedValue value6 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue();
320 EXPECT_FALSE(value6.IsUndefined());
321
322 JSHandle<JSTaggedValue> key7(factory->NewFromASCII("get"));
323 JSTaggedValue value7 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue();
324 EXPECT_FALSE(value7.IsUndefined());
325 }
326
HWTEST_F_L0(BuiltinsMapTest, GetIterator)327 HWTEST_F_L0(BuiltinsMapTest, GetIterator)
328 {
329 JSHandle<JSTaggedValue> map(thread, CreateBuiltinsMap(thread));
330 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
331 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
332 ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue());
333 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
334
335 // test Values()
336 JSTaggedValue result = BuiltinsMap::Values(ecmaRuntimeCallInfo);
337 JSHandle<JSMapIterator> iter(thread, result);
338 EXPECT_TRUE(iter->IsJSMapIterator());
339 EXPECT_EQ(IterationKind::VALUE, iter->GetIterationKind());
340 EXPECT_EQ(JSMap::Cast(map.GetTaggedValue().GetTaggedObject())->GetLinkedMap(), iter->GetIteratedMap());
341
342 // test Keys()
343 JSTaggedValue result1 = BuiltinsMap::Keys(ecmaRuntimeCallInfo);
344 JSHandle<JSMapIterator> iter1(thread, result1);
345 EXPECT_TRUE(iter1->IsJSMapIterator());
346 EXPECT_EQ(IterationKind::KEY, iter1->GetIterationKind());
347
348 // test entries()
349 JSTaggedValue result2 = BuiltinsMap::Entries(ecmaRuntimeCallInfo);
350 JSHandle<JSMapIterator> iter2(thread, result2);
351 EXPECT_TRUE(iter2->IsJSMapIterator());
352 EXPECT_EQ(IterationKind::KEY_AND_VALUE, iter2->GetIterationKind());
353 TestHelper::TearDownFrame(thread, prev);
354 }
355
HWTEST_F_L0(BuiltinsMapTest, Exception)356 HWTEST_F_L0(BuiltinsMapTest, Exception)
357 {
358 JSHandle<JSTaggedValue> map(thread, CreateBuiltinsMap(thread));
359 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
360 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
361 ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue());
362 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
363 auto result = JSSharedMapIterator::CreateMapIterator(thread, map, IterationKind::KEY);
364 EXPECT_TRUE(result->IsUndefined());
365 auto result1 = JSSharedMapIterator::NextInternal(thread, map);
366 EXPECT_EQ(result1, JSTaggedValue::Exception());
367 TestHelper::TearDownFrame(thread, prev);
368 }
369
370 } // namespace panda::test
371