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 "libpandafile/class_data_accessor-inl.h"
19#include "libziparchive/zip_archive.h"
20
21#include "ecmascript/global_env.h"
22#include "ecmascript/jspandafile/abc_buffer_cache.h"
23#include "ecmascript/jspandafile/js_pandafile.h"
24#include "ecmascript/jspandafile/js_pandafile_manager.h"
25#include "ecmascript/jspandafile/program_object.h"
26#include "ecmascript/tests/test_helper.h"
27
28using namespace panda::ecmascript;
29using namespace panda::panda_file;
30using namespace panda::pandasm;
31
32namespace panda::test {
33class JSPandaFileManagerTest : public testing::Test {
34public:
35    static void SetUpTestCase()
36    {
37        GTEST_LOG_(INFO) << "SetUpTestCase";
38    }
39
40    static void TearDownTestCase()
41    {
42        GTEST_LOG_(INFO) << "TearDownCase";
43    }
44
45    void SetUp() override
46    {
47        TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
48    }
49
50    void TearDown() override
51    {
52        TestHelper::DestroyEcmaVMWithScope(instance, scope);
53    }
54
55    EcmaVM *instance {nullptr};
56    EcmaHandleScope *scope {nullptr};
57    JSThread *thread {nullptr};
58};
59
60HWTEST_F_L0(JSPandaFileManagerTest, GetInstance)
61{
62    JSPandaFileManager *manager = JSPandaFileManager::GetInstance();
63    EXPECT_TRUE(manager != nullptr);
64}
65
66HWTEST_F_L0(JSPandaFileManagerTest, NewJSPandaFile)
67{
68    Parser parser;
69    std::string fileName = "__JSPandaFileManagerTest.pa";
70    const char *source = R"(
71        .function void foo() {}
72    )";
73    auto res = parser.Parse(source, fileName);
74    EXPECT_EQ(parser.ShowError().err, Error::ErrorType::ERR_NONE);
75
76    std::unique_ptr<const File> pfPtr = pandasm::AsmEmitter::Emit(res.Value());
77    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
78    std::shared_ptr<JSPandaFile> pf = pfManager->NewJSPandaFile(pfPtr.release(), CString(fileName.c_str()));
79    EXPECT_TRUE(pf != nullptr);
80
81    auto expectFileName = pf->GetJSPandaFileDesc();
82    EXPECT_STREQ(expectFileName.c_str(), "__JSPandaFileManagerTest.pa");
83    remove(fileName.c_str());
84    pfManager->RemoveJSPandaFile(pf.get());
85}
86
87HWTEST_F_L0(JSPandaFileManagerTest, OpenJSPandaFile)
88{
89    const char *filename = "__JSPandaFileManagerTest.pa";
90    const char *data = R"(
91        .function void foo() {}
92    )";
93    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
94    std::shared_ptr<JSPandaFile> ojspf = pfManager->OpenJSPandaFile(filename);
95    EXPECT_TRUE(ojspf == nullptr);
96
97    Parser parser;
98    auto res = parser.Parse(data);
99    EXPECT_TRUE(pandasm::AsmEmitter::Emit(filename, res.Value()));
100
101    ojspf = pfManager->OpenJSPandaFile(filename);
102    EXPECT_TRUE(ojspf != nullptr);
103    EXPECT_STREQ(ojspf->GetJSPandaFileDesc().c_str(), "__JSPandaFileManagerTest.pa");
104    pfManager->RemoveJSPandaFile(ojspf.get());
105
106    remove(filename);
107    ojspf = pfManager->OpenJSPandaFile(filename);
108    EXPECT_TRUE(ojspf == nullptr);
109}
110
111HWTEST_F_L0(JSPandaFileManagerTest, Add_Find_Remove_JSPandaFile)
112{
113    const char *filename1 = "__JSPandaFileManagerTest1.pa";
114    const char *filename2 = "__JSPandaFileManagerTest2.pa";
115    const char *data = R"(
116        .function void foo() {}
117    )";
118    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
119    Parser parser;
120    auto res = parser.Parse(data);
121    std::unique_ptr<const File> pfPtr1 = pandasm::AsmEmitter::Emit(res.Value());
122    std::unique_ptr<const File> pfPtr2 = pandasm::AsmEmitter::Emit(res.Value());
123    std::shared_ptr<JSPandaFile> pf1 = pfManager->NewJSPandaFile(pfPtr1.release(), CString(filename1));
124    std::shared_ptr<JSPandaFile> pf2 = pfManager->NewJSPandaFile(pfPtr2.release(), CString(filename2));
125    pfManager->AddJSPandaFile(pf1);
126    pfManager->AddJSPandaFile(pf2);
127    std::shared_ptr<JSPandaFile> foundPf1 = pfManager->FindJSPandaFile(filename1);
128    std::shared_ptr<JSPandaFile> foundPf2 = pfManager->FindJSPandaFile(filename2);
129    EXPECT_STREQ(foundPf1->GetJSPandaFileDesc().c_str(), "__JSPandaFileManagerTest1.pa");
130    EXPECT_STREQ(foundPf2->GetJSPandaFileDesc().c_str(), "__JSPandaFileManagerTest2.pa");
131
132    pfManager->RemoveJSPandaFile(pf1.get());
133    pfManager->RemoveJSPandaFile(pf2.get());
134    std::shared_ptr<JSPandaFile> afterRemovePf1 = pfManager->FindJSPandaFile(filename1);
135    std::shared_ptr<JSPandaFile> afterRemovePf2 = pfManager->FindJSPandaFile(filename2);
136    EXPECT_EQ(afterRemovePf1, nullptr);
137    EXPECT_EQ(afterRemovePf2, nullptr);
138}
139
140HWTEST_F_L0(JSPandaFileManagerTest, MultiEcmaVM_Add_Find_Remove_JSPandaFile)
141{
142    const char *filename1 = "__JSPandaFileManagerTest1.pa";
143    const char *filename2 = "__JSPandaFileManagerTest2.pa";
144    const char *data = R"(
145        .function void foo() {}
146    )";
147    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
148    Parser parser;
149    auto res = parser.Parse(data);
150    std::unique_ptr<const File> pfPtr1 = pandasm::AsmEmitter::Emit(res.Value());
151    std::unique_ptr<const File> pfPtr2 = pandasm::AsmEmitter::Emit(res.Value());
152    std::shared_ptr<JSPandaFile> pf1 = pfManager->NewJSPandaFile(pfPtr1.release(), CString(filename1));
153    std::shared_ptr<JSPandaFile> pf2 = pfManager->NewJSPandaFile(pfPtr2.release(), CString(filename2));
154    pfManager->AddJSPandaFile(pf1);
155    pfManager->AddJSPandaFile(pf2);
156
157    EcmaContext *context = instance->GetJSThread()->GetCurrentEcmaContext();
158    JSHandle<ConstantPool> constpool1 = instance->GetFactory()->NewSConstantPool(1);
159    JSHandle<ConstantPool> constpool2 = instance->GetFactory()->NewSConstantPool(2);
160    constpool1 = context->AddOrUpdateConstpool(pf1.get(), constpool1, 0);
161    constpool2 = context->AddOrUpdateConstpool(pf2.get(), constpool2, 0);
162
163    std::thread t1([&]() {
164        EcmaVM *instance1;
165        EcmaHandleScope *scope1;
166        JSThread *thread1;
167        TestHelper::CreateEcmaVMWithScope(instance1, thread1, scope1);
168        std::shared_ptr<JSPandaFile> loadedPf1 =
169            pfManager->LoadJSPandaFile(thread1, filename1, JSPandaFile::ENTRY_MAIN_FUNCTION);
170        EXPECT_TRUE(pf1 == loadedPf1);
171        EXPECT_TRUE(instance1->GetJSThread()->GetCurrentEcmaContext()->HasCachedConstpool(pf1.get()));
172        TestHelper::DestroyEcmaVMWithScope(instance1, scope1); // Remove 'instance1' when ecmaVM destruct.
173    });
174    {
175        ThreadSuspensionScope suspensionScope(thread);
176        t1.join();
177    }
178
179    std::shared_ptr<JSPandaFile> foundPf1 = pfManager->FindJSPandaFile(filename1);
180    EXPECT_TRUE(foundPf1 != nullptr);
181
182    pfManager->RemoveJSPandaFile(pf1.get());
183    pfManager->RemoveJSPandaFile(pf2.get());
184    std::shared_ptr<JSPandaFile> afterRemovePf1 = pfManager->FindJSPandaFile(filename1);
185    std::shared_ptr<JSPandaFile> afterRemovePf2 = pfManager->FindJSPandaFile(filename2);
186    EXPECT_EQ(afterRemovePf1, nullptr);
187    EXPECT_EQ(afterRemovePf2, nullptr);
188}
189
190void CreateJSPandaFileAndConstpool(EcmaVM *vm)
191{
192    const char *filename = "__JSPandaFileManagerTest1.pa";
193    const char *data = R"(
194        .function void foo() {}
195    )";
196    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
197    Parser parser;
198    auto res = parser.Parse(data);
199    std::unique_ptr<const File> pfPtr = pandasm::AsmEmitter::Emit(res.Value());
200    std::shared_ptr<JSPandaFile> pf = pfManager->NewJSPandaFile(pfPtr.release(), CString(filename));
201    pfManager->AddJSPandaFile(pf);
202
203    [[maybe_unused]] EcmaHandleScope handleScope(vm->GetJSThread());
204    JSHandle<ConstantPool> constpool = vm->GetFactory()->NewSConstantPool(1);
205    auto context = vm->GetJSThread()->GetCurrentEcmaContext();
206    constpool = context->AddOrUpdateConstpool(pf.get(), constpool, 0);
207    JSHandle<ConstantPool> newConstpool = vm->GetFactory()->NewConstantPool(1);
208    context->SetUnsharedConstpool(constpool, newConstpool.GetTaggedValue());
209}
210
211HWTEST_F_L0(JSPandaFileManagerTest, GC_Add_Find_Remove_JSPandaFile)
212{
213    const char *filename = "__JSPandaFileManagerTest1.pa";
214    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
215
216    CreateJSPandaFileAndConstpool(instance);
217    // Remove 'instance' and JSPandafile when trigger GC.
218    SharedHeap::GetInstance()->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(instance->GetJSThread());
219    SharedHeap::GetInstance()->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(instance->GetJSThread());
220    std::shared_ptr<JSPandaFile> afterRemovePf = pfManager->FindJSPandaFile(filename);
221    EXPECT_EQ(afterRemovePf, nullptr);
222}
223
224HWTEST_F_L0(JSPandaFileManagerTest, LoadJSPandaFile)
225{
226    const char *filename1 = "__JSPandaFileManagerTest1.pa";
227    const char *filename2 = "__JSPandaFileManagerTest2.pa";
228    const char *filename3 = "__JSPandaFileManagerTest3.abc";
229    const char *data = R"(
230        .function void foo() {}
231    )";
232    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
233    Parser parser;
234    auto res = parser.Parse(data);
235    std::unique_ptr<const File> pfPtr1 = pandasm::AsmEmitter::Emit(res.Value());
236    std::unique_ptr<const File> pfPtr2 = pandasm::AsmEmitter::Emit(res.Value());
237    std::unique_ptr<const File> pfPtr3 = pandasm::AsmEmitter::Emit(res.Value());
238    std::shared_ptr<JSPandaFile> pf1 = pfManager->NewJSPandaFile(pfPtr1.release(), CString(filename1));
239    std::shared_ptr<JSPandaFile> pf2 = pfManager->NewJSPandaFile(pfPtr2.release(), CString(filename2));
240    std::shared_ptr<JSPandaFile> pf3 = pfManager->NewJSPandaFile(pfPtr3.release(), CString(filename3));
241    pfManager->AddJSPandaFile(pf1);
242    pfManager->AddJSPandaFile(pf2);
243    pfManager->AddJSPandaFile(pf3);
244    std::shared_ptr<JSPandaFile> loadedPf1 =
245        pfManager->LoadJSPandaFile(thread, filename1, JSPandaFile::ENTRY_MAIN_FUNCTION);
246    std::shared_ptr<JSPandaFile> loadedPf2 =
247        pfManager->LoadJSPandaFile(thread, filename2, JSPandaFile::ENTRY_MAIN_FUNCTION, (void *)data, sizeof(data));
248    std::shared_ptr<JSPandaFile> loadedPf3 =
249        pfManager->LoadJSPandaFile(thread, filename3, JSPandaFile::ENTRY_MAIN_FUNCTION, (void *)data, sizeof(data));
250    EXPECT_TRUE(loadedPf1 != nullptr);
251    EXPECT_TRUE(loadedPf2 != nullptr);
252    EXPECT_TRUE(loadedPf3 != nullptr);
253    EXPECT_TRUE(pf1 == loadedPf1);
254    EXPECT_TRUE(pf2 == loadedPf2);
255    EXPECT_TRUE(pf3 == loadedPf3);
256    EXPECT_STREQ(loadedPf1->GetJSPandaFileDesc().c_str(), "__JSPandaFileManagerTest1.pa");
257    EXPECT_STREQ(loadedPf2->GetJSPandaFileDesc().c_str(), "__JSPandaFileManagerTest2.pa");
258    EXPECT_STREQ(loadedPf3->GetJSPandaFileDesc().c_str(), "__JSPandaFileManagerTest3.abc");
259
260    pfManager->RemoveJSPandaFile(pf1.get());
261    pfManager->RemoveJSPandaFile(pf2.get());
262    pfManager->RemoveJSPandaFile(pf3.get());
263}
264
265HWTEST_F_L0(JSPandaFileManagerTest, GenerateProgram)
266{
267    Parser parser;
268    auto vm = thread->GetEcmaVM();
269    const char *filename = "__JSPandaFileManagerTest.pa";
270    const uint8_t *typeDesc = utf::CStringAsMutf8("L_GLOBAL;");
271    const char *data = R"(
272        .function void foo() {}
273    )";
274    auto res = parser.Parse(data);
275    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
276    std::unique_ptr<const File> pfPtr = pandasm::AsmEmitter::Emit(res.Value());
277    std::shared_ptr<JSPandaFile> pf = pfManager->NewJSPandaFile(pfPtr.release(), CString(filename));
278    const File *file = pf->GetPandaFile();
279    File::EntityId classId = file->GetClassId(typeDesc);
280    ClassDataAccessor cda(*file, classId);
281    std::vector<File::EntityId> methodId {};
282    cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
283        methodId.push_back(mda.GetMethodId());
284    });
285    pf->UpdateMainMethodIndex(methodId[0].GetOffset());
286    MethodLiteral *method = new MethodLiteral(methodId[0]);
287    pf->SetMethodLiteralToMap(method);
288    pfManager->AddJSPandaFile(pf);
289
290    JSHandle<ecmascript::Program> program = pfManager->GenerateProgram(vm, pf.get(), JSPandaFile::ENTRY_FUNCTION_NAME);
291    JSHandle<JSFunction> mainFunc(thread, program->GetMainFunction());
292    JSHandle<JSTaggedValue> funcName = JSFunction::GetFunctionName(thread, JSHandle<JSFunctionBase>(mainFunc));
293    EXPECT_STREQ(EcmaStringAccessor(JSHandle<EcmaString>::Cast(funcName)).ToCString().c_str(), "foo");
294
295    pfManager->RemoveJSPandaFile(pf.get());
296}
297
298HWTEST_F_L0(JSPandaFileManagerTest, GetJSPtExtractor)
299{
300    const char *filename = "__JSPandaFileManagerTest.pa";
301    const char *data = R"(
302        .function void foo() {}
303    )";
304    Parser parser;
305    auto res = parser.Parse(data);
306    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
307    std::unique_ptr<const File> pfPtr = pandasm::AsmEmitter::Emit(res.Value());
308    std::shared_ptr<JSPandaFile> pf = pfManager->NewJSPandaFile(pfPtr.release(), CString(filename));
309    pfManager->AddJSPandaFile(pf);
310    DebugInfoExtractor *extractor = pfManager->GetJSPtExtractor(pf.get());
311    EXPECT_TRUE(extractor != nullptr);
312
313    pfManager->RemoveJSPandaFile(pf.get());
314}
315
316HWTEST_F_L0(JSPandaFileManagerTest, EnumerateJSPandaFiles)
317{
318    const char *filename1 = "__JSPandaFileManagerTest3.pa";
319    const char *filename2 = "__JSPandaFileManagerTest4.pa";
320    const char *data = R"(
321        .function void foo() {}
322    )";
323    Parser parser;
324    auto res = parser.Parse(data);
325    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
326    std::unique_ptr<const File> pfPtr1 = pandasm::AsmEmitter::Emit(res.Value());
327    std::unique_ptr<const File> pfPtr2 = pandasm::AsmEmitter::Emit(res.Value());
328    std::shared_ptr<JSPandaFile> pf1 = pfManager->NewJSPandaFile(pfPtr1.release(), CString(filename1));
329    std::shared_ptr<JSPandaFile> pf2 = pfManager->NewJSPandaFile(pfPtr2.release(), CString(filename2));
330    pfManager->AddJSPandaFile(pf1);
331    pfManager->AddJSPandaFile(pf2);
332    std::vector<CString> descList{};
333    int count = 0;
334    pfManager->EnumerateJSPandaFiles([&](const std::shared_ptr<JSPandaFile> &file) -> bool {
335        auto desc = file->GetJSPandaFileDesc();
336        std::cout << "desc:" << desc << std::endl;
337        descList.push_back(desc);
338        count++;
339        return true;
340    });
341    EXPECT_EQ(count, 2); // 2 : test number of files
342    // Sort by the hash value of the element, the output is unordered
343    EXPECT_STREQ(descList[0].c_str(), "__JSPandaFileManagerTest4.pa");
344    EXPECT_STREQ(descList[1].c_str(), "__JSPandaFileManagerTest3.pa");
345
346    pfManager->RemoveJSPandaFile(pf1.get());
347    pfManager->RemoveJSPandaFile(pf2.get());
348}
349
350HWTEST_F_L0(JSPandaFileManagerTest, CheckFilePath)
351{
352    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
353    const char *fileName = "__JSPandaFileManagerTest3.abc";
354    const char *data = R"(
355        .function void foo() {}
356    )";
357    Parser parser;
358    auto res = parser.Parse(data);
359    std::unique_ptr<const File> pfPtr = pandasm::AsmEmitter::Emit(res.Value());
360    std::shared_ptr<JSPandaFile> pf = pfManager->NewJSPandaFile(pfPtr.release(), CString(fileName));
361    pfManager->AddJSPandaFile(pf);
362    bool result = pfManager->CheckFilePath(thread, fileName);
363    EXPECT_TRUE(result);
364    pfManager->RemoveJSPandaFile(pf.get());
365}
366
367HWTEST_F_L0(JSPandaFileManagerTest, GetJSPandaFileByBufferFiles)
368{
369    JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
370    const char *fileName = "__JSPandaFileManagerTest3.abc";
371    const char *data = R"(
372        .function void foo() {}
373    )";
374    Parser parser;
375    auto res = parser.Parse(data);
376    std::unique_ptr<const File> pfPtr = pandasm::AsmEmitter::Emit(res.Value());
377    std::shared_ptr<JSPandaFile> pf = pfManager->NewJSPandaFile(pfPtr.release(), CString(fileName));
378    std::shared_ptr<JSPandaFile> jsPandaFile;
379    pfManager->AddJSPandaFile(pf);
380    AbcBufferCache *abcBufferCache = thread->GetCurrentEcmaContext()->GetAbcBufferCache();
381    abcBufferCache->AddAbcBufferToCache(CString(fileName), (void *)data, sizeof(data), AbcBufferType::NORMAL_BUFFER);
382    AbcBufferInfo bufferInfo = abcBufferCache->FindJSPandaFileInAbcBufferCache(CString(fileName));
383    EXPECT_TRUE(bufferInfo.buffer_ != nullptr);
384    abcBufferCache->DeleteAbcBufferFromCache(CString(fileName));
385    jsPandaFile = pfManager->LoadJSPandaFile(thread, CString(fileName), "");
386    EXPECT_TRUE(jsPandaFile != nullptr);
387}
388}  // namespace panda::test
389