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