mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-10-07 08:03:29 +00:00
Bugfix of HotReload
Signed-off-by: lijiamin2019 <lijiamin24@huawei.com> Change-Id: I4c6eba7f78975e1cdbb34e4f2040ee8444ef59ca
This commit is contained in:
parent
a4162e5b55
commit
01ff276627
@ -17,12 +17,14 @@
|
||||
#include "ecmascript/global_handle_collection.h"
|
||||
#include "ecmascript/interpreter/interpreter-inl.h"
|
||||
#include "ecmascript/jspandafile/js_pandafile_manager.h"
|
||||
#include "ecmascript/jspandafile/literal_data_extractor.h"
|
||||
#include "ecmascript/mem/c_string.h"
|
||||
#include "ecmascript/napi/include/jsnapi.h"
|
||||
|
||||
namespace panda::ecmascript {
|
||||
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;
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
@ -56,7 +58,7 @@ PatchErrorCode PatchLoader::LoadPatchInternal(JSThread *thread, const JSPandaFil
|
||||
|
||||
// create empty patch constpool for replace method constpool.
|
||||
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.
|
||||
thread->GetCurrentEcmaContext()->ClearPatchModules();
|
||||
@ -252,7 +254,8 @@ void PatchLoader::ReplaceMethod(JSThread *thread,
|
||||
}
|
||||
|
||||
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();
|
||||
const CMap<int32_t, JSTaggedValue> &baseConstpoolValues = context->FindConstpools(baseFile).value();
|
||||
@ -274,7 +277,8 @@ void PatchLoader::FindAndReplaceSameMethod(JSThread *thread, const JSPandaFile *
|
||||
if (constpoolValue.IsMethod()) {
|
||||
Method *baseMethod = Method::Cast(constpoolValue.GetTaggedObject());
|
||||
EntityId baseMethodId = baseMethod->GetMethodId();
|
||||
MethodLiteral *patchMethodLiteral = FindSameMethod(patchInfo, baseFile, baseMethodId);
|
||||
MethodLiteral *patchMethodLiteral =
|
||||
FindSameMethod(patchInfo, baseFile, baseMethodId, baseClassInfo);
|
||||
if (patchMethodLiteral == nullptr) {
|
||||
continue;
|
||||
}
|
||||
@ -300,7 +304,8 @@ void PatchLoader::FindAndReplaceSameMethod(JSThread *thread, const JSPandaFile *
|
||||
JSFunctionBase *func = JSFunctionBase::Cast(literalItem.GetTaggedObject());
|
||||
Method *baseMethod = Method::Cast(func->GetMethod().GetTaggedObject());
|
||||
EntityId baseMethodId = baseMethod->GetMethodId();
|
||||
MethodLiteral *patchMethodLiteral = FindSameMethod(patchInfo, baseFile, baseMethodId);
|
||||
MethodLiteral *patchMethodLiteral =
|
||||
FindSameMethod(patchInfo, baseFile, baseMethodId, baseClassInfo);
|
||||
if (patchMethodLiteral == nullptr) {
|
||||
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);
|
||||
auto recordIter = patchMethodLiterals.find(baseRecordName);
|
||||
if (recordIter == patchMethodLiterals.end()) {
|
||||
return nullptr;
|
||||
CString baseClassName = "default";
|
||||
auto iter = baseClassInfo.find(baseMethodId.GetOffset());
|
||||
if (iter != baseClassInfo.end()) {
|
||||
baseClassName = iter->second;
|
||||
}
|
||||
|
||||
CString baseMethodName = MethodLiteral::GetMethodName(baseFile, baseMethodId);
|
||||
auto methodIter = recordIter->second.find(baseMethodName);
|
||||
if (methodIter == recordIter->second.end()) {
|
||||
CString baseMethodName = GetRealName(baseFile, baseMethodId, baseClassName);
|
||||
PatchMethodIndex patchMethodIndex = {baseRecordName, baseClassName, baseMethodName};
|
||||
auto methodIter = patchMethodLiterals.find(patchMethodIndex);
|
||||
if (methodIter == patchMethodLiterals.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -348,13 +355,20 @@ void PatchLoader::SaveBaseMethodInfo(PatchInfo &patchInfo, const JSPandaFile *ba
|
||||
|
||||
PatchInfo PatchLoader::GeneratePatchInfo(const JSPandaFile *patchFile)
|
||||
{
|
||||
CMap<uint32_t, CString> patchClassInfo = CollectClassInfo(patchFile);
|
||||
|
||||
const auto &map = patchFile->GetMethodLiteralMap();
|
||||
CMap<CString, CMap<CString, MethodLiteral*>> methodLiterals;
|
||||
CMap<PatchMethodIndex, MethodLiteral*> patchMethodLiterals;
|
||||
PatchInfo patchInfo;
|
||||
for (const auto &item : map) {
|
||||
MethodLiteral *methodLiteral = item.second;
|
||||
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 ||
|
||||
methodName == JSPandaFile::PATCH_FUNCTION_NAME_1) {
|
||||
continue;
|
||||
@ -366,17 +380,89 @@ PatchInfo PatchLoader::GeneratePatchInfo(const JSPandaFile *patchFile)
|
||||
patchInfo.replacedRecordNames.emplace(recordName);
|
||||
}
|
||||
|
||||
auto iter = methodLiterals.find(recordName);
|
||||
if (iter != methodLiterals.end()) {
|
||||
iter->second.emplace(methodName, methodLiteral);
|
||||
} else {
|
||||
CMap<CString, MethodLiteral*> methodNameInfo = {{methodName, methodLiteral}};
|
||||
methodLiterals.emplace(recordName, std::move(methodNameInfo));
|
||||
PatchMethodIndex patchMethodIndex = {recordName, className, methodName};
|
||||
if (patchMethodLiterals.find(patchMethodIndex) == patchMethodLiterals.end()) {
|
||||
patchMethodLiterals.emplace(patchMethodIndex, methodLiteral);
|
||||
}
|
||||
}
|
||||
|
||||
patchInfo.patchFileName = patchFile->GetJSPandaFileDesc();
|
||||
patchInfo.patchMethodLiterals = std::move(methodLiterals);
|
||||
patchInfo.patchMethodLiterals = std::move(patchMethodLiterals);
|
||||
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
|
@ -25,6 +25,9 @@
|
||||
namespace panda::ecmascript {
|
||||
using PatchErrorCode = panda::JSNApi::PatchErrorCode;
|
||||
using JSRecordInfo = JSPandaFile::JSRecordInfo;
|
||||
using LiteralDataAccessor = panda_file::LiteralDataAccessor;
|
||||
using LiteralValue = panda_file::LiteralDataAccessor::LiteralValue;
|
||||
using LiteralTag = panda_file::LiteralTag;
|
||||
class ConstantPool;
|
||||
|
||||
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 {
|
||||
// patch file name.
|
||||
CString patchFileName;
|
||||
// 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>
|
||||
CMap<BaseMethodIndex, MethodLiteral *> baseMethodInfo;
|
||||
// save base constpool in global for avoid gc.
|
||||
@ -74,20 +96,25 @@ public:
|
||||
NO_MOVE_SEMANTIC(PatchLoader);
|
||||
|
||||
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,
|
||||
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(
|
||||
JSThread *thread, const JSPandaFile *jsPandaFile, const PatchInfo &patchInfo, bool loadPatch = true);
|
||||
static CMap<uint32_t, CString> CollectClassInfo(const JSPandaFile *jsPandaFile);
|
||||
|
||||
private:
|
||||
static PatchInfo GeneratePatchInfo(const JSPandaFile *patchFile);
|
||||
static CString GetRealName(const JSPandaFile *jsPandaFile, EntityId entityId, CString &className);
|
||||
static void FindAndReplaceSameMethod(JSThread *thread,
|
||||
const JSPandaFile *baseFile,
|
||||
const JSPandaFile *patchFile,
|
||||
PatchInfo &patchInfo);
|
||||
PatchInfo &patchInfo,
|
||||
const CMap<uint32_t, CString> &baseClassInfo);
|
||||
static void SaveBaseMethodInfo(PatchInfo &patchInfo, const JSPandaFile *baseFile,
|
||||
EntityId baseMethodId, const BaseMethodIndex &indexs);
|
||||
static void ReplaceMethod(JSThread *thread,
|
||||
|
@ -66,7 +66,10 @@ void QuickFixManager::LoadPatchIfNeeded(JSThread *thread, const JSPandaFile *bas
|
||||
}
|
||||
|
||||
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) {
|
||||
LOG_ECMA(ERROR) << "Load patch fail of: " << baseFileName;
|
||||
return;
|
||||
@ -100,7 +103,10 @@ PatchErrorCode QuickFixManager::LoadPatch(JSThread *thread, const std::string &p
|
||||
}
|
||||
|
||||
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) {
|
||||
LOG_ECMA(ERROR) << "Load patch fail!";
|
||||
return ret;
|
||||
@ -136,7 +142,10 @@ PatchErrorCode QuickFixManager::LoadPatch(JSThread *thread,
|
||||
}
|
||||
|
||||
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) {
|
||||
LOG_ECMA(ERROR) << "Load patch fail!";
|
||||
return ret;
|
||||
@ -185,7 +194,7 @@ JSTaggedValue QuickFixManager::CheckAndGetPatch(JSThread *thread, const JSPandaF
|
||||
}
|
||||
|
||||
PatchInfo patchInfo = iter->second;
|
||||
MethodLiteral *patchMethodLiteral = PatchLoader::FindSameMethod(patchInfo, baseFile, baseMethodId);
|
||||
MethodLiteral *patchMethodLiteral = PatchLoader::FindSameMethod(patchInfo, baseFile, baseMethodId, baseClassInfo_);
|
||||
if (patchMethodLiteral == nullptr) {
|
||||
return JSTaggedValue::Hole();
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ private:
|
||||
std::string &patchFileName,
|
||||
void **patchBuffer,
|
||||
size_t &patchSize)> callBack_;
|
||||
CMap<uint32_t, CString> baseClassInfo_ {};
|
||||
};
|
||||
} // namespace panda::ecmascript
|
||||
#endif // ECMASCRIPT_PATCH_QUICK_FIX_MANAGER_H
|
Loading…
Reference in New Issue
Block a user