1 /*
2 * Copyright (c) 2022 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 "assembler/assembly-emitter.h"
17 #include "assembler/assembly-parser.h"
18 #include "gtest/gtest.h"
19 #include "libpandabase/utils/utf.h"
20 #include "libpandafile/class_data_accessor-inl.h"
21
22 #include "ecmascript/mem/c_containers.h"
23 #include "ecmascript/jspandafile/js_pandafile.h"
24 #include "ecmascript/jspandafile/js_pandafile_manager.h"
25 #include "ecmascript/tests/test_helper.h"
26
27 using namespace panda::ecmascript;
28 using namespace panda::panda_file;
29 using namespace panda::pandasm;
30
31 namespace panda::test {
32 class JSPandaFileTest : 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 TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
47 }
48
TearDown()49 void TearDown() override
50 {
51 TestHelper::DestroyEcmaVMWithScope(instance, scope);
52 }
53
54 EcmaVM *instance {nullptr};
55 EcmaHandleScope *scope {nullptr};
56 JSThread *thread {nullptr};
57 protected:
CreateJSPandaFile(const char * source,const CString filename)58 std::shared_ptr<JSPandaFile> CreateJSPandaFile(const char *source, const CString filename)
59 {
60 Parser parser;
61 const std::string fn = "SRC.pa"; // test file name : "SRC.pa"
62 auto res = parser.Parse(source, fn);
63 EXPECT_EQ(parser.ShowError().err, Error::ErrorType::ERR_NONE);
64
65 std::unique_ptr<const File> pfPtr = pandasm::AsmEmitter::Emit(res.Value());
66 JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
67 std::shared_ptr<JSPandaFile> pf = pfManager->NewJSPandaFile(pfPtr.release(), filename);
68 return pf;
69 }
70 };
71
HWTEST_F_L0(JSPandaFileTest,CreateJSPandaFile)72 HWTEST_F_L0(JSPandaFileTest, CreateJSPandaFile)
73 {
74 const char *source = R"(
75 .function void foo() {}
76 )";
77 const CString fileName = "test.pa";
78 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
79 EXPECT_TRUE(pf != nullptr);
80 }
81
HWTEST_F_L0(JSPandaFileTest,GetJSPandaFileDesc)82 HWTEST_F_L0(JSPandaFileTest, GetJSPandaFileDesc)
83 {
84 const char *source = R"(
85 .function void foo() {}
86 )";
87 const CString fileName = "test.pa";
88 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
89 const CString expectFileName = pf->GetJSPandaFileDesc();
90 EXPECT_STREQ(expectFileName.c_str(), "test.pa");
91 }
92
HWTEST_F_L0(JSPandaFileTest,GetPandaFile)93 HWTEST_F_L0(JSPandaFileTest, GetPandaFile)
94 {
95 const char *source = R"(
96 .function void foo() {}
97 )";
98 const CString fileName = "test.pa";
99 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
100 const File *file = pf->GetPandaFile();
101 EXPECT_TRUE(file != nullptr);
102 }
103
HWTEST_F_L0(JSPandaFileTest,GetMethodLiterals_GetNumMethods)104 HWTEST_F_L0(JSPandaFileTest, GetMethodLiterals_GetNumMethods)
105 {
106 const char *source = R"(
107 .function void foo1() {}
108 .function void foo2() {}
109 .function void foo3() {}
110 )";
111 const CString fileName = "test.pa";
112 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
113 MethodLiteral *method = pf->GetMethodLiterals();
114 EXPECT_TRUE(method != nullptr);
115
116 uint32_t methodNum = pf->GetNumMethods();
117 EXPECT_EQ(methodNum, 3U); // 3 : number of methods
118 }
119
HWTEST_F_L0(JSPandaFileTest,SetMethodLiteralToMap_FindMethodLiteral)120 HWTEST_F_L0(JSPandaFileTest, SetMethodLiteralToMap_FindMethodLiteral)
121 {
122 const char *source = R"(
123 .function void foo1() {}
124 .function void foo2() {}
125 .function void foo3() {}
126 )";
127 const CString fileName = "test.pa";
128 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
129 const File *file = pf->GetPandaFile();
130 const uint8_t *typeDesc = utf::CStringAsMutf8("L_GLOBAL;");
131 File::EntityId classId = file->GetClassId(typeDesc);
132 EXPECT_TRUE(classId.IsValid());
133
134 ClassDataAccessor cda(*file, classId);
135 std::vector<File::EntityId> methodId {};
136 int count = 0;
137 cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
138 methodId.push_back(mda.GetMethodId());
139 count++;
140 });
141 EXPECT_EQ(count, 3); // 3 : number of methods
142
143 MethodLiteral *method1 = new MethodLiteral(methodId[0]);
144 MethodLiteral *method2 = new MethodLiteral(methodId[1]);
145 MethodLiteral *method3 = new MethodLiteral(methodId[2]);
146 pf->SetMethodLiteralToMap(method1);
147 pf->SetMethodLiteralToMap(method2);
148 pf->SetMethodLiteralToMap(method3);
149 EXPECT_STREQ(MethodLiteral::ParseFunctionName(pf.get(), methodId[0]).c_str(), "foo1");
150 EXPECT_STREQ(MethodLiteral::ParseFunctionName(pf.get(), methodId[1]).c_str(), "foo2");
151 EXPECT_STREQ(MethodLiteral::ParseFunctionName(pf.get(), methodId[2]).c_str(), "foo3");
152 }
153
HWTEST_F_L0(JSPandaFileTest,GetOrInsertConstantPool_GetConstpoolIndex_GetConstpoolMap)154 HWTEST_F_L0(JSPandaFileTest, GetOrInsertConstantPool_GetConstpoolIndex_GetConstpoolMap)
155 {
156 const char *source = R"(
157 .function void foo1() {}
158 .function void foo2() {}
159 .function void foo3() {}
160 )";
161 const CString fileName = "test.pa";
162 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
163 const File *file = pf->GetPandaFile();
164 const uint8_t *typeDesc = utf::CStringAsMutf8("L_GLOBAL;");
165 File::EntityId classId = file->GetClassId(typeDesc);
166 EXPECT_TRUE(classId.IsValid());
167
168 ClassDataAccessor cda(*file, classId);
169 std::vector<File::EntityId> methodId {};
170 int count = 0;
171 cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
172 methodId.push_back(mda.GetMethodId());
173 count++;
174 });
175 EXPECT_EQ(count, 3); // 3 : number of methods
176
177 uint32_t index1 = pf->GetOrInsertConstantPool(ConstPoolType::METHOD, methodId[0].GetOffset());
178 uint32_t index2 = pf->GetOrInsertConstantPool(ConstPoolType::METHOD, methodId[1].GetOffset());
179 uint32_t index3 = pf->GetOrInsertConstantPool(ConstPoolType::METHOD, methodId[2].GetOffset());
180 EXPECT_EQ(index1, 0U);
181 EXPECT_EQ(index2, 1U);
182 EXPECT_EQ(index3, 2U);
183
184 uint32_t conPoolIndex = pf->GetConstpoolIndex();
185 EXPECT_EQ(conPoolIndex, 3U);
186
187 CUnorderedMap<uint32_t, uint64_t> constpoolMap = pf->GetConstpoolMap();
188 ConstPoolValue constPoolValue1(constpoolMap.at(methodId[0].GetOffset()));
189 ConstPoolValue constPoolValue2(constpoolMap.at(methodId[1].GetOffset()));
190 ConstPoolValue constPoolValue3(constpoolMap.at(methodId[2].GetOffset()));
191 ConstPoolType type1 = constPoolValue1.GetConstpoolType();
192 ConstPoolType type2 = constPoolValue2.GetConstpoolType();
193 ConstPoolType type3 = constPoolValue3.GetConstpoolType();
194 uint32_t gotIndex1 = constPoolValue1.GetConstpoolIndex();
195 uint32_t gotIndex2 = constPoolValue2.GetConstpoolIndex();
196 uint32_t gotIndex3 = constPoolValue3.GetConstpoolIndex();
197 EXPECT_EQ(type1, ConstPoolType::METHOD);
198 EXPECT_EQ(type2, ConstPoolType::METHOD);
199 EXPECT_EQ(type3, ConstPoolType::METHOD);
200 EXPECT_EQ(gotIndex1, 0U);
201 EXPECT_EQ(gotIndex2, 1U);
202 EXPECT_EQ(gotIndex3, 2U);
203 }
204
HWTEST_F_L0(JSPandaFileTest,GetMainMethodIndex_UpdateMainMethodIndex)205 HWTEST_F_L0(JSPandaFileTest, GetMainMethodIndex_UpdateMainMethodIndex)
206 {
207 const char *source = R"(
208 .function void func1() {}
209 .function void func2() {}
210 )";
211 const CString fileName = "test.pa";
212 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
213 const File *file = pf->GetPandaFile();
214 const uint8_t *typeDesc = utf::CStringAsMutf8("L_GLOBAL;");
215 File::EntityId classId = file->GetClassId(typeDesc);
216 EXPECT_TRUE(classId.IsValid());
217
218 ClassDataAccessor cda(*file, classId);
219 std::vector<File::EntityId> methodId {};
220 int count = 0;
221 cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
222 methodId.push_back(mda.GetMethodId());
223 count++;
224 });
225 EXPECT_EQ(count, 2); // 2 : number of methods
226
227 uint32_t mainMethodIndex = pf->GetMainMethodIndex();
228 EXPECT_EQ(mainMethodIndex, 0U);
229
230 pf->UpdateMainMethodIndex(methodId[0].GetOffset());
231 mainMethodIndex = pf->GetMainMethodIndex();
232 EXPECT_EQ(mainMethodIndex, methodId[0].GetOffset());
233
234 pf->UpdateMainMethodIndex(methodId[1].GetOffset());
235 mainMethodIndex = pf->GetMainMethodIndex();
236 EXPECT_EQ(mainMethodIndex, methodId[1].GetOffset());
237 }
238
HWTEST_F_L0(JSPandaFileTest,GetClasses)239 HWTEST_F_L0(JSPandaFileTest, GetClasses)
240 {
241 const char *source = R"(
242 .function void foo() {}
243 )";
244 const CString fileName = "test.pa";
245 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
246 const File *file = pf->GetPandaFile();
247 const uint8_t *typeDesc = utf::CStringAsMutf8("L_GLOBAL;");
248 File::EntityId classId = file->GetClassId(typeDesc);
249 EXPECT_TRUE(classId.IsValid());
250
251 const File::Header *header = file->GetHeader();
252 Span fileData(file->GetBase(), header->file_size);
253 Span classIdxData = fileData.SubSpan(header->class_idx_off, header->num_classes * sizeof(uint32_t));
254 auto classesData = Span(reinterpret_cast<const uint32_t *>(classIdxData.data()), header->num_classes);
255
256 Span<const uint32_t> classes = pf->GetClasses();
257 EXPECT_EQ(classes.Data(), classesData.Data());
258 }
259
HWTEST_F_L0(JSPandaFileTest,IsModule_IsCjs)260 HWTEST_F_L0(JSPandaFileTest, IsModule_IsCjs)
261 {
262 const char *source1 = R"(
263 .function void foo1() {}
264 )";
265 const CString fileName1 = "test1.pa";
266 std::shared_ptr<JSPandaFile> pf1 = CreateJSPandaFile(source1, fileName1);
267 JSPandaFile::JSRecordInfo info =
268 const_cast<JSPandaFile *>(pf1.get())-> FindRecordInfo(JSPandaFile::ENTRY_FUNCTION_NAME);
269 EXPECT_EQ(pf1->IsModule(&info), false);
270 EXPECT_EQ(pf1->IsCjs(&info), false);
271 }
272
HWTEST_F_L0(JSPandaFileTest,SetLoadedAOTStatus_IsLoadedAOT)273 HWTEST_F_L0(JSPandaFileTest, SetLoadedAOTStatus_IsLoadedAOT)
274 {
275 const char *source = R"(
276 .function void foo() {}
277 )";
278 const CString fileName = "test.pa";
279 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
280 bool isLoadedAOT = pf->IsLoadedAOT();
281 EXPECT_EQ(isLoadedAOT, false);
282
283 pf->SetAOTFileInfoIndex(0);
284 isLoadedAOT = pf->IsLoadedAOT();
285 EXPECT_EQ(isLoadedAOT, true);
286 }
287
HWTEST_F_L0(JSPandaFileTest,GetFileUniqId)288 HWTEST_F_L0(JSPandaFileTest, GetFileUniqId)
289 {
290 const char *source = R"(
291 .function void foo() {}
292 )";
293 const CString fileName = "test.pa";
294 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
295 EXPECT_EQ(pf->GetFileUniqId(), merge_hashes(panda_file::File::CalcFilenameHash(""),
296 GetHash32(reinterpret_cast<const uint8_t *>(pf->GetPandaFile()->GetHeader()),
297 sizeof(panda_file::File::Header))));
298 }
299
HWTEST_F_L0(JSPandaFileTest,IsParsedConstpoolOfCurrentVM)300 HWTEST_F_L0(JSPandaFileTest, IsParsedConstpoolOfCurrentVM)
301 {
302 const char *source = R"(
303 .function void foo() {}
304 )";
305 const CString fileName = "test.pa";
306 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
307 auto &recordInfo = pf->FindRecordInfo(JSPandaFile::ENTRY_FUNCTION_NAME);
308 EXPECT_TRUE(!recordInfo.IsParsedConstpoolOfCurrentVM(instance));
309 recordInfo.SetParsedConstpoolVM(instance);
310 EXPECT_TRUE(pf->FindRecordInfo(JSPandaFile::ENTRY_FUNCTION_NAME).IsParsedConstpoolOfCurrentVM(instance));
311 }
312
HWTEST_F_L0(JSPandaFileTest,NormalizedFileDescTest)313 HWTEST_F_L0(JSPandaFileTest, NormalizedFileDescTest)
314 {
315 const char *source = R"(
316 .function void foo() {}
317 )";
318 CString fileName = "/data/storage/el1/bundle/entry/ets/modules.abc";
319 std::shared_ptr<JSPandaFile> pf = CreateJSPandaFile(source, fileName);
320 EXPECT_EQ(pf->GetNormalizedFileDesc(), "entry/ets/modules.abc");
321
322 fileName = "/data/storage/el1/bundle/entry/ets/widgets.abc";
323 pf = CreateJSPandaFile(source, fileName);
324 EXPECT_EQ(pf->GetNormalizedFileDesc(), "entry/ets/widgets.abc");
325
326 fileName = "/data/app/el1/bundle/public/com.xx.xx/entry.hsp/entry/ets/modules.abc";
327 pf = CreateJSPandaFile(source, fileName);
328 EXPECT_EQ(pf->GetNormalizedFileDesc(), "entry/ets/modules.abc");
329
330 fileName = "/data/app/el1/bundle/public/com.xx.xx/entry.hap/entry/ets/widgets.abc";
331 pf = CreateJSPandaFile(source, fileName);
332 EXPECT_EQ(pf->GetNormalizedFileDesc(), "entry/ets/widgets.abc");
333
334 fileName = "/system/app/Camera/Camera.hap/entry1/ets/modules.abc";
335 pf = CreateJSPandaFile(source, fileName);
336 EXPECT_EQ(pf->GetNormalizedFileDesc(), "entry1/ets/modules.abc");
337
338 fileName = "test.pa";
339 pf = CreateJSPandaFile(source, fileName);
340 EXPECT_EQ(pf->GetNormalizedFileDesc(), fileName);
341
342 fileName = "libapp.ability.AbilityStage.z.so/app.ability.AbilityStage.js";
343 pf = CreateJSPandaFile(source, fileName);
344 EXPECT_EQ(pf->GetNormalizedFileDesc(), fileName);
345 }
346 } // namespace panda::test