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 26using namespace panda::ecmascript; 27using namespace panda::panda_file; 28using namespace panda::pandasm; 29 30namespace panda::test { 31using PatchErrorCode = panda::JSNApi::PatchErrorCode; 32using Program = panda::ecmascript::Program; 33using EcmaContext = panda::ecmascript::EcmaContext; 34class QuickFixTest : public testing::Test { 35public: 36 static void SetUpTestCase() 37 { 38 GTEST_LOG_(INFO) << "SetUpTestCase"; 39 } 40 41 static void TearDownTestCase() 42 { 43 GTEST_LOG_(INFO) << "TearDownCase"; 44 } 45 46 void SetUp() override 47 { 48 TestHelper::CreateEcmaVMWithScope(instance, thread, scope, false, false, false); 49 } 50 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 61HWTEST_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 84HWTEST_F_L0(QuickFixTest, HotReload_Restart_SingleFile) 85{ 86 std::string baseFileName = QUICKFIX_ABC_PATH "single_file/base/index.abc"; 87 std::string patchFileName = QUICKFIX_ABC_PATH "single_file/patch/index.abc"; 88 89 JSNApi::EnableUserUncaughtErrorHandler(instance); 90 91 JSNApi::SetBundle(instance, false); 92 93 bool result = JSNApi::Execute(instance, baseFileName, "index"); 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 result = JSNApi::Execute(instance, baseFileName, "index"); 104 EXPECT_TRUE(result); 105 106 res = JSNApi::UnloadPatch(instance, patchFileName); 107 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 108 109 res = JSNApi::LoadPatch(instance, patchFileName, baseFileName); 110 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 111 112 exception = JSNApi::GetAndClearUncaughtException(instance); 113 result = JSNApi::IsQuickFixCausedException(instance, exception, patchFileName); 114 EXPECT_FALSE(result); 115 116 res = JSNApi::UnloadPatch(instance, patchFileName); 117 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 118} 119 120HWTEST_F_L0(QuickFixTest, HotReload_MultiFile) 121{ 122 std::string baseFileName = QUICKFIX_ABC_PATH "multi_file/base/merge.abc"; 123 std::string patchFileName = QUICKFIX_ABC_PATH "multi_file/patch/merge.abc"; 124 125 JSNApi::EnableUserUncaughtErrorHandler(instance); 126 127 JSNApi::SetBundle(instance, false); 128 129 bool result = JSNApi::Execute(instance, baseFileName, "main"); 130 EXPECT_TRUE(result); 131 132 auto res = JSNApi::LoadPatch(instance, patchFileName, baseFileName); 133 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 134 135 Local<ObjectRef> exception = JSNApi::GetAndClearUncaughtException(instance); 136 result = JSNApi::IsQuickFixCausedException(instance, exception, patchFileName); 137 EXPECT_FALSE(result); 138 139 res = JSNApi::UnloadPatch(instance, patchFileName); 140 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 141} 142 143HWTEST_F_L0(QuickFixTest, HotReload_Restart_MultiFile) 144{ 145 std::string baseFileName = QUICKFIX_ABC_PATH "multi_file/base/merge.abc"; 146 std::string patchFileName = QUICKFIX_ABC_PATH "multi_file/patch/merge.abc"; 147 148 JSNApi::EnableUserUncaughtErrorHandler(instance); 149 150 JSNApi::SetBundle(instance, false); 151 152 bool result = JSNApi::Execute(instance, baseFileName, "main"); 153 EXPECT_TRUE(result); 154 155 auto res = JSNApi::LoadPatch(instance, patchFileName, baseFileName); 156 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 157 158 Local<ObjectRef> exception = JSNApi::GetAndClearUncaughtException(instance); 159 result = JSNApi::IsQuickFixCausedException(instance, exception, patchFileName); 160 EXPECT_FALSE(result); 161 162 result = JSNApi::Execute(instance, baseFileName, "main"); 163 EXPECT_TRUE(result); 164 165 res = JSNApi::UnloadPatch(instance, patchFileName); 166 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 167 168 res = JSNApi::LoadPatch(instance, patchFileName, baseFileName); 169 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 170 171 res = JSNApi::UnloadPatch(instance, patchFileName); 172 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 173} 174 175HWTEST_F_L0(QuickFixTest, HotReload_MultiHap) 176{ 177 std::string baseFileName1 = QUICKFIX_ABC_PATH "single_file/base/index.abc"; 178 std::string patchFileName1 = QUICKFIX_ABC_PATH "single_file/patch/index.abc"; 179 180 std::string baseFileName2 = QUICKFIX_ABC_PATH "multi_file/base/merge.abc"; 181 std::string patchFileName2 = QUICKFIX_ABC_PATH "multi_file/patch/merge.abc"; 182 183 JSNApi::SetBundle(instance, false); 184 185 bool result = JSNApi::Execute(instance, baseFileName1, "index"); 186 EXPECT_TRUE(result); 187 188 result = JSNApi::Execute(instance, baseFileName2, "main"); 189 EXPECT_TRUE(result); 190 191 auto res = JSNApi::LoadPatch(instance, patchFileName1, baseFileName1); 192 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 193 194 res = JSNApi::LoadPatch(instance, patchFileName2, baseFileName2); 195 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 196 197 res = JSNApi::UnloadPatch(instance, patchFileName1); 198 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 199 200 res = JSNApi::UnloadPatch(instance, patchFileName2); 201 EXPECT_TRUE(res == PatchErrorCode::SUCCESS); 202} 203 204HWTEST_F_L0(QuickFixTest, HotReload_Buffer) 205{ 206 const char *baseFileName = "__base.pa"; 207 const char *baseData = R"( 208 .function void foo() {} 209 )"; 210 const char *patchFileName = "__patch.pa"; 211 const char *patchData = R"( 212 .function void foo() {} 213 )"; 214 215 JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance(); 216 Parser parser; 217 auto res = parser.Parse(patchData); 218 std::unique_ptr<const File> basePF = pandasm::AsmEmitter::Emit(res.Value()); 219 std::unique_ptr<const File> patchPF = pandasm::AsmEmitter::Emit(res.Value()); 220 std::shared_ptr<JSPandaFile> baseFile = pfManager->NewJSPandaFile(basePF.release(), CString(baseFileName)); 221 std::shared_ptr<JSPandaFile> patchFile = pfManager->NewJSPandaFile(patchPF.release(), CString(patchFileName)); 222 pfManager->AddJSPandaFile(baseFile); 223 pfManager->AddJSPandaFile(patchFile); 224 225 auto result = JSNApi::LoadPatch(instance, patchFileName, (uint8_t *)patchData, sizeof(patchData), 226 baseFileName, (uint8_t *)baseData, sizeof(baseData)); 227 EXPECT_FALSE(result == PatchErrorCode::SUCCESS); 228 229 pfManager->RemoveJSPandaFile(baseFile.get()); 230 pfManager->RemoveJSPandaFile(patchFile.get()); 231} 232 233HWTEST_F_L0(QuickFixTest, HotReload_Instantiate) 234{ 235 ThreadManagedScope managedScope(thread); 236 237 CString baseFileName = QUICKFIX_ABC_PATH "multi_file/base/merge.abc"; 238 std::shared_ptr<JSPandaFile> baseFile = 239 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, baseFileName, ""); 240 EXPECT_TRUE(baseFile != nullptr); 241 242 CString patchFileName = QUICKFIX_ABC_PATH "multi_file/patch/merge.abc"; 243 std::shared_ptr<JSPandaFile> patchFile = 244 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, patchFileName, ""); 245 EXPECT_TRUE(patchFile != nullptr); 246 247 CString replacedRecordName = "main"; 248 EcmaContext *context = thread->GetCurrentEcmaContext(); 249 context->SetStageOfHotReload(StageOfHotReload::BEGIN_EXECUTE_PATCHMAIN); 250 251 ModuleManager *moduleManager = context->GetModuleManager(); 252 JSHandle<JSTaggedValue> module = 253 moduleManager->HostResolveImportedModuleWithMergeForHotReload(patchFileName, replacedRecordName, false); 254 EXPECT_FALSE(module->IsHole()); 255 256 JSHandle<Program> program = 257 JSPandaFileManager::GetInstance()->GenerateProgram(instance, patchFile.get(), replacedRecordName); 258 EXPECT_FALSE(program.IsEmpty()); 259 260 SourceTextModule::Instantiate(thread, module, false); 261 EXPECT_TRUE(JSHandle<SourceTextModule>::Cast(module)->GetStatus() == ModuleStatus::INSTANTIATED); 262 263 context->SetStageOfHotReload(StageOfHotReload::LOAD_END_EXECUTE_PATCHMAIN); 264 JSHandle<SourceTextModule>::Cast(module)->SetStatus(ModuleStatus::UNINSTANTIATED); 265 SourceTextModule::Instantiate(thread, module, false); 266 EXPECT_TRUE(JSHandle<SourceTextModule>::Cast(module)->GetStatus() == ModuleStatus::INSTANTIATED); 267} 268 269bool QuickFixQueryFunc( 270 std::string baseFileName, std::string &patchFileName, uint8_t ** patchBuffer, size_t &patchBufferSize) 271{ 272 if (baseFileName != QUICKFIX_ABC_PATH "multi_file/base/merge.abc") { 273 return false; 274 } 275 276 patchFileName = "__index.pa"; 277 const char data[] = R"( 278 .function void foo() {} 279 )"; 280 281 Parser parser; 282 auto res = parser.Parse(data); 283 uint8_t *bufferData = reinterpret_cast<uint8_t *>((const_cast<char *>(data))); 284 *patchBuffer = bufferData; 285 patchBufferSize = sizeof(data); 286 return true; 287} 288 289HWTEST_F_L0(QuickFixTest, HotReload_RegisterQuickFixQueryFunc) 290{ 291 std::string baseFileName = QUICKFIX_ABC_PATH "multi_file/base/merge.abc"; 292 std::string patchFileName = "__index.pa"; 293 JSNApi::RegisterQuickFixQueryFunc(instance, QuickFixQueryFunc); 294 295 std::shared_ptr<JSPandaFile> baseFile = 296 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, baseFileName.c_str(), ""); 297 EXPECT_TRUE(baseFile != nullptr); 298 std::shared_ptr<JSPandaFile> patchFile = 299 JSPandaFileManager::GetInstance()->FindJSPandaFile(patchFileName.c_str()); 300 EXPECT_TRUE(patchFile == nullptr); 301 302 QuickFixManager *quickFixManager = instance->GetQuickFixManager(); 303 quickFixManager->LoadPatchIfNeeded(thread, baseFile.get()); 304 305 JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance(); 306 pfManager->RemoveJSPandaFile(baseFile.get()); 307} 308} // namespace panda::test 309