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/base/file_header.h"
17 #include "ecmascript/tests/test_helper.h"
18
19 #include "ecmascript/ecma_vm.h"
20 #include "ecmascript/global_env.h"
21 #include "ecmascript/js_handle.h"
22 #include "ecmascript/js_hclass.h"
23 #include "ecmascript/js_thread.h"
24 #include "ecmascript/jspandafile/program_object.h"
25 #include "ecmascript/object_factory.h"
26 #include "ecmascript/snapshot/mem/snapshot.h"
27 #include "ecmascript/snapshot/mem/snapshot_processor.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
63 EcmaVM *ecmaVm {nullptr};
64 ecmascript::EcmaHandleScope *scope {nullptr};
65 JSThread *thread {nullptr};
66 };
67
HWTEST_F_L0(SnapshotTest,SerializeConstPool)68 HWTEST_F_L0(SnapshotTest, SerializeConstPool)
69 {
70 auto factory = ecmaVm->GetFactory();
71 auto env = ecmaVm->GetGlobalEnv();
72
73 JSHandle<ConstantPool> constpool = factory->NewConstantPool(6);
74 JSHandle<JSFunction> funcFunc(env->GetFunctionFunction());
75 JSHandle<JSFunction> dateFunc(env->GetDateFunction());
76 JSHandle<JSFunction> numberFunc(env->GetNumberFunction());
77 JSHandle<EcmaString> str1 = factory->NewFromASCII("str11");
78 JSHandle<EcmaString> str2 = factory->NewFromASCII("str22");
79 constpool->SetObjectToCache(thread, 0, funcFunc.GetTaggedValue());
80 constpool->SetObjectToCache(thread, 1, dateFunc.GetTaggedValue());
81 constpool->SetObjectToCache(thread, 2, str1.GetTaggedValue());
82 constpool->SetObjectToCache(thread, 3, numberFunc.GetTaggedValue());
83 constpool->SetObjectToCache(thread, 4, str2.GetTaggedValue());
84 constpool->SetObjectToCache(thread, 5, str1.GetTaggedValue());
85
86 CString fileName = "snapshot";
87 Snapshot snapshotSerialize(ecmaVm);
88 // serialize
89 snapshotSerialize.Serialize(*constpool, nullptr, fileName);
90 // deserialize
91 Snapshot snapshotDeserialize(ecmaVm);
92 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName);
93
94 auto beginRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetOldSpace()->GetCurrentRegion();
95 auto constpool1 = reinterpret_cast<ConstantPool *>(beginRegion->GetBegin());
96 EXPECT_EQ(constpool->GetClass()->SizeFromJSHClass(*constpool),
97 constpool1->GetClass()->SizeFromJSHClass(constpool1));
98 EXPECT_TRUE(constpool1->GetObjectFromCache(0).IsJSFunction());
99 EXPECT_TRUE(constpool1->GetObjectFromCache(1).IsJSFunction());
100 EXPECT_TRUE(constpool1->GetObjectFromCache(3).IsJSFunction());
101 EcmaString *str11 = reinterpret_cast<EcmaString *>(constpool1->Get(2).GetTaggedObject());
102 EcmaString *str22 = reinterpret_cast<EcmaString *>(constpool1->Get(4).GetTaggedObject());
103 EcmaString *str33 = reinterpret_cast<EcmaString *>(constpool1->Get(5).GetTaggedObject());
104 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str11).ToCString().c_str(), "str11"), 0);
105 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str22).ToCString().c_str(), "str22"), 0);
106 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str33).ToCString().c_str(), "str11"), 0);
107 std::remove(fileName.c_str());
108 }
109
HWTEST_F_L0(SnapshotTest,SerializeDifferentSpace)110 HWTEST_F_L0(SnapshotTest, SerializeDifferentSpace)
111 {
112 auto factory = ecmaVm->GetFactory();
113 JSHandle<ConstantPool> constpool = factory->NewConstantPool(400);
114 for (int i = 0; i < 100; i++) {
115 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
116 constpool->SetObjectToCache(thread, i, array.GetTaggedValue());
117 }
118 for (int i = 0; i < 100; i++) {
119 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
120 constpool->SetObjectToCache(thread, i + 100, array.GetTaggedValue());
121 }
122 for (int i = 0; i < 100; i++) {
123 JSHandle<MachineCode> codeObj = factory->NewMachineCodeObject(0, nullptr);
124 constpool->SetObjectToCache(thread, i + 200, codeObj.GetTaggedValue());
125 }
126 for (int i = 0; i < 100; i++) {
127 JSHandle<ConstantPool> constpool1 = factory->NewConstantPool(10);
128 constpool->SetObjectToCache(thread, i + 300, constpool1.GetTaggedValue());
129 }
130
131 CString fileName = "snapshot";
132 Snapshot snapshotSerialize(ecmaVm);
133 // serialize
134 snapshotSerialize.Serialize(*constpool, nullptr, fileName);
135 // deserialize
136 Snapshot snapshotDeserialize(ecmaVm);
137 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName);
138
139 auto beginRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetOldSpace()->GetCurrentRegion();
140 auto constpool1 = reinterpret_cast<ConstantPool *>(beginRegion->GetBegin());
141 EXPECT_EQ(constpool->GetClass()->SizeFromJSHClass(*constpool),
142 constpool1->GetClass()->SizeFromJSHClass(constpool1));
143 EXPECT_TRUE(constpool1->GetObjectFromCache(0).IsTaggedArray());
144 EXPECT_TRUE(constpool1->GetObjectFromCache(100).IsTaggedArray());
145 EXPECT_TRUE(constpool1->GetObjectFromCache(200).IsMachineCodeObject());
146 EXPECT_TRUE(constpool1->GetObjectFromCache(300).IsTaggedArray());
147 auto obj1 = constpool1->GetObjectFromCache(0).GetTaggedObject();
148 EXPECT_TRUE(Region::ObjectAddressToRange(obj1)->InOldSpace());
149 auto obj2 = constpool1->GetObjectFromCache(100).GetTaggedObject();
150 EXPECT_TRUE(Region::ObjectAddressToRange(obj2)->InOldSpace());
151 auto obj3 = constpool1->GetObjectFromCache(200).GetTaggedObject();
152 auto region = Region::ObjectAddressToRange(obj3);
153 EXPECT_TRUE(region->InMachineCodeSpace());
154
155 std::remove(fileName.c_str());
156 }
157
HWTEST_F_L0(SnapshotTest,SerializeMultiFile)158 HWTEST_F_L0(SnapshotTest, SerializeMultiFile)
159 {
160 auto factory = ecmaVm->GetFactory();
161 JSHandle<ConstantPool> constpool1 = factory->NewConstantPool(400);
162 JSHandle<ConstantPool> constpool2 = factory->NewConstantPool(400);
163 for (int i = 0; i < 100; i++) {
164 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
165 constpool1->SetObjectToCache(thread, i, array.GetTaggedValue());
166 constpool2->SetObjectToCache(thread, i, array.GetTaggedValue());
167 }
168 for (int i = 0; i < 100; i++) {
169 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
170 constpool1->SetObjectToCache(thread, i + 100, array.GetTaggedValue());
171 constpool2->SetObjectToCache(thread, i + 100, array.GetTaggedValue());
172 }
173 for (int i = 0; i < 100; i++) {
174 JSHandle<MachineCode> codeObj = factory->NewMachineCodeObject(0, nullptr);
175 constpool1->SetObjectToCache(thread, i + 200, codeObj.GetTaggedValue());
176 constpool2->SetObjectToCache(thread, i + 200, codeObj.GetTaggedValue());
177 }
178 for (int i = 0; i < 100; i++) {
179 JSHandle<ConstantPool> constpool3 = factory->NewConstantPool(10);
180 constpool1->SetObjectToCache(thread, i + 300, constpool3.GetTaggedValue());
181 constpool2->SetObjectToCache(thread, i + 300, constpool3.GetTaggedValue());
182 }
183
184 CString fileName1 = "snapshot1";
185 CString fileName2 = "snapshot2";
186 Snapshot snapshotSerialize(ecmaVm);
187 // serialize
188 snapshotSerialize.Serialize(*constpool1, nullptr, fileName1);
189 snapshotSerialize.Serialize(*constpool2, nullptr, fileName2);
190 // deserialize
191 Snapshot snapshotDeserialize(ecmaVm);
192 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName1);
193 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName2);
194
195 auto beginRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetOldSpace()->GetCurrentRegion();
196 auto constpool = reinterpret_cast<ConstantPool *>(beginRegion->GetBegin());
197 EXPECT_TRUE(constpool->GetObjectFromCache(0).IsTaggedArray());
198 EXPECT_TRUE(constpool->GetObjectFromCache(100).IsTaggedArray());
199 EXPECT_TRUE(constpool->GetObjectFromCache(200).IsMachineCodeObject());
200 auto obj1 = constpool->GetObjectFromCache(0).GetTaggedObject();
201 EXPECT_TRUE(Region::ObjectAddressToRange(obj1)->InOldSpace());
202 auto obj2 = constpool->GetObjectFromCache(100).GetTaggedObject();
203 EXPECT_TRUE(Region::ObjectAddressToRange(obj2)->InOldSpace());
204 auto obj3 = constpool->GetObjectFromCache(200).GetTaggedObject();
205 auto region = Region::ObjectAddressToRange(obj3);
206 EXPECT_TRUE(region->InMachineCodeSpace());
207
208 std::remove(fileName1.c_str());
209 std::remove(fileName2.c_str());
210 }
211
HWTEST_F_L0(SnapshotTest,SerializeBuiltins)212 HWTEST_F_L0(SnapshotTest, SerializeBuiltins)
213 {
214 // remove builtins.snapshot file first if exist
215 CString fileName = "builtins.snapshot";
216 std::remove(fileName.c_str());
217 // generate builtins.snapshot file
218 JSRuntimeOptions options1;
219 options1.SetArkProperties(ArkProperties::ENABLE_SNAPSHOT_SERIALIZE);
220 EcmaVM *ecmaVm1 = JSNApi::CreateEcmaVM(options1);
221 JSNApi::DestroyJSVM(ecmaVm1);
222
223 // create EcmaVM use builtins deserialzie
224 JSRuntimeOptions options2;
225 options2.SetArkProperties(ArkProperties::ENABLE_SNAPSHOT_DESERIALIZE);
226 EcmaVM *ecmaVm2 = JSNApi::CreateEcmaVM(options2);
227 EXPECT_TRUE(ecmaVm2->GetGlobalEnv()->GetClass()->GetObjectType() == JSType::GLOBAL_ENV);
228 auto globalConst = const_cast<GlobalEnvConstants *>(ecmaVm2->GetJSThread()->GlobalConstants());
229 size_t hclassEndIndex = static_cast<size_t>(ConstantIndex::UNDEFINED_INDEX);
230 size_t hclassIndex = 0;
231 globalConst->VisitRangeSlot([&hclassIndex, &hclassEndIndex]([[maybe_unused]]Root type,
232 ObjectSlot start, ObjectSlot end) {
233 while (start < end) {
234 JSTaggedValue object(start.GetTaggedType());
235 start++;
236 if (hclassIndex < hclassEndIndex) {
237 EXPECT_TRUE(object.IsJSHClass());
238 }
239 hclassIndex++;
240 }
241 });
242 JSNApi::DestroyJSVM(ecmaVm2);
243
244 std::remove(fileName.c_str());
245 }
246
HWTEST_F_L0(SnapshotTest,SerializeHugeObject)247 HWTEST_F_L0(SnapshotTest, SerializeHugeObject)
248 {
249 auto factory = ecmaVm->GetFactory();
250 auto env = ecmaVm->GetGlobalEnv();
251
252 JSHandle<TaggedArray> array1 = factory->NewTaggedArray(300 * 1024 / 8);
253 JSHandle<TaggedArray> array2 = factory->NewTaggedArray(300 * 1024 / 8);
254 JSHandle<TaggedArray> array3 = factory->NewTaggedArray(100);
255
256 JSHandle<JSFunction> funcFunc(env->GetFunctionFunction());
257 JSHandle<JSFunction> dateFunc(env->GetDateFunction());
258 JSHandle<JSFunction> numberFunc(env->GetNumberFunction());
259 array1->Set(thread, 0, array2.GetTaggedValue());
260 array1->Set(thread, 1, funcFunc.GetTaggedValue());
261 array1->Set(thread, 2, dateFunc.GetTaggedValue());
262 array1->Set(thread, 3, numberFunc.GetTaggedValue());
263 array2->Set(thread, 0, array3.GetTaggedValue());
264 array2->Set(thread, 1, funcFunc.GetTaggedValue());
265 array2->Set(thread, 2, dateFunc.GetTaggedValue());
266 array2->Set(thread, 3, numberFunc.GetTaggedValue());
267
268 CString fileName = "snapshot";
269 Snapshot snapshotSerialize(ecmaVm);
270 // serialize
271 snapshotSerialize.Serialize(*array1, nullptr, fileName);
272 // deserialize
273 Snapshot snapshotDeserialize(ecmaVm);
274 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName);
275
276 auto lastRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetHugeObjectSpace()->GetCurrentRegion();
277 auto array4 = reinterpret_cast<TaggedArray *>(lastRegion->GetBegin());
278 EXPECT_TRUE(array4->Get(0).IsTaggedArray());
279 EXPECT_TRUE(array4->Get(1).IsJSFunction());
280 EXPECT_TRUE(array4->Get(2).IsJSFunction());
281 EXPECT_TRUE(array4->Get(3).IsJSFunction());
282 std::remove(fileName.c_str());
283 }
284
HWTEST_F_L0(SnapshotTest,VersionTest)285 HWTEST_F_L0(SnapshotTest, VersionTest)
286 {
287 base::FileHeader::VersionType version = {4, 3, 2, 1};
288 uint32_t versionNumber = 0x04030201U;
289 EXPECT_EQ(version, base::FileHeader::ToVersion(versionNumber));
290 EXPECT_EQ(versionNumber, base::FileHeader::ToVersionNumber(version));
291 }
292 } // namespace panda::test
293