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