• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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