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