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