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