14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#include "ecmascript/ecma_vm.h"
174514f5e3Sopenharmony_ci#include "ecmascript/js_symbol.h"
184514f5e3Sopenharmony_ci#include "ecmascript/object_factory.h"
194514f5e3Sopenharmony_ci#include "ecmascript/property_attributes.h"
204514f5e3Sopenharmony_ci#include "ecmascript/tagged_hash_table.h"
214514f5e3Sopenharmony_ci#include "ecmascript/tests/test_helper.h"
224514f5e3Sopenharmony_ci#include "ecmascript/transitions_dictionary.h"
234514f5e3Sopenharmony_ci
244514f5e3Sopenharmony_ciusing namespace panda::ecmascript;
254514f5e3Sopenharmony_ci
264514f5e3Sopenharmony_cinamespace panda::test {
274514f5e3Sopenharmony_ciclass TransitionsDictionaryTest : public BaseTestWithScope<false> {
284514f5e3Sopenharmony_ci};
294514f5e3Sopenharmony_ci
304514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, IsMatch)
314514f5e3Sopenharmony_ci{
324514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> key(thread, JSTaggedValue::True());
334514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> otherKey(thread, JSTaggedValue::False());
344514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
354514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> otherDetails(thread, JSTaggedValue::Null());
364514f5e3Sopenharmony_ci    bool result = TransitionsDictionary::IsMatch(key.GetTaggedValue(), metaData.GetTaggedValue(),
374514f5e3Sopenharmony_ci        otherKey.GetTaggedValue(), otherDetails.GetTaggedValue());
384514f5e3Sopenharmony_ci    EXPECT_FALSE(result);
394514f5e3Sopenharmony_ci
404514f5e3Sopenharmony_ci    result = TransitionsDictionary::IsMatch(key.GetTaggedValue(), metaData.GetTaggedValue(),
414514f5e3Sopenharmony_ci        key.GetTaggedValue(), metaData.GetTaggedValue());
424514f5e3Sopenharmony_ci    EXPECT_TRUE(result);
434514f5e3Sopenharmony_ci}
444514f5e3Sopenharmony_ci
454514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, Hash)
464514f5e3Sopenharmony_ci{
474514f5e3Sopenharmony_ci    auto vm = thread->GetEcmaVM();
484514f5e3Sopenharmony_ci    auto factory = vm->GetFactory();
494514f5e3Sopenharmony_ci
504514f5e3Sopenharmony_ci    // test when key is string.
514514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> key1(factory->NewFromStdString("k"));
524514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> metaData1(thread, JSTaggedValue(1)); // test metaData : 1
534514f5e3Sopenharmony_ci    int hash = TransitionsDictionary::Hash(key1.GetTaggedValue(), metaData1.GetTaggedValue());
544514f5e3Sopenharmony_ci    // "k" : 107, hashSeed : 0, shift : 5, metaData : 1
554514f5e3Sopenharmony_ci    EXPECT_EQ(hash, 108); // 108 : (0 << 5) - 0 + 107 + 1
564514f5e3Sopenharmony_ci
574514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> key2(factory->NewFromStdString("key"));
584514f5e3Sopenharmony_ci    hash = TransitionsDictionary::Hash(key2.GetTaggedValue(), metaData1.GetTaggedValue());
594514f5e3Sopenharmony_ci    EXPECT_EQ(hash, 106080);
604514f5e3Sopenharmony_ci
614514f5e3Sopenharmony_ci    // test when key is symbol.
624514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> symbolName(factory->NewFromStdString("s"));
634514f5e3Sopenharmony_ci    JSHandle<JSSymbol> privateNameSymbol = factory->NewPrivateNameSymbol(symbolName);
644514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> symbolValue = JSHandle<JSTaggedValue>::Cast(privateNameSymbol);
654514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> metaData2(thread, JSTaggedValue(2)); // test metaData : 2
664514f5e3Sopenharmony_ci    hash = TransitionsDictionary::Hash(symbolValue.GetTaggedValue(), metaData2.GetTaggedValue());
674514f5e3Sopenharmony_ci    EXPECT_EQ(hash, 117); // 117 : 115 + 2
684514f5e3Sopenharmony_ci}
694514f5e3Sopenharmony_ci
704514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, GetKeyIndex)
714514f5e3Sopenharmony_ci{
724514f5e3Sopenharmony_ci    int entry = 10;
734514f5e3Sopenharmony_ci    EXPECT_EQ(TransitionsDictionary::GetKeyIndex(entry), 33); // 33 : 3 + 10 * 3 + 0
744514f5e3Sopenharmony_ci}
754514f5e3Sopenharmony_ci
764514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, GetValueIndex)
774514f5e3Sopenharmony_ci{
784514f5e3Sopenharmony_ci    int entry = 10;
794514f5e3Sopenharmony_ci    EXPECT_EQ(TransitionsDictionary::GetValueIndex(entry), 34); // 34 : 3 + 10 * 3 + 1
804514f5e3Sopenharmony_ci}
814514f5e3Sopenharmony_ci
824514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, GetEntryIndex)
834514f5e3Sopenharmony_ci{
844514f5e3Sopenharmony_ci    int entry = 10;
854514f5e3Sopenharmony_ci    EXPECT_EQ(TransitionsDictionary::GetEntryIndex(entry), 33); // 33 : 3 + 10 * 3
864514f5e3Sopenharmony_ci}
874514f5e3Sopenharmony_ci
884514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, Create)
894514f5e3Sopenharmony_ci{
904514f5e3Sopenharmony_ci    int numberOfElements = 8;
914514f5e3Sopenharmony_ci    JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
924514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->GetLength(), 27U); // 27 : 3 + 8 * 3
934514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->EntriesCount(), 0);
944514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->HoleEntriesCount(), 0);
954514f5e3Sopenharmony_ci}
964514f5e3Sopenharmony_ci
974514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, Shrink)
984514f5e3Sopenharmony_ci{
994514f5e3Sopenharmony_ci    auto vm = thread->GetEcmaVM();
1004514f5e3Sopenharmony_ci    auto factory = vm->GetFactory();
1014514f5e3Sopenharmony_ci    int numberOfElements = 64;
1024514f5e3Sopenharmony_ci    JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
1034514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->GetLength(), 195U); // 195 : 3 + 64 * 3
1044514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->EntriesCount(), 0);
1054514f5e3Sopenharmony_ci
1064514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
1074514f5e3Sopenharmony_ci    int eleNum = 7;
1084514f5e3Sopenharmony_ci    for (int index = 0; index < eleNum; index++) {
1094514f5e3Sopenharmony_ci        std::string keyStr = "key" + std::to_string(index);
1104514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> key(factory->NewFromStdString(keyStr));
1114514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> value(factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT));
1124514f5e3Sopenharmony_ci        TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
1134514f5e3Sopenharmony_ci    }
1144514f5e3Sopenharmony_ci    JSHandle<TransitionsDictionary> transDicAfterShink = TransitionsDictionary::Shrink(thread, transDic);
1154514f5e3Sopenharmony_ci    EXPECT_EQ(transDicAfterShink->GetLength(), 51U); // (1 << (32 - Clz((7 + (7 >> 1)) - 1))) * 3 + 3
1164514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->EntriesCount(), eleNum);
1174514f5e3Sopenharmony_ci}
1184514f5e3Sopenharmony_ci
1194514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, Get_Set_Attributes)
1204514f5e3Sopenharmony_ci{
1214514f5e3Sopenharmony_ci    int numberOfElements = 8;
1224514f5e3Sopenharmony_ci    JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
1234514f5e3Sopenharmony_ci    uint32_t length = transDic->GetLength();
1244514f5e3Sopenharmony_ci    EXPECT_EQ(length, 27U); // 27 : 3 + 8 * 3
1254514f5e3Sopenharmony_ci
1264514f5e3Sopenharmony_ci    for (int index = 0; index < numberOfElements; index++) {
1274514f5e3Sopenharmony_ci        transDic->SetAttributes(thread, index, JSTaggedValue(index));
1284514f5e3Sopenharmony_ci        JSTaggedValue value = transDic->GetAttributes(index);
1294514f5e3Sopenharmony_ci        EXPECT_EQ(value, JSTaggedValue(index));
1304514f5e3Sopenharmony_ci    }
1314514f5e3Sopenharmony_ci}
1324514f5e3Sopenharmony_ci
1334514f5e3Sopenharmony_ciusing TestCommonCB = std::function<void(JSThread *, int index,
1344514f5e3Sopenharmony_ci                                        JSHandle<JSTaggedValue> &, JSHandle<JSTaggedValue> &)>;
1354514f5e3Sopenharmony_civoid TestCommon(JSThread *thread, int numberOfElements, TestCommonCB cb)
1364514f5e3Sopenharmony_ci{
1374514f5e3Sopenharmony_ci    auto factory = thread->GetEcmaVM()->GetFactory();
1384514f5e3Sopenharmony_ci
1394514f5e3Sopenharmony_ci    for (int index = 0; index < numberOfElements; index++) {
1404514f5e3Sopenharmony_ci        std::string keyStr = "key" + std::to_string(index);
1414514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> key(factory->NewFromStdString(keyStr));
1424514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> value(factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT));
1434514f5e3Sopenharmony_ci        cb(thread, index, key, value);
1444514f5e3Sopenharmony_ci    }
1454514f5e3Sopenharmony_ci}
1464514f5e3Sopenharmony_ci
1474514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, SetEntry)
1484514f5e3Sopenharmony_ci{
1494514f5e3Sopenharmony_ci    int numberOfElements = 8;
1504514f5e3Sopenharmony_ci    JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
1514514f5e3Sopenharmony_ci
1524514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
1534514f5e3Sopenharmony_ci    TestCommon(thread, numberOfElements,
1544514f5e3Sopenharmony_ci                [&](JSThread *thread, int index, JSHandle<JSTaggedValue> &key, JSHandle<JSTaggedValue> &value) {
1554514f5e3Sopenharmony_ci                    transDic->SetEntry(thread, index, key.GetTaggedValue(), value.GetTaggedValue(),
1564514f5e3Sopenharmony_ci                                       metaData.GetTaggedValue());
1574514f5e3Sopenharmony_ci                    EXPECT_EQ(transDic->GetKey(index), key.GetTaggedValue());
1584514f5e3Sopenharmony_ci                });
1594514f5e3Sopenharmony_ci}
1604514f5e3Sopenharmony_ci
1614514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, FindEntry)
1624514f5e3Sopenharmony_ci{
1634514f5e3Sopenharmony_ci    int numberOfElements = 8;
1644514f5e3Sopenharmony_ci    JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
1654514f5e3Sopenharmony_ci
1664514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
1674514f5e3Sopenharmony_ci    TestCommon(thread, numberOfElements,
1684514f5e3Sopenharmony_ci               [&](JSThread *thread, int index, JSHandle<JSTaggedValue> &key, JSHandle<JSTaggedValue> &value) {
1694514f5e3Sopenharmony_ci                   transDic = TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
1704514f5e3Sopenharmony_ci                   int foundEntry = transDic->FindEntry(key.GetTaggedValue(), metaData.GetTaggedValue());
1714514f5e3Sopenharmony_ci                   EXPECT_EQ(index + 3, foundEntry);  // 3 : table header size
1724514f5e3Sopenharmony_ci               });
1734514f5e3Sopenharmony_ci}
1744514f5e3Sopenharmony_ci
1754514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, RemoveElement)
1764514f5e3Sopenharmony_ci{
1774514f5e3Sopenharmony_ci    int numberOfElements = 8;
1784514f5e3Sopenharmony_ci    JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
1794514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
1804514f5e3Sopenharmony_ci    TestCommon(thread, numberOfElements,
1814514f5e3Sopenharmony_ci               [&](JSThread *thread, int index, JSHandle<JSTaggedValue> &key, JSHandle<JSTaggedValue> &value) {
1824514f5e3Sopenharmony_ci                   transDic = TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
1834514f5e3Sopenharmony_ci               });
1844514f5e3Sopenharmony_ci    auto factory = thread->GetEcmaVM()->GetFactory();
1854514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> key7(factory->NewFromStdString("key7")); // test remove element by "key7"
1864514f5e3Sopenharmony_ci    int foundEntry = transDic->FindEntry(key7.GetTaggedValue(), metaData.GetTaggedValue());
1874514f5e3Sopenharmony_ci    EXPECT_EQ(foundEntry, 7 + 3);
1884514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->EntriesCount(), 8);
1894514f5e3Sopenharmony_ci
1904514f5e3Sopenharmony_ci    transDic->RemoveElement(thread, foundEntry);
1914514f5e3Sopenharmony_ci    foundEntry = transDic->FindEntry(key7.GetTaggedValue(), metaData.GetTaggedValue());
1924514f5e3Sopenharmony_ci    EXPECT_EQ(foundEntry, -1); // -1 : not found entry
1934514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->EntriesCount(), 7);
1944514f5e3Sopenharmony_ci}
1954514f5e3Sopenharmony_ci
1964514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, PutIfAbsent)
1974514f5e3Sopenharmony_ci{
1984514f5e3Sopenharmony_ci    auto vm = thread->GetEcmaVM();
1994514f5e3Sopenharmony_ci    auto factory = vm->GetFactory();
2004514f5e3Sopenharmony_ci    int numberOfElements = 8;
2014514f5e3Sopenharmony_ci    JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
2024514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
2034514f5e3Sopenharmony_ci    vm->SetEnableForceGC(false);
2044514f5e3Sopenharmony_ci    for (int index = 0; index < numberOfElements; index++) {
2054514f5e3Sopenharmony_ci        std::string keyStr = "key" + std::to_string(index);
2064514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> key(factory->NewFromStdString(keyStr));
2074514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> value(factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT));
2084514f5e3Sopenharmony_ci        transDic = TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
2094514f5e3Sopenharmony_ci        int foundEntry = transDic->FindEntry(key.GetTaggedValue(), metaData.GetTaggedValue());
2104514f5e3Sopenharmony_ci        EXPECT_EQ(foundEntry, index + 3);
2114514f5e3Sopenharmony_ci
2124514f5e3Sopenharmony_ci        JSTaggedValue foundValue = transDic->GetValue(foundEntry);
2134514f5e3Sopenharmony_ci        JSTaggedValue weakValue = value->CreateAndGetWeakRef();
2144514f5e3Sopenharmony_ci        EXPECT_EQ(foundValue, weakValue);
2154514f5e3Sopenharmony_ci    }
2164514f5e3Sopenharmony_ci    vm->SetEnableForceGC(true);
2174514f5e3Sopenharmony_ci}
2184514f5e3Sopenharmony_ci
2194514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, Remove)
2204514f5e3Sopenharmony_ci{
2214514f5e3Sopenharmony_ci    int numberOfElements = 64;
2224514f5e3Sopenharmony_ci    int eleNum = 7;
2234514f5e3Sopenharmony_ci    JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
2244514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
2254514f5e3Sopenharmony_ci    TestCommon(thread, eleNum,
2264514f5e3Sopenharmony_ci               [&](JSThread *thread, int index, JSHandle<JSTaggedValue> &key, JSHandle<JSTaggedValue> &value) {
2274514f5e3Sopenharmony_ci                   transDic = TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
2284514f5e3Sopenharmony_ci               });
2294514f5e3Sopenharmony_ci    auto factory = thread->GetEcmaVM()->GetFactory();
2304514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> key6(factory->NewFromStdString("key6")); // test remove element by "key6"
2314514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->EntriesCount(), 7);
2324514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->GetLength(), 195U); // 195 : 3 + 64 * 3
2334514f5e3Sopenharmony_ci
2344514f5e3Sopenharmony_ci    transDic = TransitionsDictionary::Remove(thread, transDic, key6, metaData.GetTaggedValue());
2354514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->EntriesCount(), 6); // 6 : 7 - 1
2364514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->GetLength(), 51U);  // (1 << (32 - Clz((6 + (6 >> 1)) - 1))) * 3 + 3
2374514f5e3Sopenharmony_ci}
2384514f5e3Sopenharmony_ci
2394514f5e3Sopenharmony_ciHWTEST_F_L0(TransitionsDictionaryTest, Rehash)
2404514f5e3Sopenharmony_ci{
2414514f5e3Sopenharmony_ci    int numberOfElements = 64;
2424514f5e3Sopenharmony_ci    int eleNum = 7;
2434514f5e3Sopenharmony_ci    JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
2444514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
2454514f5e3Sopenharmony_ci    TestCommon(thread, eleNum,
2464514f5e3Sopenharmony_ci               [&](JSThread *thread, int index, JSHandle<JSTaggedValue> &key, JSHandle<JSTaggedValue> &value) {
2474514f5e3Sopenharmony_ci                   transDic = TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
2484514f5e3Sopenharmony_ci               });
2494514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->HoleEntriesCount(), 0);
2504514f5e3Sopenharmony_ci
2514514f5e3Sopenharmony_ci    int lastEntry = 7 + 3;
2524514f5e3Sopenharmony_ci    transDic->RemoveElement(thread, lastEntry); // remove one element
2534514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->HoleEntriesCount(), 1);
2544514f5e3Sopenharmony_ci
2554514f5e3Sopenharmony_ci    transDic->Rehash(thread, *transDic);
2564514f5e3Sopenharmony_ci    EXPECT_EQ(transDic->HoleEntriesCount(), 0);
2574514f5e3Sopenharmony_ci}
2584514f5e3Sopenharmony_ci}  // namespace panda::test