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