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