• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 <zlib.h>
17 #include <unistd.h>
18 #include "assembler/assembly-emitter.h"
19 #include "assembler/assembly-parser.h"
20 #include "libpandafile/class_data_accessor-inl.h"
21 
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/js_function_kind.h"
24 #include "ecmascript/jspandafile/js_pandafile.h"
25 #include "ecmascript/jspandafile/js_pandafile_manager.h"
26 #include "ecmascript/jspandafile/js_pandafile_snapshot.h"
27 #include "ecmascript/jspandafile/method_literal.h"
28 #include "ecmascript/jspandafile/panda_file_translator.h"
29 #include "ecmascript/jspandafile/program_object.h"
30 #include "ecmascript/tests/test_helper.h"
31 
32 using namespace panda::ecmascript;
33 using namespace panda::panda_file;
34 using namespace panda::pandasm;
35 
36 namespace panda::test {
37 class MockJSPandaFileSnapshot : public JSPandaFileSnapshot {
38 public:
WriteDataToFile(JSThread * thread,JSPandaFile * jsPandaFile,const CString & path,const CString & version)39     static bool WriteDataToFile(JSThread *thread, JSPandaFile *jsPandaFile, const CString &path, const CString &version)
40     {
41         return JSPandaFileSnapshot::WriteDataToFile(thread, jsPandaFile, path, version);
42     }
43 };
44 class JSPandaFileSnapshotTest : public testing::Test {
45 public:
SetUpTestCase()46     static void SetUpTestCase()
47     {
48         GTEST_LOG_(INFO) << "SetUpTestCase";
49     }
50 
TearDownTestCase()51     static void TearDownTestCase()
52     {
53         GTEST_LOG_(INFO) << "TearDownCase";
54     }
55 
SetUp()56     void SetUp() override
57     {
58         CString path = GetSnapshotPath();
59         CString fileName = path + "entry" + JSPandaFileSnapshot::JSPANDAFILE_FILE_NAME.data();
60         if (remove(fileName.c_str()) != 0) {
61             GTEST_LOG_(ERROR) << "remove " << fileName << " failed";
62         }
63         TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
64     }
65 
TearDown()66     void TearDown() override
67     {
68         CString path = GetSnapshotPath();
69         CString fileName = path + "entry" + JSPandaFileSnapshot::JSPANDAFILE_FILE_NAME.data();
70         if (remove(fileName.c_str()) != 0) {
71             GTEST_LOG_(ERROR) << "remove " << fileName << " failed";
72         }
73         TestHelper::DestroyEcmaVMWithScope(instance, scope);
74     }
75 
GetSnapshotPath()76     static CString GetSnapshotPath()
77     {
78         char buff[FILENAME_MAX];
79         getcwd(buff, FILENAME_MAX);
80         CString currentPath(buff);
81         if (currentPath.back() != '/') {
82             currentPath += "/";
83         }
84         return currentPath;
85     }
86 
NewMockJSPandaFile() const87     std::shared_ptr<JSPandaFile> NewMockJSPandaFile() const
88     {
89         Parser parser;
90         const char *filename = "/data/storage/el1/bundle/entry/ets/main/modules.abc";
91         const char *data = R"(
92             .function any func_main_0(any a0, any a1, any a2) {
93                 ldai 1
94                 return
95             }
96         )";
97         auto res = parser.Parse(data);
98         JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
99         std::unique_ptr<const File> pfPtr = pandasm::AsmEmitter::Emit(res.Value());
100         return pfManager->NewJSPandaFile(pfPtr.release(), CString(filename));
101     }
102 
NormalTranslateJSPandaFile(const std::shared_ptr<JSPandaFile> & pf) const103     void NormalTranslateJSPandaFile(const std::shared_ptr<JSPandaFile>& pf) const
104     {
105         const File *file = pf->GetPandaFile();
106         const uint8_t *typeDesc = utf::CStringAsMutf8("L_GLOBAL;");
107         const File::EntityId classId = file->GetClassId(typeDesc);
108         ClassDataAccessor cda(*file, classId);
109         std::vector<File::EntityId> methodId {};
110         cda.EnumerateMethods([&](const MethodDataAccessor &mda) {
111             methodId.push_back(mda.GetMethodId());
112         });
113         pf->UpdateMainMethodIndex(methodId[0].GetOffset());
114         const char *methodName = MethodLiteral::GetMethodName(pf.get(), methodId[0]);
115         PandaFileTranslator::TranslateClasses(thread, pf.get(), CString(methodName));
116     }
117 
CheckMethodLiteral(MethodLiteral * serializeMethodLiteral,MethodLiteral * deserializeMethodLiteral)118     static void CheckMethodLiteral(MethodLiteral *serializeMethodLiteral, MethodLiteral *deserializeMethodLiteral)
119     {
120         EXPECT_TRUE(serializeMethodLiteral != nullptr);
121         EXPECT_TRUE(deserializeMethodLiteral != nullptr);
122         ASSERT_EQ(serializeMethodLiteral->GetCallField(), deserializeMethodLiteral->GetCallField());
123         ASSERT_EQ(*serializeMethodLiteral->GetBytecodeArray(), *deserializeMethodLiteral->GetBytecodeArray());
124         ASSERT_EQ(serializeMethodLiteral->GetLiteralInfo(), deserializeMethodLiteral->GetLiteralInfo());
125         ASSERT_EQ(serializeMethodLiteral->GetExtraLiteralInfo(), deserializeMethodLiteral->GetExtraLiteralInfo());
126     }
CheckJSRecordInfo(JSRecordInfo * serializeRecordInfo,JSRecordInfo * deserializeRecordInfo)127     static void CheckJSRecordInfo(JSRecordInfo *serializeRecordInfo, JSRecordInfo *deserializeRecordInfo)
128     {
129         ASSERT_TRUE(serializeRecordInfo != nullptr);
130         ASSERT_TRUE(deserializeRecordInfo != nullptr);
131         ASSERT_EQ(serializeRecordInfo->mainMethodIndex, deserializeRecordInfo->mainMethodIndex);
132         ASSERT_EQ(serializeRecordInfo->isCjs, deserializeRecordInfo->isCjs);
133         ASSERT_EQ(serializeRecordInfo->isJson, deserializeRecordInfo->isJson);
134         ASSERT_EQ(serializeRecordInfo->isSharedModule, deserializeRecordInfo->isSharedModule);
135         ASSERT_EQ(serializeRecordInfo->jsonStringId, deserializeRecordInfo->jsonStringId);
136         ASSERT_EQ(serializeRecordInfo->moduleRecordIdx, deserializeRecordInfo->moduleRecordIdx);
137         ASSERT_EQ(serializeRecordInfo->hasTopLevelAwait, deserializeRecordInfo->hasTopLevelAwait);
138         ASSERT_EQ(serializeRecordInfo->lazyImportIdx, deserializeRecordInfo->lazyImportIdx);
139         ASSERT_EQ(serializeRecordInfo->classId, deserializeRecordInfo->classId);
140         ASSERT_EQ(serializeRecordInfo->npmPackageName, deserializeRecordInfo->npmPackageName);
141     }
142     EcmaVM *instance {nullptr};
143     EcmaHandleScope *scope {nullptr};
144     JSThread *thread {nullptr};
145 };
146 
HWTEST_F_L0(JSPandaFileSnapshotTest,SerializeAndDeserializeTest)147 HWTEST_F_L0(JSPandaFileSnapshotTest, SerializeAndDeserializeTest)
148 {
149     // construct JSPandaFile
150     CString path = GetSnapshotPath();
151     CString version = "version 205.0.1.120(SP20)";
152     const std::shared_ptr<JSPandaFile> serializePf = NewMockJSPandaFile();
153     NormalTranslateJSPandaFile(serializePf);
154     // serialize and persist
155     ASSERT_TRUE(MockJSPandaFileSnapshot::WriteDataToFile(thread, serializePf.get(), path, version));
156 
157     // deserialize
158     const std::shared_ptr<JSPandaFile> pf = NewMockJSPandaFile();
159     JSPandaFile *deserializePf = pf.get();
160     ASSERT_TRUE(JSPandaFileSnapshot::ReadData(thread, deserializePf, path, version));
161 
162     // check numMethods
163     EXPECT_EQ(serializePf->GetNumMethods(), deserializePf->GetNumMethods());
164     // check MethodLiterals
165     ASSERT_EQ(serializePf->GetMainMethodIndex(), deserializePf->GetMainMethodIndex());
166     MethodLiteral *serializeMethodLiteral = serializePf->FindMethodLiteral(serializePf->GetMainMethodIndex());
167     MethodLiteral *deserializeMethodLiteral = deserializePf->FindMethodLiteral(deserializePf->GetMainMethodIndex());
168     CheckMethodLiteral(serializeMethodLiteral, deserializeMethodLiteral);
169     // check jsRecord
170     JSRecordInfo *serializeRecordInfo = serializePf->CheckAndGetRecordInfo("func_main_0");
171     JSRecordInfo *deserializeRecordInfo = deserializePf->CheckAndGetRecordInfo("func_main_0");
172     CheckJSRecordInfo(serializeRecordInfo, deserializeRecordInfo);
173 }
174 
HWTEST_F_L0(JSPandaFileSnapshotTest,ShouldNotSerializeWhenFileIsExists)175 HWTEST_F_L0(JSPandaFileSnapshotTest, ShouldNotSerializeWhenFileIsExists)
176 {
177     // construct JSPandaFile
178     CString path = GetSnapshotPath();
179     CString fileName = path + "entry" + JSPandaFileSnapshot::JSPANDAFILE_FILE_NAME.data();
180     CString version = "version 205.0.1.120(SP20)";
181     const std::shared_ptr<JSPandaFile> serializePf = NewMockJSPandaFile();
182     NormalTranslateJSPandaFile(serializePf);
183     // serialize and persist
184     ASSERT_TRUE(MockJSPandaFileSnapshot::WriteDataToFile(thread, serializePf.get(), path, version));
185     ASSERT_TRUE(FileExist(fileName.c_str()));
186     // return false when file is already exists
187     ASSERT_FALSE(MockJSPandaFileSnapshot::WriteDataToFile(thread, serializePf.get(), path, version));
188 }
189 
HWTEST_F_L0(JSPandaFileSnapshotTest,ShouldNotDeSerializeWhenFileIsNotExists)190 HWTEST_F_L0(JSPandaFileSnapshotTest, ShouldNotDeSerializeWhenFileIsNotExists)
191 {
192     // construct JSPandaFile
193     CString path = GetSnapshotPath();
194     CString fileName = path + "entry" + JSPandaFileSnapshot::JSPANDAFILE_FILE_NAME.data();
195     CString version = "version 205.0.1.120(SP20)";
196     const std::shared_ptr<JSPandaFile> deserializePf = NewMockJSPandaFile();
197     // return false when file is not exists
198     ASSERT_FALSE(FileExist(fileName.c_str()));
199     ASSERT_FALSE(JSPandaFileSnapshot::ReadData(thread, deserializePf.get(), path, version));
200 }
201 
HWTEST_F_L0(JSPandaFileSnapshotTest,ShouldDeSerializeFailedWhenFileIsEmpty)202 HWTEST_F_L0(JSPandaFileSnapshotTest, ShouldDeSerializeFailedWhenFileIsEmpty)
203 {
204     // construct JSPandaFile
205     CString path = GetSnapshotPath();
206     CString fileName = path + "entry" + JSPandaFileSnapshot::JSPANDAFILE_FILE_NAME.data();
207     CString version = "version 205.0.1.120(SP20)";
208     const std::shared_ptr<JSPandaFile> deserializePf = NewMockJSPandaFile();
209     std::ofstream ofStream(fileName.c_str());
210     ofStream.close();
211     // return false when file is empty
212     ASSERT_TRUE(FileExist(fileName.c_str()));
213     ASSERT_FALSE(JSPandaFileSnapshot::ReadData(thread, deserializePf.get(), path, version));
214     // check file is deleted
215     ASSERT_FALSE(FileExist(fileName.c_str()));
216 }
217 
HWTEST_F_L0(JSPandaFileSnapshotTest,ShouldDeSerializeFailedWhenCheckSumIsNotMatch)218 HWTEST_F_L0(JSPandaFileSnapshotTest, ShouldDeSerializeFailedWhenCheckSumIsNotMatch)
219 {
220     // construct JSPandaFile
221     CString path = GetSnapshotPath();
222     CString fileName = path + "entry" + JSPandaFileSnapshot::JSPANDAFILE_FILE_NAME.data();
223     CString version = "version 205.0.1.120(SP20)";
224     const std::shared_ptr<JSPandaFile> serializePf = NewMockJSPandaFile();
225     NormalTranslateJSPandaFile(serializePf);
226     // serialize and persist
227     ASSERT_TRUE(MockJSPandaFileSnapshot::WriteDataToFile(thread, serializePf.get(), path, version));
228     ASSERT_TRUE(FileExist(fileName.c_str()));
229     // modify file content
230     std::ofstream ofStream(fileName.c_str(), std::ios::app);
231     uint32_t mockCheckSum = 123456;
232     ofStream << mockCheckSum;
233     ofStream.close();
234     // deserialize failed when checksum is not match
235     const std::shared_ptr<JSPandaFile> deserializePf = NewMockJSPandaFile();
236     ASSERT_FALSE(JSPandaFileSnapshot::ReadData(thread, deserializePf.get(), path, version));
237     // check file is deleted
238     ASSERT_FALSE(FileExist(fileName.c_str()));
239 }
240 
HWTEST_F_L0(JSPandaFileSnapshotTest,ShouldDeSerializeFailedWhenAppVersionCodeIsNotMatch)241 HWTEST_F_L0(JSPandaFileSnapshotTest, ShouldDeSerializeFailedWhenAppVersionCodeIsNotMatch)
242 {
243     // construct JSPandaFile
244     CString path = GetSnapshotPath();
245     CString fileName = path + "entry" + JSPandaFileSnapshot::JSPANDAFILE_FILE_NAME.data();
246     CString version = "version 205.0.1.120(SP20)";
247     const std::shared_ptr<JSPandaFile> serializePf = NewMockJSPandaFile();
248     NormalTranslateJSPandaFile(serializePf);
249     // serialize and persist
250     ASSERT_TRUE(MockJSPandaFileSnapshot::WriteDataToFile(thread, serializePf.get(), path, version));
251     ASSERT_TRUE(FileExist(fileName.c_str()));
252     // modify app version code
253     thread->GetEcmaVM()->SetApplicationVersionCode(1);
254     // deserialize failed when app version code is not match
255     const std::shared_ptr<JSPandaFile> deserializePf = NewMockJSPandaFile();
256     ASSERT_FALSE(JSPandaFileSnapshot::ReadData(thread, deserializePf.get(), path, version));
257     // check file is deleted
258     ASSERT_FALSE(FileExist(fileName.c_str()));
259 }
260 
HWTEST_F_L0(JSPandaFileSnapshotTest,ShouldDeSerializeFailedWhenVersionCodeIsNotMatch)261 HWTEST_F_L0(JSPandaFileSnapshotTest, ShouldDeSerializeFailedWhenVersionCodeIsNotMatch)
262 {
263     // construct JSPandaFile
264     CString path = GetSnapshotPath();
265     CString fileName = path + "entry" + JSPandaFileSnapshot::JSPANDAFILE_FILE_NAME.data();
266     CString version = "version 205.0.1.120(SP20)";
267     const std::shared_ptr<JSPandaFile> serializePf = NewMockJSPandaFile();
268     NormalTranslateJSPandaFile(serializePf);
269     // serialize and persist
270     ASSERT_TRUE(MockJSPandaFileSnapshot::WriteDataToFile(thread, serializePf.get(), path, version));
271     ASSERT_TRUE(FileExist(fileName.c_str()));
272     // deserialize failed when version code is not match
273     CString updatedVersion = "version 205.0.1.125(SP20)";
274     const std::shared_ptr<JSPandaFile> deserializePf = NewMockJSPandaFile();
275     ASSERT_FALSE(JSPandaFileSnapshot::ReadData(thread, deserializePf.get(), path, updatedVersion));
276     // check file is deleted
277     ASSERT_FALSE(FileExist(fileName.c_str()));
278 }
279 
HWTEST_F_L0(JSPandaFileSnapshotTest,ShouldDeSerializeFailedWhenFileSizeIsNotMatch)280 HWTEST_F_L0(JSPandaFileSnapshotTest, ShouldDeSerializeFailedWhenFileSizeIsNotMatch)
281 {
282     // construct JSPandaFile
283     CString path = GetSnapshotPath();
284     CString fileName = path + "entry" + JSPandaFileSnapshot::JSPANDAFILE_FILE_NAME.data();
285     CString version = "version 205.0.1.120(SP20)";
286     const std::shared_ptr<JSPandaFile> serializePf = NewMockJSPandaFile();
287     NormalTranslateJSPandaFile(serializePf);
288     // serialize and persist
289     ASSERT_TRUE(MockJSPandaFileSnapshot::WriteDataToFile(thread, serializePf.get(), path, version));
290     ASSERT_TRUE(FileExist(fileName.c_str()));
291     // construct a different JSPandaFile
292     Parser parser;
293     const char *filename = "/data/storage/el1/bundle/entry/ets/main/modules.abc";
294     const char *data = R"(
295         .function any func_main_0(any a0, any a1, any a2) {
296             ldai 1
297             ldai 2
298             return
299         }
300     )";
301     auto res = parser.Parse(data);
302     JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
303     std::unique_ptr<const File> pfPtr = pandasm::AsmEmitter::Emit(res.Value());
304     const std::shared_ptr<JSPandaFile> deserializePf = pfManager->NewJSPandaFile(pfPtr.release(), CString(filename));
305     // deserialize failed when file size is not match
306     ASSERT_FALSE(JSPandaFileSnapshot::ReadData(thread, deserializePf.get(), path, version));
307     // check file is deleted
308     ASSERT_FALSE(FileExist(fileName.c_str()));
309 }
310 
HWTEST_F_L0(JSPandaFileSnapshotTest,ShouldDeSerializeFailedWhenModuleNameIsNotMatch)311 HWTEST_F_L0(JSPandaFileSnapshotTest, ShouldDeSerializeFailedWhenModuleNameIsNotMatch)
312 {
313     // construct JSPandaFile
314     CString path = GetSnapshotPath();
315     CString fileName = path + "entry" + JSPandaFileSnapshot::JSPANDAFILE_FILE_NAME.data();
316     CString version = "version 205.0.1.120(SP20)";
317     const std::shared_ptr<JSPandaFile> serializePf = NewMockJSPandaFile();
318     NormalTranslateJSPandaFile(serializePf);
319     // serialize and persist
320     ASSERT_TRUE(MockJSPandaFileSnapshot::WriteDataToFile(thread, serializePf.get(), path, version));
321     ASSERT_TRUE(FileExist(fileName.c_str()));
322     // change moduleName from entry to ntest
323     uint32_t fileSize = sizeof(uint32_t);
324     uint32_t appVersionCodeSize = sizeof(uint32_t);
325     uint32_t versionStrLenSize = sizeof(uint32_t);
326     uint32_t versionStrLen = version.size();
327     uint32_t moduleNameLenSize = sizeof(uint32_t);
328     uint32_t moduleNameOffset = appVersionCodeSize + versionStrLenSize + versionStrLen + fileSize + moduleNameLenSize;
329     CString moduleName = "ntest";
330     std::fstream fStream(fileName.c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary);
331     fStream.seekp(moduleNameOffset);
332     fStream << moduleName;
333     fStream.close();
334     // adapt checksum
335     uint32_t newCheckSum;
336     uint32_t checksumSize = sizeof(uint32_t);
337     uint32_t contentSize;
338     {
339         MemMap fileMapMem = FileMap(fileName.c_str(), FILE_RDONLY, PAGE_PROT_READ, 0);
340         MemMapScope memMapScope(fileMapMem);
341         contentSize = fileMapMem.GetSize() - checksumSize;
342         newCheckSum = adler32(0, static_cast<const Bytef*>(fileMapMem.GetOriginAddr()), contentSize);
343     }
344     fStream.open(fileName.c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary);
345     fStream.seekp(contentSize);
346     fStream.write(reinterpret_cast<char *>(&newCheckSum), checksumSize);
347     fStream.close();
348     // deserialize failed when module Name is not match
349     const std::shared_ptr<JSPandaFile> deserializePf = NewMockJSPandaFile();
350     ASSERT_FALSE(JSPandaFileSnapshot::ReadData(thread, deserializePf.get(), path, version));
351     // check file is deleted
352     ASSERT_FALSE(FileExist(fileName.c_str()));
353 }
354 }