• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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/jspandafile/quick_fix_loader.h"
16 
17 #include "ecmascript/interpreter/interpreter-inl.h"
18 #include "ecmascript/jspandafile/js_pandafile_executor.h"
19 #include "ecmascript/jspandafile/js_pandafile_manager.h"
20 #include "ecmascript/mem/c_string.h"
21 #include "libpandafile/class_data_accessor.h"
22 #include "libpandafile/method_data_accessor.h"
23 
24 namespace panda::ecmascript {
~QuickFixLoader()25 QuickFixLoader::~QuickFixLoader()
26 {
27     ClearReservedInfo();
28 }
29 
LoadPatch(JSThread * thread,const CString & patchFileName,const CString & baseFileName)30 bool QuickFixLoader::LoadPatch(JSThread *thread, const CString &patchFileName, const CString &baseFileName)
31 {
32     const JSPandaFile *baseFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(baseFileName);
33     if (baseFile == nullptr) {
34         LOG_ECMA(ERROR) << "find base jsPandafile failed";
35         return false;
36     }
37 
38     // The entry point is not work for merge abc.
39     const JSPandaFile *patchFile =
40         JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, patchFileName, JSPandaFile::ENTRY_MAIN_FUNCTION);
41     if (patchFile == nullptr) {
42         LOG_ECMA(ERROR) << "load patch jsPandafile failed";
43         return false;
44     }
45 
46     return LoadPatchInternal(thread, baseFile, patchFile);
47 }
48 
LoadPatch(JSThread * thread,const CString & patchFileName,const void * patchBuffer,size_t patchSize,const CString & baseFileName)49 bool QuickFixLoader::LoadPatch(JSThread *thread, const CString &patchFileName, const void *patchBuffer,
50                                size_t patchSize, const CString &baseFileName)
51 {
52     const JSPandaFile *baseFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(baseFileName);
53     if (baseFile == nullptr) {
54         LOG_ECMA(ERROR) << "find base jsPandafile failed";
55         return false;
56     }
57 
58     const JSPandaFile *patchFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(
59         thread, patchFileName, JSPandaFile::ENTRY_MAIN_FUNCTION, patchBuffer, patchSize);
60     if (patchFile == nullptr) {
61         LOG_ECMA(ERROR) << "load patch jsPandafile failed";
62         return false;
63     }
64 
65     return LoadPatchInternal(thread, baseFile, patchFile);
66 }
67 
LoadPatchInternal(JSThread * thread,const JSPandaFile * baseFile,const JSPandaFile * patchFile)68 bool QuickFixLoader::LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile, const JSPandaFile *patchFile)
69 {
70     // support multi constpool: baseFile->GetPandaFile()->GetHeader()->num_indexes
71     EcmaVM *vm = thread->GetEcmaVM();
72 
73     // hot reload and hot patch only support merge-abc file.
74     if (baseFile->IsBundlePack() || patchFile->IsBundlePack()) {
75         LOG_ECMA(ERROR) << "base or patch is not merge abc!";
76         return false;
77     }
78 
79     // Get base constpool.
80     ParseAllConstpoolWithMerge(thread, baseFile);
81     JSTaggedValue baseConstpoolValue = vm->FindConstpool(baseFile, 0);
82     if (baseConstpoolValue.IsHole()) {
83         LOG_ECMA(ERROR) << "base constpool is hole";
84         return false;
85     }
86 
87     JSHandle<ConstantPool> baseConstpool = JSHandle<ConstantPool>(thread, baseConstpoolValue);
88 
89     [[maybe_unused]] EcmaHandleScope handleScope(thread);
90     // Get patch constpool.
91     [[maybe_unused]] CVector<JSHandle<Program>> patchPrograms = ParseAllConstpoolWithMerge(thread, patchFile);
92     JSTaggedValue patchConstpoolValue = vm->FindConstpool(patchFile, 0);
93     if (patchConstpoolValue.IsHole()) {
94         LOG_ECMA(ERROR) << "patch constpool is hole";
95         return false;
96     }
97 
98     JSHandle<ConstantPool> patchConstpool = JSHandle<ConstantPool>(thread, patchConstpoolValue);
99 
100     // Only esmodule support check
101     if (CheckIsInvalidPatch(baseFile, patchFile, vm)) {
102         LOG_ECMA(ERROR) << "Invalid patch";
103         return false;
104     }
105 
106     if (!ReplaceMethod(thread, baseFile, patchFile, baseConstpool, patchConstpool)) {
107         LOG_ECMA(ERROR) << "replace method failed";
108         return false;
109     }
110 
111     vm->GetJsDebuggerManager()->GetHotReloadManager()->NotifyPatchLoaded(baseFile, patchFile);
112 
113     baseFileName_ = baseFile->GetJSPandaFileDesc();
114     LOG_ECMA(INFO) << "LoadPatch success!";
115     return true;
116 }
117 
ParseAllConstpoolWithMerge(JSThread * thread,const JSPandaFile * jsPandaFile)118 CVector<JSHandle<Program>> QuickFixLoader::ParseAllConstpoolWithMerge(JSThread *thread, const JSPandaFile *jsPandaFile)
119 {
120     EcmaVM *vm = thread->GetEcmaVM();
121     JSHandle<ConstantPool> constpool;
122     bool isNewVersion = jsPandaFile->IsNewVersion();
123     if (!isNewVersion) {
124         JSTaggedValue constpoolVal = vm->FindConstpool(jsPandaFile, 0);
125         if (constpoolVal.IsHole()) {
126             constpool = PandaFileTranslator::ParseConstPool(vm, jsPandaFile);
127             // old version dont support multi constpool
128             vm->AddConstpool(jsPandaFile, constpool.GetTaggedValue());
129         } else {
130             constpool = JSHandle<ConstantPool>(thread, constpoolVal);
131         }
132     }
133 
134     CVector<JSHandle<Program>> programs;
135     auto recordInfos = jsPandaFile->GetJSRecordInfo();
136     const CString &fileName = jsPandaFile->GetJSPandaFileDesc();
137     for (const auto &item : recordInfos) {
138         const CString &recordName = item.first;
139 
140         vm->GetModuleManager()->HostResolveImportedModuleWithMerge(fileName, recordName);
141 
142         uint32_t mainMethodIndex = jsPandaFile->GetMainMethodIndex(recordName);
143         ASSERT(mainMethodIndex != 0);
144         if (!isNewVersion) {
145             PandaFileTranslator::ParseFuncAndLiteralConstPool(vm, jsPandaFile, recordName, constpool);
146         } else {
147             constpool = vm->FindOrCreateConstPool(jsPandaFile, panda_file::File::EntityId(mainMethodIndex));
148         }
149 
150         // Generate Program for every record.
151         JSHandle<Program> program =
152             PandaFileTranslator::GenerateProgramInternal(vm, jsPandaFile, mainMethodIndex, constpool);
153         programs.emplace_back(program);
154     }
155     if (isNewVersion) {
156         GenerateConstpoolCache(thread, jsPandaFile, constpool);
157     }
158     return programs;
159 }
160 
GenerateConstpoolCache(JSThread * thread,const JSPandaFile * jsPandaFile,JSHandle<ConstantPool> constpool)161 void QuickFixLoader::GenerateConstpoolCache(JSThread *thread, const JSPandaFile *jsPandaFile,
162                                             JSHandle<ConstantPool> constpool)
163 {
164     ASSERT(jsPandaFile->IsNewVersion());
165     const panda_file::File *pf = jsPandaFile->GetPandaFile();
166     Span<const uint32_t> classIndexes = jsPandaFile->GetClasses();
167     for (const uint32_t index : classIndexes) {
168         panda_file::File::EntityId classId(index);
169         if (pf->IsExternal(classId)) {
170             continue;
171         }
172         panda_file::ClassDataAccessor cda(*pf, classId);
173         CString entry = jsPandaFile->ParseEntryPoint(utf::Mutf8AsCString(cda.GetDescriptor()));
174         cda.EnumerateMethods([pf, thread, constpool, &entry](panda_file::MethodDataAccessor &mda) {
175             auto codeId = mda.GetCodeId();
176             ASSERT(codeId.has_value());
177             panda_file::CodeDataAccessor codeDataAccessor(*pf, codeId.value());
178             uint32_t codeSize = codeDataAccessor.GetCodeSize();
179             const uint8_t *insns = codeDataAccessor.GetInstructions();
180 
181             auto bcIns = BytecodeInstruction(insns);
182             auto bcInsLast = bcIns.JumpTo(codeSize);
183             while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
184                 if (!bcIns.HasFlag(BytecodeInstruction::Flags::STRING_ID) ||
185                     !BytecodeInstruction::HasId(BytecodeInstruction::GetFormat(bcIns.GetOpcode()), 0)) {
186                     BytecodeInstruction::Opcode opcode = bcIns.GetOpcode();
187                     switch (opcode) {
188                         // add opcode about method
189                         case EcmaOpcode::DEFINEMETHOD_IMM8_ID16_IMM8:
190                             U_FALLTHROUGH;
191                         case EcmaOpcode::DEFINEMETHOD_IMM16_ID16_IMM8:
192                             U_FALLTHROUGH;
193                         case EcmaOpcode::DEFINEFUNC_IMM8_ID16_IMM8:
194                             U_FALLTHROUGH;
195                         case EcmaOpcode::DEFINEFUNC_IMM16_ID16_IMM8: {
196                             uint32_t id = bcIns.GetId().AsRawValue();
197                             ConstantPool::GetMethodFromCache(thread, constpool.GetTaggedValue(), id);
198                             break;
199                         }
200                         case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM8_ID16:
201                             U_FALLTHROUGH;
202                         case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: {
203                             uint32_t id = bcIns.GetId().AsRawValue();
204                             ConstantPool::GetLiteralFromCache<ConstPoolType::OBJECT_LITERAL>(
205                                 thread, constpool.GetTaggedValue(), id, entry);
206                             break;
207                         }
208                         case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16:
209                             U_FALLTHROUGH;
210                         case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: {
211                             uint32_t id = bcIns.GetId().AsRawValue();
212                             ConstantPool::GetLiteralFromCache<ConstPoolType::ARRAY_LITERAL>(
213                                 thread, constpool.GetTaggedValue(), id, entry);
214                             break;
215                         }
216                         case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8:
217                             U_FALLTHROUGH;
218                         case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: {
219                             uint32_t methodId = bcIns.GetId(0).AsRawValue();
220                             uint32_t literalId = bcIns.GetId(1).AsRawValue();
221                             ConstantPool::GetClassMethodFromCache(thread, constpool, methodId);
222                             ConstantPool::GetClassLiteralFromCache(thread, constpool, literalId, entry);
223                             break;
224                         }
225                         default:
226                             break;
227                     }
228                 }
229                 bcIns = bcIns.GetNext();
230             }
231         });
232     }
233 }
234 
ReplaceMethod(JSThread * thread,const JSPandaFile * baseFile,const JSPandaFile * patchFile,const JSHandle<ConstantPool> & baseConstpool,const JSHandle<ConstantPool> & patchConstpool)235 bool QuickFixLoader::ReplaceMethod(JSThread *thread,
236                                    const JSPandaFile *baseFile,
237                                    const JSPandaFile *patchFile,
238                                    const JSHandle<ConstantPool> &baseConstpool,
239                                    const JSHandle<ConstantPool> &patchConstpool)
240 {
241     CUnorderedMap<uint32_t, MethodLiteral *> patchMethodLiterals = patchFile->GetMethodLiteralMap();
242     auto baseConstpoolSize = baseConstpool->GetCacheLength();
243 
244     // Loop patch methodLiterals and base constpool to find same methodName and recordName both in base and patch.
245     for (const auto &item : patchMethodLiterals) {
246         MethodLiteral *patch = item.second;
247         auto methodId = patch->GetMethodId();
248         const char *patchMethodName = MethodLiteral::GetMethodName(patchFile, methodId);
249         // Skip func_main_0, patch_main_0 and patch_main_1.
250         if (std::strcmp(patchMethodName, JSPandaFile::ENTRY_FUNCTION_NAME) == 0 ||
251             std::strcmp(patchMethodName, JSPandaFile::PATCH_FUNCTION_NAME_0) == 0 ||
252             std::strcmp(patchMethodName, JSPandaFile::PATCH_FUNCTION_NAME_1) == 0) {
253             continue;
254         }
255 
256         CString patchRecordName = GetRecordName(patchFile, methodId);
257         for (uint32_t index = 0; index < baseConstpoolSize; index++) {
258             JSTaggedValue constpoolValue = baseConstpool->GetObjectFromCache(index);
259             // For class inner function modified.
260             if (constpoolValue.IsClassLiteral()) {
261                 JSHandle<ClassLiteral> classLiteral(thread, constpoolValue);
262                 JSHandle<TaggedArray> literalArray(thread, classLiteral->GetArray());
263                 for (uint32_t i = 0; i < literalArray->GetLength(); i++) {
264                     JSTaggedValue literalItem = literalArray->Get(thread, i);
265                     if (!literalItem.IsJSFunctionBase()) {
266                         continue;
267                     }
268                     // Skip method that already been replaced.
269                     if (HasClassMethodReplaced(index, i)) {
270                         continue;
271                     }
272                     JSFunctionBase *func = JSFunctionBase::Cast(literalItem.GetTaggedObject());
273                     Method *baseMethod = Method::Cast(func->GetMethod().GetTaggedObject());
274                     if (std::strcmp(patchMethodName, baseMethod->GetMethodName()) != 0) {
275                         continue;
276                     }
277                     // For merge abc, method and record name must be same to base.
278                     CString baseRecordName = GetRecordName(baseFile, baseMethod->GetMethodId());
279                     if (patchRecordName != baseRecordName) {
280                         continue;
281                     }
282 
283                     // Save base MethodLiteral and literal index.
284                     MethodLiteral *base = baseFile->FindMethodLiteral(baseMethod->GetMethodId().GetOffset());
285                     ASSERT(base != nullptr);
286                     InsertBaseClassMethodInfo(index, i, base);
287 
288                     ReplaceMethodInner(thread, baseMethod, patch, patchConstpool.GetTaggedValue());
289                     LOG_ECMA(INFO) << "Replace class method: " << patchRecordName << ":" << patchMethodName;
290                     break;
291                 }
292             }
293             // For normal function and class constructor modified.
294             if (constpoolValue.IsMethod()) {
295                 // Skip method that already been replaced.
296                 if (HasNormalMethodReplaced(index)) {
297                     continue;
298                 }
299                 Method *baseMethod = Method::Cast(constpoolValue.GetTaggedObject());
300                 if (std::strcmp(patchMethodName, baseMethod->GetMethodName()) != 0) {
301                     continue;
302                 }
303                 // For merge abc, method and record name must be same to base.
304                 CString baseRecordName = GetRecordName(baseFile, baseMethod->GetMethodId());
305                 if (patchRecordName != baseRecordName) {
306                     continue;
307                 }
308 
309                 // Save base MethodLiteral and constpool index.
310                 MethodLiteral *base = baseFile->FindMethodLiteral(baseMethod->GetMethodId().GetOffset());
311                 ASSERT(base != nullptr);
312                 reservedBaseMethodInfo_.emplace(index, base);
313 
314                 ReplaceMethodInner(thread, baseMethod, patch, patchConstpool.GetTaggedValue());
315                 LOG_ECMA(INFO) << "Replace normal method: " << patchRecordName << ":" << patchMethodName;
316                 break;
317             }
318         }
319     }
320 
321     if (reservedBaseMethodInfo_.empty() && reservedBaseClassInfo_.empty()) {
322         LOG_ECMA(ERROR) << "can not find same method to base";
323         return false;
324     }
325     return true;
326 }
327 
ExecutePatchMain(JSThread * thread,const JSHandle<Program> & patchProgram,const JSPandaFile * patchFile)328 bool QuickFixLoader::ExecutePatchMain(
329     JSThread *thread, const JSHandle<Program> &patchProgram, const JSPandaFile *patchFile)
330 {
331     if (patchProgram->GetMainFunction().IsUndefined()) {
332         return true;
333     }
334 
335     // For add a new function, Call patch_main_0.
336     JSHandle<JSTaggedValue> global = thread->GetEcmaVM()->GetGlobalEnv()->GetJSGlobalObject();
337     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
338     JSHandle<JSTaggedValue> func(thread, patchProgram->GetMainFunction());
339     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, global, undefined, 0);
340     EcmaInterpreter::Execute(info);
341 
342     if (thread->HasPendingException()) {
343         // clear exception and rollback.
344         thread->ClearException();
345         UnloadPatch(thread, patchFile->GetJSPandaFileDesc());
346         LOG_ECMA(ERROR) << "execute patch main has exception";
347         return false;
348     }
349     return true;
350 }
351 
ExecutePatchMain(JSThread * thread,const CVector<JSHandle<Program>> & programs,const JSPandaFile * patchFile)352 bool QuickFixLoader::ExecutePatchMain(
353     JSThread *thread, const CVector<JSHandle<Program>> &programs, const JSPandaFile *patchFile)
354 {
355     for (const auto &item : programs) {
356         if (!ExecutePatchMain(thread, item, patchFile)) {
357             return false;
358         }
359     }
360     return true;
361 }
362 
GetRecordName(const JSPandaFile * jsPandaFile,EntityId methodId)363 CString QuickFixLoader::GetRecordName(const JSPandaFile *jsPandaFile, EntityId methodId)
364 {
365     const panda_file::File *pf = jsPandaFile->GetPandaFile();
366     panda_file::MethodDataAccessor patchMda(*pf, methodId);
367     panda_file::ClassDataAccessor patchCda(*pf, patchMda.GetClassId());
368     CString desc = utf::Mutf8AsCString(patchCda.GetDescriptor());
369     return jsPandaFile->ParseEntryPoint(desc);
370 }
371 
UnloadPatch(JSThread * thread,const CString & patchFileName)372 bool QuickFixLoader::UnloadPatch(JSThread *thread, const CString &patchFileName)
373 {
374     const JSPandaFile *baseFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(baseFileName_);
375     if (baseFile == nullptr) {
376         LOG_ECMA(ERROR) << "find base jsPandafile failed";
377         return false;
378     }
379 
380     const JSPandaFile *patchFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(patchFileName);
381     if (patchFile == nullptr) {
382         LOG_ECMA(ERROR) << "find patch jsPandafile failed";
383         return false;
384     }
385 
386     if (reservedBaseMethodInfo_.empty() && reservedBaseClassInfo_.empty()) {
387         LOG_ECMA(ERROR) << "no method need to unload";
388         return false;
389     }
390 
391     EcmaVM *vm = thread->GetEcmaVM();
392     JSTaggedValue baseConstpoolValue = vm->FindConstpool(baseFile, 0);
393     if (baseConstpoolValue.IsHole()) {
394         LOG_ECMA(ERROR) << "base constpool is hole";
395         return false;
396     }
397 
398     ConstantPool *baseConstpool = ConstantPool::Cast(baseConstpoolValue.GetTaggedObject());
399     for (const auto& item : reservedBaseMethodInfo_) {
400         uint32_t constpoolIndex = item.first;
401         MethodLiteral *base = item.second;
402 
403         JSTaggedValue value = baseConstpool->GetObjectFromCache(constpoolIndex);
404         ASSERT(value.IsMethod());
405         Method *method = Method::Cast(value.GetTaggedObject());
406 
407         ReplaceMethodInner(thread, method, base, baseConstpoolValue);
408         LOG_ECMA(INFO) << "Replace normal method: " << method->GetMethodName();
409     }
410 
411     for (const auto& item : reservedBaseClassInfo_) {
412         uint32_t constpoolIndex = item.first;
413         CUnorderedMap<uint32_t, MethodLiteral *> classLiteralInfo = item.second;
414         JSHandle<ClassLiteral> classLiteral(thread, baseConstpool->GetObjectFromCache(constpoolIndex));
415         JSHandle<TaggedArray> literalArray(thread, classLiteral->GetArray());
416 
417         for (const auto& classItem : classLiteralInfo) {
418             MethodLiteral *base = classItem.second;
419 
420             JSTaggedValue value = literalArray->Get(thread, classItem.first);
421             ASSERT(value.IsJSFunctionBase());
422             JSFunctionBase *func = JSFunctionBase::Cast(value.GetTaggedObject());
423             Method *method = Method::Cast(func->GetMethod().GetTaggedObject());
424 
425             ReplaceMethodInner(thread, method, base, baseConstpoolValue);
426             LOG_ECMA(INFO) << "Replace class method: " << method->GetMethodName();
427         }
428     }
429 
430     vm->GetJsDebuggerManager()->GetHotReloadManager()->NotifyPatchUnloaded(patchFile);
431 
432     ClearReservedInfo();
433     ClearPatchInfo(thread, patchFileName);
434 
435     LOG_ECMA(INFO) << "UnloadPatch success!";
436     return true;
437 }
438 
ClearPatchInfo(JSThread * thread,const CString & patchFileName) const439 void QuickFixLoader::ClearPatchInfo(JSThread *thread, const CString &patchFileName) const
440 {
441     EcmaVM *vm = thread->GetEcmaVM();
442 
443     vm->GetGlobalEnv()->SetGlobalPatch(thread, vm->GetFactory()->EmptyArray());
444 
445     // For release patch constpool and JSPandaFile.
446     vm->CollectGarbage(TriggerGCType::FULL_GC);
447 
448     const JSPandaFile *patchFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(patchFileName);
449     if (patchFile != nullptr) {
450         LOG_ECMA(INFO) << "patch jsPandaFile is not nullptr";
451     }
452 }
453 
ReplaceMethodInner(JSThread * thread,Method * destMethod,MethodLiteral * srcMethodLiteral,JSTaggedValue srcConstpool)454 void QuickFixLoader::ReplaceMethodInner(JSThread *thread,
455                                         Method* destMethod,
456                                         MethodLiteral *srcMethodLiteral,
457                                         JSTaggedValue srcConstpool)
458 {
459     destMethod->SetCallField(srcMethodLiteral->GetCallField());
460     destMethod->SetLiteralInfo(srcMethodLiteral->GetLiteralInfo());
461     destMethod->SetCodeEntryOrLiteral(reinterpret_cast<uintptr_t>(srcMethodLiteral));
462     destMethod->SetExtraLiteralInfo(srcMethodLiteral->GetExtraLiteralInfo());
463     destMethod->SetNativePointerOrBytecodeArray(const_cast<void *>(srcMethodLiteral->GetNativePointer()));
464     destMethod->SetConstantPool(thread, srcConstpool);
465     destMethod->SetProfileTypeInfo(thread, JSTaggedValue::Undefined());
466     destMethod->SetAotCodeBit(false);
467 }
468 
HasNormalMethodReplaced(uint32_t index) const469 bool QuickFixLoader::HasNormalMethodReplaced(uint32_t index) const
470 {
471     if (reservedBaseMethodInfo_.find(index) != reservedBaseMethodInfo_.end()) {
472         return true;
473     }
474     return false;
475 }
476 
HasClassMethodReplaced(uint32_t constpoolIndex,uint32_t literalIndex) const477 bool QuickFixLoader::HasClassMethodReplaced(uint32_t constpoolIndex, uint32_t literalIndex) const
478 {
479     auto iter = reservedBaseClassInfo_.find(constpoolIndex);
480     if (iter != reservedBaseClassInfo_.end()) {
481         auto &classLiteralInfo = iter->second;
482         if (classLiteralInfo.find(literalIndex) != classLiteralInfo.end()) {
483             return true;
484         }
485     }
486     return false;
487 }
488 
InsertBaseClassMethodInfo(uint32_t constpoolIndex,uint32_t literalIndex,MethodLiteral * base)489 void QuickFixLoader::InsertBaseClassMethodInfo(uint32_t constpoolIndex, uint32_t literalIndex, MethodLiteral *base)
490 {
491     auto iter = reservedBaseClassInfo_.find(constpoolIndex);
492     if (iter != reservedBaseClassInfo_.end()) {
493         auto &literalInfo = iter->second;
494         if (literalInfo.find(literalIndex) != literalInfo.end()) {
495             return;
496         }
497         literalInfo.emplace(literalIndex, base);
498     } else {
499         CUnorderedMap<uint32_t, MethodLiteral *> classLiteralInfo {{literalIndex, base}};
500         reservedBaseClassInfo_.emplace(constpoolIndex, classLiteralInfo);
501     }
502 }
503 
CheckIsInvalidPatch(const JSPandaFile * baseFile,const JSPandaFile * patchFile,EcmaVM * vm) const504 bool QuickFixLoader::CheckIsInvalidPatch(const JSPandaFile *baseFile, const JSPandaFile *patchFile, EcmaVM *vm) const
505 {
506     DISALLOW_GARBAGE_COLLECTION;
507 
508     auto thread = vm->GetJSThread();
509     auto moduleManager = vm->GetModuleManager();
510     auto patchRecordInfos = patchFile->GetJSRecordInfo();
511     [[maybe_unused]] auto baseRecordInfos = baseFile->GetJSRecordInfo();
512 
513     for (const auto &patchItem : patchRecordInfos) {
514         const CString &patchRecordName = patchItem.first;
515         CString baseRecordName = patchRecordName;
516         ASSERT(baseRecordInfos.find(baseRecordName) != baseRecordInfos.end());
517 
518         JSHandle<SourceTextModule> patchModule =
519             JSHandle<SourceTextModule>::Cast(moduleManager->ResolveModuleWithMerge(thread,
520             patchFile, patchRecordName));
521         JSHandle<SourceTextModule> baseModule = moduleManager->HostGetImportedModule(baseRecordName);
522 
523         if (CheckIsModuleMismatch(thread, patchModule, baseModule)) {
524             return true;
525         }
526     }
527 
528     return false;
529 }
530 
CheckIsModuleMismatch(JSThread * thread,JSHandle<SourceTextModule> patchModule,JSHandle<SourceTextModule> baseModule)531 bool QuickFixLoader::CheckIsModuleMismatch(JSThread *thread, JSHandle<SourceTextModule> patchModule,
532                                            JSHandle<SourceTextModule> baseModule)
533 {
534     JSTaggedValue patch;
535     JSTaggedValue base;
536 
537     // ImportEntries: array or undefined
538     patch = patchModule->GetImportEntries();
539     base = baseModule->GetImportEntries();
540     if (CheckImportEntriesMismatch(thread, patch, base)) {
541         return true;
542     }
543 
544     // LocalExportEntries: array or LocalExportEntry or undefined
545     patch = patchModule->GetLocalExportEntries();
546     base = baseModule->GetLocalExportEntries();
547     if (CheckLocalExportEntriesMismatch(thread, patch, base)) {
548         return true;
549     }
550 
551     // IndirectExportEntries: array or IndirectExportEntry or undefined
552     patch = patchModule->GetIndirectExportEntries();
553     base = baseModule->GetIndirectExportEntries();
554     if (CheckIndirectExportEntriesMismatch(thread, patch, base)) {
555         return true;
556     }
557 
558     // StarExportEntries: array or StarExportEntry or undefined
559     patch = patchModule->GetStarExportEntries();
560     base = baseModule->GetStarExportEntries();
561     if (CheckStarExportEntriesMismatch(thread, patch, base)) {
562         return true;
563     }
564 
565     return false;
566 }
567 
CheckImportEntriesMismatch(JSThread * thread,JSTaggedValue patch,JSTaggedValue base)568 bool QuickFixLoader::CheckImportEntriesMismatch(JSThread *thread, JSTaggedValue patch, JSTaggedValue base)
569 {
570     if (patch.IsTaggedArray() && base.IsTaggedArray()) {
571         auto patchArr = TaggedArray::Cast(patch);
572         auto baseArr = TaggedArray::Cast(base);
573         uint32_t size = patchArr->GetLength();
574         if (size != baseArr->GetLength()) {
575             LOG_ECMA(ERROR) << "ModuleMismatch of ImportEntries length";
576             return true;
577         }
578         for (uint32_t i = 0; i < size; i++) {
579             auto patchEntry = ImportEntry::Cast(patchArr->Get(thread, i));
580             auto baseEntry = ImportEntry::Cast(baseArr->Get(thread, i));
581             if (CheckImportEntryMismatch(patchEntry, baseEntry)) {
582                 return true;
583             }
584         }
585     } else if (!patch.IsUndefined() || !base.IsUndefined()) {
586         LOG_ECMA(ERROR) << "ModuleMismatch of ImportEntries type";
587         return true;
588     }
589 
590     return false;
591 }
592 
CheckLocalExportEntriesMismatch(JSThread * thread,JSTaggedValue patch,JSTaggedValue base)593 bool QuickFixLoader::CheckLocalExportEntriesMismatch(JSThread *thread, JSTaggedValue patch, JSTaggedValue base)
594 {
595     if (patch.IsTaggedArray() && base.IsTaggedArray()) {
596         auto patchArr = TaggedArray::Cast(patch);
597         auto baseArr = TaggedArray::Cast(base);
598         uint32_t size = patchArr->GetLength();
599         if (size != baseArr->GetLength()) {
600             LOG_ECMA(ERROR) << "ModuleMismatch of LocalExportEntries length";
601             return true;
602         }
603         for (uint32_t i = 0; i < size; i++) {
604             auto patchEntry = LocalExportEntry::Cast(patchArr->Get(thread, i));
605             auto baseEntry = LocalExportEntry::Cast(baseArr->Get(thread, i));
606             if (CheckLocalExportEntryMismatch(patchEntry, baseEntry)) {
607                 return true;
608             }
609         }
610     } else if (patch.IsLocalExportEntry() && base.IsLocalExportEntry()) {
611         auto patchEntry = LocalExportEntry::Cast(patch);
612         auto baseEntry = LocalExportEntry::Cast(base);
613         if (CheckLocalExportEntryMismatch(patchEntry, baseEntry)) {
614             return true;
615         }
616     } else if (!patch.IsUndefined() || !base.IsUndefined()) {
617         LOG_ECMA(ERROR) << "ModuleMismatch of LocalExportEntries type";
618         return true;
619     }
620 
621     return false;
622 }
623 
CheckIndirectExportEntriesMismatch(JSThread * thread,JSTaggedValue patch,JSTaggedValue base)624 bool QuickFixLoader::CheckIndirectExportEntriesMismatch(JSThread *thread, JSTaggedValue patch, JSTaggedValue base)
625 {
626     if (patch.IsTaggedArray() && base.IsTaggedArray()) {
627         auto patchArr = TaggedArray::Cast(patch);
628         auto baseArr = TaggedArray::Cast(base);
629         uint32_t size = patchArr->GetLength();
630         if (size != baseArr->GetLength()) {
631             LOG_ECMA(ERROR) << "ModuleMismatch of IndirectExportEntries length";
632             return true;
633         }
634         for (uint32_t i = 0; i < size; i++) {
635             auto patchEntry = IndirectExportEntry::Cast(patchArr->Get(thread, i));
636             auto baseEntry = IndirectExportEntry::Cast(baseArr->Get(thread, i));
637             if (CheckIndirectExportEntryMismatch(patchEntry, baseEntry)) {
638                 return true;
639             }
640         }
641     } else if (patch.IsIndirectExportEntry() && base.IsIndirectExportEntry()) {
642         auto patchEntry = IndirectExportEntry::Cast(patch);
643         auto baseEntry = IndirectExportEntry::Cast(base);
644         if (CheckIndirectExportEntryMismatch(patchEntry, baseEntry)) {
645             return true;
646         }
647     } else if (!patch.IsUndefined() || !base.IsUndefined()) {
648         LOG_ECMA(ERROR) << "ModuleMismatch of IndirectExportEntries type";
649         return true;
650     }
651 
652     return false;
653 }
654 
CheckStarExportEntriesMismatch(JSThread * thread,JSTaggedValue patch,JSTaggedValue base)655 bool QuickFixLoader::CheckStarExportEntriesMismatch(JSThread *thread, JSTaggedValue patch, JSTaggedValue base)
656 {
657     if (patch.IsTaggedArray() && base.IsTaggedArray()) {
658         auto patchArr = TaggedArray::Cast(patch);
659         auto baseArr = TaggedArray::Cast(base);
660         uint32_t size = patchArr->GetLength();
661         if (size != baseArr->GetLength()) {
662             LOG_ECMA(ERROR) << "ModuleMismatch of StarExportEntries length";
663             return true;
664         }
665         for (uint32_t i = 0; i < size; i++) {
666             auto patchEntry = StarExportEntry::Cast(patchArr->Get(thread, i));
667             auto baseEntry = StarExportEntry::Cast(baseArr->Get(thread, i));
668             if (CheckStarExportEntryMismatch(patchEntry, baseEntry)) {
669                 return true;
670             }
671         }
672     } else if (patch.IsStarExportEntry() && base.IsStarExportEntry()) {
673         auto patchEntry = StarExportEntry::Cast(patch);
674         auto baseEntry = StarExportEntry::Cast(base);
675         if (CheckStarExportEntryMismatch(patchEntry, baseEntry)) {
676             return true;
677         }
678     } else if (!patch.IsUndefined() || !base.IsUndefined()) {
679         LOG_ECMA(ERROR) << "ModuleMismatch of StarExportEntries type";
680         return true;
681     }
682 
683     return false;
684 }
685 
CheckImportEntryMismatch(ImportEntry * patch,ImportEntry * base)686 bool QuickFixLoader::CheckImportEntryMismatch(ImportEntry *patch, ImportEntry *base)
687 {
688     auto patchModuleRequest = EcmaString::Cast(patch->GetModuleRequest());
689     auto baseModuleRequest = EcmaString::Cast(base->GetModuleRequest());
690     auto patchImportName = EcmaString::Cast(patch->GetImportName());
691     auto baseImportName = EcmaString::Cast(base->GetImportName());
692     auto patchLocalName = EcmaString::Cast(patch->GetLocalName());
693     auto baseLocalName = EcmaString::Cast(base->GetLocalName());
694     if (!EcmaStringAccessor::StringsAreEqual(patchModuleRequest, baseModuleRequest)) {
695         LOG_ECMA(ERROR) << "ModuleMismatch of ImportEntry: "
696                         << EcmaStringAccessor(patchModuleRequest).ToStdString()
697                         << " vs "
698                         << EcmaStringAccessor(baseModuleRequest).ToStdString();
699         return true;
700     }
701     if (!EcmaStringAccessor::StringsAreEqual(patchImportName, baseImportName)) {
702         LOG_ECMA(ERROR) << "ModuleMismatch of ImportEntry: "
703                         << EcmaStringAccessor(patchImportName).ToStdString()
704                         << " vs "
705                         << EcmaStringAccessor(baseImportName).ToStdString();
706         return true;
707     }
708     if (!EcmaStringAccessor::StringsAreEqual(patchLocalName, baseLocalName)) {
709         LOG_ECMA(ERROR) << "ModuleMismatch of ImportEntry: "
710                         << EcmaStringAccessor(patchLocalName).ToStdString()
711                         << " vs "
712                         << EcmaStringAccessor(baseLocalName).ToStdString();
713         return true;
714     }
715 
716     return false;
717 }
718 
CheckLocalExportEntryMismatch(LocalExportEntry * patch,LocalExportEntry * base)719 bool QuickFixLoader::CheckLocalExportEntryMismatch(LocalExportEntry *patch, LocalExportEntry *base)
720 {
721     auto patchExportName = EcmaString::Cast(patch->GetExportName());
722     auto baseExportName = EcmaString::Cast(base->GetExportName());
723     auto patchLocalName = EcmaString::Cast(patch->GetLocalName());
724     auto baseLocalName = EcmaString::Cast(base->GetLocalName());
725     if (!EcmaStringAccessor::StringsAreEqual(patchExportName, baseExportName)) {
726         LOG_ECMA(ERROR) << "ModuleMismatch of LocalExportEntry: "
727                         << EcmaStringAccessor(patchExportName).ToStdString()
728                         << " vs "
729                         << EcmaStringAccessor(baseExportName).ToStdString();
730         return true;
731     }
732     if (!EcmaStringAccessor::StringsAreEqual(patchLocalName, baseLocalName)) {
733         LOG_ECMA(ERROR) << "ModuleMismatch of LocalExportEntry: "
734                         << EcmaStringAccessor(patchLocalName).ToStdString()
735                         << " vs "
736                         << EcmaStringAccessor(baseLocalName).ToStdString();
737         return true;
738     }
739 
740     return false;
741 }
742 
CheckIndirectExportEntryMismatch(IndirectExportEntry * patch,IndirectExportEntry * base)743 bool QuickFixLoader::CheckIndirectExportEntryMismatch(IndirectExportEntry *patch, IndirectExportEntry *base)
744 {
745     auto patchExportName = EcmaString::Cast(patch->GetExportName());
746     auto baseExportName = EcmaString::Cast(base->GetExportName());
747     auto patchModuleRequest = EcmaString::Cast(patch->GetModuleRequest());
748     auto baseModuleRequest = EcmaString::Cast(base->GetModuleRequest());
749     auto patchImportName = EcmaString::Cast(patch->GetImportName());
750     auto baseImportName = EcmaString::Cast(base->GetImportName());
751     if (!EcmaStringAccessor::StringsAreEqual(patchExportName, baseExportName)) {
752         LOG_ECMA(ERROR) << "ModuleMismatch of IndirectExportEntry: "
753                         << EcmaStringAccessor(patchExportName).ToStdString()
754                         << " vs "
755                         << EcmaStringAccessor(baseExportName).ToStdString();
756         return true;
757     }
758     if (!EcmaStringAccessor::StringsAreEqual(patchModuleRequest, baseModuleRequest)) {
759         LOG_ECMA(ERROR) << "ModuleMismatch of IndirectExportEntry: "
760                         << EcmaStringAccessor(patchModuleRequest).ToStdString()
761                         << " vs "
762                         << EcmaStringAccessor(baseModuleRequest).ToStdString();
763         return true;
764     }
765     if (!EcmaStringAccessor::StringsAreEqual(patchImportName, baseImportName)) {
766         LOG_ECMA(ERROR) << "ModuleMismatch of IndirectExportEntry: "
767                         << EcmaStringAccessor(patchImportName).ToStdString()
768                         << " vs "
769                         << EcmaStringAccessor(baseImportName).ToStdString();
770         return true;
771     }
772 
773     return false;
774 }
775 
CheckStarExportEntryMismatch(StarExportEntry * patch,StarExportEntry * base)776 bool QuickFixLoader::CheckStarExportEntryMismatch(StarExportEntry *patch, StarExportEntry *base)
777 {
778     auto patchModuleRequest = EcmaString::Cast(patch->GetModuleRequest());
779     auto baseModuleRequest = EcmaString::Cast(base->GetModuleRequest());
780     if (!EcmaStringAccessor::StringsAreEqual(patchModuleRequest, baseModuleRequest)) {
781         LOG_ECMA(ERROR) << "ModuleMismatch of StarExportEntry: "
782                         << EcmaStringAccessor(patchModuleRequest).ToStdString()
783                         << " vs "
784                         << EcmaStringAccessor(baseModuleRequest).ToStdString();
785         return true;
786     }
787 
788     return false;
789 }
790 }  // namespace panda::ecmascript
791