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