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/tests/test_helper.h"
17
18 #include "ecmascript/ecma_vm.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/js_handle.h"
21 #include "ecmascript/js_hclass.h"
22 #include "ecmascript/js_thread.h"
23 #include "ecmascript/jspandafile/program_object.h"
24 #include "ecmascript/object_factory.h"
25 #include "ecmascript/snapshot/mem/snapshot.h"
26 #include "ecmascript/snapshot/mem/snapshot_processor.h"
27 #include "ecmascript/snapshot/tests/snapshot_mock.h"
28 #include "ecmascript/ts_types/ts_manager.h"
29
30 using namespace panda::ecmascript;
31
32 namespace panda::test {
33 class SnapshotTest : public testing::Test {
34 public:
SetUpTestCase()35 static void SetUpTestCase()
36 {
37 GTEST_LOG_(INFO) << "SetUpTestCase";
38 }
39
TearDownTestCase()40 static void TearDownTestCase()
41 {
42 GTEST_LOG_(INFO) << "TearDownCase";
43 }
44
SetUp()45 void SetUp() override
46 {
47 JSRuntimeOptions options;
48 ecmaVm = JSNApi::CreateEcmaVM(options);
49 ASSERT_TRUE(ecmaVm != nullptr) << "Cannot create EcmaVM";
50 thread = ecmaVm->GetJSThread();
51 scope = new EcmaHandleScope(thread);
52 }
53
TearDown()54 void TearDown() override
55 {
56 delete scope;
57 scope = nullptr;
58 ecmaVm->SetEnableForceGC(false);
59 thread->ClearException();
60 JSNApi::DestroyJSVM(ecmaVm);
61 }
62
CompatibilityHelper(base::FileHeaderBase::VersionType serializeVersion,base::FileHeaderBase::VersionType deserializeVersion,bool expected)63 void CompatibilityHelper(base::FileHeaderBase::VersionType serializeVersion,
64 base::FileHeaderBase::VersionType deserializeVersion, bool expected)
65 {
66 static constexpr uint32_t ARRAY_SIZE = 300;
67 static constexpr uint32_t KILO_BITS = 1024;
68 auto factory = ecmaVm->GetFactory();
69 auto env = ecmaVm->GetGlobalEnv();
70
71 JSHandle<TaggedArray> array1 = factory->NewTaggedArray(ARRAY_SIZE * KILO_BITS / sizeof(uint8_t));
72
73 JSHandle<JSFunction> funcFunc(env->GetFunctionFunction());
74 array1->Set(thread, 0, funcFunc.GetTaggedValue());
75
76 CString fileName = "snapshot";
77 SnapshotMock snapshotSerialize(ecmaVm);
78 snapshotSerialize.SetLastVersion(serializeVersion);
79 // serialize in earlier version tag
80 snapshotSerialize.Serialize(*array1, nullptr, fileName);
81
82 JSRuntimeOptions options;
83 EcmaVM *ecmaVm2 = JSNApi::CreateEcmaVM(options);
84 // deserialize with last version tag
85 SnapshotMock snapshotDeserialize(ecmaVm2);
86 snapshotDeserialize.SetLastVersion(deserializeVersion);
87 EXPECT_EQ(snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName), expected);
88
89 ASSERT_EQ(const_cast<Heap *>(ecmaVm2->GetHeap())->GetHugeObjectSpace()->GetFirstRegion() != nullptr, expected);
90 JSNApi::DestroyJSVM(ecmaVm2);
91 std::remove(fileName.c_str());
92 }
93
94 EcmaVM *ecmaVm {nullptr};
95 ecmascript::EcmaHandleScope *scope {nullptr};
96 JSThread *thread {nullptr};
97 };
98
HWTEST_F_L0(SnapshotTest,SerializeConstPool)99 HWTEST_F_L0(SnapshotTest, SerializeConstPool)
100 {
101 auto factory = ecmaVm->GetFactory();
102 auto env = ecmaVm->GetGlobalEnv();
103
104 JSHandle<ConstantPool> constpool = factory->NewConstantPool(10);
105 JSHandle<JSFunction> funcFunc(env->GetFunctionFunction());
106 JSHandle<JSFunction> dateFunc(env->GetDateFunction());
107 JSHandle<JSFunction> numberFunc(env->GetNumberFunction());
108 JSHandle<EcmaString> str1 = factory->NewFromASCII("str11");
109 JSHandle<EcmaString> str2 = factory->NewFromASCII("str22");
110 JSHandle<EcmaString> str3 = factory->NewFromASCII("str333333333333");
111 JSHandle<EcmaString> str4 = factory->ConcatFromString(str1, str3);
112 JSHandle<EcmaString> str5 = factory->NewFromASCII("str44");
113 constpool->SetObjectToCache(thread, 0, funcFunc.GetTaggedValue());
114 constpool->SetObjectToCache(thread, 1, dateFunc.GetTaggedValue());
115 constpool->SetObjectToCache(thread, 2, str1.GetTaggedValue());
116 constpool->SetObjectToCache(thread, 3, numberFunc.GetTaggedValue());
117 constpool->SetObjectToCache(thread, 4, str2.GetTaggedValue());
118 constpool->SetObjectToCache(thread, 5, str1.GetTaggedValue());
119 constpool->SetObjectToCache(thread, 6, str3.GetTaggedValue());
120 constpool->SetObjectToCache(thread, 7, str4.GetTaggedValue());
121 constpool->SetObjectToCache(thread, 8, str4.GetTaggedValue());
122 constpool->SetObjectToCache(thread, 9, str5.GetTaggedValue());
123
124 CString fileName = "snapshot";
125 Snapshot snapshotSerialize(ecmaVm);
126 // serialize
127 snapshotSerialize.Serialize(*constpool, nullptr, fileName);
128 // deserialize
129 Snapshot snapshotDeserialize(ecmaVm);
130 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName);
131
132 auto beginRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetOldSpace()->GetCurrentRegion();
133 auto constpool1 = reinterpret_cast<ConstantPool *>(beginRegion->GetBegin());
134 EXPECT_EQ(constpool->GetClass()->SizeFromJSHClass(*constpool),
135 constpool1->GetClass()->SizeFromJSHClass(constpool1));
136 EXPECT_TRUE(constpool1->GetObjectFromCache(0).IsJSFunction());
137 EXPECT_TRUE(constpool1->GetObjectFromCache(1).IsJSFunction());
138 EXPECT_TRUE(constpool1->GetObjectFromCache(3).IsJSFunction());
139 EcmaString *str11 = reinterpret_cast<EcmaString *>(constpool1->Get(2).GetTaggedObject());
140 EcmaString *str22 = reinterpret_cast<EcmaString *>(constpool1->Get(4).GetTaggedObject());
141 EcmaString *str33 = reinterpret_cast<EcmaString *>(constpool1->Get(5).GetTaggedObject());
142 EcmaString *str44 = reinterpret_cast<EcmaString *>(constpool1->Get(6).GetTaggedObject());
143 EcmaString *str55 = reinterpret_cast<EcmaString *>(constpool1->Get(7).GetTaggedObject());
144 EcmaString *str66 = reinterpret_cast<EcmaString *>(constpool1->Get(8).GetTaggedObject());
145 EcmaString *str77 = reinterpret_cast<EcmaString *>(constpool1->Get(9).GetTaggedObject());
146 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str11).ToCString().c_str(), "str11"), 0);
147 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str22).ToCString().c_str(), "str22"), 0);
148 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str33).ToCString().c_str(), "str11"), 0);
149 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str44).ToCString().c_str(), "str333333333333"), 0);
150 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str55).ToCString().c_str(), "str11str333333333333"), 0);
151 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str66).ToCString().c_str(), "str11str333333333333"), 0);
152 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str77).ToCString().c_str(), "str44"), 0);
153 std::remove(fileName.c_str());
154 }
155
HWTEST_F_L0(SnapshotTest,SerializeDifferentSpace)156 HWTEST_F_L0(SnapshotTest, SerializeDifferentSpace)
157 {
158 auto factory = ecmaVm->GetFactory();
159 JSHandle<ConstantPool> constpool = factory->NewConstantPool(400);
160 for (int i = 0; i < 100; i++) {
161 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
162 constpool->SetObjectToCache(thread, i, array.GetTaggedValue());
163 }
164 for (int i = 0; i < 100; i++) {
165 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
166 constpool->SetObjectToCache(thread, i + 100, array.GetTaggedValue());
167 }
168 for (int i = 0; i < 100; i++) {
169 JSHandle<ConstantPool> constpool1 = factory->NewConstantPool(10);
170 constpool->SetObjectToCache(thread, i + 300, constpool1.GetTaggedValue());
171 }
172
173 CString fileName = "snapshot";
174 Snapshot snapshotSerialize(ecmaVm);
175 // serialize
176 snapshotSerialize.Serialize(*constpool, nullptr, fileName);
177 // deserialize
178 Snapshot snapshotDeserialize(ecmaVm);
179 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName);
180
181 auto beginRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetOldSpace()->GetCurrentRegion();
182 auto constpool1 = reinterpret_cast<ConstantPool *>(beginRegion->GetBegin());
183 EXPECT_EQ(constpool->GetClass()->SizeFromJSHClass(*constpool),
184 constpool1->GetClass()->SizeFromJSHClass(constpool1));
185 EXPECT_TRUE(constpool1->GetObjectFromCache(0).IsTaggedArray());
186 EXPECT_TRUE(constpool1->GetObjectFromCache(100).IsTaggedArray());
187 EXPECT_TRUE(constpool1->GetObjectFromCache(300).IsTaggedArray());
188
189 auto obj1 = constpool1->GetObjectFromCache(0).GetTaggedObject();
190 EXPECT_TRUE(Region::ObjectAddressToRange(obj1)->InOldSpace());
191 auto obj2 = constpool1->GetObjectFromCache(100).GetTaggedObject();
192 EXPECT_TRUE(Region::ObjectAddressToRange(obj2)->InOldSpace());
193 std::remove(fileName.c_str());
194 }
195
HWTEST_F_L0(SnapshotTest,SerializeMultiFile)196 HWTEST_F_L0(SnapshotTest, SerializeMultiFile)
197 {
198 auto factory = ecmaVm->GetFactory();
199 JSHandle<ConstantPool> constpool1 = factory->NewConstantPool(400);
200 JSHandle<ConstantPool> constpool2 = factory->NewConstantPool(400);
201 for (int i = 0; i < 100; i++) {
202 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
203 constpool1->SetObjectToCache(thread, i, array.GetTaggedValue());
204 constpool2->SetObjectToCache(thread, i, array.GetTaggedValue());
205 }
206 for (int i = 0; i < 100; i++) {
207 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
208 constpool1->SetObjectToCache(thread, i + 100, array.GetTaggedValue());
209 constpool2->SetObjectToCache(thread, i + 100, array.GetTaggedValue());
210 }
211 for (int i = 0; i < 100; i++) {
212 JSHandle<ConstantPool> constpool3 = factory->NewConstantPool(10);
213 constpool1->SetObjectToCache(thread, i + 300, constpool3.GetTaggedValue());
214 constpool2->SetObjectToCache(thread, i + 300, constpool3.GetTaggedValue());
215 }
216
217 CString fileName1 = "snapshot1";
218 CString fileName2 = "snapshot2";
219 Snapshot snapshotSerialize(ecmaVm);
220 // serialize
221 snapshotSerialize.Serialize(*constpool1, nullptr, fileName1);
222 snapshotSerialize.Serialize(*constpool2, nullptr, fileName2);
223 // deserialize
224 Snapshot snapshotDeserialize(ecmaVm);
225 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName1);
226 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName2);
227
228 auto beginRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetOldSpace()->GetCurrentRegion();
229 auto constpool = reinterpret_cast<ConstantPool *>(beginRegion->GetBegin());
230 EXPECT_TRUE(constpool->GetObjectFromCache(0).IsTaggedArray());
231 EXPECT_TRUE(constpool->GetObjectFromCache(100).IsTaggedArray());
232 auto obj1 = constpool->GetObjectFromCache(0).GetTaggedObject();
233 EXPECT_TRUE(Region::ObjectAddressToRange(obj1)->InOldSpace());
234 auto obj2 = constpool->GetObjectFromCache(100).GetTaggedObject();
235 EXPECT_TRUE(Region::ObjectAddressToRange(obj2)->InOldSpace());
236 std::remove(fileName1.c_str());
237 std::remove(fileName2.c_str());
238 }
239
HWTEST_F_L0(SnapshotTest,SerializeBuiltins)240 HWTEST_F_L0(SnapshotTest, SerializeBuiltins)
241 {
242 // remove builtins.snapshot file first if exist
243 CString fileName = "builtins.snapshot";
244 std::remove(fileName.c_str());
245 // generate builtins.snapshot file
246 JSRuntimeOptions options1;
247 options1.SetArkProperties(ArkProperties::ENABLE_SNAPSHOT_SERIALIZE);
248 EcmaVM *ecmaVm1 = JSNApi::CreateEcmaVM(options1);
249 JSNApi::DestroyJSVM(ecmaVm1);
250
251 // create EcmaVM use builtins deserialzie
252 JSRuntimeOptions options2;
253 options2.SetArkProperties(ArkProperties::ENABLE_SNAPSHOT_DESERIALIZE);
254 EcmaVM *ecmaVm2 = JSNApi::CreateEcmaVM(options2);
255 EXPECT_TRUE(ecmaVm2->GetGlobalEnv()->GetClass()->GetObjectType() == JSType::GLOBAL_ENV);
256 auto globalConst = const_cast<GlobalEnvConstants *>(ecmaVm2->GetJSThread()->GlobalConstants());
257 size_t hclassEndIndex = static_cast<size_t>(ConstantIndex::UNDEFINED_INDEX);
258 size_t hclassIndex = 0;
259 globalConst->VisitRangeSlot([&hclassIndex, &hclassEndIndex]([[maybe_unused]] Root type,
260 ObjectSlot start, ObjectSlot end) {
261 while (start < end) {
262 JSTaggedValue object(start.GetTaggedType());
263 start++;
264 if (hclassIndex < hclassEndIndex) {
265 EXPECT_TRUE(object.IsJSHClass());
266 }
267 hclassIndex++;
268 }
269 });
270 JSNApi::DestroyJSVM(ecmaVm2);
271
272 std::remove(fileName.c_str());
273 }
274
HWTEST_F_L0(SnapshotTest,SerializeHugeObject)275 HWTEST_F_L0(SnapshotTest, SerializeHugeObject)
276 {
277 auto factory = ecmaVm->GetFactory();
278 auto env = ecmaVm->GetGlobalEnv();
279
280 JSHandle<TaggedArray> array1 = factory->NewTaggedArray(300 * 1024 / 8);
281 JSHandle<TaggedArray> array2 = factory->NewTaggedArray(300 * 1024 / 8);
282 JSHandle<TaggedArray> array3 = factory->NewTaggedArray(100);
283
284 JSHandle<JSFunction> funcFunc(env->GetFunctionFunction());
285 JSHandle<JSFunction> dateFunc(env->GetDateFunction());
286 JSHandle<JSFunction> numberFunc(env->GetNumberFunction());
287 array1->Set(thread, 0, array2.GetTaggedValue());
288 array1->Set(thread, 1, funcFunc.GetTaggedValue());
289 array1->Set(thread, 2, dateFunc.GetTaggedValue());
290 array1->Set(thread, 3, numberFunc.GetTaggedValue());
291 array2->Set(thread, 0, array3.GetTaggedValue());
292 array2->Set(thread, 1, funcFunc.GetTaggedValue());
293 array2->Set(thread, 2, dateFunc.GetTaggedValue());
294 array2->Set(thread, 3, numberFunc.GetTaggedValue());
295
296 CString fileName = "snapshot";
297 Snapshot snapshotSerialize(ecmaVm);
298 // serialize
299 snapshotSerialize.Serialize(*array1, nullptr, fileName);
300 // deserialize
301 Snapshot snapshotDeserialize(ecmaVm);
302 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName);
303
304 auto lastRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetHugeObjectSpace()->GetCurrentRegion();
305 auto array4 = reinterpret_cast<TaggedArray *>(lastRegion->GetBegin());
306 EXPECT_TRUE(array4->Get(0).IsTaggedArray());
307 EXPECT_TRUE(array4->Get(1).IsJSFunction());
308 EXPECT_TRUE(array4->Get(2).IsJSFunction());
309 EXPECT_TRUE(array4->Get(3).IsJSFunction());
310 std::remove(fileName.c_str());
311 }
312
HWTEST_F_L0(SnapshotTest,BackwardCompatibility)313 HWTEST_F_L0(SnapshotTest, BackwardCompatibility)
314 {
315 base::FileHeaderBase::VersionType oldVersion = {0, 0, 0, 1};
316 base::FileHeaderBase::VersionType newVersion = {4, 0, 0, 1};
317 CompatibilityHelper(oldVersion, newVersion, false);
318 }
319
HWTEST_F_L0(SnapshotTest,ForwardCompatibility)320 HWTEST_F_L0(SnapshotTest, ForwardCompatibility)
321 {
322 base::FileHeaderBase::VersionType oldVersion = {0, 0, 0, 1};
323 base::FileHeaderBase::VersionType newVersion = {4, 0, 0, 1};
324 CompatibilityHelper(newVersion, oldVersion, false);
325 }
326
HWTEST_F_L0(SnapshotTest,StrictCompatibility)327 HWTEST_F_L0(SnapshotTest, StrictCompatibility)
328 {
329 base::FileHeaderBase::VersionType newVersion = {4, 0, 0, 1};
330 CompatibilityHelper(newVersion, newVersion, true);
331 }
332
HWTEST_F_L0(SnapshotTest,VersionTest)333 HWTEST_F_L0(SnapshotTest, VersionTest)
334 {
335 base::FileHeaderBase::VersionType version = {4, 3, 2, 1};
336 uint32_t versionNumber = 0x04030201U;
337 EXPECT_EQ(version, base::FileHeaderBase::ToVersion(versionNumber));
338 EXPECT_EQ(versionNumber, base::FileHeaderBase::ToVersionNumber(version));
339 }
340
341 } // namespace panda::test
342