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