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/ecma_vm.h"
17 #include "ecmascript/js_symbol.h"
18 #include "ecmascript/object_factory.h"
19 #include "ecmascript/property_attributes.h"
20 #include "ecmascript/tagged_hash_table.h"
21 #include "ecmascript/tests/test_helper.h"
22 #include "ecmascript/transitions_dictionary.h"
23
24 using namespace panda::ecmascript;
25
26 namespace panda::test {
27 class TransitionsDictionaryTest : public testing::Test {
28 public:
SetUpTestCase()29 static void SetUpTestCase()
30 {
31 GTEST_LOG_(INFO) << "SetUpTestCase";
32 }
33
TearDownTestCase()34 static void TearDownTestCase()
35 {
36 GTEST_LOG_(INFO) << "TearDownCase";
37 }
38
SetUp()39 void SetUp() override
40 {
41 TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
42 }
43
TearDown()44 void TearDown() override
45 {
46 TestHelper::DestroyEcmaVMWithScope(instance, scope);
47 }
48
49 EcmaVM *instance {nullptr};
50 EcmaHandleScope *scope {nullptr};
51 JSThread *thread {nullptr};
52 };
53
HWTEST_F_L0(TransitionsDictionaryTest,IsMatch)54 HWTEST_F_L0(TransitionsDictionaryTest, IsMatch)
55 {
56 JSHandle<JSTaggedValue> key(thread, JSTaggedValue::True());
57 JSHandle<JSTaggedValue> otherKey(thread, JSTaggedValue::False());
58 JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
59 JSHandle<JSTaggedValue> otherDetails(thread, JSTaggedValue::Null());
60 bool result = TransitionsDictionary::IsMatch(key.GetTaggedValue(), metaData.GetTaggedValue(),
61 otherKey.GetTaggedValue(), otherDetails.GetTaggedValue());
62 EXPECT_FALSE(result);
63
64 result = TransitionsDictionary::IsMatch(key.GetTaggedValue(), metaData.GetTaggedValue(),
65 key.GetTaggedValue(), metaData.GetTaggedValue());
66 EXPECT_TRUE(result);
67 }
68
HWTEST_F_L0(TransitionsDictionaryTest,Hash)69 HWTEST_F_L0(TransitionsDictionaryTest, Hash)
70 {
71 auto vm = thread->GetEcmaVM();
72 auto factory = vm->GetFactory();
73
74 // test when key is string.
75 JSHandle<JSTaggedValue> key1(factory->NewFromStdString("k"));
76 JSHandle<JSTaggedValue> metaData1(thread, JSTaggedValue(1)); // test metaData : 1
77 int hash = TransitionsDictionary::Hash(key1.GetTaggedValue(), metaData1.GetTaggedValue());
78 // "k" : 107, hashSeed : 0, shift : 5, metaData : 1
79 EXPECT_EQ(hash, 108); // 108 : (0 << 5) - 0 + 107 + 1
80
81 JSHandle<JSTaggedValue> key2(factory->NewFromStdString("key"));
82 hash = TransitionsDictionary::Hash(key2.GetTaggedValue(), metaData1.GetTaggedValue());
83 EXPECT_EQ(hash, 106080);
84
85 // test when key is symbol.
86 JSHandle<JSTaggedValue> symbolName(factory->NewFromStdString("s"));
87 JSHandle<JSSymbol> privateNameSymbol = factory->NewPrivateNameSymbol(symbolName);
88 JSHandle<JSTaggedValue> symbolValue = JSHandle<JSTaggedValue>::Cast(privateNameSymbol);
89 JSHandle<JSTaggedValue> metaData2(thread, JSTaggedValue(2)); // test metaData : 2
90 hash = TransitionsDictionary::Hash(symbolValue.GetTaggedValue(), metaData2.GetTaggedValue());
91 EXPECT_EQ(hash, 117); // 117 : 115 + 2
92 }
93
HWTEST_F_L0(TransitionsDictionaryTest,GetKeyIndex)94 HWTEST_F_L0(TransitionsDictionaryTest, GetKeyIndex)
95 {
96 int entry = 10;
97 EXPECT_EQ(TransitionsDictionary::GetKeyIndex(entry), 33); // 33 : 3 + 10 * 3 + 0
98 }
99
HWTEST_F_L0(TransitionsDictionaryTest,GetValueIndex)100 HWTEST_F_L0(TransitionsDictionaryTest, GetValueIndex)
101 {
102 int entry = 10;
103 EXPECT_EQ(TransitionsDictionary::GetValueIndex(entry), 34); // 34 : 3 + 10 * 3 + 1
104 }
105
HWTEST_F_L0(TransitionsDictionaryTest,GetEntryIndex)106 HWTEST_F_L0(TransitionsDictionaryTest, GetEntryIndex)
107 {
108 int entry = 10;
109 EXPECT_EQ(TransitionsDictionary::GetEntryIndex(entry), 33); // 33 : 3 + 10 * 3
110 }
111
HWTEST_F_L0(TransitionsDictionaryTest,Create)112 HWTEST_F_L0(TransitionsDictionaryTest, Create)
113 {
114 int numberOfElements = 8;
115 JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
116 EXPECT_EQ(transDic->GetLength(), 27U); // 27 : 3 + 8 * 3
117 EXPECT_EQ(transDic->EntriesCount(), 0);
118 EXPECT_EQ(transDic->HoleEntriesCount(), 0);
119 }
120
HWTEST_F_L0(TransitionsDictionaryTest,Shrink)121 HWTEST_F_L0(TransitionsDictionaryTest, Shrink)
122 {
123 auto vm = thread->GetEcmaVM();
124 auto factory = vm->GetFactory();
125 int numberOfElements = 64;
126 JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
127 EXPECT_EQ(transDic->GetLength(), 195U); // 195 : 3 + 64 * 3
128 EXPECT_EQ(transDic->EntriesCount(), 0);
129
130 JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
131 int eleNum = 7;
132 for (int index = 0; index < eleNum; index++) {
133 std::string keyStr = "key" + std::to_string(index);
134 std::string valueStr = "value" + std::to_string(index);
135 JSHandle<JSTaggedValue> key(factory->NewFromStdString(keyStr));
136 JSHandle<JSTaggedValue> value(factory->NewFromStdString(valueStr));
137 TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
138 }
139 JSHandle<TransitionsDictionary> transDicAfterShink = TransitionsDictionary::Shrink(thread, transDic);
140 EXPECT_EQ(transDicAfterShink->GetLength(), 51U); // (1 << (32 - Clz((7 + (7 >> 1)) - 1))) * 3 + 3
141 EXPECT_EQ(transDic->EntriesCount(), eleNum);
142 }
143
HWTEST_F_L0(TransitionsDictionaryTest,Get_Set_Attributes)144 HWTEST_F_L0(TransitionsDictionaryTest, Get_Set_Attributes)
145 {
146 int numberOfElements = 8;
147 JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
148 uint32_t length = transDic->GetLength();
149 EXPECT_EQ(length, 27U); // 27 : 3 + 8 * 3
150
151 for (int index = 0; index < numberOfElements; index++) {
152 transDic->SetAttributes(thread, index, JSTaggedValue(index));
153 JSTaggedValue value = transDic->GetAttributes(index);
154 EXPECT_EQ(value, JSTaggedValue(index));
155 }
156 }
157
HWTEST_F_L0(TransitionsDictionaryTest,SetEntry)158 HWTEST_F_L0(TransitionsDictionaryTest, SetEntry)
159 {
160 auto vm = thread->GetEcmaVM();
161 auto factory = vm->GetFactory();
162 int numberOfElements = 8;
163 JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
164 JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
165 for (int index = 0; index < numberOfElements; index++) {
166 std::string keyStr = "key" + std::to_string(index);
167 std::string valueStr = "value" + std::to_string(index);
168 JSHandle<JSTaggedValue> key(factory->NewFromStdString(keyStr));
169 JSHandle<JSTaggedValue> value(factory->NewFromStdString(valueStr));
170 transDic->SetEntry(thread, index, key.GetTaggedValue(), value.GetTaggedValue(), metaData.GetTaggedValue());
171 EXPECT_EQ(transDic->GetKey(index), key.GetTaggedValue());
172 }
173 }
174
HWTEST_F_L0(TransitionsDictionaryTest,FindEntry)175 HWTEST_F_L0(TransitionsDictionaryTest, FindEntry)
176 {
177 auto vm = thread->GetEcmaVM();
178 auto factory = vm->GetFactory();
179 int numberOfElements = 8;
180 JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
181 JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
182 for (int index = 0; index < numberOfElements; index++) {
183 std::string keyStr = "key" + std::to_string(index);
184 std::string valueStr = "value" + std::to_string(index);
185 JSHandle<JSTaggedValue> key(factory->NewFromStdString(keyStr));
186 JSHandle<JSTaggedValue> value(factory->NewFromStdString(valueStr));
187 transDic = TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
188 int foundEntry = transDic->FindEntry(key.GetTaggedValue(), metaData.GetTaggedValue());
189 EXPECT_EQ(index + 3, foundEntry); // 3 : table header size
190 }
191 }
192
HWTEST_F_L0(TransitionsDictionaryTest,RemoveElement)193 HWTEST_F_L0(TransitionsDictionaryTest, RemoveElement)
194 {
195 auto vm = thread->GetEcmaVM();
196 auto factory = vm->GetFactory();
197 int numberOfElements = 8;
198 JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
199 JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
200 for (int index = 0; index < numberOfElements; index++) {
201 std::string keyStr = "key" + std::to_string(index);
202 std::string valueStr = "value" + std::to_string(index);
203 JSHandle<JSTaggedValue> key(factory->NewFromStdString(keyStr));
204 JSHandle<JSTaggedValue> value(factory->NewFromStdString(valueStr));
205 transDic = TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
206 }
207 JSHandle<JSTaggedValue> key7(factory->NewFromStdString("key7")); // test remove element by "key7"
208 int foundEntry = transDic->FindEntry(key7.GetTaggedValue(), metaData.GetTaggedValue());
209 EXPECT_EQ(foundEntry, 7 + 3);
210 EXPECT_EQ(transDic->EntriesCount(), 8);
211
212 transDic->RemoveElement(thread, foundEntry);
213 foundEntry = transDic->FindEntry(key7.GetTaggedValue(), metaData.GetTaggedValue());
214 EXPECT_EQ(foundEntry, -1); // -1 : not found entry
215 EXPECT_EQ(transDic->EntriesCount(), 7);
216 }
217
HWTEST_F_L0(TransitionsDictionaryTest,PutIfAbsent)218 HWTEST_F_L0(TransitionsDictionaryTest, PutIfAbsent)
219 {
220 auto vm = thread->GetEcmaVM();
221 auto factory = vm->GetFactory();
222 int numberOfElements = 8;
223 JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
224 JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
225 vm->SetEnableForceGC(false);
226 for (int index = 0; index < numberOfElements; index++) {
227 std::string keyStr = "key" + std::to_string(index);
228 std::string valueStr = "value" + std::to_string(index);
229 JSHandle<JSTaggedValue> key(factory->NewFromStdString(keyStr));
230 JSHandle<JSTaggedValue> value(factory->NewFromStdString(valueStr));
231 transDic = TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
232 int foundEntry = transDic->FindEntry(key.GetTaggedValue(), metaData.GetTaggedValue());
233 EXPECT_EQ(foundEntry, index + 3);
234
235 JSHandle<JSTaggedValue> foundValue(thread, transDic->GetValue(foundEntry));
236 JSHandle<JSTaggedValue> weakValue(thread, value->CreateAndGetWeakRef());
237 EXPECT_EQ(foundValue.GetTaggedValue(), weakValue.GetTaggedValue());
238 }
239 vm->SetEnableForceGC(true);
240 }
241
HWTEST_F_L0(TransitionsDictionaryTest,Remove)242 HWTEST_F_L0(TransitionsDictionaryTest, Remove)
243 {
244 auto vm = thread->GetEcmaVM();
245 auto factory = vm->GetFactory();
246 int numberOfElements = 64;
247 int eleNum = 7;
248 JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
249 JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
250 for (int index = 0; index < eleNum; index++) {
251 std::string keyStr = "key" + std::to_string(index);
252 std::string valueStr = "value" + std::to_string(index);
253 JSHandle<JSTaggedValue> key(factory->NewFromStdString(keyStr));
254 JSHandle<JSTaggedValue> value(factory->NewFromStdString(valueStr));
255 transDic = TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
256 }
257 JSHandle<JSTaggedValue> key6(factory->NewFromStdString("key6")); // test remove element by "key6"
258 EXPECT_EQ(transDic->EntriesCount(), 7);
259 EXPECT_EQ(transDic->GetLength(), 195U); // 195 : 3 + 64 * 3
260
261 transDic = TransitionsDictionary::Remove(thread, transDic, key6, metaData.GetTaggedValue());
262 EXPECT_EQ(transDic->EntriesCount(), 6); // 6 : 7 - 1
263 EXPECT_EQ(transDic->GetLength(), 51U); // (1 << (32 - Clz((6 + (6 >> 1)) - 1))) * 3 + 3
264 }
265
HWTEST_F_L0(TransitionsDictionaryTest,Rehash)266 HWTEST_F_L0(TransitionsDictionaryTest, Rehash)
267 {
268 auto vm = thread->GetEcmaVM();
269 auto factory = vm->GetFactory();
270 int numberOfElements = 64;
271 int eleNum = 7;
272 JSHandle<TransitionsDictionary> transDic = TransitionsDictionary::Create(thread, numberOfElements);
273 JSHandle<JSTaggedValue> metaData(thread, JSTaggedValue::Undefined());
274 for (int index = 0; index < eleNum; index++) {
275 std::string keyStr = "key" + std::to_string(index);
276 std::string valueStr = "value" + std::to_string(index);
277 JSHandle<JSTaggedValue> key(factory->NewFromStdString(keyStr));
278 JSHandle<JSTaggedValue> value(factory->NewFromStdString(valueStr));
279 transDic = TransitionsDictionary::PutIfAbsent(thread, transDic, key, value, metaData);
280 }
281 EXPECT_EQ(transDic->HoleEntriesCount(), 0);
282
283 int lastEntry = 7 + 3;
284 transDic->RemoveElement(thread, lastEntry); // remove one element
285 EXPECT_EQ(transDic->HoleEntriesCount(), 1);
286
287 transDic->Rehash(thread, *transDic);
288 EXPECT_EQ(transDic->HoleEntriesCount(), 0);
289 }
290 } // namespace panda::test