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
33using namespace panda::ecmascript;
34using namespace panda::ecmascript::builtins;
35
36namespace panda::test {
37using BuiltinsSet = ecmascript::builtins::BuiltinsSet;
38using JSSet = ecmascript::JSSet;
39
40class BuiltinsSetTest : public BaseTestWithScope<false> {
41public:
42    class TestClass : public base::BuiltinsBase {
43    public:
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
58JSSet *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
75enum class AlgorithmType {
76    ADD,
77    HAS,
78};
79
80JSTaggedValue 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()
106HWTEST_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
145HWTEST_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
189HWTEST_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
225HWTEST_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
277HWTEST_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
327HWTEST_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
350HWTEST_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