Bugfix of HotReload

Signed-off-by: lijiamin2019 <lijiamin24@huawei.com>
Change-Id: I4c6eba7f78975e1cdbb34e4f2040ee8444ef59ca
This commit is contained in:
lijiamin2019 2023-12-12 10:08:22 +08:00
parent a4162e5b55
commit 01ff276627
4 changed files with 154 additions and 31 deletions

View File

@ -17,12 +17,14 @@
#include "ecmascript/global_handle_collection.h" #include "ecmascript/global_handle_collection.h"
#include "ecmascript/interpreter/interpreter-inl.h" #include "ecmascript/interpreter/interpreter-inl.h"
#include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/jspandafile/js_pandafile_manager.h"
#include "ecmascript/jspandafile/literal_data_extractor.h"
#include "ecmascript/mem/c_string.h" #include "ecmascript/mem/c_string.h"
#include "ecmascript/napi/include/jsnapi.h" #include "ecmascript/napi/include/jsnapi.h"
namespace panda::ecmascript { namespace panda::ecmascript {
PatchErrorCode PatchLoader::LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile, PatchErrorCode PatchLoader::LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile,
const JSPandaFile *patchFile, PatchInfo &patchInfo) const JSPandaFile *patchFile, PatchInfo &patchInfo,
const CMap<uint32_t, CString> &baseClassInfo)
{ {
DISALLOW_GARBAGE_COLLECTION; DISALLOW_GARBAGE_COLLECTION;
EcmaVM *vm = thread->GetEcmaVM(); EcmaVM *vm = thread->GetEcmaVM();
@ -56,7 +58,7 @@ PatchErrorCode PatchLoader::LoadPatchInternal(JSThread *thread, const JSPandaFil
// create empty patch constpool for replace method constpool. // create empty patch constpool for replace method constpool.
thread->GetCurrentEcmaContext()->CreateAllConstpool(patchFile); thread->GetCurrentEcmaContext()->CreateAllConstpool(patchFile);
FindAndReplaceSameMethod(thread, baseFile, patchFile, patchInfo); FindAndReplaceSameMethod(thread, baseFile, patchFile, patchInfo, baseClassInfo);
// cached patch modules can only be clear before load patch. // cached patch modules can only be clear before load patch.
thread->GetCurrentEcmaContext()->ClearPatchModules(); thread->GetCurrentEcmaContext()->ClearPatchModules();
@ -252,7 +254,8 @@ void PatchLoader::ReplaceMethod(JSThread *thread,
} }
void PatchLoader::FindAndReplaceSameMethod(JSThread *thread, const JSPandaFile *baseFile, void PatchLoader::FindAndReplaceSameMethod(JSThread *thread, const JSPandaFile *baseFile,
const JSPandaFile *patchFile, PatchInfo &patchInfo) const JSPandaFile *patchFile, PatchInfo &patchInfo,
const CMap<uint32_t, CString> &baseClassInfo)
{ {
auto context = thread->GetCurrentEcmaContext(); auto context = thread->GetCurrentEcmaContext();
const CMap<int32_t, JSTaggedValue> &baseConstpoolValues = context->FindConstpools(baseFile).value(); const CMap<int32_t, JSTaggedValue> &baseConstpoolValues = context->FindConstpools(baseFile).value();
@ -274,7 +277,8 @@ void PatchLoader::FindAndReplaceSameMethod(JSThread *thread, const JSPandaFile *
if (constpoolValue.IsMethod()) { if (constpoolValue.IsMethod()) {
Method *baseMethod = Method::Cast(constpoolValue.GetTaggedObject()); Method *baseMethod = Method::Cast(constpoolValue.GetTaggedObject());
EntityId baseMethodId = baseMethod->GetMethodId(); EntityId baseMethodId = baseMethod->GetMethodId();
MethodLiteral *patchMethodLiteral = FindSameMethod(patchInfo, baseFile, baseMethodId); MethodLiteral *patchMethodLiteral =
FindSameMethod(patchInfo, baseFile, baseMethodId, baseClassInfo);
if (patchMethodLiteral == nullptr) { if (patchMethodLiteral == nullptr) {
continue; continue;
} }
@ -300,7 +304,8 @@ void PatchLoader::FindAndReplaceSameMethod(JSThread *thread, const JSPandaFile *
JSFunctionBase *func = JSFunctionBase::Cast(literalItem.GetTaggedObject()); JSFunctionBase *func = JSFunctionBase::Cast(literalItem.GetTaggedObject());
Method *baseMethod = Method::Cast(func->GetMethod().GetTaggedObject()); Method *baseMethod = Method::Cast(func->GetMethod().GetTaggedObject());
EntityId baseMethodId = baseMethod->GetMethodId(); EntityId baseMethodId = baseMethod->GetMethodId();
MethodLiteral *patchMethodLiteral = FindSameMethod(patchInfo, baseFile, baseMethodId); MethodLiteral *patchMethodLiteral =
FindSameMethod(patchInfo, baseFile, baseMethodId, baseClassInfo);
if (patchMethodLiteral == nullptr) { if (patchMethodLiteral == nullptr) {
continue; continue;
} }
@ -317,18 +322,20 @@ void PatchLoader::FindAndReplaceSameMethod(JSThread *thread, const JSPandaFile *
} }
} }
MethodLiteral* PatchLoader::FindSameMethod(PatchInfo &patchInfo, const JSPandaFile *baseFile, EntityId baseMethodId) MethodLiteral* PatchLoader::FindSameMethod(PatchInfo &patchInfo, const JSPandaFile *baseFile,
EntityId baseMethodId, const CMap<uint32_t, CString> &baseClassInfo)
{ {
const CMap<CString, CMap<CString, MethodLiteral*>> &patchMethodLiterals = patchInfo.patchMethodLiterals; const CMap<PatchMethodIndex, MethodLiteral*> &patchMethodLiterals = patchInfo.patchMethodLiterals;
CString baseRecordName = MethodLiteral::GetRecordName(baseFile, baseMethodId); CString baseRecordName = MethodLiteral::GetRecordName(baseFile, baseMethodId);
auto recordIter = patchMethodLiterals.find(baseRecordName); CString baseClassName = "default";
if (recordIter == patchMethodLiterals.end()) { auto iter = baseClassInfo.find(baseMethodId.GetOffset());
return nullptr; if (iter != baseClassInfo.end()) {
baseClassName = iter->second;
} }
CString baseMethodName = GetRealName(baseFile, baseMethodId, baseClassName);
CString baseMethodName = MethodLiteral::GetMethodName(baseFile, baseMethodId); PatchMethodIndex patchMethodIndex = {baseRecordName, baseClassName, baseMethodName};
auto methodIter = recordIter->second.find(baseMethodName); auto methodIter = patchMethodLiterals.find(patchMethodIndex);
if (methodIter == recordIter->second.end()) { if (methodIter == patchMethodLiterals.end()) {
return nullptr; return nullptr;
} }
@ -348,13 +355,20 @@ void PatchLoader::SaveBaseMethodInfo(PatchInfo &patchInfo, const JSPandaFile *ba
PatchInfo PatchLoader::GeneratePatchInfo(const JSPandaFile *patchFile) PatchInfo PatchLoader::GeneratePatchInfo(const JSPandaFile *patchFile)
{ {
CMap<uint32_t, CString> patchClassInfo = CollectClassInfo(patchFile);
const auto &map = patchFile->GetMethodLiteralMap(); const auto &map = patchFile->GetMethodLiteralMap();
CMap<CString, CMap<CString, MethodLiteral*>> methodLiterals; CMap<PatchMethodIndex, MethodLiteral*> patchMethodLiterals;
PatchInfo patchInfo; PatchInfo patchInfo;
for (const auto &item : map) { for (const auto &item : map) {
MethodLiteral *methodLiteral = item.second; MethodLiteral *methodLiteral = item.second;
EntityId methodId = EntityId(item.first); EntityId methodId = EntityId(item.first);
CString methodName = MethodLiteral::GetMethodName(patchFile, methodId); CString className = "default"; // for normal method and constructor.
auto iter = patchClassInfo.find(methodId.GetOffset());
if (iter!= patchClassInfo.end()) {
className = iter->second;
}
CString methodName = GetRealName(patchFile, methodId, className);
if (methodName == JSPandaFile::PATCH_FUNCTION_NAME_0 || if (methodName == JSPandaFile::PATCH_FUNCTION_NAME_0 ||
methodName == JSPandaFile::PATCH_FUNCTION_NAME_1) { methodName == JSPandaFile::PATCH_FUNCTION_NAME_1) {
continue; continue;
@ -366,17 +380,89 @@ PatchInfo PatchLoader::GeneratePatchInfo(const JSPandaFile *patchFile)
patchInfo.replacedRecordNames.emplace(recordName); patchInfo.replacedRecordNames.emplace(recordName);
} }
auto iter = methodLiterals.find(recordName); PatchMethodIndex patchMethodIndex = {recordName, className, methodName};
if (iter != methodLiterals.end()) { if (patchMethodLiterals.find(patchMethodIndex) == patchMethodLiterals.end()) {
iter->second.emplace(methodName, methodLiteral); patchMethodLiterals.emplace(patchMethodIndex, methodLiteral);
} else {
CMap<CString, MethodLiteral*> methodNameInfo = {{methodName, methodLiteral}};
methodLiterals.emplace(recordName, std::move(methodNameInfo));
} }
} }
patchInfo.patchFileName = patchFile->GetJSPandaFileDesc(); patchInfo.patchFileName = patchFile->GetJSPandaFileDesc();
patchInfo.patchMethodLiterals = std::move(methodLiterals); patchInfo.patchMethodLiterals = std::move(patchMethodLiterals);
return patchInfo; return patchInfo;
} }
CMap<uint32_t, CString> PatchLoader::CollectClassInfo(const JSPandaFile *jsPandaFile)
{
CMap<uint32_t, CString> classInfo {};
auto &pandaFile = *jsPandaFile->GetPandaFile();
auto classes = jsPandaFile->GetClasses();
const auto &map = jsPandaFile->GetMethodLiteralMap();
for (size_t i = 0; i < classes.Size(); i++) {
EntityId classId(classes[i]);
if (!classId.IsValid() || jsPandaFile->IsExternal(classId)) {
continue;
}
panda_file::ClassDataAccessor cda(pandaFile, classId);
cda.EnumerateMethods([&pandaFile, &map, &classInfo, jsPandaFile](panda_file::MethodDataAccessor &mda) {
EntityId methodId = mda.GetMethodId();
auto iter = map.find(methodId.GetOffset());
MethodLiteral *methodLiteral = nullptr;
if (iter != map.end()) {
methodLiteral = iter->second;
}
auto codeId = mda.GetCodeId();
ASSERT(codeId.has_value());
panda_file::CodeDataAccessor codeDataAccessor(pandaFile, codeId.value());
uint32_t codeSize = codeDataAccessor.GetCodeSize();
const uint8_t *insns = codeDataAccessor.GetInstructions();
auto bcIns = BytecodeInst(insns);
auto bcInsLast = bcIns.JumpTo(codeSize);
while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
BytecodeInstruction::Opcode opcode = static_cast<BytecodeInstruction::Opcode>(bcIns.GetOpcode());
if (opcode == BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8 ||
opcode == BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8) {
auto entityId = jsPandaFile->ResolveMethodIndex(methodLiteral->GetMethodId(),
(bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 0>()).AsRawValue());
CString className = "";
className = GetRealName(jsPandaFile, entityId, className);
CString recordName = MethodLiteral::GetRecordName(jsPandaFile, methodId);
auto literalId = jsPandaFile->ResolveMethodIndex(methodLiteral->GetMethodId(),
(bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 1>()).AsRawValue());
LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
lda.EnumerateLiteralVals(literalId, [&classInfo, className]
(const LiteralValue &value, const LiteralTag &tag) {
switch (tag) {
case LiteralTag::METHOD:
case LiteralTag::GENERATORMETHOD: {
uint32_t methodOffset = std::get<uint32_t>(value);
classInfo.emplace(methodOffset, std::move(className));
break;
}
default: {
break;
}
}
});
}
auto nextInst = bcIns.GetNext();
bcIns = nextInst;
}
});
}
return classInfo;
}
CString PatchLoader::GetRealName(const JSPandaFile *jsPandaFile, EntityId entityId, CString &className)
{
std::string methodName(MethodLiteral::GetMethodName(jsPandaFile, entityId));
size_t poiIndex = methodName.find_last_of('#');
if (poiIndex != std::string::npos && poiIndex < methodName.size() - 1 && className != "default") {
methodName = methodName.substr(poiIndex + 1);
}
return ConvertToString(methodName);
}
} // namespace panda::ecmascript } // namespace panda::ecmascript

View File

@ -25,6 +25,9 @@
namespace panda::ecmascript { namespace panda::ecmascript {
using PatchErrorCode = panda::JSNApi::PatchErrorCode; using PatchErrorCode = panda::JSNApi::PatchErrorCode;
using JSRecordInfo = JSPandaFile::JSRecordInfo; using JSRecordInfo = JSPandaFile::JSRecordInfo;
using LiteralDataAccessor = panda_file::LiteralDataAccessor;
using LiteralValue = panda_file::LiteralDataAccessor::LiteralValue;
using LiteralTag = panda_file::LiteralTag;
class ConstantPool; class ConstantPool;
struct BaseMethodIndex { struct BaseMethodIndex {
@ -46,11 +49,30 @@ struct BaseMethodIndex {
} }
}; };
struct PatchMethodIndex {
CString recordName;
CString className;
CString methodName;
bool operator < (const PatchMethodIndex &patchIndex) const
{
if (recordName < patchIndex.recordName) {
return true;
}
if (recordName == patchIndex.recordName && className < patchIndex.className) {
return true;
}
if (recordName == patchIndex.recordName && className == patchIndex.className) {
return methodName < patchIndex.methodName;
}
return false;
}
};
struct PatchInfo { struct PatchInfo {
// patch file name. // patch file name.
CString patchFileName; CString patchFileName;
// patch methodLiterals for load patch, <recordName, <methodName, MethodLiteral>> // patch methodLiterals for load patch, <recordName, <methodName, MethodLiteral>>
CMap<CString, CMap<CString, MethodLiteral*>> patchMethodLiterals; CMap<PatchMethodIndex, MethodLiteral*> patchMethodLiterals;
// base method info for unload patch, <BaseMethodIndex, base MethodLiteral> // base method info for unload patch, <BaseMethodIndex, base MethodLiteral>
CMap<BaseMethodIndex, MethodLiteral *> baseMethodInfo; CMap<BaseMethodIndex, MethodLiteral *> baseMethodInfo;
// save base constpool in global for avoid gc. // save base constpool in global for avoid gc.
@ -74,20 +96,25 @@ public:
NO_MOVE_SEMANTIC(PatchLoader); NO_MOVE_SEMANTIC(PatchLoader);
static PatchErrorCode LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile, static PatchErrorCode LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile,
const JSPandaFile *patchFile, PatchInfo &patchInfo); const JSPandaFile *patchFile, PatchInfo &patchInfo,
const CMap<uint32_t, CString> &baseClassInfo);
static PatchErrorCode UnloadPatchInternal(JSThread *thread, const CString &patchFileName, static PatchErrorCode UnloadPatchInternal(JSThread *thread, const CString &patchFileName,
const CString &baseFileName, PatchInfo &patchInfo); const CString &baseFileName, PatchInfo &patchInfo);
static MethodLiteral *FindSameMethod(PatchInfo &patchInfo, const JSPandaFile *baseFile, EntityId baseMethodId); static MethodLiteral *FindSameMethod(PatchInfo &patchInfo, const JSPandaFile *baseFile,
EntityId baseMethodId, const CMap<uint32_t, CString> &baseClassInfo);
static void ExecuteFuncOrPatchMain( static void ExecuteFuncOrPatchMain(
JSThread *thread, const JSPandaFile *jsPandaFile, const PatchInfo &patchInfo, bool loadPatch = true); JSThread *thread, const JSPandaFile *jsPandaFile, const PatchInfo &patchInfo, bool loadPatch = true);
static CMap<uint32_t, CString> CollectClassInfo(const JSPandaFile *jsPandaFile);
private: private:
static PatchInfo GeneratePatchInfo(const JSPandaFile *patchFile); static PatchInfo GeneratePatchInfo(const JSPandaFile *patchFile);
static CString GetRealName(const JSPandaFile *jsPandaFile, EntityId entityId, CString &className);
static void FindAndReplaceSameMethod(JSThread *thread, static void FindAndReplaceSameMethod(JSThread *thread,
const JSPandaFile *baseFile, const JSPandaFile *baseFile,
const JSPandaFile *patchFile, const JSPandaFile *patchFile,
PatchInfo &patchInfo); PatchInfo &patchInfo,
const CMap<uint32_t, CString> &baseClassInfo);
static void SaveBaseMethodInfo(PatchInfo &patchInfo, const JSPandaFile *baseFile, static void SaveBaseMethodInfo(PatchInfo &patchInfo, const JSPandaFile *baseFile,
EntityId baseMethodId, const BaseMethodIndex &indexs); EntityId baseMethodId, const BaseMethodIndex &indexs);
static void ReplaceMethod(JSThread *thread, static void ReplaceMethod(JSThread *thread,

View File

@ -66,7 +66,10 @@ void QuickFixManager::LoadPatchIfNeeded(JSThread *thread, const JSPandaFile *bas
} }
PatchInfo patchInfo; PatchInfo patchInfo;
auto ret = PatchLoader::LoadPatchInternal(thread, baseFile, patchFile.get(), patchInfo); if (baseClassInfo_.empty()) {
baseClassInfo_ = PatchLoader::CollectClassInfo(baseFile);
}
auto ret = PatchLoader::LoadPatchInternal(thread, baseFile, patchFile.get(), patchInfo, baseClassInfo_);
if (ret != PatchErrorCode::SUCCESS) { if (ret != PatchErrorCode::SUCCESS) {
LOG_ECMA(ERROR) << "Load patch fail of: " << baseFileName; LOG_ECMA(ERROR) << "Load patch fail of: " << baseFileName;
return; return;
@ -100,7 +103,10 @@ PatchErrorCode QuickFixManager::LoadPatch(JSThread *thread, const std::string &p
} }
PatchInfo patchInfo; PatchInfo patchInfo;
auto ret = PatchLoader::LoadPatchInternal(thread, baseFile.get(), patchFile.get(), patchInfo); if (baseClassInfo_.empty()) {
baseClassInfo_ = PatchLoader::CollectClassInfo(baseFile.get());
}
auto ret = PatchLoader::LoadPatchInternal(thread, baseFile.get(), patchFile.get(), patchInfo, baseClassInfo_);
if (ret != PatchErrorCode::SUCCESS) { if (ret != PatchErrorCode::SUCCESS) {
LOG_ECMA(ERROR) << "Load patch fail!"; LOG_ECMA(ERROR) << "Load patch fail!";
return ret; return ret;
@ -136,7 +142,10 @@ PatchErrorCode QuickFixManager::LoadPatch(JSThread *thread,
} }
PatchInfo patchInfo; PatchInfo patchInfo;
auto ret = PatchLoader::LoadPatchInternal(thread, baseFile.get(), patchFile.get(), patchInfo); if (baseClassInfo_.empty()) {
baseClassInfo_ = PatchLoader::CollectClassInfo(baseFile.get());
}
auto ret = PatchLoader::LoadPatchInternal(thread, baseFile.get(), patchFile.get(), patchInfo, baseClassInfo_);
if (ret != PatchErrorCode::SUCCESS) { if (ret != PatchErrorCode::SUCCESS) {
LOG_ECMA(ERROR) << "Load patch fail!"; LOG_ECMA(ERROR) << "Load patch fail!";
return ret; return ret;
@ -185,7 +194,7 @@ JSTaggedValue QuickFixManager::CheckAndGetPatch(JSThread *thread, const JSPandaF
} }
PatchInfo patchInfo = iter->second; PatchInfo patchInfo = iter->second;
MethodLiteral *patchMethodLiteral = PatchLoader::FindSameMethod(patchInfo, baseFile, baseMethodId); MethodLiteral *patchMethodLiteral = PatchLoader::FindSameMethod(patchInfo, baseFile, baseMethodId, baseClassInfo_);
if (patchMethodLiteral == nullptr) { if (patchMethodLiteral == nullptr) {
return JSTaggedValue::Hole(); return JSTaggedValue::Hole();
} }

View File

@ -55,6 +55,7 @@ private:
std::string &patchFileName, std::string &patchFileName,
void **patchBuffer, void **patchBuffer,
size_t &patchSize)> callBack_; size_t &patchSize)> callBack_;
CMap<uint32_t, CString> baseClassInfo_ {};
}; };
} // namespace panda::ecmascript } // namespace panda::ecmascript
#endif // ECMASCRIPT_PATCH_QUICK_FIX_MANAGER_H #endif // ECMASCRIPT_PATCH_QUICK_FIX_MANAGER_H