/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/builtins/builtins_shared_set.h" #include "ecmascript/shared_objects/js_shared_set.h" #include "ecmascript/shared_objects/js_shared_set_iterator.h" #include "ecmascript/ecma_string.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" #include "ecmascript/js_array.h" #include "ecmascript/linked_hash_table.h" #include "ecmascript/js_set.h" #include "ecmascript/js_set_iterator.h" #include "ecmascript/js_handle.h" #include "ecmascript/js_hclass.h" #include "ecmascript/js_object-inl.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/js_thread.h" #include "ecmascript/object_factory.h" #include "ecmascript/tests/test_helper.h" using namespace panda::ecmascript; using namespace panda::ecmascript::builtins; namespace panda::test { using BuiltinsSharedSet = ecmascript::builtins::BuiltinsSharedSet; using JSSharedSet = ecmascript::JSSharedSet; class BuiltinsSharedSetTest : public BaseTestWithScope { public: class TestClass : public base::BuiltinsBase { public: static JSTaggedValue TestFunc(EcmaRuntimeCallInfo *argv) { JSTaggedValue key = GetCallArg(argv, 0).GetTaggedValue(); if (key.IsUndefined()) { return JSTaggedValue::Undefined(); } JSArray *jsArray = JSArray::Cast(GetThis(argv)->GetTaggedObject()); uint32_t length = jsArray->GetArrayLength() + 1U; jsArray->SetArrayLength(argv->GetThread(), length); return JSTaggedValue::Undefined(); } }; }; JSSharedSet *CreateBuiltinsSharedSet(JSThread *thread) { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle newTarget(env->GetSBuiltininSetFunction()); // 4 : test case auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4); ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); JSTaggedValue result = BuiltinsSharedSet::Constructor(ecmaRuntimeCallInfo); TestHelper::TearDownFrame(thread, prev); EXPECT_TRUE(result.IsECMAObject()); JSSharedSet *jsSSet = JSSharedSet::Cast(reinterpret_cast(result.GetRawData())); return jsSSet; } JSSet *CreateJSSet(JSThread *thread) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle constructor = thread->GetEcmaVM()->GetGlobalEnv()->GetBuiltinsSetFunction(); JSHandle set = JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); JSHandle hashSet = LinkedHashSet::Create(thread); set->SetLinkedSet(thread, hashSet); return JSSet::Cast(set.GetTaggedValue().GetTaggedObject()); } enum class AlgorithmType { ADD, HAS, }; JSTaggedValue SharedSetAlgorithm(JSThread *thread, JSTaggedValue jsSet, std::vector& args, uint32_t argLen = 8, AlgorithmType type = AlgorithmType::ADD) { auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), argLen); ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined()); ecmaRuntimeCallInfos->SetThis(jsSet); for (size_t i = 0; i < args.size(); i++) { ecmaRuntimeCallInfos->SetCallArg(i, args[i]); } auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos); JSTaggedValue result; switch (type) { case AlgorithmType::ADD: result = BuiltinsSharedSet::Add(ecmaRuntimeCallInfos); break; case AlgorithmType::HAS: result = BuiltinsSharedSet::Has(ecmaRuntimeCallInfos); break; default: break; } TestHelper::TearDownFrame(thread, prev); return result; } HWTEST_F_L0(BuiltinsSharedSetTest, CreateSetIteratorTest001) { JSHandle jsSet(thread, CreateJSSet(thread)); EXPECT_TRUE(*jsSet != nullptr); JSHandle setIteratorValue1 = JSSharedSetIterator::CreateSetIterator(thread, JSHandle(jsSet), IterationKind::KEY); EXPECT_EQ(setIteratorValue1->IsJSSetIterator(), false); } HWTEST_F_L0(BuiltinsSharedSetTest, NextInternalTest001) { JSTaggedValue setIteratorValue1 = JSSharedSetIterator::NextInternal(thread, JSHandle(thread, JSTaggedValue::Undefined())); EXPECT_EQ(setIteratorValue1.IsJSSharedSetIterator(), false); } HWTEST_F_L0(BuiltinsSharedSetTest, DeleteTest001) { JSHandle set(thread, CreateBuiltinsSharedSet(thread)); JSHandle value1(thread, JSTaggedValue(0)); JSHandle value2(thread, JSTaggedValue(1)); JSSharedSet::Add(thread, set, value1); JSSharedSet::Add(thread, set, value2); JSSharedSet::Delete(thread, set, JSHandle(thread, JSTaggedValue(20))); } // new Set("abrupt").toString() HWTEST_F_L0(BuiltinsSharedSetTest, CreateAndGetSize) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle newTarget(env->GetSBuiltininSetFunction()); JSHandle set(thread, CreateBuiltinsSharedSet(thread)); auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); { [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); JSTaggedValue result = BuiltinsSharedSet::GetSize(ecmaRuntimeCallInfo); TestHelper::TearDownFrame(thread, prev); EXPECT_EQ(result.GetRawData(), JSTaggedValue(0).GetRawData()); } JSHandle array(factory->NewTaggedArray(5)); for (int i = 0; i < 5; i++) { array->Set(thread, i, JSTaggedValue(i)); } JSHandle values = JSArray::CreateArrayFromList(thread, array); auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); ecmaRuntimeCallInfo1->SetFunction(newTarget.GetTaggedValue()); ecmaRuntimeCallInfo1->SetThis(set.GetTaggedValue()); ecmaRuntimeCallInfo1->SetCallArg(0, values.GetTaggedValue()); ecmaRuntimeCallInfo1->SetNewTarget(newTarget.GetTaggedValue()); { [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); JSTaggedValue result1 = BuiltinsSharedSet::Constructor(ecmaRuntimeCallInfo1); TestHelper::TearDownFrame(thread, prev); JSHandle set1(thread, JSSharedSet::Cast(reinterpret_cast(result1.GetRawData()))); EXPECT_EQ(JSSharedSet::GetSize(thread, set1), 5); } } HWTEST_F_L0(BuiltinsSharedSetTest, AddAndHas) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // create jsSet JSHandle set(thread, CreateBuiltinsSharedSet(thread)); JSHandle key(thread, factory->NewFromASCII("key").GetTaggedValue()); auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); JSTaggedValue result1 = BuiltinsSharedSet::Has(ecmaRuntimeCallInfo); TestHelper::TearDownFrame(thread, prev); EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); // test Add() JSTaggedValue result2 = BuiltinsSharedSet::Add(ecmaRuntimeCallInfo); EXPECT_TRUE(result2.IsECMAObject()); JSSharedSet *jsSSet = JSSharedSet::Cast(reinterpret_cast(result2.GetRawData())); JSHandle set2(thread, jsSSet); EXPECT_EQ(JSSharedSet::GetSize(thread, set2), 1); // test Has() JSTaggedValue jsSetTag(jsSSet); std::vector args{key.GetTaggedValue()}; auto result3 = SharedSetAlgorithm(thread, jsSetTag, args, 6, AlgorithmType::HAS); EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); // test -0.0 JSHandle negativeZero(thread, JSTaggedValue(-0.0)); JSHandle positiveZero(thread, JSTaggedValue(+0.0)); args[0] = negativeZero.GetTaggedValue(); SharedSetAlgorithm(thread, jsSetTag, args, 6, AlgorithmType::ADD); args[0] = positiveZero.GetTaggedValue(); auto result4 = SharedSetAlgorithm(thread, jsSetTag, args, 6, AlgorithmType::HAS); EXPECT_EQ(result4.GetRawData(), JSTaggedValue::True().GetRawData()); } HWTEST_F_L0(BuiltinsSharedSetTest, ForEach) { // generate a set has 5 entry{key1,key2,key3,key4,key5} ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle set(thread, CreateBuiltinsSharedSet(thread)); char keyArray[] = "key0"; for (uint32_t i = 0U; i < 5U; i++) { keyArray[3] = '1' + i; JSHandle key(factory->NewFromASCII(keyArray)); std::vector args{key.GetTaggedValue()}; auto result1 = SharedSetAlgorithm(thread, set.GetTaggedValue(), args, 6, AlgorithmType::ADD); EXPECT_TRUE(result1.IsECMAObject()); JSHandle set1(thread, JSSharedSet::Cast(reinterpret_cast(result1.GetRawData()))); EXPECT_EQ(JSSharedSet::GetSize(thread, set1), static_cast(i) + 1); } // test foreach JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); JSHandle func = factory->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv(), reinterpret_cast(TestClass::TestFunc)); auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo1->SetThis(set.GetTaggedValue()); ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); JSTaggedValue result2 = BuiltinsSharedSet::ForEach(ecmaRuntimeCallInfo1); TestHelper::TearDownFrame(thread, prev); EXPECT_EQ(result2.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); EXPECT_EQ(jsArray->GetArrayLength(), 5U); } HWTEST_F_L0(BuiltinsSharedSetTest, DeleteAndRemove) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // create jsSet JSHandle set(thread, CreateBuiltinsSharedSet(thread)); // add 40 keys char keyArray[] = "key0"; for (uint32_t i = 0; i < 40; i++) { keyArray[3] = '1' + i; JSHandle key(factory->NewFromASCII(keyArray)); std::vector args{key.GetTaggedValue()}; auto result1 = SharedSetAlgorithm(thread, set.GetTaggedValue(), args, 6, AlgorithmType::ADD); EXPECT_TRUE(result1.IsECMAObject()); JSHandle set1(thread, JSSharedSet::Cast(reinterpret_cast(result1.GetRawData()))); EXPECT_EQ(JSSharedSet::GetSize(thread, set1), static_cast(i) + 1); } // whether jsSet has delete key keyArray[3] = '1' + 8; JSHandle deleteKey(factory->NewFromASCII(keyArray)); auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo1->SetThis(set.GetTaggedValue()); ecmaRuntimeCallInfo1->SetCallArg(0, deleteKey.GetTaggedValue()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); JSTaggedValue result2 = BuiltinsSharedSet::Has(ecmaRuntimeCallInfo1); EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); // delete JSTaggedValue result3 = BuiltinsSharedSet::Delete(ecmaRuntimeCallInfo1); EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); // check deleteKey is deleted JSTaggedValue result4 = BuiltinsSharedSet::Has(ecmaRuntimeCallInfo1); EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); JSTaggedValue result5 = BuiltinsSharedSet::GetSize(ecmaRuntimeCallInfo1); EXPECT_EQ(result5.GetRawData(), JSTaggedValue(39).GetRawData()); // clear JSTaggedValue result6 = BuiltinsSharedSet::Clear(ecmaRuntimeCallInfo1); EXPECT_EQ(result6.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); EXPECT_EQ(set->JSSharedSet::GetSize(thread, set), 0); } HWTEST_F_L0(BuiltinsSharedSetTest, Species) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle set(thread, CreateBuiltinsSharedSet(thread)); JSHandle speciesSymbol = env->GetSpeciesSymbol(); EXPECT_TRUE(!speciesSymbol->IsUndefined()); JSHandle newTarget(env->GetSBuiltininSetFunction()); JSTaggedValue value = JSObject::GetProperty(thread, JSHandle(newTarget), speciesSymbol).GetValue().GetTaggedValue(); JSHandle valueHandle(thread, value); EXPECT_EQ(value, newTarget.GetTaggedValue()); // to string tag JSHandle toStringTagSymbol = env->GetToStringTagSymbol(); JSHandle stringTag(JSObject::GetProperty(thread, set, toStringTagSymbol).GetValue()); EXPECT_TRUE(!stringTag.GetTaggedValue().IsUndefined()); JSHandle constructor = JSHandle::Cast(JSTaggedValue::ToObject(thread, valueHandle)); EXPECT_EQ(JSTaggedValue::GetPrototype(thread, set), constructor->GetFunctionPrototype()); JSHandle key1(factory->NewFromASCII("add")); JSTaggedValue value1 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); EXPECT_FALSE(value1.IsUndefined()); JSHandle key2(factory->NewFromASCII("has")); JSTaggedValue value2 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); EXPECT_FALSE(value2.IsUndefined()); JSHandle key3(factory->NewFromASCII("clear")); JSTaggedValue value3 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); EXPECT_FALSE(value3.IsUndefined()); JSHandle key4(factory->NewFromASCII("size")); JSTaggedValue value4 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); EXPECT_FALSE(value4.IsUndefined()); JSHandle key5(factory->NewFromASCII("delete")); JSTaggedValue value5 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); EXPECT_FALSE(value5.IsUndefined()); JSHandle key6(factory->NewFromASCII("forEach")); JSTaggedValue value6 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); EXPECT_FALSE(value6.IsUndefined()); } HWTEST_F_L0(BuiltinsSharedSetTest, GetIterator) { JSHandle set(thread, CreateBuiltinsSharedSet(thread)); auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); // test Values() JSTaggedValue result = BuiltinsSharedSet::Values(ecmaRuntimeCallInfo); JSHandle iter(thread, result); EXPECT_TRUE(result.IsJSSharedSetIterator()); EXPECT_EQ(IterationKind::VALUE, IterationKind(iter->GetIterationKind())); // test entries() JSTaggedValue result2 = BuiltinsSharedSet::Entries(ecmaRuntimeCallInfo); JSHandle iter2(thread, result2); EXPECT_TRUE(result2.IsJSSharedSetIterator()); EXPECT_EQ(IterationKind::KEY_AND_VALUE, iter2->GetIterationKind()); } HWTEST_F_L0(BuiltinsSharedSetTest, GetValue) { JSHandle set(thread, CreateBuiltinsSharedSet(thread)); auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); JSTaggedValue result = BuiltinsSharedSet::Values(ecmaRuntimeCallInfo); JSHandle iter(thread, result); EXPECT_TRUE(result.IsJSSharedSetIterator()); EXPECT_EQ(IterationKind::VALUE, IterationKind(iter->GetIterationKind())); JSTaggedValue result2 = BuiltinsSharedSet::Entries(ecmaRuntimeCallInfo); JSHandle iter2(thread, result2); EXPECT_TRUE(result2.IsJSSharedSetIterator()); EXPECT_EQ(IterationKind::KEY_AND_VALUE, iter2->GetIterationKind()); } HWTEST_F_L0(BuiltinsSharedSetTest, KEY_AND_VALUE_Next) { JSHandle index0(thread, JSTaggedValue(0)); JSHandle index1(thread, JSTaggedValue(1)); JSHandle setIterator; JSHandle jsSet(thread, CreateBuiltinsSharedSet(thread)); EXPECT_TRUE(*jsSet != nullptr); for (int i = 0; i < 3; i++) { // 3 : 3 default numberOfElements JSHandle key(thread, JSTaggedValue(i)); JSSharedSet::Add(thread, jsSet, key); } // set IterationKind(key or value) JSHandle setIteratorValue = JSSharedSetIterator::CreateSetIterator(thread, JSHandle(jsSet), IterationKind::KEY_AND_VALUE); setIterator = JSHandle(setIteratorValue); std::vector args{JSTaggedValue::Undefined()}; auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, args, 6, setIteratorValue.GetTaggedValue()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); for (int i = 0; i <= 3; i++) { // 3 : 3 default numberOfElements JSTaggedValue result = JSSharedSetIterator::Next(ecmaRuntimeCallInfo); JSHandle resultObj(thread, result); if (i < 3) { JSHandle arrayList(thread, JSIterator::IteratorValue(thread, resultObj).GetTaggedValue()); EXPECT_EQ(setIterator->GetNextIndex(), static_cast(i+1)); EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(arrayList), index0).GetValue()->GetInt(), i); EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(arrayList), index1).GetValue()->GetInt(), i); } else { EXPECT_EQ(JSIterator::IteratorValue(thread, resultObj).GetTaggedValue(), JSTaggedValue::Undefined()); } } TestHelper::TearDownFrame(thread, prev); } } // namespace panda::test