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:
g00416891 2023-02-16 20:28:51 +08:00
parent b2ae7019f4
commit fb94e4d58e
29 changed files with 465 additions and 398 deletions

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -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;

View File

@ -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()
}

View File

@ -13,4 +13,6 @@
* limitations under the License.
*/
A()
A()
print("test cold patch")
E()

View File

@ -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

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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()
}

View File

@ -13,4 +13,6 @@
* limitations under the License.
*/
A();
A();
print("test cold patch")
A1();

View File

@ -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

View File

@ -13,4 +13,7 @@
* limitations under the License.
*/
export var nop = undefined
function f() {
print("base f")
}
globalThis.f = f;

View File

@ -13,4 +13,7 @@
* limitations under the License.
*/
export var nop = undefined
function f() {
print("patch f")
}

View File

@ -17,3 +17,5 @@ globalThis.foo()
globalThis.A()
globalThis.B()
globalThis.C()
print("test cold patch")
globalThis.f()