1/*
2 * Copyright (c) 2022 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/global_dictionary-inl.h"
17#include "ecmascript/symbol_table.h"
18#include "ecmascript/property_attributes.h"
19#include "ecmascript/tests/test_helper.h"
20
21using namespace panda::ecmascript;
22
23namespace panda::test {
24class GlobalDictionaryTest : public BaseTestWithScope<false> {
25};
26
27/**
28 * @tc.name: IsMatch
29 * @tc.desc: Check whether two JSTaggedValue is equal through calling IsMatch function is within expectations.
30 * @tc.type: FUNC
31 * @tc.require:
32 */
33HWTEST_F_L0(GlobalDictionaryTest, IsMatch)
34{
35    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
36
37    JSHandle<EcmaString> globalKey = factory->NewFromASCII("key");
38    JSTaggedValue globalOtherKey = globalKey.GetTaggedValue();
39
40    EXPECT_EQ(GlobalDictionary::IsMatch(globalKey.GetTaggedValue(), globalOtherKey), true);
41    EXPECT_EQ(GlobalDictionary::IsMatch(globalKey.GetTaggedValue(), JSTaggedValue::Undefined()), false);
42}
43
44/**
45 * @tc.name: Hash
46 * @tc.desc: Check whether the hash size through calling Hash function is within expectations.
47 * @tc.type: FUNC
48 * @tc.require:
49 */
50HWTEST_F_L0(GlobalDictionaryTest, Hash)
51{
52    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
53    // test obj is jsSymbol
54    JSHandle<JSSymbol> jsSymbol = factory->NewJSSymbol();
55    uint32_t hashField = static_cast<uint32_t>(GlobalDictionary::Hash(jsSymbol.GetTaggedValue()));
56    EXPECT_EQ(hashField, SymbolTable::Hash(jsSymbol.GetTaggedValue()));
57
58    // test obj is string(uint8_t)
59    uint8_t utf8ArrayName[4] = {0, 2, 5}; // The last element is "\0"
60    uint32_t utf8ArrayNameLen = sizeof(utf8ArrayName) - 1;
61    JSHandle<EcmaString> nameStringUtf8Obj = factory->NewFromUtf8(utf8ArrayName, utf8ArrayNameLen);
62    EXPECT_EQ(GlobalDictionary::Hash(nameStringUtf8Obj.GetTaggedValue()), 67); // 67 = (0 << 5 - 0 + 2) << 5 - 2 + 5
63    // test obj is string(uint16_t)
64    uint16_t utf16ArrayName[] = {0x1, 0x2, 0x1};
65    uint32_t utf16ArrayNameLen = sizeof(utf16ArrayName) / sizeof(utf16ArrayName[0]);
66    JSHandle<EcmaString> nameStringUtf16Obj = factory->NewFromUtf16(utf16ArrayName, utf16ArrayNameLen);
67    // 1024 = (1 << 5 - 0 + 1) << 5 - 1 + 1
68    EXPECT_EQ(GlobalDictionary::Hash(nameStringUtf16Obj.GetTaggedValue()), 1024);
69}
70
71/**
72 * @tc.name: GetBoxAndValue
73 * @tc.desc: Check whether the Box and Value through calling SetEntry function is within expectations.
74 * @tc.type: FUNC
75 * @tc.require:
76 */
77HWTEST_F_L0(GlobalDictionaryTest, GetBoxAndValue)
78{
79    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
80
81    JSHandle<JSTaggedValue> globalKey(factory->NewFromASCII("key"));
82    JSHandle<JSTaggedValue> globalKey1(factory->NewFromASCII("value"));
83    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(123));
84    JSHandle<JSTaggedValue> handleValue1(thread, JSTaggedValue(100));
85    JSHandle<JSTaggedValue> propertyBox(factory->NewPropertyBox(handleValue));
86    JSHandle<JSTaggedValue> propertyBox1(factory->NewPropertyBox(handleValue1));
87    PropertyAttributes attribute(1);
88    // create GlobalDictionary
89    JSHandle<GlobalDictionary> handleDict = GlobalDictionary::Create(thread, 4);
90    EXPECT_TRUE(*handleDict != nullptr);
91    handleDict->SetEntry(thread, 0, globalKey.GetTaggedValue(), propertyBox.GetTaggedValue(), attribute);
92    // put value and cerate new dictionary
93    JSHandle<GlobalDictionary> newDict =
94        handleDict->PutIfAbsent(thread, handleDict, globalKey1, propertyBox1, attribute);
95
96    EXPECT_TRUE(handleDict->GetBox(0) != nullptr);
97    EXPECT_EQ(handleDict->GetValue(0).GetInt(), 123);
98
99    EXPECT_TRUE(newDict->GetBox(0) != nullptr);
100    EXPECT_EQ(newDict->GetValue(0).GetInt(), 123);
101    EXPECT_TRUE(newDict->GetBox(1) != nullptr);
102    EXPECT_EQ(newDict->GetValue(1).GetInt(), 100);
103}
104
105/**
106 * @tc.name: GetAttributes
107 * @tc.desc: Check whether the Attributes Get through calling SetAttributes function is within expectations.
108 * @tc.type: FUNC
109 * @tc.require:
110 */
111HWTEST_F_L0(GlobalDictionaryTest, GetAttributes)
112{
113    // create GlobalDictionary
114    int numberofElements = 4;
115    JSHandle<GlobalDictionary> handleDict = GlobalDictionary::Create(thread, numberofElements);
116    EXPECT_TRUE(*handleDict != nullptr);
117    // set attributes call SetAttributes function
118    for (int i = 0; i < numberofElements; i++) {
119        handleDict->SetAttributes(thread, i, PropertyAttributes(i));
120        EXPECT_EQ(handleDict->GetAttributes(i).GetPropertyMetaData(), i);
121    }
122}
123
124/**
125 * @tc.name: ClearEntry
126 * @tc.desc: Create dictionary and set entry calling SetEntry function,Check whether Attributes is
127 *           the same as Attributes after calling the ClearEntry function.
128 * @tc.type: FUNC
129 * @tc.require:
130 */
131HWTEST_F_L0(GlobalDictionaryTest, ClearEntry)
132{
133    // create GlobalDictionary
134    int numberofElements = 16;
135    JSHandle<GlobalDictionary> handleDict = GlobalDictionary::Create(thread, numberofElements);
136    EXPECT_TRUE(*handleDict != nullptr);
137    // set entry
138    for (int i = 0; i < numberofElements; i++) {
139        JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(i));
140        JSHandle<JSTaggedValue> handleValueKey(JSTaggedValue::ToString(thread, handleValue));
141        handleDict->SetEntry(thread, i, handleValueKey.GetTaggedValue(),
142                                        handleValue.GetTaggedValue(), PropertyAttributes(i));
143    }
144    // check attributes in three
145    EXPECT_EQ(handleDict->GetAttributes(3).GetPropertyMetaData(), 3);
146    handleDict->ClearEntry(thread, 3);
147    EXPECT_EQ(handleDict->GetAttributes(3).GetPropertyMetaData(), 0);
148}
149
150/**
151 * @tc.name: UpdateValueAndAttributes
152 * @tc.desc: Update value and Attributes through calling UpdateValueAndAttributes function.
153 * @tc.type: FUNC
154 * @tc.require:
155 */
156HWTEST_F_L0(GlobalDictionaryTest, UpdateValueAndAttributes)
157{
158    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
159    // create GlobalDictionary
160    int numberofElements = 16;
161    JSHandle<GlobalDictionary> handleDict = GlobalDictionary::Create(thread, numberofElements);
162    EXPECT_TRUE(*handleDict != nullptr);
163    // set entry
164    for (int i = 0; i < numberofElements; i++) {
165        JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(i));
166        JSHandle<JSTaggedValue> propertyBox(factory->NewPropertyBox(handleValue));
167        JSHandle<JSTaggedValue> handleValueKey(JSTaggedValue::ToString(thread, handleValue));
168        handleDict->SetEntry(thread, i, handleValueKey.GetTaggedValue(),
169                                        propertyBox.GetTaggedValue(), PropertyAttributes(i));
170    }
171    // check attributes in five
172    EXPECT_EQ(handleDict->GetAttributes(5).GetPropertyMetaData(), 5);
173    EXPECT_EQ(handleDict->GetValue(5).GetInt(), 5);
174    // Update value and attributes
175    for (int i = 0; i < numberofElements; i++) {
176        JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(static_cast<int>(i + 1)));
177        JSHandle<JSTaggedValue> propertyBox(factory->NewPropertyBox(handleValue));
178        handleDict->UpdateValueAndAttributes(thread, i, propertyBox.GetTaggedValue(),
179                                             PropertyAttributes(static_cast<int>(i + 1)));
180    }
181    // check attributes in five
182    EXPECT_EQ(handleDict->GetAttributes(5).GetPropertyMetaData(), 6);
183    EXPECT_EQ(handleDict->GetValue(5).GetInt(), 6);
184}
185
186/**
187 * @tc.name: GetAllKeys
188 * @tc.desc: Get all Attributes from dictionary and store it in the TaggedArray.
189 * @tc.type: FUNC
190 * @tc.require:
191 */
192HWTEST_F_L0(GlobalDictionaryTest, GetAllKeys)
193{
194    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
195    // create GlobalDictionary
196    int numberofElements = 16;
197    std::vector<CString> nameKey = {"a", "b", "c", "d", "e", "f",
198                                    "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"};
199    JSHandle<GlobalDictionary> handleDict = GlobalDictionary::Create(thread, numberofElements);
200    EXPECT_TRUE(*handleDict != nullptr);
201    JSMutableHandle<GlobalDictionary> dictHandle(thread, handleDict);
202    for (int i = 0; i < numberofElements; i++) {
203        JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(i));
204        JSHandle<JSTaggedValue> handleNameKey(factory->NewFromASCII(nameKey[i]));
205        PropertyAttributes metaData;
206        // insert value
207        JSHandle<GlobalDictionary> dict(GlobalDictionary::PutIfAbsent(thread, dictHandle,
208                                                                      handleNameKey, handleValue, metaData));
209        dictHandle.Update(dict.GetTaggedValue());
210    }
211    uint32_t offset = 7;
212    // keyArray capacity must be enough for dictionary
213    int arraySize = numberofElements + static_cast<int>(offset);
214    JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(arraySize);
215    dictHandle->GetAllKeys(thread, offset, *keyArray);
216    // Skip the first seven positions
217    for (uint32_t i = 0; i < offset; i++) {
218        EXPECT_TRUE(keyArray->Get(i).IsHole());
219    }
220    // check key name
221    JSHandle<EcmaString> resultFirstKey(thread, keyArray->Get(offset));
222    JSHandle<EcmaString> resultLastKey(thread, keyArray->Get(arraySize - 1));
223    EXPECT_EQ(nameKey[0], EcmaStringAccessor(resultFirstKey).ToCString().c_str());
224    EXPECT_EQ(nameKey[15], EcmaStringAccessor(resultLastKey).ToCString().c_str());
225}
226
227/**
228 * @tc.name: GetEnumAllKeys
229 * @tc.desc: Get all Enumerable Attributes from dictionary and store it in the TaggedArray.
230 * @tc.type: FUNC
231 * @tc.require:
232 */
233HWTEST_F_L0(GlobalDictionaryTest, GetEnumAllKeys)
234{
235    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
236    // create GlobalDictionary
237    int numberofElements = 16;
238    std::vector<CString> nameKey = {"a", "b", "c", "d", "e", "f",
239                                    "g", "h", "i", "j", "k", "l", "m", "n", "o", "q"};
240    JSHandle<GlobalDictionary> handleDict = GlobalDictionary::Create(thread, numberofElements);
241    EXPECT_TRUE(*handleDict != nullptr);
242    JSMutableHandle<GlobalDictionary> dictHandle(thread, handleDict);
243    bool enumerable;
244    for (int i = 0; i < numberofElements; i++) {
245        JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(i));
246        JSHandle<JSTaggedValue> handleNameKey(factory->NewFromASCII(nameKey[i]));
247        PropertyAttributes metaData;
248        enumerable = true;
249        if (!(i % 2)) {
250            enumerable = false;
251        }
252        metaData.SetEnumerable(enumerable);
253        // insert value
254        JSHandle<GlobalDictionary> dict(GlobalDictionary::PutIfAbsent(thread, dictHandle,
255                                                                      handleNameKey, handleValue, metaData));
256        dictHandle.Update(dict.GetTaggedValue());
257    }
258    uint32_t offset = 7;
259    uint32_t keys = 0;
260    // keyArray capacity must be enough for dictionary
261    uint32_t arraySize = static_cast<uint32_t>(numberofElements) + offset;
262    JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(arraySize);
263    dictHandle->GetEnumAllKeys(thread, offset, *keyArray, &keys);
264    EXPECT_EQ(keys, 8U);
265    JSHandle<EcmaString> resultFirstKey(thread, keyArray->Get(offset));
266    JSHandle<EcmaString> resultLastKey(thread, keyArray->Get(offset + keys - 1U));
267    EXPECT_EQ(nameKey[1], EcmaStringAccessor(resultFirstKey).ToCString().c_str());
268    EXPECT_EQ(nameKey[15], EcmaStringAccessor(resultLastKey).ToCString().c_str());
269}
270
271/**
272 * @tc.name: CompKey
273 * @tc.desc: The second element in the two structures is compared.If it is less than,return true.
274 * @tc.type: FUNC
275 * @tc.require:
276 */
277HWTEST_F_L0(GlobalDictionaryTest, CompKey)
278{
279    std::pair<JSTaggedValue, uint32_t> a(JSTaggedValue(1), 1);
280    std::pair<JSTaggedValue, uint32_t> b(JSTaggedValue(2), 2);
281    std::pair<JSTaggedValue, uint32_t> c(JSTaggedValue(0), 0);
282    EXPECT_TRUE(GlobalDictionary::CompKey(a, b));
283    EXPECT_TRUE(!GlobalDictionary::CompKey(a, c));
284}
285
286/**
287 * @tc.name: InvalidatePropertyBox
288 * @tc.desc: Invalidate value which is Configurable in a dictionary.
289 * @tc.type: FUNC
290 * @tc.require:
291 */
292HWTEST_F_L0(GlobalDictionaryTest, InvalidatePropertyBox)
293{
294    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
295    // create GlobalDictionary
296    int numberofElements = 16;
297    std::vector<CString> nameKey = {"a", "b", "s", "t", "e", "f",
298                                    "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"};
299    JSHandle<GlobalDictionary> handleDict = GlobalDictionary::Create(thread, numberofElements);
300    EXPECT_TRUE(*handleDict != nullptr);
301    int invalidatedSet = 3;
302    int invalidatedPosition = 12;
303    for (int i = 0; i < numberofElements; i++) {
304        JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII(nameKey[i]));
305        JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(i));
306        JSHandle<JSTaggedValue> propertyBox(factory->NewPropertyBox(handleValue));
307        PropertyAttributes metaData;
308        if (i == invalidatedSet) {
309            metaData.SetDictionaryOrder(invalidatedPosition);
310        }
311        else if (i == invalidatedPosition) {
312            metaData.SetConfigurable(true);
313        }
314        handleDict->SetEntry(thread, i, handleKey.GetTaggedValue(),
315                                        propertyBox.GetTaggedValue(), metaData);
316    }
317    // calling InvalidatePropertyBox function to Invalidate the PropertyBox
318    PropertyAttributes newAttr(10);
319    GlobalDictionary::InvalidatePropertyBox(thread, handleDict, invalidatedPosition);
320    EXPECT_EQ(handleDict->GetAttributes(invalidatedPosition).GetBoxType(), PropertyBoxType::MUTABLE);
321    EXPECT_EQ(handleDict->GetAttributes(invalidatedSet).GetDictionaryOrder(), invalidatedPosition);
322    EXPECT_EQ(handleDict->GetValue(invalidatedPosition).GetInt(), invalidatedPosition);
323}
324}  // namespace panda::test