• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "ecmascript/patch/patch_loader.h"
16 
17 #include "ecmascript/interpreter/interpreter-inl.h"
18 
19 namespace panda::ecmascript {
LoadPatchInternal(JSThread * thread,const JSPandaFile * baseFile,const JSPandaFile * patchFile,PatchInfo & patchInfo,const CMap<uint32_t,CString> & baseClassInfo)20 PatchErrorCode PatchLoader::LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile,
21                                               const JSPandaFile *patchFile, PatchInfo &patchInfo,
22                                               const CMap<uint32_t, CString> &baseClassInfo)
23 {
24     DISALLOW_GARBAGE_COLLECTION;
25     EcmaVM *vm = thread->GetEcmaVM();
26 
27     // hot reload and hot patch only support merge-abc file.
28     if (baseFile->IsBundlePack() || patchFile->IsBundlePack()) {
29         LOG_ECMA(ERROR) << "base or patch is not merge abc!";
30         return PatchErrorCode::PACKAGE_NOT_ESMODULE;
31     }
32 
33     // Generate patchInfo for hot reload, hot patch and cold patch.
34     patchInfo = PatchLoader::GeneratePatchInfo(patchFile);
35 
36     if (!thread->GetCurrentEcmaContext()->HasCachedConstpool(baseFile)) {
37         LOG_ECMA(INFO) << "cold patch!";
38         vm->GetJsDebuggerManager()->GetHotReloadManager()->NotifyPatchLoaded(baseFile, patchFile);
39         return PatchErrorCode::SUCCESS;
40     }
41 
42     [[maybe_unused]] EcmaHandleScope handleScope(thread);
43 
44     // store base constpool in global object for avoid gc.
45     GlobalHandleCollection gloalHandleCollection(thread);
46     for (uint32_t idx = 0; idx < baseFile->GetConstpoolNum(); idx++) {
47         JSTaggedValue constpool = thread->GetCurrentEcmaContext()->FindConstpool(baseFile, idx);
48         if (!constpool.IsHole()) {
49             JSHandle<JSTaggedValue> constpoolHandle =
50                 gloalHandleCollection.NewHandle<JSTaggedValue>(constpool.GetRawData());
51             patchInfo.baseConstpools.emplace_back(constpoolHandle);
52         }
53     }
54 
55     // create empty patch constpool for replace method constpool.
56     thread->GetCurrentEcmaContext()->CreateAllConstpool(patchFile);
57     FindAndReplaceSameMethod(thread, baseFile, patchFile, patchInfo, baseClassInfo);
58 
59     // cached patch modules can only be clear before load patch.
60     thread->GetCurrentEcmaContext()->ClearPatchModules();
61     // execute patch func_main_0 for hot reload, and patch_main_0 for hot patch.
62     ExecuteFuncOrPatchMain(thread, patchFile, patchInfo);
63     UpdateJSFunction(thread, patchInfo);
64 
65     vm->GetJsDebuggerManager()->GetHotReloadManager()->NotifyPatchLoaded(baseFile, patchFile);
66     return PatchErrorCode::SUCCESS;
67 }
68 
ExecuteFuncOrPatchMain(JSThread * thread,const JSPandaFile * jsPandaFile,const PatchInfo & patchInfo,bool loadPatch)69 void PatchLoader::ExecuteFuncOrPatchMain(
70     JSThread *thread, const JSPandaFile *jsPandaFile, const PatchInfo &patchInfo, bool loadPatch)
71 {
72     ThreadManagedScope managedScope(thread);
73     LOG_ECMA(DEBUG) << "execute main begin";
74     EcmaContext *context = thread->GetCurrentEcmaContext();
75     context->SetStageOfHotReload(StageOfHotReload::BEGIN_EXECUTE_PATCHMAIN);
76 
77     const auto &replacedRecordNames = patchInfo.replacedRecordNames;
78 
79     // Resolve all patch module records.
80     CMap<CString, JSHandle<JSTaggedValue>> moduleRecords {};
81 
82     ModuleManager *moduleManager = context->GetModuleManager();
83     CString fileName = jsPandaFile->GetJSPandaFileDesc();
84     for (const auto &recordName : replacedRecordNames) {
85         JSHandle<JSTaggedValue> moduleRecord = moduleManager->
86             HostResolveImportedModuleWithMergeForHotReload(fileName, recordName, false);
87         moduleRecords.emplace(recordName, moduleRecord);
88     }
89 
90     for (const auto &recordName : replacedRecordNames) {
91         LOG_ECMA(DEBUG) << "func main record name " << recordName;
92         JSHandle<Program> program =
93             JSPandaFileManager::GetInstance()->GenerateProgram(thread->GetEcmaVM(), jsPandaFile, recordName);
94         if (program.IsEmpty()) {
95             LOG_ECMA(ERROR) << "program is empty, invoke entrypoint failed";
96             continue;
97         }
98 
99         JSHandle<JSTaggedValue> moduleRecord = moduleRecords[recordName];
100         SourceTextModule::Instantiate(thread, moduleRecord, false);
101         RETURN_IF_ABRUPT_COMPLETION(thread);
102         JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleRecord);
103         SourceTextModule::Evaluate(thread, module);
104     }
105 
106     if (loadPatch) {
107         context->SetStageOfHotReload(StageOfHotReload::LOAD_END_EXECUTE_PATCHMAIN);
108     } else {
109         context->SetStageOfHotReload(StageOfHotReload::UNLOAD_END_EXECUTE_PATCHMAIN);
110     }
111     LOG_ECMA(DEBUG) << "execute main end";
112 }
113 
UnloadPatchInternal(JSThread * thread,const CString & patchFileName,const CString & baseFileName,PatchInfo & patchInfo)114 PatchErrorCode PatchLoader::UnloadPatchInternal(JSThread *thread, const CString &patchFileName,
115                                                 const CString &baseFileName, PatchInfo &patchInfo)
116 {
117     std::shared_ptr<JSPandaFile> baseFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(baseFileName);
118     if (baseFile == nullptr) {
119         LOG_ECMA(ERROR) << "find base jsPandafile failed";
120         return PatchErrorCode::FILE_NOT_EXECUTED;
121     }
122 
123     std::shared_ptr<JSPandaFile> patchFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(patchFileName);
124     if (patchFile == nullptr) {
125         LOG_ECMA(ERROR) << "find patch jsPandafile failed";
126         return PatchErrorCode::FILE_NOT_FOUND;
127     }
128 
129     const auto &baseMethodInfo = patchInfo.baseMethodInfo;
130     if (baseMethodInfo.empty()) {
131         LOG_ECMA(INFO) << "no method need to unload";
132         return PatchErrorCode::SUCCESS;
133     }
134 
135     EcmaVM *vm = thread->GetEcmaVM();
136     EcmaContext *context = thread->GetCurrentEcmaContext();
137     auto baseConstpoolValues = context->FindConstpools(baseFile.get());
138     if (!baseConstpoolValues.has_value()) {
139         LOG_ECMA(ERROR) << "base constpool is empty";
140         return PatchErrorCode::INTERNAL_ERROR;
141     }
142 
143     patchInfo.replacedPatchMethods.clear();
144     for (const auto &item : baseMethodInfo) {
145         const auto &methodIndex = item.first;
146         JSTaggedValue baseConstpool = baseConstpoolValues.value().get()[methodIndex.constpoolNum];
147 
148         Method *patchMethod = GetPatchMethod(thread, methodIndex, baseConstpool);
149 
150         MethodLiteral *baseMethodLiteral = item.second;
151         EntityId baseMethodId = baseMethodLiteral->GetMethodId();
152         JSTaggedValue baseConstpoolValue = context->FindConstpool(baseFile.get(), baseMethodId);
153         patchInfo.replacedPatchMethods.emplace(baseMethodId, patchMethod->GetRecordNameStr());
154         ReplaceMethod(thread, patchMethod, baseMethodLiteral, baseConstpoolValue);
155         LOG_ECMA(DEBUG) << "Replace base method: "
156                        << patchMethod->GetRecordNameStr()
157                        << ":" << patchMethod->GetMethodName();
158     }
159 
160     context->ClearPatchModules();
161     // execute base func_main_0 for recover global object.
162     ExecuteFuncOrPatchMain(thread, baseFile.get(), patchInfo, false);
163     UpdateJSFunction(thread, patchInfo);
164 
165     vm->GetJsDebuggerManager()->GetHotReloadManager()->NotifyPatchUnloaded(patchFile.get());
166 
167     // release base constpool.
168     CVector<JSHandle<JSTaggedValue>> &baseConstpools = patchInfo.baseConstpools;
169     GlobalHandleCollection gloalHandleCollection(thread);
170     for (auto &item : baseConstpools) {
171         gloalHandleCollection.Dispose(item);
172     }
173 
174     ClearPatchInfo(thread, patchFileName);
175     return PatchErrorCode::SUCCESS;
176 }
177 
GetPatchMethod(JSThread * thread,const BaseMethodIndex & methodIndex,const JSTaggedValue constpoolVal)178 Method *PatchLoader::GetPatchMethod(JSThread *thread,
179     const BaseMethodIndex &methodIndex, const JSTaggedValue constpoolVal)
180 {
181     uint32_t constpoolIndex = methodIndex.constpoolIndex;
182     uint32_t literalIndex = methodIndex.literalIndex;
183     Method *patchMethod = nullptr;
184     ConstantPool *baseConstpool = ConstantPool::Cast(constpoolVal.GetTaggedObject());
185     if (literalIndex == UINT32_MAX) {
186         JSTaggedValue value = baseConstpool->GetObjectFromCache(constpoolIndex);
187         ASSERT(value.IsMethod());
188         patchMethod = Method::Cast(value.GetTaggedObject());
189     } else {
190         ClassLiteral *classLiteral;
191         if (baseConstpool->GetObjectFromCache(constpoolIndex).IsHole()) {
192             JSTaggedValue unsharedBaseConstpool = thread->GetCurrentEcmaContext()->
193                 FindOrCreateUnsharedConstpool(constpoolVal);
194             classLiteral = ClassLiteral::Cast(
195                 ConstantPool::Cast(unsharedBaseConstpool.GetTaggedObject())->GetObjectFromCache(constpoolIndex));
196         } else {
197             classLiteral = ClassLiteral::Cast(baseConstpool->GetObjectFromCache(constpoolIndex));
198         }
199         TaggedArray *literalArray = TaggedArray::Cast(classLiteral->GetArray());
200         JSTaggedValue value = literalArray->Get(thread, literalIndex);
201         ASSERT(value.IsFunctionTemplate());
202         FunctionTemplate *func = FunctionTemplate::Cast(value.GetTaggedObject());
203         patchMethod = Method::Cast(func->GetMethod().GetTaggedObject());
204     }
205     return patchMethod;
206 }
207 
ClearPatchInfo(JSThread * thread,const CString & patchFileName)208 void PatchLoader::ClearPatchInfo(JSThread *thread, const CString &patchFileName)
209 {
210     EcmaVM *vm = thread->GetEcmaVM();
211 
212     vm->GetGlobalEnv()->SetGlobalPatch(thread, vm->GetFactory()->EmptyArray());
213 
214     // For release patch constpool and JSPandaFile.
215     vm->CollectGarbage(TriggerGCType::FULL_GC);
216 
217     std::shared_ptr<JSPandaFile> patchFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(patchFileName);
218     if (patchFile != nullptr) {
219         LOG_ECMA(INFO) << "patch jsPandaFile is not nullptr";
220     }
221 }
222 
ReplaceMethod(JSThread * thread,Method * destMethod,MethodLiteral * srcMethodLiteral,JSTaggedValue srcConstpool)223 void PatchLoader::ReplaceMethod(JSThread *thread,
224                                 Method *destMethod,
225                                 MethodLiteral *srcMethodLiteral,
226                                 JSTaggedValue srcConstpool)
227 {
228     // Update destmethod exclude ExtraLiteralInfo(FunctionKind). Method FunctionKind will be set after
229     // building class inheritance relationship or defining gettersetter by value.
230     //
231     // HotReload of class inheritance will be affected.
232     destMethod->SetCallField(srcMethodLiteral->GetCallField());
233     destMethod->SetLiteralInfo(srcMethodLiteral->GetLiteralInfo());
234     destMethod->SetCodeEntryOrLiteral(reinterpret_cast<uintptr_t>(srcMethodLiteral));
235     destMethod->SetNativePointerOrBytecodeArray(const_cast<void *>(srcMethodLiteral->GetNativePointer()));
236     destMethod->SetConstantPool(thread, srcConstpool);
237     destMethod->SetAotCodeBit(false);
238     destMethod->SetFpDelta(0);
239 }
240 
241 // Iterator heap to update module in JSFunction.
UpdateJSFunction(JSThread * thread,PatchInfo & patchInfo)242 void PatchLoader::UpdateJSFunction(JSThread *thread, PatchInfo &patchInfo)
243 {
244     auto &replacedPatchMethods = patchInfo.replacedPatchMethods;
245     const Heap *heap = thread->GetEcmaVM()->GetHeap();
246     heap->GetSweeper()->EnsureAllTaskFinished();
247     heap->IterateOverObjects([&replacedPatchMethods, thread]([[maybe_unused]] TaggedObject *obj) {
248         if (JSTaggedValue(obj).IsJSFunction()) {
249             JSFunction *function = JSFunction::Cast(obj);
250             EntityId methodId = Method::Cast(function->GetMethod())->GetMethodId();
251             if (replacedPatchMethods.count(methodId) > 0) {
252                 JSHandle<JSTaggedValue> moduleRecord =
253                     thread->GetCurrentEcmaContext()->FindPatchModule(replacedPatchMethods[methodId]);
254                 function->SetModule(thread, moduleRecord.GetTaggedValue());
255                 function->SetRawProfileTypeInfo(thread, thread->GlobalConstants()->GetEmptyProfileTypeInfoCell(),
256                                                 SKIP_BARRIER);
257             }
258         } else if (JSTaggedValue(obj).IsFunctionTemplate()) {
259             auto funcTemp = FunctionTemplate::Cast(obj);
260             EntityId methodId = Method::Cast(funcTemp->GetMethod())->GetMethodId();
261             if (replacedPatchMethods.count(methodId) > 0) {
262                 JSHandle<JSTaggedValue> moduleRecord =
263                     thread->GetCurrentEcmaContext()->FindPatchModule(replacedPatchMethods[methodId]);
264                 funcTemp->SetModule(thread, moduleRecord.GetTaggedValue());
265                 funcTemp->SetRawProfileTypeInfo(thread, thread->GlobalConstants()->GetEmptyProfileTypeInfoCell(),
266                                                 SKIP_BARRIER);
267             }
268         }
269     });
270 }
271 
UpdateModuleForColdPatch(JSThread * thread,EntityId methodId,CString & recordName,bool hasModule)272 void PatchLoader::UpdateModuleForColdPatch(JSThread *thread, EntityId methodId, CString &recordName, bool hasModule)
273 {
274     const Heap *heap = thread->GetEcmaVM()->GetHeap();
275     heap->GetSweeper()->EnsureAllTaskFinished();
276     heap->IterateOverObjects([methodId, &recordName, hasModule, thread]([[maybe_unused]] TaggedObject *obj) {
277         if (JSTaggedValue(obj).IsJSFunction()) {
278             JSFunction *function = nullptr;
279             function = JSFunction::Cast(obj);
280             EntityId methodIdLoop = Method::Cast(function->GetMethod())->GetMethodId();
281             if (methodId == methodIdLoop) {
282                 JSHandle<JSTaggedValue> moduleRecord =
283                 thread->GetCurrentEcmaContext()->FindPatchModule(recordName);
284                 if (hasModule) {
285                     function->SetModule(thread, moduleRecord.GetTaggedValue());
286                 } else {
287                     function->SetModule(thread, JSTaggedValue::Undefined());
288                 }
289             }
290         } else if (JSTaggedValue(obj).IsFunctionTemplate()) {
291             auto funcTemp = FunctionTemplate::Cast(obj);
292             EntityId methodIdLoop = Method::Cast(funcTemp->GetMethod())->GetMethodId();
293             if (methodId == methodIdLoop) {
294                 JSHandle<JSTaggedValue> moduleRecord =
295                 thread->GetCurrentEcmaContext()->FindPatchModule(recordName);
296                 if (hasModule) {
297                     funcTemp->SetModule(thread, moduleRecord.GetTaggedValue());
298                 } else {
299                     funcTemp->SetModule(thread, JSTaggedValue::Undefined());
300                 }
301             }
302         }
303     });
304 }
305 
FindAndReplaceSameMethod(JSThread * thread,const JSPandaFile * baseFile,const JSPandaFile * patchFile,PatchInfo & patchInfo,const CMap<uint32_t,CString> & baseClassInfo)306 void PatchLoader::FindAndReplaceSameMethod(JSThread *thread, const JSPandaFile *baseFile,
307                                            const JSPandaFile *patchFile, PatchInfo &patchInfo,
308                                            const CMap<uint32_t, CString> &baseClassInfo)
309 {
310     auto context = thread->GetCurrentEcmaContext();
311     const CMap<int32_t, JSTaggedValue> &baseConstpoolValues = context->FindConstpools(baseFile).value();
312     for (const auto &item : baseConstpoolValues) {
313         if (item.second.IsHole()) {
314             continue;
315         }
316 
317         ConstantPool *baseConstpool = ConstantPool::Cast(item.second.GetTaggedObject());
318         uint32_t constpoolNum = item.first;
319         uint32_t baseConstpoolSize = baseConstpool->GetCacheLength();
320         for (uint32_t constpoolIndex = 0; constpoolIndex < baseConstpoolSize; constpoolIndex++) {
321             JSTaggedValue constpoolValue = baseConstpool->GetObjectFromCache(constpoolIndex);
322             if (!constpoolValue.IsMethod() && !constpoolValue.IsClassLiteral()) {
323                 continue;
324             }
325 
326             // For normal function and class constructor.
327             if (constpoolValue.IsMethod()) {
328                 Method *baseMethod = Method::Cast(constpoolValue.GetTaggedObject());
329                 EntityId baseMethodId = baseMethod->GetMethodId();
330                 MethodLiteral *patchMethodLiteral =
331                     FindSameMethod(patchInfo, baseFile, baseMethodId, baseClassInfo);
332                 if (patchMethodLiteral == nullptr) {
333                     continue;
334                 }
335 
336                 EntityId patchMethodId = patchMethodLiteral->GetMethodId();
337                 JSTaggedValue patchConstpoolValue = context->FindConstpool(patchFile, patchMethodId);
338                 patchInfo.replacedPatchMethods.emplace(patchMethodId, baseMethod->GetRecordNameStr());
339                 ReplaceMethod(thread, baseMethod, patchMethodLiteral, patchConstpoolValue);
340                 LOG_ECMA(DEBUG) << "Replace base method: "
341                                 << baseMethod->GetRecordNameStr() << ": "
342                                 << baseMethod->GetMethodName();
343 
344                 BaseMethodIndex indexs = {constpoolNum, constpoolIndex};
345                 SaveBaseMethodInfo(patchInfo, baseFile, baseMethodId, indexs);
346             } else if (constpoolValue.IsClassLiteral()) {
347                 // For class literal.
348                 FindAndReplaceClassLiteral(thread, baseFile, patchFile, constpoolValue, patchInfo,
349                     constpoolIndex, constpoolNum, baseClassInfo);
350             }
351         }
352 
353         JSTaggedValue unsharedConstpool = context->FindOrCreateUnsharedConstpool(item.second);
354         ConstantPool *baseUnsharedConstpool = ConstantPool::Cast(unsharedConstpool.GetTaggedObject());
355         const uint32_t len = baseUnsharedConstpool->GetCacheLength();
356         for (uint32_t constpoolIndex = 0; constpoolIndex < len; constpoolIndex++) {
357             JSTaggedValue constpoolValue = baseUnsharedConstpool->GetObjectFromCache(constpoolIndex);
358             if (!constpoolValue.IsMethod() && !constpoolValue.IsClassLiteral()) {
359                 continue;
360             }
361             if (constpoolValue.IsClassLiteral()) {
362                 FindAndReplaceClassLiteral(thread, baseFile, patchFile, constpoolValue, patchInfo,
363                     constpoolIndex, constpoolNum, baseClassInfo);
364             }
365         }
366     }
367 }
FindAndReplaceClassLiteral(JSThread * thread,const JSPandaFile * baseFile,const JSPandaFile * patchFile,JSTaggedValue constpoolValue,PatchInfo & patchInfo,uint32_t constpoolIndex,uint32_t constpoolNum,const CMap<uint32_t,CString> & baseClassInfo)368 void PatchLoader::FindAndReplaceClassLiteral(JSThread *thread, const JSPandaFile *baseFile,
369                                              const JSPandaFile *patchFile, JSTaggedValue constpoolValue,
370                                              PatchInfo &patchInfo, uint32_t constpoolIndex,
371                                              uint32_t constpoolNum, const CMap<uint32_t, CString> &baseClassInfo)
372 {
373     // For class literal.
374     ClassLiteral *classLiteral = ClassLiteral::Cast(constpoolValue);
375     TaggedArray *literalArray = TaggedArray::Cast(classLiteral->GetArray());
376     uint32_t literalLength = literalArray->GetLength();
377     for (uint32_t literalIndex = 0; literalIndex < literalLength; literalIndex++) {
378         JSTaggedValue literalItem = literalArray->Get(thread, literalIndex);
379         if (!literalItem.IsFunctionTemplate()) {
380             continue;
381         }
382 
383         // Every record is the same in current class literal.
384         FunctionTemplate *func = FunctionTemplate::Cast(literalItem.GetTaggedObject());
385         Method *baseMethod = Method::Cast(func->GetMethod().GetTaggedObject());
386         EntityId baseMethodId = baseMethod->GetMethodId();
387         MethodLiteral *patchMethodLiteral =
388             FindSameMethod(patchInfo, baseFile, baseMethodId, baseClassInfo);
389         if (patchMethodLiteral == nullptr) {
390             continue;
391         }
392 
393         EntityId patchMethodId = patchMethodLiteral->GetMethodId();
394         JSTaggedValue patchConstpoolValue = thread->GetCurrentEcmaContext()->FindConstpool(patchFile, patchMethodId);
395         patchInfo.replacedPatchMethods.emplace(patchMethodId, baseMethod->GetRecordNameStr());
396         ReplaceMethod(thread, baseMethod, patchMethodLiteral, patchConstpoolValue);
397         LOG_ECMA(DEBUG) << "Replace base method: "
398                         << baseMethod->GetRecordNameStr() << ": "
399                         << baseMethod->GetMethodName();
400 
401         BaseMethodIndex indexs = {constpoolNum, constpoolIndex, literalIndex};
402         SaveBaseMethodInfo(patchInfo, baseFile, baseMethodId, indexs);
403     }
404 }
405 
FindSameMethod(PatchInfo & patchInfo,const JSPandaFile * baseFile,EntityId baseMethodId,const CMap<uint32_t,CString> & baseClassInfo)406 MethodLiteral* PatchLoader::FindSameMethod(PatchInfo &patchInfo, const JSPandaFile *baseFile,
407     EntityId baseMethodId, const CMap<uint32_t, CString> &baseClassInfo)
408 {
409     const CUnorderedMap<PatchMethodIndex, MethodLiteral*, PatchMethodIndex::Hash> &patchMethodLiterals =
410         patchInfo.patchMethodLiterals;
411     if (!baseMethodId.IsValid() || baseMethodId.GetOffset() >= baseFile->GetFileSize()) {
412         return nullptr;
413     }
414     CString baseRecordName = MethodLiteral::GetRecordName(baseFile, baseMethodId);
415     CString baseClassName = "default";
416     auto iter = baseClassInfo.find(baseMethodId.GetOffset());
417     if (iter != baseClassInfo.end()) {
418         baseClassName = iter->second;
419     }
420     CString baseMethodName = GetRealName(baseFile, baseMethodId, baseClassName);
421     PatchMethodIndex patchMethodIndex = {baseRecordName, baseClassName, baseMethodName};
422     auto methodIter = patchMethodLiterals.find(patchMethodIndex);
423     if (methodIter == patchMethodLiterals.end()) {
424         return nullptr;
425     }
426 
427     // Reserved for HotPatch.
428     patchInfo.replacedRecordNames.emplace(baseRecordName);
429     return methodIter->second;
430 }
431 
SaveBaseMethodInfo(PatchInfo & patchInfo,const JSPandaFile * baseFile,EntityId baseMethodId,const BaseMethodIndex & indexs)432 void PatchLoader::SaveBaseMethodInfo(PatchInfo &patchInfo, const JSPandaFile *baseFile,
433                                      EntityId baseMethodId, const BaseMethodIndex &indexs)
434 {
435     CUnorderedMap<BaseMethodIndex, MethodLiteral *, BaseMethodIndex::Hash> &baseMethodInfo = patchInfo.baseMethodInfo;
436     MethodLiteral *baseMethodLiteral = baseFile->FindMethodLiteral(baseMethodId.GetOffset());
437     CHECK_INPUT_NULLPTR(baseMethodLiteral, "SaveBaseMethodInfo:baseMethodLiteral is nullptr, offset: "
438                                             + std::to_string(baseMethodId.GetOffset()));
439     baseMethodInfo.emplace(indexs, baseMethodLiteral);
440 }
441 
GeneratePatchInfo(const JSPandaFile * patchFile)442 PatchInfo PatchLoader::GeneratePatchInfo(const JSPandaFile *patchFile)
443 {
444     CMap<uint32_t, CString> patchClassInfo = CollectClassInfo(patchFile);
445 
446     const auto &map = patchFile->GetMethodLiteralMap();
447     CUnorderedMap<PatchMethodIndex, MethodLiteral*, PatchMethodIndex::Hash> patchMethodLiterals;
448     PatchInfo patchInfo;
449     for (const auto &item : map) {
450         MethodLiteral *methodLiteral = item.second;
451         EntityId methodId = EntityId(item.first);
452         CString className = "default"; // for normal method and constructor.
453         auto iter = patchClassInfo.find(methodId.GetOffset());
454         if (iter!= patchClassInfo.end()) {
455             className = iter->second;
456         }
457         CString methodName = GetRealName(patchFile, methodId, className);
458         if (methodName == JSPandaFile::PATCH_FUNCTION_NAME_0 ||
459             methodName == JSPandaFile::PATCH_FUNCTION_NAME_1) {
460             continue;
461         }
462 
463         // if patchFile only include varibales, add recordName specially.
464         CString recordName = MethodLiteral::GetRecordName(patchFile, methodId);
465         if (methodName == JSPandaFile::ENTRY_FUNCTION_NAME) {
466             patchInfo.replacedRecordNames.emplace(recordName);
467         }
468 
469         PatchMethodIndex patchMethodIndex = {recordName, className, methodName};
470         if (patchMethodLiterals.find(patchMethodIndex) == patchMethodLiterals.end()) {
471             patchMethodLiterals.emplace(patchMethodIndex, methodLiteral);
472         }
473     }
474 
475     patchInfo.patchFileName = patchFile->GetJSPandaFileDesc();
476     patchInfo.patchMethodLiterals = std::move(patchMethodLiterals);
477     return patchInfo;
478 }
479 
CollectClassInfo(const JSPandaFile * jsPandaFile)480 CMap<uint32_t, CString> PatchLoader::CollectClassInfo(const JSPandaFile *jsPandaFile)
481 {
482     CMap<uint32_t, CString> classInfo {};
483     auto &pandaFile = *jsPandaFile->GetPandaFile();
484     auto classes = jsPandaFile->GetClasses();
485     const auto &map = jsPandaFile->GetMethodLiteralMap();
486     for (size_t i = 0; i < classes.Size(); i++) {
487         EntityId classId(classes[i]);
488         if (!classId.IsValid() || jsPandaFile->IsExternal(classId)) {
489             continue;
490         }
491 
492         panda_file::ClassDataAccessor cda(pandaFile, classId);
493         cda.EnumerateMethods([&pandaFile, &map, &classInfo, jsPandaFile](panda_file::MethodDataAccessor &mda) {
494             EntityId methodId = mda.GetMethodId();
495             auto iter = map.find(methodId.GetOffset());
496             MethodLiteral *methodLiteral = nullptr;
497             if (iter != map.end()) {
498                 methodLiteral = iter->second;
499             }
500 
501             auto codeId = mda.GetCodeId();
502             ASSERT(codeId.has_value());
503             panda_file::CodeDataAccessor codeDataAccessor(pandaFile, codeId.value());
504             uint32_t codeSize = codeDataAccessor.GetCodeSize();
505             const uint8_t *insns = codeDataAccessor.GetInstructions();
506 
507             auto bcIns = BytecodeInst(insns);
508             auto bcInsLast = bcIns.JumpTo(codeSize);
509             while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
510                 BytecodeInstruction::Opcode opcode = static_cast<BytecodeInstruction::Opcode>(bcIns.GetOpcode());
511                 if (opcode == BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8 ||
512                     opcode == BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8) {
513                     EntityId entityId;
514                     EntityId literalId;
515                     if (opcode == BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8) {
516                         entityId = jsPandaFile->ResolveMethodIndex(methodLiteral->GetMethodId(),
517                             (bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 0>()).AsRawValue());
518                         literalId = jsPandaFile->ResolveMethodIndex(methodLiteral->GetMethodId(),
519                             (bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 1>()).AsRawValue());
520                     } else if (opcode == BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8) {
521                         entityId = jsPandaFile->ResolveMethodIndex(methodLiteral->GetMethodId(),
522                             (bcIns.GetId <BytecodeInstruction::Format::IMM16_ID16_ID16_IMM16_V8, 0>()).AsRawValue());
523                         literalId = jsPandaFile->ResolveMethodIndex(methodLiteral->GetMethodId(),
524                             (bcIns.GetId <BytecodeInstruction::Format::IMM16_ID16_ID16_IMM16_V8, 1>()).AsRawValue());
525                     }
526                     CString className = "";
527                     className = GetRealName(jsPandaFile, entityId, className);
528                     CString recordName = MethodLiteral::GetRecordName(jsPandaFile, methodId);
529 
530                     LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
531                     lda.EnumerateLiteralVals(literalId, [&classInfo, className]
532                         (const LiteralValue &value, const LiteralTag &tag) {
533                             switch (tag) {
534                                 case LiteralTag::METHOD:
535                                 case LiteralTag::GENERATORMETHOD: {
536                                     uint32_t methodOffset = std::get<uint32_t>(value);
537                                     classInfo.emplace(methodOffset, std::move(className));
538                                     break;
539                                 }
540                                 default: {
541                                     break;
542                                 }
543                             }
544                     });
545                 }
546                 auto nextInst = bcIns.GetNext();
547                 bcIns = nextInst;
548             }
549         });
550     }
551     return classInfo;
552 }
553 
GetRealName(const JSPandaFile * jsPandaFile,EntityId entityId,CString & className)554 CString PatchLoader::GetRealName(const JSPandaFile *jsPandaFile, EntityId entityId, CString &className)
555 {
556     std::string methodName(MethodLiteral::GetMethodName(jsPandaFile, entityId));
557     size_t poiIndex = methodName.find_last_of('#');
558     ASSERT(methodName.size() > 0);
559     if (poiIndex != std::string::npos && poiIndex < methodName.size() - 1 && className != "default") {
560         methodName = methodName.substr(poiIndex + 1);  // #...#functionName
561         if (methodName.find('^') != std::string::npos) {
562             poiIndex = methodName.find_last_of('^');
563             methodName = methodName.substr(0, poiIndex);  // #...#functionName^1
564         }
565     }
566     return ConvertToString(methodName);
567 }
568 }  // namespace panda::ecmascript
569