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