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