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