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