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