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
19 #include "ecmascript/jspandafile/js_pandafile.h"
20 #include "ecmascript/jspandafile/js_pandafile_manager.h"
21 #include "ecmascript/tests/test_helper.h"
22 #include "ecmascript/napi/include/jsnapi.h"
23 #include "ecmascript/patch/quick_fix_manager.h"
24 #include "ecmascript/jspandafile/program_object.h"
25 #include "ecmascript/module/module_resolver.h"
26
27 using namespace panda::ecmascript;
28 using namespace panda::panda_file;
29 using namespace panda::pandasm;
30
31 namespace panda::test {
32 using Program = panda::ecmascript::Program;
33 class QuickFixTest : public testing::Test {
34 public:
SetUpTestCase()35 static void SetUpTestCase()
36 {
37 GTEST_LOG_(INFO) << "SetUpTestCase";
38 }
39
TearDownTestCase()40 static void TearDownTestCase()
41 {
42 GTEST_LOG_(INFO) << "TearDownCase";
43 }
44
SetUp()45 void SetUp() override
46 {
47 TestHelper::CreateEcmaVMWithScope(instance, thread, scope, false, false, false);
48 }
49
TearDown()50 void TearDown() override
51 {
52 TestHelper::DestroyEcmaVMWithScope(instance, scope, false);
53 }
54
55 EcmaVM *instance {nullptr};
56 EcmaHandleScope *scope {nullptr};
57 JSThread *thread {nullptr};
58 };
59
HWTEST_F_L0(QuickFixTest,HotReload_SingleFile)60 HWTEST_F_L0(QuickFixTest, HotReload_SingleFile)
61 {
62 std::string baseFileName = QUICKFIX_ABC_PATH "single_file/base/index.abc";
63 std::string patchFileName = QUICKFIX_ABC_PATH "single_file/patch/index.abc";
64
65 JSNApi::EnableUserUncaughtErrorHandler(instance);
66
67 JSNApi::SetBundle(instance, false);
68
69 bool result = JSNApi::Execute(instance, baseFileName, "index");
70 EXPECT_TRUE(result);
71
72 auto res = JSNApi::LoadPatch(instance, patchFileName, baseFileName);
73 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
74
75 Local<ObjectRef> exception = JSNApi::GetAndClearUncaughtException(instance);
76 result = JSNApi::IsQuickFixCausedException(instance, exception, patchFileName);
77 EXPECT_FALSE(result);
78
79 res = JSNApi::UnloadPatch(instance, patchFileName);
80 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
81 }
82
HWTEST_F_L0(QuickFixTest,HotReload_MultiFile)83 HWTEST_F_L0(QuickFixTest, HotReload_MultiFile)
84 {
85 std::string baseFileName = QUICKFIX_ABC_PATH "multi_file/base/merge.abc";
86 std::string patchFileName = QUICKFIX_ABC_PATH "multi_file/patch/merge.abc";
87
88 JSNApi::EnableUserUncaughtErrorHandler(instance);
89
90 JSNApi::SetBundle(instance, false);
91
92 bool result = JSNApi::Execute(instance, baseFileName, "main");
93 EXPECT_TRUE(result);
94
95 auto res = JSNApi::LoadPatch(instance, patchFileName, baseFileName);
96 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
97
98 Local<ObjectRef> exception = JSNApi::GetAndClearUncaughtException(instance);
99 result = JSNApi::IsQuickFixCausedException(instance, exception, patchFileName);
100 EXPECT_FALSE(result);
101
102 res = JSNApi::UnloadPatch(instance, patchFileName);
103 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
104 }
105
HWTEST_F_L0(QuickFixTest,HotReload_MultiHap)106 HWTEST_F_L0(QuickFixTest, HotReload_MultiHap)
107 {
108 std::string baseFileName1 = QUICKFIX_ABC_PATH "single_file/base/index.abc";
109 std::string patchFileName1 = QUICKFIX_ABC_PATH "single_file/patch/index.abc";
110
111 std::string baseFileName2 = QUICKFIX_ABC_PATH "multi_file/base/merge.abc";
112 std::string patchFileName2 = QUICKFIX_ABC_PATH "multi_file/patch/merge.abc";
113
114 JSNApi::SetBundle(instance, false);
115
116 bool result = JSNApi::Execute(instance, baseFileName1, "index");
117 EXPECT_TRUE(result);
118
119 result = JSNApi::Execute(instance, baseFileName2, "main");
120 EXPECT_TRUE(result);
121
122 auto res = JSNApi::LoadPatch(instance, patchFileName1, baseFileName1);
123 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
124
125 res = JSNApi::LoadPatch(instance, patchFileName2, baseFileName2);
126 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
127
128 res = JSNApi::UnloadPatch(instance, patchFileName1);
129 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
130
131 res = JSNApi::UnloadPatch(instance, patchFileName2);
132 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
133 }
134
HWTEST_F_L0(QuickFixTest,HotReload_Buffer)135 HWTEST_F_L0(QuickFixTest, HotReload_Buffer)
136 {
137 const char *baseFileName = "__base.pa";
138 const char *baseData = R"(
139 .function void foo() {}
140 )";
141 const char *patchFileName = "__patch.pa";
142 const char *patchData = R"(
143 .function void foo() {}
144 )";
145
146 JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
147 Parser parser;
148 auto res = parser.Parse(patchData);
149 std::unique_ptr<const File> basePF = pandasm::AsmEmitter::Emit(res.Value());
150 std::unique_ptr<const File> patchPF = pandasm::AsmEmitter::Emit(res.Value());
151 std::shared_ptr<JSPandaFile> baseFile = pfManager->NewJSPandaFile(basePF.release(), CString(baseFileName));
152 std::shared_ptr<JSPandaFile> patchFile = pfManager->NewJSPandaFile(patchPF.release(), CString(patchFileName));
153 pfManager->AddJSPandaFile(baseFile);
154 pfManager->AddJSPandaFile(patchFile);
155
156 auto result = JSNApi::LoadPatch(instance, patchFileName, (uint8_t *)patchData, sizeof(patchData),
157 baseFileName, (uint8_t *)baseData, sizeof(baseData));
158 EXPECT_FALSE(result == PatchErrorCode::SUCCESS);
159
160 pfManager->RemoveJSPandaFile(baseFile.get());
161 pfManager->RemoveJSPandaFile(patchFile.get());
162 }
163
HWTEST_F_L0(QuickFixTest,HotReload_Instantiate)164 HWTEST_F_L0(QuickFixTest, HotReload_Instantiate)
165 {
166 ecmascript::ThreadManagedScope managedScope(thread);
167
168 CString baseFileName = QUICKFIX_ABC_PATH "multi_file/base/merge.abc";
169 std::shared_ptr<JSPandaFile> baseFile =
170 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, baseFileName, "");
171 EXPECT_TRUE(baseFile != nullptr);
172
173 CString patchFileName = QUICKFIX_ABC_PATH "multi_file/patch/merge.abc";
174 std::shared_ptr<JSPandaFile> patchFile =
175 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, patchFileName, "");
176 EXPECT_TRUE(patchFile != nullptr);
177
178 CString replacedRecordName = "main";
179 thread->SetStageOfHotReload(StageOfHotReload::BEGIN_EXECUTE_PATCHMAIN);
180
181 JSHandle<JSTaggedValue> module = ModuleResolver::HostResolveImportedModuleForHotReload(thread,
182 patchFileName, replacedRecordName);
183 EXPECT_FALSE(module->IsHole());
184
185 JSHandle<Program> program =
186 JSPandaFileManager::GetInstance()->GenerateProgram(instance, patchFile.get(), replacedRecordName);
187 EXPECT_FALSE(program.IsEmpty());
188
189 SourceTextModule::Instantiate(thread, module);
190 EXPECT_TRUE(JSHandle<SourceTextModule>::Cast(module)->GetStatus() == ModuleStatus::INSTANTIATED);
191
192 thread->SetStageOfHotReload(StageOfHotReload::LOAD_END_EXECUTE_PATCHMAIN);
193 JSHandle<SourceTextModule>::Cast(module)->SetStatus(ModuleStatus::UNINSTANTIATED);
194 SourceTextModule::Instantiate(thread, module);
195 EXPECT_TRUE(JSHandle<SourceTextModule>::Cast(module)->GetStatus() == ModuleStatus::INSTANTIATED);
196 }
197
QuickFixQueryFunc(std::string baseFileName,std::string & patchFileName,uint8_t ** patchBuffer,size_t & patchBufferSize)198 bool QuickFixQueryFunc(
199 std::string baseFileName, std::string &patchFileName, uint8_t ** patchBuffer, size_t &patchBufferSize)
200 {
201 if (baseFileName != QUICKFIX_ABC_PATH "multi_file/base/merge.abc") {
202 return false;
203 }
204
205 patchFileName = "__index.pa";
206 const char data[] = R"(
207 .function void foo() {}
208 )";
209
210 Parser parser;
211 auto res = parser.Parse(data);
212 uint8_t *bufferData = reinterpret_cast<uint8_t *>((const_cast<char *>(data)));
213 *patchBuffer = bufferData;
214 patchBufferSize = sizeof(data);
215 return true;
216 }
217
HWTEST_F_L0(QuickFixTest,HotReload_RegisterQuickFixQueryFunc)218 HWTEST_F_L0(QuickFixTest, HotReload_RegisterQuickFixQueryFunc)
219 {
220 std::string baseFileName = QUICKFIX_ABC_PATH "multi_file/base/merge.abc";
221 std::string patchFileName = "__index.pa";
222 JSNApi::RegisterQuickFixQueryFunc(instance, QuickFixQueryFunc);
223
224 std::shared_ptr<JSPandaFile> baseFile =
225 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, baseFileName.c_str(), "");
226 EXPECT_TRUE(baseFile != nullptr);
227 std::shared_ptr<JSPandaFile> patchFile =
228 JSPandaFileManager::GetInstance()->FindJSPandaFile(patchFileName.c_str());
229 EXPECT_TRUE(patchFile == nullptr);
230
231 QuickFixManager *quickFixManager = instance->GetQuickFixManager();
232 quickFixManager->LoadPatchIfNeeded(thread, baseFile.get());
233
234 JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
235 pfManager->RemoveJSPandaFile(baseFile.get());
236 }
237 } // namespace panda::test
238