From b58a6850524d4073d3e8c733bc2753fe3443382f Mon Sep 17 00:00:00 2001 From: lijiamin Date: Tue, 22 Nov 2022 17:05:27 +0800 Subject: [PATCH] Adapt to hot patch and add testcases Signed-off-by: lijiamin Change-Id: Ida501c720acd3c78005d9252aed11606dc522d53 --- ecmascript/jspandafile/quick_fix_loader.cpp | 85 +++++++++++-------- ecmascript/jspandafile/quick_fix_loader.h | 1 + ecmascript/quick_fix/main.cpp | 2 +- test/quickfix/BUILD.gn | 21 ++++- test/quickfix/add_callfunction/base.js | 25 ++++++ test/quickfix/add_callfunction/base.txt | 2 + test/quickfix/add_callfunction/base_modify.js | 29 +++++++ .../add_callfunction/expect_output.txt | 26 ++++++ test/quickfix/add_callfunction/module.js | 16 ++++ .../add_callfunction/module_modify.js | 16 ++++ test/quickfix/add_callfunction/patch.txt | 2 + test/quickfix/add_callfunction/test.js | 15 ++++ test/test_helper.gni | 17 ++++ 13 files changed, 215 insertions(+), 42 deletions(-) create mode 100644 test/quickfix/add_callfunction/base.js create mode 100644 test/quickfix/add_callfunction/base.txt create mode 100644 test/quickfix/add_callfunction/base_modify.js create mode 100644 test/quickfix/add_callfunction/expect_output.txt create mode 100644 test/quickfix/add_callfunction/module.js create mode 100644 test/quickfix/add_callfunction/module_modify.js create mode 100644 test/quickfix/add_callfunction/patch.txt create mode 100644 test/quickfix/add_callfunction/test.js diff --git a/ecmascript/jspandafile/quick_fix_loader.cpp b/ecmascript/jspandafile/quick_fix_loader.cpp index 3a7158824f..052ee4b176 100644 --- a/ecmascript/jspandafile/quick_fix_loader.cpp +++ b/ecmascript/jspandafile/quick_fix_loader.cpp @@ -101,7 +101,12 @@ bool QuickFixLoader::LoadPatchInternal(JSThread *thread, const JSPandaFile *base } if (!ReplaceMethod(thread, baseFile, patchFile, baseConstpoolValues.value())) { - LOG_ECMA(ERROR) << "replace method failed"; + LOG_ECMA(ERROR) << "Replace method failed"; + return false; + } + + if (isHotPatch_ && !ExecutePatchMain(thread, patchPrograms, patchFile)) { + LOG_ECMA(ERROR) << "Execute patch main failed"; return false; } @@ -138,6 +143,13 @@ CVector> QuickFixLoader::ParseAllConstpoolWithMerge(JSThread * uint32_t mainMethodIndex = jsPandaFile->GetMainMethodIndex(recordName); ASSERT(mainMethodIndex != 0); + + const char *mainMethodName = + MethodLiteral::GetMethodName(jsPandaFile, panda_file::File::EntityId(mainMethodIndex)); + if (!isHotPatch_ && std::strcmp(mainMethodName, JSPandaFile::PATCH_FUNCTION_NAME_0) == 0) { + isHotPatch_ = true; + } + if (!isNewVersion) { PandaFileTranslator::ParseFuncAndLiteralConstPool(vm, jsPandaFile, recordName, constpool); } else { @@ -404,49 +416,48 @@ bool QuickFixLoader::UnloadPatch(JSThread *thread, const CString &patchFileName) return false; } - for (const auto &iter : baseConstpoolValues.value().get()) { - uint32_t index = iter.first; - ConstantPool *baseConstpool = ConstantPool::Cast((iter.second).GetTaggedObject()); - for (const auto &item : reservedBaseNormalMethodInfo_) { - NormalMethodIndex normalMethodIndex = item.first; - if (index == normalMethodIndex.constpoolNum) { - uint32_t constpoolIndex = normalMethodIndex.constpoolIndex; - MethodLiteral *base = item.second; - - JSTaggedValue value = baseConstpool->GetObjectFromCache(constpoolIndex); - ASSERT(value.IsMethod()); - Method *method = Method::Cast(value.GetTaggedObject()); - - JSTaggedValue baseConstpoolValue = FindConstpoolVal(thread, baseFile, base->GetMethodId()); - ReplaceMethodInner(thread, method, base, baseConstpoolValue); - LOG_ECMA(INFO) << "Replace normal method: " << method->GetMethodName(); - } - } - - for (auto &item : reservedBaseClassMethodInfo_) { - ClassMethodIndex classMethodIndex = item.first; - if (index == classMethodIndex.constpoolNum) { - uint32_t constpoolIndex = classMethodIndex.constpoolIndex; - MethodLiteral *base = item.second; - - TaggedArray *classLiteral = TaggedArray::Cast(baseConstpool->GetObjectFromCache(constpoolIndex)); - JSTaggedValue value = classLiteral->Get(thread, classMethodIndex.literalIndex); - ASSERT(value.IsJSFunctionBase()); - JSFunctionBase *func = JSFunctionBase::Cast(value.GetTaggedObject()); - Method *method = Method::Cast(func->GetMethod().GetTaggedObject()); + for (const auto &item : reservedBaseNormalMethodInfo_) { + NormalMethodIndex normalMethodIndex = item.first; + ConstantPool *baseConstpool = ConstantPool::Cast( + (baseConstpoolValues.value().get()[normalMethodIndex.constpoolNum]).GetTaggedObject()); - JSTaggedValue baseConstpoolValue = FindConstpoolVal(thread, baseFile, base->GetMethodId()); - ReplaceMethodInner(thread, method, base, baseConstpoolValue); - LOG_ECMA(INFO) << "Replace class method: " << method->GetMethodName(); - } - } + uint32_t constpoolIndex = normalMethodIndex.constpoolIndex; + MethodLiteral *base = item.second; + + JSTaggedValue value = baseConstpool->GetObjectFromCache(constpoolIndex); + ASSERT(value.IsMethod()); + Method *method = Method::Cast(value.GetTaggedObject()); + + JSTaggedValue baseConstpoolValue = FindConstpoolVal(thread, baseFile, base->GetMethodId()); + ReplaceMethodInner(thread, method, base, baseConstpoolValue); + LOG_ECMA(INFO) << "Replace normal method: " << method->GetMethodName(); + } + + for (auto &item : reservedBaseClassMethodInfo_) { + ClassMethodIndex classMethodIndex = item.first; + ConstantPool *baseConstpool = ConstantPool::Cast( + (baseConstpoolValues.value().get()[classMethodIndex.constpoolNum]).GetTaggedObject()); + + uint32_t constpoolIndex = classMethodIndex.constpoolIndex; + MethodLiteral *base = item.second; + + TaggedArray *classLiteral = TaggedArray::Cast(baseConstpool->GetObjectFromCache(constpoolIndex)); + JSTaggedValue value = classLiteral->Get(thread, classMethodIndex.literalIndex); + ASSERT(value.IsJSFunctionBase()); + JSFunctionBase *func = JSFunctionBase::Cast(value.GetTaggedObject()); + Method *method = Method::Cast(func->GetMethod().GetTaggedObject()); + + JSTaggedValue baseConstpoolValue = FindConstpoolVal(thread, baseFile, base->GetMethodId()); + ReplaceMethodInner(thread, method, base, baseConstpoolValue); + LOG_ECMA(INFO) << "Replace class method: " << method->GetMethodName(); } vm->GetJsDebuggerManager()->GetHotReloadManager()->NotifyPatchUnloaded(patchFile); ClearReservedInfo(); ClearPatchInfo(thread, patchFileName); - + isHotPatch_ = false; + LOG_ECMA(INFO) << "UnloadPatch success!"; return true; } diff --git a/ecmascript/jspandafile/quick_fix_loader.h b/ecmascript/jspandafile/quick_fix_loader.h index e7c1a3fad6..a3a640b61b 100644 --- a/ecmascript/jspandafile/quick_fix_loader.h +++ b/ecmascript/jspandafile/quick_fix_loader.h @@ -84,6 +84,7 @@ private: static bool CheckStarExportEntryMismatch(StarExportEntry *patch, StarExportEntry *base); CString baseFileName_; + bool isHotPatch_ {false}; // true: HotPatch; false: HotReload. struct NormalMethodIndex { uint32_t constpoolNum {UINT32_MAX}; diff --git a/ecmascript/quick_fix/main.cpp b/ecmascript/quick_fix/main.cpp index 0ad36ceb31..9773a10580 100644 --- a/ecmascript/quick_fix/main.cpp +++ b/ecmascript/quick_fix/main.cpp @@ -105,7 +105,7 @@ int Main(const int argc, const char **argv) #endif uint32_t len = fileNames.size(); if (len < 4) { // 4: four abc file - std::cout << "Must include base.abc, patch.abc, test1.abc, test2.abc absolute path" << std::endl; + std::cout << "Must include base.abc, patch.abc, test.abc, retest.abc absolute path" << std::endl; return -1; } std::string baseFileName = fileNames[0]; diff --git a/test/quickfix/BUILD.gn b/test/quickfix/BUILD.gn index 43f5c05318..bc3453fe49 100644 --- a/test/quickfix/BUILD.gn +++ b/test/quickfix/BUILD.gn @@ -13,7 +13,7 @@ import("//arkcompiler/ets_runtime/test/test_helper.gni") -test_list = [ +hot_reload_test_list = [ "check_import", "class_inheritance", "class_mem_func", @@ -28,7 +28,7 @@ test_list = [ ] if (!is_debug) { - test_list += [ + hot_reload_test_list += [ "multi_classconstpool", "multi_closureconstpool", "multi_constructorconstpool", @@ -37,6 +37,8 @@ if (!is_debug) { ] } +hot_patch_test_list = [ "add_callfunction" ] + host_quickfix_test_action("multi_patch") { extra_patches = [ "patch1", @@ -45,17 +47,28 @@ host_quickfix_test_action("multi_patch") { entry_point = "--entry-point=base" } -foreach(testcase, test_list) { +foreach(testcase, hot_reload_test_list) { host_quickfix_test_action("${testcase}") { entry_point = "--entry-point=base" } } +foreach(testcase, hot_patch_test_list) { + host_quickfix_test_action("${testcase}") { + entry_point = "--entry-point=base" + is_hotpatch = true + } +} + group("ark_quickfix_test") { testonly = true deps = [] - foreach(testcase, test_list) { + foreach(testcase, hot_reload_test_list) { + deps += [ ":${testcase}QuickfixAction" ] + } + + foreach(testcase, hot_patch_test_list) { deps += [ ":${testcase}QuickfixAction" ] } diff --git a/test/quickfix/add_callfunction/base.js b/test/quickfix/add_callfunction/base.js new file mode 100644 index 0000000000..2254890aad --- /dev/null +++ b/test/quickfix/add_callfunction/base.js @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +function A() { + print("base A()"); + B(); +} + +function B() { + print("base B()"); +} + +globalThis.A = A; +globalThis.B = B; \ No newline at end of file diff --git a/test/quickfix/add_callfunction/base.txt b/test/quickfix/add_callfunction/base.txt new file mode 100644 index 0000000000..aa0e09f689 --- /dev/null +++ b/test/quickfix/add_callfunction/base.txt @@ -0,0 +1,2 @@ +base.js;base;module;base.js +module.js;module;module;module.js \ No newline at end of file diff --git a/test/quickfix/add_callfunction/base_modify.js b/test/quickfix/add_callfunction/base_modify.js new file mode 100644 index 0000000000..414ccfa1b9 --- /dev/null +++ b/test/quickfix/add_callfunction/base_modify.js @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +function A() { + print("patch A()"); + B(); + C(); +} + +function B() { + print("patch B()"); +} + +function C() { + print("patch C()"); +} + +globalThis.C = C; \ No newline at end of file diff --git a/test/quickfix/add_callfunction/expect_output.txt b/test/quickfix/add_callfunction/expect_output.txt new file mode 100644 index 0000000000..e7ae22d110 --- /dev/null +++ b/test/quickfix/add_callfunction/expect_output.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +QuickFix Execute start +QuickFix start load patch +QuickFix load patch success +patch A() +patch B() +patch C() +QuickFix start check exception +QuickFix have no exception +QuickFix start unload patch +QuickFix unload patch success +base A() +base B() +QuickFix Execute end diff --git a/test/quickfix/add_callfunction/module.js b/test/quickfix/add_callfunction/module.js new file mode 100644 index 0000000000..e66e2b24d9 --- /dev/null +++ b/test/quickfix/add_callfunction/module.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export var nop = undefined diff --git a/test/quickfix/add_callfunction/module_modify.js b/test/quickfix/add_callfunction/module_modify.js new file mode 100644 index 0000000000..e66e2b24d9 --- /dev/null +++ b/test/quickfix/add_callfunction/module_modify.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export var nop = undefined diff --git a/test/quickfix/add_callfunction/patch.txt b/test/quickfix/add_callfunction/patch.txt new file mode 100644 index 0000000000..72a6f6290f --- /dev/null +++ b/test/quickfix/add_callfunction/patch.txt @@ -0,0 +1,2 @@ +base_modify.js;base;module;base.js +module_modify.js;module;module;module.js \ No newline at end of file diff --git a/test/quickfix/add_callfunction/test.js b/test/quickfix/add_callfunction/test.js new file mode 100644 index 0000000000..86a84c282c --- /dev/null +++ b/test/quickfix/add_callfunction/test.js @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +A() \ No newline at end of file diff --git a/test/test_helper.gni b/test/test_helper.gni index 0285f7bbb0..a277ac6fb8 100644 --- a/test/test_helper.gni +++ b/test/test_helper.gni @@ -630,6 +630,10 @@ template("host_quickfix_test_action") { _script_args_ = invoker.entry_point + " " } + if (defined(invoker.is_hotpatch) && invoker.is_hotpatch) { + _test_map_path_ = "$target_out_dir/${_target_name_}/base.map" + } + foreach(filename, _test_file_name_) { merge_file_raw = "//arkcompiler/ets_runtime/test/quickfix/" if (filename == "test" || filename == "retest") { @@ -670,10 +674,23 @@ template("host_quickfix_test_action") { "--merge-abc", ] + if (defined(invoker.is_hotpatch) && filename == "patch") { + extra_args += [ "--generate-patch" ] + } + + if (defined(invoker.is_hotpatch) && filename == "base") { + dump_symbol_table = rebase_path(_test_map_path_) + } + + if (defined(invoker.is_hotpatch) && filename == "patch") { + input_symbol_table = rebase_path(_test_map_path_) + } + in_puts = [ _test_expect_path_, merge_file, ] + out_puts = [ abc_path ] }