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
26 using namespace panda::ecmascript;
27 using namespace panda::panda_file;
28 using namespace panda::pandasm;
29
30 namespace panda::test {
31 using PatchErrorCode = panda::JSNApi::PatchErrorCode;
32 using Program = panda::ecmascript::Program;
33 using EcmaContext = panda::ecmascript::EcmaContext;
34 class QuickFixTest : public testing::Test {
35 public:
SetUpTestCase()36 static void SetUpTestCase()
37 {
38 GTEST_LOG_(INFO) << "SetUpTestCase";
39 }
40
TearDownTestCase()41 static void TearDownTestCase()
42 {
43 GTEST_LOG_(INFO) << "TearDownCase";
44 }
45
SetUp()46 void SetUp() override
47 {
48 TestHelper::CreateEcmaVMWithScope(instance, thread, scope, false, false, false);
49 }
50
TearDown()51 void TearDown() override
52 {
53 TestHelper::DestroyEcmaVMWithScope(instance, scope, false);
54 }
55
56 EcmaVM *instance {nullptr};
57 EcmaHandleScope *scope {nullptr};
58 JSThread *thread {nullptr};
59 };
60
HWTEST_F_L0(QuickFixTest,HotReload_SingleFile)61 HWTEST_F_L0(QuickFixTest, HotReload_SingleFile)
62 {
63 std::string baseFileName = QUICKFIX_ABC_PATH "single_file/base/index.abc";
64 std::string patchFileName = QUICKFIX_ABC_PATH "single_file/patch/index.abc";
65
66 JSNApi::EnableUserUncaughtErrorHandler(instance);
67
68 JSNApi::SetBundle(instance, false);
69
70 bool result = JSNApi::Execute(instance, baseFileName, "index");
71 EXPECT_TRUE(result);
72
73 auto res = JSNApi::LoadPatch(instance, patchFileName, baseFileName);
74 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
75
76 Local<ObjectRef> exception = JSNApi::GetAndClearUncaughtException(instance);
77 result = JSNApi::IsQuickFixCausedException(instance, exception, patchFileName);
78 EXPECT_FALSE(result);
79
80 res = JSNApi::UnloadPatch(instance, patchFileName);
81 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
82 }
83
HWTEST_F_L0(QuickFixTest,HotReload_MultiFile)84 HWTEST_F_L0(QuickFixTest, HotReload_MultiFile)
85 {
86 std::string baseFileName = QUICKFIX_ABC_PATH "multi_file/base/merge.abc";
87 std::string patchFileName = QUICKFIX_ABC_PATH "multi_file/patch/merge.abc";
88
89 JSNApi::EnableUserUncaughtErrorHandler(instance);
90
91 JSNApi::SetBundle(instance, false);
92
93 bool result = JSNApi::Execute(instance, baseFileName, "main");
94 EXPECT_TRUE(result);
95
96 auto res = JSNApi::LoadPatch(instance, patchFileName, baseFileName);
97 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
98
99 Local<ObjectRef> exception = JSNApi::GetAndClearUncaughtException(instance);
100 result = JSNApi::IsQuickFixCausedException(instance, exception, patchFileName);
101 EXPECT_FALSE(result);
102
103 res = JSNApi::UnloadPatch(instance, patchFileName);
104 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
105 }
106
HWTEST_F_L0(QuickFixTest,HotReload_MultiHap)107 HWTEST_F_L0(QuickFixTest, HotReload_MultiHap)
108 {
109 std::string baseFileName1 = QUICKFIX_ABC_PATH "single_file/base/index.abc";
110 std::string patchFileName1 = QUICKFIX_ABC_PATH "single_file/patch/index.abc";
111
112 std::string baseFileName2 = QUICKFIX_ABC_PATH "multi_file/base/merge.abc";
113 std::string patchFileName2 = QUICKFIX_ABC_PATH "multi_file/patch/merge.abc";
114
115 JSNApi::SetBundle(instance, false);
116
117 bool result = JSNApi::Execute(instance, baseFileName1, "index");
118 EXPECT_TRUE(result);
119
120 result = JSNApi::Execute(instance, baseFileName2, "main");
121 EXPECT_TRUE(result);
122
123 auto res = JSNApi::LoadPatch(instance, patchFileName1, baseFileName1);
124 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
125
126 res = JSNApi::LoadPatch(instance, patchFileName2, baseFileName2);
127 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
128
129 res = JSNApi::UnloadPatch(instance, patchFileName1);
130 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
131
132 res = JSNApi::UnloadPatch(instance, patchFileName2);
133 EXPECT_TRUE(res == PatchErrorCode::SUCCESS);
134 }
135
HWTEST_F_L0(QuickFixTest,HotReload_Buffer)136 HWTEST_F_L0(QuickFixTest, HotReload_Buffer)
137 {
138 const char *baseFileName = "__base.pa";
139 const char *baseData = R"(
140 .function void foo() {}
141 )";
142 const char *patchFileName = "__patch.pa";
143 const char *patchData = R"(
144 .function void foo() {}
145 )";
146
147 JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
148 Parser parser;
149 auto res = parser.Parse(patchData);
150 std::unique_ptr<const File> basePF = pandasm::AsmEmitter::Emit(res.Value());
151 std::unique_ptr<const File> patchPF = pandasm::AsmEmitter::Emit(res.Value());
152 std::shared_ptr<JSPandaFile> baseFile = pfManager->NewJSPandaFile(basePF.release(), CString(baseFileName));
153 std::shared_ptr<JSPandaFile> patchFile = pfManager->NewJSPandaFile(patchPF.release(), CString(patchFileName));
154 pfManager->AddJSPandaFile(baseFile);
155 pfManager->AddJSPandaFile(patchFile);
156
157 auto result = JSNApi::LoadPatch(instance, patchFileName, (uint8_t *)patchData, sizeof(patchData),
158 baseFileName, (uint8_t *)baseData, sizeof(baseData));
159 EXPECT_FALSE(result == PatchErrorCode::SUCCESS);
160
161 pfManager->RemoveJSPandaFile(baseFile.get());
162 pfManager->RemoveJSPandaFile(patchFile.get());
163 }
164
HWTEST_F_L0(QuickFixTest,HotReload_Instantiate)165 HWTEST_F_L0(QuickFixTest, HotReload_Instantiate)
166 {
167 ThreadManagedScope managedScope(thread);
168
169 CString baseFileName = QUICKFIX_ABC_PATH "multi_file/base/merge.abc";
170 std::shared_ptr<JSPandaFile> baseFile =
171 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, baseFileName, "");
172 EXPECT_TRUE(baseFile != nullptr);
173
174 CString patchFileName = QUICKFIX_ABC_PATH "multi_file/patch/merge.abc";
175 std::shared_ptr<JSPandaFile> patchFile =
176 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, patchFileName, "");
177 EXPECT_TRUE(patchFile != nullptr);
178
179 CString replacedRecordName = "main";
180 EcmaContext *context = thread->GetCurrentEcmaContext();
181 context->SetStageOfHotReload(StageOfHotReload::BEGIN_EXECUTE_PATCHMAIN);
182
183 ModuleManager *moduleManager = context->GetModuleManager();
184 JSHandle<JSTaggedValue> module =
185 moduleManager->HostResolveImportedModuleWithMergeForHotReload(patchFileName, replacedRecordName, false);
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, false);
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, false);
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