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
27using namespace panda::ecmascript;
28using namespace panda::panda_file;
29using namespace panda::pandasm;
30
31namespace panda::test {
32class JSPandaFileTest : public testing::Test {
33public:
34    static void SetUpTestCase()
35    {
36        GTEST_LOG_(INFO) << "SetUpTestCase";
37    }
38
39    static void TearDownTestCase()
40    {
41        GTEST_LOG_(INFO) << "TearDownCase";
42    }
43
44    void SetUp() override
45    {
46        TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
47    }
48
49    void TearDown() override
50    {
51        TestHelper::DestroyEcmaVMWithScope(instance, scope);
52    }
53
54    EcmaVM *instance {nullptr};
55    EcmaHandleScope *scope {nullptr};
56    JSThread *thread {nullptr};
57protected:
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
72HWTEST_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
82HWTEST_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
93HWTEST_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
104HWTEST_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
120HWTEST_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
154HWTEST_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
205HWTEST_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
239HWTEST_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
260HWTEST_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
273HWTEST_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
288HWTEST_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
300HWTEST_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
313HWTEST_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