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