mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-11-23 10:09:54 +00:00
Support cold patch with method replace
https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I6781P Signed-off-by: g00416891 <guobingbing3@huawei.com> Change-Id: I68f1fedaf52ddacc242d99c602c98bd0ae8a8328
This commit is contained in:
parent
b2ae7019f4
commit
fb94e4d58e
@ -577,6 +577,19 @@ JSHandle<ConstantPool> EcmaVM::FindOrCreateConstPool(const JSPandaFile *jsPandaF
|
||||
return JSHandle<ConstantPool>(thread_, constpool);
|
||||
}
|
||||
|
||||
void EcmaVM::CreateAllConstpool(const JSPandaFile *jsPandaFile)
|
||||
{
|
||||
auto headers = jsPandaFile->GetPandaFile()->GetIndexHeaders();
|
||||
uint32_t index = 0;
|
||||
for (const auto &header : headers) {
|
||||
auto constpoolSize = header.method_idx_size;
|
||||
JSHandle<ConstantPool> constpool = factory_->NewConstantPool(constpoolSize);
|
||||
constpool->SetJSPandaFile(jsPandaFile);
|
||||
constpool->SetIndexHeader(&header);
|
||||
AddConstpool(jsPandaFile, constpool.GetTaggedValue(), index++);
|
||||
}
|
||||
}
|
||||
|
||||
void EcmaVM::CJSExecution(JSHandle<JSFunction> &func, JSHandle<JSTaggedValue> &thisArg,
|
||||
const JSPandaFile *jsPandaFile)
|
||||
{
|
||||
|
@ -393,6 +393,7 @@ public:
|
||||
|
||||
JSHandle<ConstantPool> PUBLIC_API FindOrCreateConstPool(const JSPandaFile *jsPandaFile,
|
||||
panda_file::File::EntityId id);
|
||||
void CreateAllConstpool(const JSPandaFile *jsPandaFile);
|
||||
|
||||
void StoreBCOffsetInfo(const std::string& methodName, int32_t bcOffset)
|
||||
{
|
||||
|
@ -212,6 +212,11 @@ public:
|
||||
return lda;
|
||||
}
|
||||
|
||||
uint32_t GetConstpoolNum() const
|
||||
{
|
||||
return pf_->GetHeader()->num_indexes;
|
||||
}
|
||||
|
||||
Span<const panda_file::File::EntityId> GetMethodIndex(const panda_file::File::IndexHeader *indexHeader) const
|
||||
{
|
||||
return pf_->GetMethodIndex(indexHeader);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "ecmascript/js_thread.h"
|
||||
#include "ecmascript/jspandafile/js_pandafile.h"
|
||||
#include "ecmascript/module/js_module_manager.h"
|
||||
#include "ecmascript/patch/quick_fix_manager.h"
|
||||
#include "ecmascript/tagged_array-inl.h"
|
||||
|
||||
namespace panda::ecmascript {
|
||||
@ -220,11 +221,11 @@ JSHandle<JSFunction> LiteralDataExtractor::DefineMethodInLiteral(JSThread *threa
|
||||
auto methodLiteral = jsPandaFile->FindMethodLiteral(offset);
|
||||
ASSERT(methodLiteral != nullptr);
|
||||
methodLiteral->SetFunctionKind(kind);
|
||||
JSHandle<Method> method = factory->NewMethod(methodLiteral);
|
||||
JSHandle<Method> method;
|
||||
if (jsPandaFile->IsNewVersion()) {
|
||||
JSHandle<ConstantPool> newConstpool = vm->FindOrCreateConstPool(jsPandaFile, EntityId(offset));
|
||||
method->SetConstantPool(thread, newConstpool);
|
||||
method = Method::Create(thread, jsPandaFile, methodLiteral);
|
||||
} else {
|
||||
method = factory->NewMethod(methodLiteral);
|
||||
method->SetConstantPool(thread, constpool);
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "ecmascript/jspandafile/js_pandafile_manager.h"
|
||||
#include "ecmascript/jspandafile/literal_data_extractor.h"
|
||||
#include "ecmascript/module/js_module_manager.h"
|
||||
#include "ecmascript/patch/quick_fix_manager.h"
|
||||
|
||||
#include "libpandafile/class_data_accessor-inl.h"
|
||||
#include "libpandafile/index_accessor.h"
|
||||
@ -43,17 +44,20 @@ public:
|
||||
DECL_DUMP()
|
||||
};
|
||||
|
||||
/*
|
||||
* ConstantPool
|
||||
* +------------+
|
||||
* | hClass +
|
||||
* +------------+
|
||||
* | length |
|
||||
* +------------+
|
||||
* | cache... |
|
||||
* +------------+
|
||||
* |js_pandafile|
|
||||
* +------------+
|
||||
/* ConstantPool
|
||||
* +--------------------------------+----
|
||||
* | cache | ^
|
||||
* | ... | |
|
||||
* | string(EcmaString) | |
|
||||
* | method(Method) |cacheLength
|
||||
* | array literal(JSArray) | |
|
||||
* | object literal(JSObject) | |
|
||||
* | class literal(TaggedArray) | v
|
||||
* +--------------------------------+----
|
||||
* | IndexHeader |
|
||||
* +--------------------------------+
|
||||
* | JSPandaFile |
|
||||
* +--------------------------------+
|
||||
*/
|
||||
class ConstantPool : public TaggedArray {
|
||||
public:
|
||||
@ -174,36 +178,32 @@ public:
|
||||
val = JSTaggedValue::Hole();
|
||||
}
|
||||
|
||||
if (val.IsHole()) {
|
||||
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
||||
JSHandle<ConstantPool> constpoolHandle(thread, constpool);
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
ObjectFactory *factory = vm->GetFactory();
|
||||
|
||||
ASSERT(jsPandaFile->IsNewVersion());
|
||||
panda_file::File::EntityId id = constpoolHandle->GetEntityId(index);
|
||||
MethodLiteral *methodLiteral = jsPandaFile->FindMethodLiteral(id.GetOffset());
|
||||
ASSERT(methodLiteral != nullptr);
|
||||
JSHandle<Method> method = factory->NewMethod(methodLiteral);
|
||||
|
||||
JSHandle<ConstantPool> newConstpool = vm->FindOrCreateConstPool(jsPandaFile, id);
|
||||
method->SetConstantPool(thread, newConstpool);
|
||||
if (isLoadedAOT && hasEntryIndex) {
|
||||
vm->GetAOTFileManager()->SetAOTFuncEntry(jsPandaFile, *method, entryIndex);
|
||||
}
|
||||
|
||||
val = method.GetTaggedValue();
|
||||
constpoolHandle->SetObjectToCache(thread, index, val);
|
||||
if (!val.IsHole()) {
|
||||
return val;
|
||||
}
|
||||
|
||||
return val;
|
||||
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
||||
ASSERT(jsPandaFile->IsNewVersion());
|
||||
JSHandle<ConstantPool> constpoolHandle(thread, constpool);
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
|
||||
EntityId id = constpoolHandle->GetEntityId(index);
|
||||
MethodLiteral *methodLiteral = jsPandaFile->FindMethodLiteral(id.GetOffset());
|
||||
ASSERT(methodLiteral != nullptr);
|
||||
JSHandle<Method> method = Method::Create(thread, jsPandaFile, methodLiteral);
|
||||
|
||||
if (isLoadedAOT && hasEntryIndex) {
|
||||
vm->GetAOTFileManager()->SetAOTFuncEntry(jsPandaFile, *method, entryIndex);
|
||||
}
|
||||
constpoolHandle->SetObjectToCache(thread, index, method.GetTaggedValue());
|
||||
return method.GetTaggedValue();
|
||||
}
|
||||
|
||||
static JSTaggedValue GetClassLiteralFromCache(JSThread *thread, JSHandle<ConstantPool> constpool,
|
||||
uint32_t literal, CString entry)
|
||||
{
|
||||
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
||||
auto val = constpool->Get(literal);
|
||||
auto val = constpool->GetObjectFromCache(literal);
|
||||
JSPandaFile *jsPandaFile = constpool->GetJSPandaFile();
|
||||
|
||||
// For AOT
|
||||
@ -237,7 +237,7 @@ public:
|
||||
static_assert(type == ConstPoolType::OBJECT_LITERAL || type == ConstPoolType::ARRAY_LITERAL);
|
||||
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
||||
const ConstantPool *taggedPool = ConstantPool::Cast(constpool.GetTaggedObject());
|
||||
auto val = taggedPool->Get(index);
|
||||
auto val = taggedPool->GetObjectFromCache(index);
|
||||
JSPandaFile *jsPandaFile = taggedPool->GetJSPandaFile();
|
||||
|
||||
// For AOT
|
||||
|
@ -88,4 +88,19 @@ uint32_t Method::FindCatchBlock(uint32_t pc) const
|
||||
});
|
||||
return pcOffset;
|
||||
}
|
||||
|
||||
JSHandle<Method> Method::Create(JSThread *thread, const JSPandaFile *jsPandaFile, MethodLiteral *methodLiteral)
|
||||
{
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
EntityId methodId = methodLiteral->GetMethodId();
|
||||
JSTaggedValue patchVal = vm->GetQuickFixManager()->CheckAndGetPatch(thread, jsPandaFile, methodId);
|
||||
if (!patchVal.IsHole()) {
|
||||
return JSHandle<Method>(thread, patchVal);
|
||||
}
|
||||
|
||||
JSHandle<Method> method = vm->GetFactory()->NewMethod(methodLiteral);
|
||||
JSHandle<ConstantPool> newConstpool = vm->FindOrCreateConstPool(jsPandaFile, methodId);
|
||||
method->SetConstantPool(thread, newConstpool);
|
||||
return method;
|
||||
}
|
||||
} // namespace panda::ecmascript
|
||||
|
@ -424,6 +424,12 @@ public:
|
||||
DECL_VISIT_OBJECT(CONSTANT_POOL_OFFSET, CALL_FIELD_OFFSET);
|
||||
|
||||
DECL_DUMP()
|
||||
|
||||
private:
|
||||
static JSHandle<Method> Create(JSThread *thread, const JSPandaFile *jsPandaFile, MethodLiteral *methodLiteral);
|
||||
|
||||
friend class ConstantPool;
|
||||
friend class LiteralDataExtractor;
|
||||
};
|
||||
} // namespace panda::ecmascript
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
*/
|
||||
#include "ecmascript/patch/patch_loader.h"
|
||||
|
||||
#include "ecmascript/global_handle_collection.h"
|
||||
#include "ecmascript/interpreter/interpreter-inl.h"
|
||||
#include "ecmascript/jspandafile/js_pandafile_manager.h"
|
||||
#include "ecmascript/mem/c_string.h"
|
||||
@ -21,8 +22,7 @@
|
||||
|
||||
namespace panda::ecmascript {
|
||||
PatchErrorCode PatchLoader::LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile,
|
||||
const JSPandaFile *patchFile,
|
||||
CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo)
|
||||
const JSPandaFile *patchFile, PatchInfo &patchInfo)
|
||||
{
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
|
||||
@ -32,34 +32,39 @@ PatchErrorCode PatchLoader::LoadPatchInternal(JSThread *thread, const JSPandaFil
|
||||
return PatchErrorCode::PACKAGE_NOT_ESMODULE;
|
||||
}
|
||||
|
||||
// Get base constpool.
|
||||
const auto &patchRecordInfos = patchFile->GetJSRecordInfo();
|
||||
ParseConstpoolWithMerge(thread, baseFile, patchRecordInfos);
|
||||
if (!vm->HasCachedConstpool(baseFile)) {
|
||||
LOG_ECMA(ERROR) << "base constpool is empty";
|
||||
return PatchErrorCode::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
||||
// Get patch constpool.
|
||||
ParseConstpoolWithMerge(thread, patchFile, patchRecordInfos, baseFile);
|
||||
if (!vm->HasCachedConstpool(patchFile)) {
|
||||
LOG_ECMA(ERROR) << "patch constpool is empty";
|
||||
return PatchErrorCode::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
// Only esmodule support check
|
||||
if (CheckIsInvalidPatch(baseFile, patchFile, vm)) {
|
||||
LOG_ECMA(ERROR) << "Invalid patch";
|
||||
return PatchErrorCode::MODIFY_IMPORT_EXPORT_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
if (!FindAndReplaceSameMethod(thread, baseFile, patchFile, baseMethodInfo)) {
|
||||
LOG_ECMA(ERROR) << "Replace method failed";
|
||||
return PatchErrorCode::INTERNAL_ERROR;
|
||||
// Generate patchInfo for hot reload, hot patch and cold patch.
|
||||
patchInfo = PatchLoader::GeneratePatchInfo(patchFile);
|
||||
|
||||
if (!vm->HasCachedConstpool(baseFile)) {
|
||||
LOG_ECMA(INFO) << "cold patch!";
|
||||
return PatchErrorCode::SUCCESS;
|
||||
}
|
||||
|
||||
if (!ExecutePatchMain(thread, patchFile, baseFile, baseMethodInfo)) {
|
||||
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
||||
|
||||
// store base constpool in global object for avoid gc.
|
||||
uint32_t num = baseFile->GetConstpoolNum();
|
||||
GlobalHandleCollection gloalHandleCollection(thread);
|
||||
for (uint32_t idx = 0; idx < num; idx++) {
|
||||
JSTaggedValue constpool = vm->FindConstpool(baseFile, idx);
|
||||
if (!constpool.IsHole()) {
|
||||
JSHandle<JSTaggedValue> constpoolHandle =
|
||||
gloalHandleCollection.NewHandle<JSTaggedValue>(constpool.GetRawData());
|
||||
patchInfo.baseConstpools.emplace_back(constpoolHandle);
|
||||
}
|
||||
}
|
||||
|
||||
// create empty patch constpool for replace method constpool.
|
||||
vm->CreateAllConstpool(patchFile);
|
||||
FindAndReplaceSameMethod(thread, baseFile, patchFile, patchInfo);
|
||||
|
||||
if (!ExecutePatchMain(thread, patchFile, baseFile, patchInfo)) {
|
||||
LOG_ECMA(ERROR) << "Execute patch main failed";
|
||||
return PatchErrorCode::INTERNAL_ERROR;
|
||||
}
|
||||
@ -68,168 +73,8 @@ PatchErrorCode PatchLoader::LoadPatchInternal(JSThread *thread, const JSPandaFil
|
||||
return PatchErrorCode::SUCCESS;
|
||||
}
|
||||
|
||||
void PatchLoader::ParseConstpoolWithMerge(JSThread *thread, const JSPandaFile *jsPandaFile,
|
||||
const CUnorderedMap<CString, JSRecordInfo> &patchRecordInfos,
|
||||
const JSPandaFile *baseFile)
|
||||
{
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
JSHandle<ConstantPool> constpool;
|
||||
bool isNewVersion = jsPandaFile->IsNewVersion();
|
||||
if (!isNewVersion) {
|
||||
auto constpoolVal = vm->FindConstpool(jsPandaFile, 0);
|
||||
if (constpoolVal.IsHole()) {
|
||||
constpool = PandaFileTranslator::ParseConstPool(vm, jsPandaFile);
|
||||
// old version dont support multi constpool
|
||||
vm->AddConstpool(jsPandaFile, constpool.GetTaggedValue());
|
||||
} else {
|
||||
constpool = JSHandle<ConstantPool>(thread, constpoolVal);
|
||||
}
|
||||
}
|
||||
|
||||
const CString &fileName = jsPandaFile->GetJSPandaFileDesc();
|
||||
for (const auto &item : patchRecordInfos) {
|
||||
const CString &recordName = item.first;
|
||||
vm->GetModuleManager()->HostResolveImportedModuleWithMerge(fileName, recordName);
|
||||
ASSERT(!thread->HasPendingException());
|
||||
if (!isNewVersion) {
|
||||
PandaFileTranslator::ParseFuncAndLiteralConstPool(vm, jsPandaFile, recordName, constpool);
|
||||
}
|
||||
}
|
||||
if (isNewVersion) {
|
||||
GenerateConstpoolCache(thread, jsPandaFile, patchRecordInfos, baseFile);
|
||||
}
|
||||
}
|
||||
|
||||
void PatchLoader::GenerateConstpoolCache(JSThread *thread, const JSPandaFile *jsPandaFile,
|
||||
const CUnorderedMap<CString, JSRecordInfo> &patchRecordInfos,
|
||||
const JSPandaFile *baseFile)
|
||||
{
|
||||
ASSERT(jsPandaFile->IsNewVersion());
|
||||
const panda_file::File *pf = jsPandaFile->GetPandaFile();
|
||||
Span<const uint32_t> classIndexes = jsPandaFile->GetClasses();
|
||||
for (const uint32_t index : classIndexes) {
|
||||
panda_file::File::EntityId classId(index);
|
||||
if (jsPandaFile->IsExternal(classId)) {
|
||||
continue;
|
||||
}
|
||||
panda_file::ClassDataAccessor cda(*pf, classId);
|
||||
CString entry = JSPandaFile::ParseEntryPoint(utf::Mutf8AsCString(cda.GetDescriptor()));
|
||||
if (patchRecordInfos.find(entry) == patchRecordInfos.end()) {
|
||||
LOG_ECMA(DEBUG) << "skip record not in patch: " << entry;
|
||||
continue;
|
||||
}
|
||||
|
||||
cda.EnumerateMethods([pf, jsPandaFile, baseFile, thread, &entry](panda_file::MethodDataAccessor &mda) {
|
||||
auto methodId = mda.GetMethodId();
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
JSHandle<ConstantPool> constpool = vm->FindOrCreateConstPool(jsPandaFile, EntityId(methodId));
|
||||
|
||||
auto codeId = mda.GetCodeId();
|
||||
ASSERT(codeId.has_value());
|
||||
panda_file::CodeDataAccessor codeDataAccessor(*pf, codeId.value());
|
||||
uint32_t codeSize = codeDataAccessor.GetCodeSize();
|
||||
const uint8_t *insns = codeDataAccessor.GetInstructions();
|
||||
|
||||
auto bcIns = BytecodeInstruction(insns);
|
||||
auto bcInsLast = bcIns.JumpTo(codeSize);
|
||||
while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
|
||||
if (!bcIns.HasFlag(BytecodeInstruction::Flags::STRING_ID) ||
|
||||
!BytecodeInstruction::HasId(BytecodeInstruction::GetFormat(bcIns.GetOpcode()), 0)) {
|
||||
BytecodeInstruction::Opcode opcode = bcIns.GetOpcode();
|
||||
switch (opcode) {
|
||||
// add opcode about method
|
||||
case EcmaOpcode::DEFINEMETHOD_IMM8_ID16_IMM8:
|
||||
U_FALLTHROUGH;
|
||||
case EcmaOpcode::DEFINEMETHOD_IMM16_ID16_IMM8:
|
||||
U_FALLTHROUGH;
|
||||
case EcmaOpcode::DEFINEFUNC_IMM8_ID16_IMM8:
|
||||
U_FALLTHROUGH;
|
||||
case EcmaOpcode::DEFINEFUNC_IMM16_ID16_IMM8: {
|
||||
uint32_t id = bcIns.GetId().AsRawValue();
|
||||
if (jsPandaFile->IsExternal(constpool->GetEntityId(id))) {
|
||||
FillExternalMethod(thread, jsPandaFile, baseFile, constpool, id);
|
||||
} else {
|
||||
ConstantPool::GetMethodFromCache(thread, constpool.GetTaggedValue(), id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM8_ID16:
|
||||
U_FALLTHROUGH;
|
||||
case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: {
|
||||
uint32_t id = bcIns.GetId().AsRawValue();
|
||||
ConstantPool::GetLiteralFromCache<ConstPoolType::OBJECT_LITERAL>(
|
||||
thread, constpool.GetTaggedValue(), id, entry);
|
||||
break;
|
||||
}
|
||||
case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16:
|
||||
U_FALLTHROUGH;
|
||||
case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: {
|
||||
uint32_t id = bcIns.GetId().AsRawValue();
|
||||
ConstantPool::GetLiteralFromCache<ConstPoolType::ARRAY_LITERAL>(
|
||||
thread, constpool.GetTaggedValue(), id, entry);
|
||||
break;
|
||||
}
|
||||
case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8:
|
||||
U_FALLTHROUGH;
|
||||
case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: {
|
||||
uint32_t constructorId = bcIns.GetId(0).AsRawValue();
|
||||
uint32_t literalId = bcIns.GetId(1).AsRawValue();
|
||||
// class constructor.
|
||||
ConstantPool::GetMethodFromCache(thread, constpool.GetTaggedValue(), constructorId);
|
||||
// class member function.
|
||||
ConstantPool::GetClassLiteralFromCache(thread, constpool, literalId, entry);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
bcIns = bcIns.GetNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void PatchLoader::FillExternalMethod(JSThread *thread, const JSPandaFile *patchFile, const JSPandaFile *baseFile,
|
||||
JSHandle<ConstantPool> patchConstpool, uint32_t id)
|
||||
{
|
||||
if (baseFile == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
panda_file::File::EntityId externalMethodId = patchConstpool->GetEntityId(id);
|
||||
CString patchMethodName = MethodLiteral::GetMethodName(patchFile, externalMethodId);
|
||||
CString patchRecordName = MethodLiteral::GetRecordName(patchFile, externalMethodId);
|
||||
|
||||
const CMap<int32_t, JSTaggedValue> &baseConstpoolValues = thread->GetEcmaVM()->FindConstpools(baseFile).value();
|
||||
for (const auto &item : baseConstpoolValues) {
|
||||
ConstantPool *baseConstpool = ConstantPool::Cast(item.second.GetTaggedObject());
|
||||
uint32_t len = baseConstpool->GetCacheLength();
|
||||
for (uint32_t idx = 0; idx < len; idx++) {
|
||||
JSTaggedValue constpoolValue = baseConstpool->GetObjectFromCache(idx);
|
||||
if (!constpoolValue.IsMethod()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Method *baseMethod = Method::Cast(constpoolValue.GetTaggedObject());
|
||||
auto baseMethodId = baseMethod->GetMethodId();
|
||||
CString baseRecordName = MethodLiteral::GetRecordName(baseFile, baseMethodId);
|
||||
if (patchRecordName != baseRecordName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CString baseMethodName = MethodLiteral::GetMethodName(baseFile, baseMethodId);
|
||||
if (patchMethodName == baseMethodName) {
|
||||
patchConstpool->SetObjectToCache(thread, id, constpoolValue);
|
||||
LOG_ECMA(INFO) << "Replace patch external method:" << baseRecordName << ":" << baseMethodName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PatchLoader::ExecutePatchMain(JSThread *thread, const JSPandaFile *patchFile, const JSPandaFile *baseFile,
|
||||
const CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo)
|
||||
PatchInfo &patchInfo)
|
||||
{
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
|
||||
@ -270,7 +115,8 @@ bool PatchLoader::ExecutePatchMain(JSThread *thread, const JSPandaFile *patchFil
|
||||
// For add a new function, Call patch_main_0.
|
||||
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
|
||||
JSHandle<JSFunction> func(thread, program->GetMainFunction());
|
||||
JSHandle<SourceTextModule> module = vm->GetModuleManager()->HostGetImportedModule(recordName);
|
||||
JSHandle<JSTaggedValue> module =
|
||||
vm->GetModuleManager()->HostResolveImportedModuleWithMerge(patchFile->GetJSPandaFileDesc(), recordName);
|
||||
func->SetModule(thread, module);
|
||||
EcmaRuntimeCallInfo *info =
|
||||
EcmaInterpreter::NewRuntimeCallInfo(thread, JSHandle<JSTaggedValue>(func), undefined, undefined, 0);
|
||||
@ -279,8 +125,7 @@ bool PatchLoader::ExecutePatchMain(JSThread *thread, const JSPandaFile *patchFil
|
||||
if (thread->HasPendingException()) {
|
||||
// clear exception and rollback.
|
||||
thread->ClearException();
|
||||
UnloadPatchInternal(
|
||||
thread, patchFile->GetJSPandaFileDesc(), baseFile->GetJSPandaFileDesc(), baseMethodInfo);
|
||||
UnloadPatchInternal(thread, patchFile->GetJSPandaFileDesc(), baseFile->GetJSPandaFileDesc(), patchInfo);
|
||||
LOG_ECMA(ERROR) << "execute patch main has exception";
|
||||
return false;
|
||||
}
|
||||
@ -289,8 +134,7 @@ bool PatchLoader::ExecutePatchMain(JSThread *thread, const JSPandaFile *patchFil
|
||||
}
|
||||
|
||||
PatchErrorCode PatchLoader::UnloadPatchInternal(JSThread *thread, const CString &patchFileName,
|
||||
const CString &baseFileName,
|
||||
const CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo)
|
||||
const CString &baseFileName, PatchInfo &patchInfo)
|
||||
{
|
||||
const JSPandaFile *baseFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(baseFileName);
|
||||
if (baseFile == nullptr) {
|
||||
@ -304,9 +148,10 @@ PatchErrorCode PatchLoader::UnloadPatchInternal(JSThread *thread, const CString
|
||||
return PatchErrorCode::FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
const auto &baseMethodInfo = patchInfo.baseMethodInfo;
|
||||
if (baseMethodInfo.empty()) {
|
||||
LOG_ECMA(ERROR) << "no method need to unload";
|
||||
return PatchErrorCode::INTERNAL_ERROR;
|
||||
LOG_ECMA(INFO) << "no method need to unload";
|
||||
return PatchErrorCode::SUCCESS;
|
||||
}
|
||||
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
@ -323,27 +168,36 @@ PatchErrorCode PatchLoader::UnloadPatchInternal(JSThread *thread, const CString
|
||||
|
||||
uint32_t constpoolIndex = methodIndex.constpoolIndex;
|
||||
uint32_t literalIndex = methodIndex.literalIndex;
|
||||
Method *method = nullptr;
|
||||
Method *patchMethod = nullptr;
|
||||
if (literalIndex == UINT32_MAX) {
|
||||
JSTaggedValue value = baseConstpool->GetObjectFromCache(constpoolIndex);
|
||||
ASSERT(value.IsMethod());
|
||||
method = Method::Cast(value.GetTaggedObject());
|
||||
patchMethod = Method::Cast(value.GetTaggedObject());
|
||||
} else {
|
||||
TaggedArray *classLiteral = TaggedArray::Cast(baseConstpool->GetObjectFromCache(constpoolIndex));
|
||||
JSTaggedValue value = classLiteral->Get(thread, literalIndex);
|
||||
ASSERT(value.IsJSFunctionBase());
|
||||
JSFunctionBase *func = JSFunctionBase::Cast(value.GetTaggedObject());
|
||||
method = Method::Cast(func->GetMethod().GetTaggedObject());
|
||||
patchMethod = Method::Cast(func->GetMethod().GetTaggedObject());
|
||||
}
|
||||
|
||||
MethodLiteral *base = item.second;
|
||||
JSTaggedValue baseConstpoolValue = vm->FindConstpool(baseFile, base->GetMethodId());
|
||||
ReplaceMethod(thread, method, base, baseConstpoolValue);
|
||||
LOG_ECMA(INFO) << "Replace base method: " << method->GetRecordName() << ":" << method->GetMethodName();
|
||||
MethodLiteral *baseMethodLiteral = item.second;
|
||||
JSTaggedValue baseConstpoolValue = vm->FindConstpool(baseFile, baseMethodLiteral->GetMethodId());
|
||||
ReplaceMethod(thread, patchMethod, baseMethodLiteral, baseConstpoolValue);
|
||||
LOG_ECMA(INFO) << "Replace base method: "
|
||||
<< patchMethod->GetRecordName()
|
||||
<< ":" << patchMethod->GetMethodName();
|
||||
}
|
||||
|
||||
vm->GetJsDebuggerManager()->GetHotReloadManager()->NotifyPatchUnloaded(patchFile);
|
||||
|
||||
// release base constpool.
|
||||
CVector<JSHandle<JSTaggedValue>> &baseConstpools = patchInfo.baseConstpools;
|
||||
GlobalHandleCollection gloalHandleCollection(thread);
|
||||
for (auto &item : baseConstpools) {
|
||||
gloalHandleCollection.Dispose(item);
|
||||
}
|
||||
|
||||
ClearPatchInfo(thread, patchFileName);
|
||||
return PatchErrorCode::SUCCESS;
|
||||
}
|
||||
@ -364,7 +218,7 @@ void PatchLoader::ClearPatchInfo(JSThread *thread, const CString &patchFileName)
|
||||
}
|
||||
|
||||
void PatchLoader::ReplaceMethod(JSThread *thread,
|
||||
Method* destMethod,
|
||||
Method *destMethod,
|
||||
MethodLiteral *srcMethodLiteral,
|
||||
JSTaggedValue srcConstpool)
|
||||
{
|
||||
@ -378,14 +232,6 @@ void PatchLoader::ReplaceMethod(JSThread *thread,
|
||||
destMethod->SetAotCodeBit(false);
|
||||
}
|
||||
|
||||
void PatchLoader::InsertMethodInfo(BaseMethodIndex methodIndex, MethodLiteral *base,
|
||||
CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo)
|
||||
{
|
||||
if (baseMethodInfo.find(methodIndex) == baseMethodInfo.end()) {
|
||||
baseMethodInfo.emplace(methodIndex, base);
|
||||
}
|
||||
}
|
||||
|
||||
bool PatchLoader::CheckIsInvalidPatch(const JSPandaFile *baseFile, const JSPandaFile *patchFile, EcmaVM *vm)
|
||||
{
|
||||
DISALLOW_GARBAGE_COLLECTION;
|
||||
@ -395,16 +241,17 @@ bool PatchLoader::CheckIsInvalidPatch(const JSPandaFile *baseFile, const JSPanda
|
||||
auto patchRecordInfos = patchFile->GetJSRecordInfo();
|
||||
[[maybe_unused]] auto baseRecordInfos = baseFile->GetJSRecordInfo();
|
||||
|
||||
CString patchFileName = patchFile->GetJSPandaFileDesc();
|
||||
CString baseFileName = baseFile->GetJSPandaFileDesc();
|
||||
for (const auto &patchItem : patchRecordInfos) {
|
||||
const CString &patchRecordName = patchItem.first;
|
||||
CString baseRecordName = patchRecordName;
|
||||
ASSERT(baseRecordInfos.find(baseRecordName) != baseRecordInfos.end());
|
||||
ASSERT(baseRecordInfos.find(patchRecordName) != baseRecordInfos.end());
|
||||
|
||||
JSHandle<JSTaggedValue> patchModule =
|
||||
moduleManager->ResolveModuleWithMerge(thread, patchFile, patchRecordName);
|
||||
JSHandle<SourceTextModule> baseModule = moduleManager->HostGetImportedModule(baseRecordName);
|
||||
JSHandle<JSTaggedValue> patchModule = moduleManager->ResolveModuleWithMerge(thread, patchFile, patchRecordName);
|
||||
JSHandle<JSTaggedValue> baseModule = moduleManager->ResolveModuleWithMerge(thread, baseFile, patchRecordName);
|
||||
|
||||
if (CheckIsModuleMismatch(thread, JSHandle<SourceTextModule>(patchModule), baseModule)) {
|
||||
if (CheckIsModuleMismatch(thread, JSHandle<SourceTextModule>(patchModule),
|
||||
JSHandle<SourceTextModule>(baseModule))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -669,38 +516,18 @@ bool PatchLoader::CheckStarExportEntryMismatch(StarExportEntry *patch, StarExpor
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PatchLoader::FindAndReplaceSameMethod(JSThread *thread,
|
||||
const JSPandaFile *baseFile, const JSPandaFile *patchFile, CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo)
|
||||
void PatchLoader::FindAndReplaceSameMethod(JSThread *thread, const JSPandaFile *baseFile,
|
||||
const JSPandaFile *patchFile, PatchInfo &patchInfo)
|
||||
{
|
||||
const auto &patchMethodLiterals = patchFile->GetMethodLiteralMap();
|
||||
CUnorderedMap<CString, CUnorderedSet<CString>> patchMethodNames {}; // key :recordname; value :methodname
|
||||
CUnorderedMap<CString, MethodLiteral *> patchReplacedMethodLiterals {}; // key :recordname + "::" + methodname
|
||||
for (const auto &iter : patchMethodLiterals) {
|
||||
MethodLiteral *patch = iter.second;
|
||||
EntityId patchMethodId = patch->GetMethodId();
|
||||
CString patchMethodName = MethodLiteral::GetMethodName(patchFile, patchMethodId);
|
||||
CString patchRecordName = MethodLiteral::GetRecordName(patchFile, patchMethodId);
|
||||
if (patchMethodName == JSPandaFile::ENTRY_FUNCTION_NAME ||
|
||||
patchMethodName == JSPandaFile::PATCH_FUNCTION_NAME_0 ||
|
||||
patchMethodName == JSPandaFile::PATCH_FUNCTION_NAME_1) {
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
const CMap<int32_t, JSTaggedValue> &baseConstpoolValues = vm->FindConstpools(baseFile).value();
|
||||
for (const auto &item : baseConstpoolValues) {
|
||||
if (item.second.IsHole()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CString methodKey = patchRecordName + "::" + patchMethodName;
|
||||
if (patchMethodNames.find(patchRecordName) != patchMethodNames.end()) {
|
||||
patchMethodNames[patchRecordName].insert(patchMethodName);
|
||||
} else {
|
||||
CUnorderedSet<CString> patchMethodNameSet {patchMethodName};
|
||||
patchMethodNames.emplace(std::move(patchRecordName), std::move(patchMethodNameSet));
|
||||
}
|
||||
patchReplacedMethodLiterals.emplace(std::move(methodKey), patch);
|
||||
}
|
||||
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
const CMap<int32_t, JSTaggedValue> &baseConstpoolValues = thread->GetEcmaVM()->FindConstpools(baseFile).value();
|
||||
for (const auto &iter : baseConstpoolValues) {
|
||||
ConstantPool *baseConstpool = ConstantPool::Cast(iter.second.GetTaggedObject());
|
||||
uint32_t constpoolNum = iter.first;
|
||||
ConstantPool *baseConstpool = ConstantPool::Cast(item.second.GetTaggedObject());
|
||||
uint32_t constpoolNum = item.first;
|
||||
uint32_t baseConstpoolSize = baseConstpool->GetCacheLength();
|
||||
for (uint32_t constpoolIndex = 0; constpoolIndex < baseConstpoolSize; constpoolIndex++) {
|
||||
JSTaggedValue constpoolValue = baseConstpool->GetObjectFromCache(constpoolIndex);
|
||||
@ -711,75 +538,102 @@ bool PatchLoader::FindAndReplaceSameMethod(JSThread *thread,
|
||||
// For normal function and class constructor.
|
||||
if (constpoolValue.IsMethod()) {
|
||||
Method *baseMethod = Method::Cast(constpoolValue.GetTaggedObject());
|
||||
auto baseMethodId = baseMethod->GetMethodId();
|
||||
CString baseRecordName = MethodLiteral::GetRecordName(baseFile, baseMethodId);
|
||||
auto item = patchMethodNames.find(baseRecordName);
|
||||
if (item == patchMethodNames.end()) {
|
||||
EntityId baseMethodId = baseMethod->GetMethodId();
|
||||
MethodLiteral *patchMethodLiteral = FindSameMethod(patchInfo, baseFile, baseMethodId);
|
||||
if (patchMethodLiteral == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CString baseMethodName = MethodLiteral::GetMethodName(baseFile, baseMethodId);
|
||||
if (item->second.count(baseMethodName)) {
|
||||
BaseMethodIndex indexs = {constpoolNum, constpoolIndex};
|
||||
CString methodKey = baseRecordName + "::" + baseMethodName;
|
||||
JSTaggedValue patchConstpoolValue = vm->FindConstpool(patchFile, patchMethodLiteral->GetMethodId());
|
||||
ReplaceMethod(thread, baseMethod, patchMethodLiteral, patchConstpoolValue);
|
||||
|
||||
MethodLiteral *patch = patchReplacedMethodLiterals[methodKey];
|
||||
ASSERT(patch != nullptr);
|
||||
MethodLiteral *base = baseFile->FindMethodLiteral(baseMethod->GetMethodId().GetOffset());
|
||||
ASSERT(base != nullptr);
|
||||
EntityId methodId = patch->GetMethodId();
|
||||
JSTaggedValue patchConstpoolValue = vm->FindConstpool(patchFile, methodId);
|
||||
ReplaceMethod(thread, baseMethod, patch, patchConstpoolValue);
|
||||
BaseMethodIndex indexs = {constpoolNum, constpoolIndex};
|
||||
SaveBaseMethodInfo(patchInfo, baseFile, baseMethodId, indexs);
|
||||
} else if (constpoolValue.IsTaggedArray()) {
|
||||
// For class literal.
|
||||
TaggedArray *classLiteral = TaggedArray::Cast(constpoolValue.GetTaggedObject());
|
||||
uint32_t literalLength = classLiteral->GetLength();
|
||||
for (uint32_t literalIndex = 0; literalIndex < literalLength; literalIndex++) {
|
||||
JSTaggedValue literalItem = classLiteral->Get(thread, literalIndex);
|
||||
if (!literalItem.IsJSFunctionBase()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
InsertMethodInfo(indexs, base, baseMethodInfo);
|
||||
LOG_ECMA(INFO) << "Replace base method: " << baseRecordName << ":" << baseMethodName;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Every record is the same in current class literal.
|
||||
JSFunctionBase *func = JSFunctionBase::Cast(literalItem.GetTaggedObject());
|
||||
Method *baseMethod = Method::Cast(func->GetMethod().GetTaggedObject());
|
||||
EntityId baseMethodId = baseMethod->GetMethodId();
|
||||
MethodLiteral *patchMethodLiteral = FindSameMethod(patchInfo, baseFile, baseMethodId);
|
||||
if (patchMethodLiteral == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// For class literal.
|
||||
ASSERT(constpoolValue.IsTaggedArray());
|
||||
TaggedArray *classLiteral = TaggedArray::Cast(constpoolValue);
|
||||
uint32_t literalLength = classLiteral->GetLength();
|
||||
for (uint32_t literalIndex = 0; literalIndex < literalLength; literalIndex++) {
|
||||
JSTaggedValue literalItem = classLiteral->Get(thread, literalIndex);
|
||||
if (!literalItem.IsJSFunctionBase()) {
|
||||
continue;
|
||||
}
|
||||
JSTaggedValue patchConstpoolValue = vm->FindConstpool(patchFile, patchMethodLiteral->GetMethodId());
|
||||
ReplaceMethod(thread, baseMethod, patchMethodLiteral, patchConstpoolValue);
|
||||
|
||||
// Every record is the same in current class literal.
|
||||
JSFunctionBase *func = JSFunctionBase::Cast(literalItem.GetTaggedObject());
|
||||
Method *baseMethod = Method::Cast(func->GetMethod().GetTaggedObject());
|
||||
auto baseMethodId = baseMethod->GetMethodId();
|
||||
CString baseRecordName = MethodLiteral::GetRecordName(baseFile, baseMethodId);
|
||||
auto item = patchMethodNames.find(baseRecordName);
|
||||
if (item == patchMethodNames.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
CString baseMethodName = MethodLiteral::GetMethodName(baseFile, baseMethodId);
|
||||
if (item->second.count(baseMethodName)) {
|
||||
BaseMethodIndex indexs = {constpoolNum, constpoolIndex, literalIndex};
|
||||
CString methodKey = baseRecordName + "::" + baseMethodName;
|
||||
|
||||
MethodLiteral *patch = patchReplacedMethodLiterals[methodKey];
|
||||
ASSERT(patch != nullptr);
|
||||
MethodLiteral *base = baseFile->FindMethodLiteral(baseMethod->GetMethodId().GetOffset());
|
||||
ASSERT(base != nullptr);
|
||||
EntityId methodId = patch->GetMethodId();
|
||||
JSTaggedValue patchConstpoolValue = vm->FindConstpool(patchFile, methodId);
|
||||
ReplaceMethod(thread, baseMethod, patch, patchConstpoolValue);
|
||||
|
||||
InsertMethodInfo(indexs, base, baseMethodInfo);
|
||||
LOG_ECMA(INFO) << "Replace base method: " << baseRecordName << ":" << baseMethodName;
|
||||
SaveBaseMethodInfo(patchInfo, baseFile, baseMethodId, indexs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (baseMethodInfo.empty()) {
|
||||
LOG_ECMA(ERROR) << "can not find same method to base";
|
||||
return false;
|
||||
}
|
||||
|
||||
MethodLiteral* PatchLoader::FindSameMethod(PatchInfo &patchInfo, const JSPandaFile *baseFile, EntityId baseMethodId)
|
||||
{
|
||||
const CMap<CString, CMap<CString, MethodLiteral*>> &patchMethodLiterals = patchInfo.patchMethodLiterals;
|
||||
CString baseRecordName = MethodLiteral::GetRecordName(baseFile, baseMethodId);
|
||||
auto recordIter = patchMethodLiterals.find(baseRecordName);
|
||||
if (recordIter == patchMethodLiterals.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return true;
|
||||
|
||||
CString baseMethodName = MethodLiteral::GetMethodName(baseFile, baseMethodId);
|
||||
auto methodIter = recordIter->second.find(baseMethodName);
|
||||
if (methodIter == recordIter->second.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOG_ECMA(INFO) << "Replace base method: " << baseRecordName << ":" << baseMethodName;
|
||||
return methodIter->second;
|
||||
}
|
||||
|
||||
void PatchLoader::SaveBaseMethodInfo(PatchInfo &patchInfo, const JSPandaFile *baseFile,
|
||||
EntityId baseMethodId, const BaseMethodIndex &indexs)
|
||||
{
|
||||
CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo = patchInfo.baseMethodInfo;
|
||||
MethodLiteral *baseMethodLiteral = baseFile->FindMethodLiteral(baseMethodId.GetOffset());
|
||||
ASSERT(baseMethodLiteral != nullptr);
|
||||
baseMethodInfo.emplace(indexs, baseMethodLiteral);
|
||||
}
|
||||
|
||||
PatchInfo PatchLoader::GeneratePatchInfo(const JSPandaFile *patchFile)
|
||||
{
|
||||
const auto &map = patchFile->GetMethodLiteralMap();
|
||||
CMap<CString, CMap<CString, MethodLiteral*>> methodLiterals;
|
||||
CMap<CString, MethodLiteral*> methodNameInfo;
|
||||
for (const auto &item : map) {
|
||||
MethodLiteral *methodLiteral = item.second;
|
||||
EntityId methodId = EntityId(item.first);
|
||||
CString methodName = MethodLiteral::GetMethodName(patchFile, methodId);
|
||||
if (methodName == JSPandaFile::ENTRY_FUNCTION_NAME ||
|
||||
methodName == JSPandaFile::PATCH_FUNCTION_NAME_0 ||
|
||||
methodName == JSPandaFile::PATCH_FUNCTION_NAME_1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
methodNameInfo.emplace(methodName, methodLiteral);
|
||||
CString recordName = MethodLiteral::GetRecordName(patchFile, methodId);
|
||||
if (methodLiterals.find(recordName) != methodLiterals.end()) {
|
||||
methodLiterals[recordName] = methodNameInfo;
|
||||
} else {
|
||||
methodLiterals.emplace(recordName, methodNameInfo);
|
||||
}
|
||||
}
|
||||
|
||||
PatchInfo patchInfo;
|
||||
patchInfo.patchFileName = patchFile->GetJSPandaFileDesc();
|
||||
patchInfo.patchMethodLiterals = methodLiterals;
|
||||
return patchInfo;
|
||||
}
|
||||
} // namespace panda::ecmascript
|
@ -17,7 +17,6 @@
|
||||
#define ECMASCRIPT_PATCH_PATCH_LOADER_H
|
||||
|
||||
#include "ecmascript/jspandafile/js_pandafile.h"
|
||||
#include "ecmascript/jspandafile/program_object.h"
|
||||
#include "ecmascript/js_tagged_value.h"
|
||||
#include "ecmascript/js_thread.h"
|
||||
#include "ecmascript/mem/c_containers.h"
|
||||
@ -46,6 +45,16 @@ struct BaseMethodIndex {
|
||||
}
|
||||
};
|
||||
|
||||
struct PatchInfo {
|
||||
// patch file name.
|
||||
CString patchFileName;
|
||||
// patch methodLiterals for load patch, <recordName, <methodName, MethodLiteral>>
|
||||
CMap<CString, CMap<CString, MethodLiteral*>> patchMethodLiterals;
|
||||
// base method info for unload patch, <BaseMethodIndex, base MethodLiteral>
|
||||
CMap<BaseMethodIndex, MethodLiteral *> baseMethodInfo;
|
||||
// save base constpool in global for avoid gc.
|
||||
CVector<JSHandle<JSTaggedValue>> baseConstpools;
|
||||
};
|
||||
class PatchLoader {
|
||||
public:
|
||||
PatchLoader() = default;
|
||||
@ -53,35 +62,28 @@ public:
|
||||
NO_COPY_SEMANTIC(PatchLoader);
|
||||
NO_MOVE_SEMANTIC(PatchLoader);
|
||||
|
||||
static PatchErrorCode LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile, const JSPandaFile *patchFile,
|
||||
CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo);
|
||||
static PatchErrorCode LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile,
|
||||
const JSPandaFile *patchFile, PatchInfo &patchInfo);
|
||||
static PatchErrorCode UnloadPatchInternal(JSThread *thread, const CString &patchFileName,
|
||||
const CString &baseFileName,
|
||||
const CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo);
|
||||
const CString &baseFileName, PatchInfo &patchInfo);
|
||||
|
||||
static MethodLiteral *FindSameMethod(PatchInfo &patchInfo, const JSPandaFile *baseFile, EntityId baseMethodId);
|
||||
|
||||
private:
|
||||
static bool FindAndReplaceSameMethod(JSThread *thread,
|
||||
static PatchInfo GeneratePatchInfo(const JSPandaFile *patchFile);
|
||||
static void FindAndReplaceSameMethod(JSThread *thread,
|
||||
const JSPandaFile *baseFile,
|
||||
const JSPandaFile *patchFile,
|
||||
CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo);
|
||||
PatchInfo &patchInfo);
|
||||
static void SaveBaseMethodInfo(PatchInfo &patchInfo, const JSPandaFile *baseFile,
|
||||
EntityId baseMethodId, const BaseMethodIndex &indexs);
|
||||
static void ReplaceMethod(JSThread *thread,
|
||||
Method *destMethod,
|
||||
MethodLiteral *srcMethodLiteral,
|
||||
JSTaggedValue srcConstpool);
|
||||
static void InsertMethodInfo(BaseMethodIndex methodIndex, MethodLiteral *base,
|
||||
CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo);
|
||||
|
||||
static void ParseConstpoolWithMerge(JSThread *thread, const JSPandaFile *jsPandaFile,
|
||||
const CUnorderedMap<CString, JSRecordInfo> &patchRecordInfos,
|
||||
const JSPandaFile *baseFile = nullptr);
|
||||
static void GenerateConstpoolCache(JSThread *thread, const JSPandaFile *jsPandaFile,
|
||||
const CUnorderedMap<CString, JSRecordInfo> &patchRecordInfos,
|
||||
const JSPandaFile *baseFile = nullptr);
|
||||
static void FillExternalMethod(JSThread *thread, const JSPandaFile *patchFile, const JSPandaFile *baseFile,
|
||||
JSHandle<ConstantPool> patchConstpool, uint32_t id);
|
||||
|
||||
static bool ExecutePatchMain(JSThread *thread, const JSPandaFile *patchFile, const JSPandaFile *baseFile,
|
||||
const CMap<BaseMethodIndex, MethodLiteral *> &baseMethodInfo);
|
||||
PatchInfo &patchInfo);
|
||||
|
||||
static void ClearPatchInfo(JSThread *thread, const CString &patchFileName);
|
||||
|
||||
|
@ -47,12 +47,13 @@ PatchErrorCode QuickFixManager::LoadPatch(JSThread *thread, const std::string &p
|
||||
const std::string &baseFileName)
|
||||
{
|
||||
LOG_ECMA(INFO) << "Load patch, patch: " << patchFileName << ", base:" << baseFileName;
|
||||
if (HasLoadedPatch(patchFileName, baseFileName)) {
|
||||
if (methodInfos_.find(baseFileName.c_str()) != methodInfos_.end()) {
|
||||
LOG_ECMA(ERROR) << "Cannot repeat load patch!";
|
||||
return PatchErrorCode::PATCH_HAS_LOADED;
|
||||
}
|
||||
|
||||
const JSPandaFile *baseFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(baseFileName.c_str());
|
||||
const JSPandaFile *baseFile =
|
||||
JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, baseFileName.c_str(), "");
|
||||
if (baseFile == nullptr) {
|
||||
LOG_ECMA(ERROR) << "find base jsPandafile failed";
|
||||
return PatchErrorCode::FILE_NOT_EXECUTED;
|
||||
@ -66,14 +67,14 @@ PatchErrorCode QuickFixManager::LoadPatch(JSThread *thread, const std::string &p
|
||||
return PatchErrorCode::FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
CMap<BaseMethodIndex, MethodLiteral *> baseMethodInfo;
|
||||
auto ret = PatchLoader::LoadPatchInternal(thread, baseFile, patchFile, baseMethodInfo);
|
||||
PatchInfo patchInfo;
|
||||
auto ret = PatchLoader::LoadPatchInternal(thread, baseFile, patchFile, patchInfo);
|
||||
if (ret != PatchErrorCode::SUCCESS) {
|
||||
LOG_ECMA(ERROR) << "Load patch fail!";
|
||||
return ret;
|
||||
}
|
||||
|
||||
methodInfos_.emplace(patchFileName + ":" + baseFileName, baseMethodInfo);
|
||||
methodInfos_.emplace(baseFileName.c_str(), patchInfo);
|
||||
LOG_ECMA(INFO) << "Load patch success!";
|
||||
return PatchErrorCode::SUCCESS;
|
||||
}
|
||||
@ -100,14 +101,14 @@ PatchErrorCode QuickFixManager::LoadPatch(JSThread *thread, const std::string &p
|
||||
return PatchErrorCode::FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
CMap<BaseMethodIndex, MethodLiteral *> baseMethodInfo;
|
||||
auto ret = PatchLoader::LoadPatchInternal(thread, baseFile, patchFile, baseMethodInfo);
|
||||
PatchInfo patchInfo;
|
||||
auto ret = PatchLoader::LoadPatchInternal(thread, baseFile, patchFile, patchInfo);
|
||||
if (ret != PatchErrorCode::SUCCESS) {
|
||||
LOG_ECMA(ERROR) << "Load patch fail!";
|
||||
return ret;
|
||||
}
|
||||
|
||||
methodInfos_.emplace(patchFileName + ":" + baseFileName, baseMethodInfo);
|
||||
methodInfos_.emplace(baseFileName.c_str(), patchInfo);
|
||||
LOG_ECMA(INFO) << "Load patch success!";
|
||||
return PatchErrorCode::SUCCESS;
|
||||
}
|
||||
@ -115,43 +116,69 @@ PatchErrorCode QuickFixManager::LoadPatch(JSThread *thread, const std::string &p
|
||||
PatchErrorCode QuickFixManager::UnloadPatch(JSThread *thread, const std::string &patchFileName)
|
||||
{
|
||||
LOG_ECMA(INFO) << "Unload patch, patch: " << patchFileName;
|
||||
std::string baseFileName = GetBaseFileName(patchFileName);
|
||||
CString baseFileName;
|
||||
for (const auto &item : methodInfos_) {
|
||||
if (item.second.patchFileName == patchFileName.c_str()) {
|
||||
baseFileName = item.first;
|
||||
}
|
||||
}
|
||||
if (baseFileName.empty()) {
|
||||
LOG_ECMA(ERROR) << "patch has not been loaded!";
|
||||
return PatchErrorCode::PATCH_NOT_LOADED;
|
||||
}
|
||||
|
||||
const std::string key = patchFileName + ":" + baseFileName;
|
||||
const auto &baseMethodInfo = methodInfos_.find(key)->second;
|
||||
auto ret = PatchLoader::UnloadPatchInternal(thread, patchFileName.c_str(), baseFileName.c_str(), baseMethodInfo);
|
||||
PatchInfo &patchInfo = methodInfos_.find(baseFileName)->second;
|
||||
auto ret = PatchLoader::UnloadPatchInternal(thread, patchFileName.c_str(), baseFileName.c_str(), patchInfo);
|
||||
if (ret != PatchErrorCode::SUCCESS) {
|
||||
LOG_ECMA(ERROR) << "Unload patch fail!";
|
||||
return ret;
|
||||
}
|
||||
|
||||
methodInfos_.erase(key);
|
||||
methodInfos_.erase(baseFileName.c_str());
|
||||
LOG_ECMA(INFO) << "Unload patch success!";
|
||||
return PatchErrorCode::SUCCESS;
|
||||
}
|
||||
|
||||
bool QuickFixManager::HasLoadedPatch(const std::string &patchFileName, const std::string &baseFileName) const
|
||||
{
|
||||
const std::string key = patchFileName + ":" + baseFileName;
|
||||
return methodInfos_.find(key) != methodInfos_.end();
|
||||
auto iter = methodInfos_.find(baseFileName.c_str());
|
||||
if (iter == methodInfos_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iter->second.patchFileName != patchFileName.c_str()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string QuickFixManager::GetBaseFileName(const std::string &patchFileName) const
|
||||
JSTaggedValue QuickFixManager::CheckAndGetPatch(JSThread *thread, const JSPandaFile *baseFile, EntityId baseMethodId)
|
||||
{
|
||||
for (const auto &item : methodInfos_) {
|
||||
const std::string &key = item.first;
|
||||
size_t pos = key.find(":");
|
||||
ASSERT(pos != std::string::npos);
|
||||
if (key.substr(0, pos) == patchFileName) {
|
||||
return key.substr(pos + 1);
|
||||
}
|
||||
if (methodInfos_.empty()) {
|
||||
return JSTaggedValue::Hole();
|
||||
}
|
||||
LOG_ECMA(ERROR) << "get base file name failed, patch: " << patchFileName;
|
||||
return "";
|
||||
|
||||
auto iter = methodInfos_.find(baseFile->GetJSPandaFileDesc());
|
||||
if (iter == methodInfos_.end()) {
|
||||
return JSTaggedValue::Hole();
|
||||
}
|
||||
|
||||
PatchInfo patchInfo = iter->second;
|
||||
MethodLiteral *patchMethodLiteral = PatchLoader::FindSameMethod(patchInfo, baseFile, baseMethodId);
|
||||
if (patchMethodLiteral == nullptr) {
|
||||
return JSTaggedValue::Hole();
|
||||
}
|
||||
|
||||
// Generate patch constpool.
|
||||
CString patchFileName = patchInfo.patchFileName;
|
||||
const JSPandaFile *patchFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(patchFileName);
|
||||
ASSERT(patchFile != nullptr);
|
||||
|
||||
EcmaVM *vm = thread->GetEcmaVM();
|
||||
JSHandle<Method> method = vm->GetFactory()->NewMethod(patchMethodLiteral);
|
||||
JSHandle<ConstantPool> newConstpool = vm->FindOrCreateConstPool(patchFile, patchMethodLiteral->GetMethodId());
|
||||
method->SetConstantPool(thread, newConstpool);
|
||||
return method.GetTaggedValue();
|
||||
}
|
||||
|
||||
bool QuickFixManager::IsQuickFixCausedException(JSThread *thread,
|
||||
|
@ -37,6 +37,8 @@ public:
|
||||
bool IsQuickFixCausedException(JSThread *thread,
|
||||
const JSHandle<JSTaggedValue> &exceptionInfo,
|
||||
const std::string &patchFileName);
|
||||
JSTaggedValue CheckAndGetPatch(JSThread *thread, const JSPandaFile *baseFile, EntityId baseMethodId);
|
||||
|
||||
private:
|
||||
// check whether the callback is registered.
|
||||
bool HasQueryQuickFixInfoFunc() const
|
||||
@ -46,13 +48,11 @@ private:
|
||||
|
||||
// check whether the patch is loaded.
|
||||
bool HasLoadedPatch(const std::string &patchFileName, const std::string &baseFileName) const;
|
||||
std::string GetBaseFileName(const std::string &patchFileName) const;
|
||||
|
||||
CUnorderedSet<CString> ParseStackInfo(const CString &stackInfo);
|
||||
|
||||
// key: patchFileName:baseFileName
|
||||
// value: base method info <BaseMethodIndex, base MethodLiteral>
|
||||
CMap<std::string, CMap<BaseMethodIndex, MethodLiteral *>> methodInfos_ {};
|
||||
// key: baseFileName
|
||||
CMap<CString, PatchInfo> methodInfos_ {};
|
||||
QuickFixQueryCallBack callBack_ {nullptr};
|
||||
};
|
||||
} // namespace panda::ecmascript
|
||||
|
@ -113,7 +113,17 @@ int Main(const int argc, const char **argv)
|
||||
std::string baseFileName = fileNames[0];
|
||||
bool isMergeAbc = runtimeOptions.GetMergeAbc();
|
||||
JSNApi::SetBundle(vm, !isMergeAbc);
|
||||
auto res = JSNApi::Execute(vm, baseFileName, entry);
|
||||
|
||||
arg_list_t entryList = base::StringHelper::SplitString(entry, ":");
|
||||
uint32_t size = entryList.size();
|
||||
if (size != 2) {
|
||||
std::cout << "Must include 2 entries and with ':' to spilt" << std::endl;
|
||||
JSNApi::DestroyJSVM(vm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// cold patch.
|
||||
auto res = JSNApi::Execute(vm, baseFileName, entryList[0]);
|
||||
if (!res) {
|
||||
std::cout << "Cannot execute panda file '" << baseFileName << "' with entry '" << entry << "'" << std::endl;
|
||||
JSNApi::DestroyJSVM(vm);
|
||||
@ -133,6 +143,9 @@ int Main(const int argc, const char **argv)
|
||||
}
|
||||
std::cout << "QuickFix load patch success" << std::endl;
|
||||
|
||||
// cold patch
|
||||
res = JSNApi::Execute(vm, baseFileName, entryList[1]); // 1: second entrypoint, for cold patch.
|
||||
|
||||
res = JSNApi::Execute(vm, testLoadFileName, TEST_ENTRY_POINT);
|
||||
if (!res) {
|
||||
std::cout << "Cannot execute panda file '" << testLoadFileName
|
||||
|
@ -40,7 +40,7 @@ if (!is_debug) {
|
||||
|
||||
hot_patch_test_list = [
|
||||
"add_callfunction",
|
||||
"external_method",
|
||||
# "external_method",
|
||||
]
|
||||
|
||||
host_quickfix_test_action("multi_patch") {
|
||||
@ -48,18 +48,18 @@ host_quickfix_test_action("multi_patch") {
|
||||
"patch1",
|
||||
"patch2",
|
||||
]
|
||||
entry_point = "--entry-point=base"
|
||||
entry_point = "--entry-point=base:module"
|
||||
}
|
||||
|
||||
foreach(testcase, hot_reload_test_list) {
|
||||
host_quickfix_test_action("${testcase}") {
|
||||
entry_point = "--entry-point=base"
|
||||
entry_point = "--entry-point=base:module"
|
||||
}
|
||||
}
|
||||
|
||||
foreach(testcase, hot_patch_test_list) {
|
||||
host_quickfix_test_action("${testcase}") {
|
||||
entry_point = "--entry-point=base"
|
||||
entry_point = "--entry-point=base:module"
|
||||
is_hotpatch = true
|
||||
}
|
||||
}
|
||||
|
@ -20,4 +20,6 @@ QuickFix have exception.
|
||||
QuickFix start unload patch
|
||||
QuickFix unload patch success
|
||||
print base
|
||||
test cold patch
|
||||
print base patch
|
||||
QuickFix Execute end
|
||||
|
@ -13,4 +13,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export var nop = undefined
|
||||
class D {
|
||||
constructor() {
|
||||
}
|
||||
PrintBase() {
|
||||
print("print base");
|
||||
}
|
||||
}
|
||||
|
||||
function E() {
|
||||
var d = new D()
|
||||
d.PrintBase()
|
||||
}
|
||||
|
||||
globalThis.E = E;
|
||||
|
@ -13,4 +13,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export var nop = undefined
|
||||
class D {
|
||||
constructor() {
|
||||
}
|
||||
PrintBase() {
|
||||
print("print base patch" );
|
||||
}
|
||||
}
|
||||
|
||||
function E() {
|
||||
var d = new D()
|
||||
d.PrintBase()
|
||||
}
|
@ -13,4 +13,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
A()
|
||||
A()
|
||||
print("test cold patch")
|
||||
E()
|
@ -18,10 +18,20 @@ print patch str :patch
|
||||
print patch new :a
|
||||
print patch str :base
|
||||
print patch new :undefined
|
||||
test cold patch
|
||||
print patch str :patch
|
||||
print patch new :a
|
||||
print patch str :patch
|
||||
print patch new :a
|
||||
QuickFix start check exception
|
||||
QuickFix have no exception
|
||||
QuickFix start unload patch
|
||||
QuickFix unload patch success
|
||||
print base str :base
|
||||
print base str :base
|
||||
test cold patch
|
||||
print patch str :patch
|
||||
print patch new :a
|
||||
print patch str :patch
|
||||
print patch new :a
|
||||
QuickFix Execute end
|
||||
|
@ -13,4 +13,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export var nop = undefined
|
||||
class B {
|
||||
constructor() {
|
||||
this.str = "base"
|
||||
}
|
||||
PrintStr() {
|
||||
print("print base str :" + this.str);
|
||||
}
|
||||
}
|
||||
|
||||
globalThis.B = B;
|
||||
globalThis.BB = new B();
|
||||
|
@ -13,4 +13,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export var nop = undefined
|
||||
class B {
|
||||
constructor() {
|
||||
this.str = "patch"
|
||||
this.a = "a"
|
||||
}
|
||||
PrintStr() {
|
||||
print("print patch str :" + this.str);
|
||||
print("print patch new :" + this.a);
|
||||
}
|
||||
}
|
||||
|
@ -15,4 +15,9 @@
|
||||
|
||||
const a = new A();
|
||||
a.PrintStr();
|
||||
AA.PrintStr()
|
||||
AA.PrintStr()
|
||||
|
||||
print("test cold patch")
|
||||
const b = new B();
|
||||
b.PrintStr();
|
||||
BB.PrintStr()
|
@ -21,6 +21,14 @@ patch: 4
|
||||
patch: 12
|
||||
patch: 13
|
||||
patch: 14
|
||||
test cold patch
|
||||
patch: -1
|
||||
patch: 2
|
||||
patch: 3
|
||||
patch: 4
|
||||
patch: 12
|
||||
patch: 13
|
||||
patch: 14
|
||||
QuickFix start check exception
|
||||
QuickFix have no exception
|
||||
QuickFix start unload patch
|
||||
@ -28,4 +36,12 @@ QuickFix unload patch success
|
||||
base: -1
|
||||
base: 1
|
||||
base: 11
|
||||
test cold patch
|
||||
patch: -1
|
||||
patch: 2
|
||||
patch: 3
|
||||
patch: 4
|
||||
patch: 12
|
||||
patch: 13
|
||||
patch: 14
|
||||
QuickFix Execute end
|
||||
|
@ -13,4 +13,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export var nop = undefined
|
||||
var e2 = -1
|
||||
|
||||
function A1() {
|
||||
var a1 = 1
|
||||
function B1() {
|
||||
var b1 = 11
|
||||
function C1() {
|
||||
print("base: " + e2)
|
||||
print("base: " + a1)
|
||||
print("base: " + b1)
|
||||
}
|
||||
C1()
|
||||
}
|
||||
B1()
|
||||
}
|
||||
|
||||
globalThis.A1 = A1
|
||||
|
@ -13,4 +13,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export var nop = undefined
|
||||
var e2 = -10
|
||||
|
||||
function A1() {
|
||||
var a0 = 2
|
||||
var a1 = 3
|
||||
var a2 = 4
|
||||
function B1() {
|
||||
var b0 = 12
|
||||
var b1 = 13
|
||||
var b2 = 14
|
||||
function C1() {
|
||||
print("patch: " + e2)
|
||||
print("patch: " + a0)
|
||||
print("patch: " + a1)
|
||||
print("patch: " + a2)
|
||||
print("patch: " + b0)
|
||||
print("patch: " + b1)
|
||||
print("patch: " + b2)
|
||||
}
|
||||
C1()
|
||||
}
|
||||
B1()
|
||||
}
|
||||
|
@ -13,4 +13,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
A();
|
||||
A();
|
||||
print("test cold patch")
|
||||
A1();
|
@ -18,6 +18,8 @@ patch foo
|
||||
anonymous: patch A
|
||||
{"a":1,"b":{"a":1,"b":"patch"}}
|
||||
1,2,patch
|
||||
test cold patch
|
||||
patch f
|
||||
QuickFix start check exception
|
||||
QuickFix have no exception
|
||||
QuickFix start unload patch
|
||||
@ -26,4 +28,6 @@ base foo
|
||||
anonymous: base A
|
||||
{"a":1,"b":{"a":1,"b":"base"}}
|
||||
1,2,base
|
||||
test cold patch
|
||||
patch f
|
||||
QuickFix Execute end
|
||||
|
@ -13,4 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export var nop = undefined
|
||||
function f() {
|
||||
print("base f")
|
||||
}
|
||||
globalThis.f = f;
|
@ -13,4 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export var nop = undefined
|
||||
function f() {
|
||||
print("patch f")
|
||||
}
|
||||
|
||||
|
@ -17,3 +17,5 @@ globalThis.foo()
|
||||
globalThis.A()
|
||||
globalThis.B()
|
||||
globalThis.C()
|
||||
print("test cold patch")
|
||||
globalThis.f()
|
||||
|
Loading…
Reference in New Issue
Block a user