1 /**
2  * Copyright (c) 2021-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 
16 #include "runtime/handle_scope-inl.h"
17 #include "runtime/include/mem/allocator.h"
18 #include "runtime/include/mem/panda_containers.h"
19 #include "runtime/include/mem/panda_string.h"
20 #include "runtime/include/thread_scopes.h"
21 #include "runtime/monitor_object_lock.h"
22 #include "runtime/monitor.h"
23 #include "libpandafile/panda_cache.h"
24 
25 #include "runtime/hotreload/hotreload.h"
26 
27 namespace ark::hotreload {
28 
GetHotreloadErrorByFlag(uint32_t flag)29 static Error GetHotreloadErrorByFlag(uint32_t flag)
30 {
31     if (flag == ChangesFlags::F_NO_STRUCT_CHANGES) {
32         return Error::NONE;
33     }
34     if ((flag & ChangesFlags::F_ACCESS_FLAGS) != 0U) {
35         return Error::CLASS_MODIFIERS;
36     }
37     if ((flag & ChangesFlags::F_FIELDS_TYPE) != 0U || (flag & ChangesFlags::F_FIELDS_AMOUNT) != 0U) {
38         return Error::FIELD_CHANGED;
39     }
40     if ((flag & ChangesFlags::F_INHERITANCE) != 0U || (flag & ChangesFlags::F_INTERFACES) != 0U) {
41         return Error::HIERARCHY_CHANGED;
42     }
43     if ((flag & ChangesFlags::F_METHOD_SIGN) != 0U) {
44         return Error::METHOD_SIGN;
45     }
46     if ((flag & ChangesFlags::F_METHOD_ADDED) != 0U) {
47         return Error::METHOD_ADDED;
48     }
49     if ((flag & ChangesFlags::F_METHOD_DELETED) != 0U) {
50         return Error::METHOD_DELETED;
51     }
52 
53     return Error::UNSUPPORTED_CHANGES;
54 }
55 
56 // ---------------------------------------------------------
57 // ------------------ Hotreload Class API ------------------
58 // ---------------------------------------------------------
59 
ReadAndOwnPandaFileFromFile(const char *location)60 const panda_file::File *ArkHotreloadBase::ReadAndOwnPandaFileFromFile(const char *location)
61 {
62     ASSERT_MANAGED_CODE();
63     ASSERT(thread_ == ManagedThread::GetCurrent());
64 
65     std::unique_ptr<const panda_file::File> pf = panda_file::OpenPandaFile(location);
66     const panda_file::File *ptr = pf.get();
67     pandaFiles_.push_back(std::move(pf));
68     return ptr;
69 }
70 
ReadAndOwnPandaFileFromMemory(const void *buffer, size_t buffSize)71 const panda_file::File *ArkHotreloadBase::ReadAndOwnPandaFileFromMemory(const void *buffer, size_t buffSize)
72 {
73     ASSERT_MANAGED_CODE();
74     ASSERT(thread_ == ManagedThread::GetCurrent());
75 
76     std::unique_ptr<const panda_file::File> pf = panda_file::OpenPandaFileFromMemory(buffer, buffSize);
77     auto ptr = pf.get();
78     pandaFiles_.push_back(std::move(pf));
79     return ptr;
80 }
81 
ProcessHotreload()82 Error ArkHotreloadBase::ProcessHotreload()
83 {
84     ASSERT_MANAGED_CODE();
85     ASSERT(thread_ == ManagedThread::GetCurrent());
86 
87     Error err = ValidateClassesHotreloadPossibility();
88     if (err != Error::NONE) {
89         return err;
90     }
91 
92     for (auto &hCls : classes_) {
93         auto changesType = RecognizeHotreloadType(&hCls);
94         if (changesType == Type::STRUCTURAL) {
95             LOG(ERROR, HOTRELOAD) << "Class " << hCls.className_
96                                   << " has structural changes. Structural changes is unsafe for hotreload.";
97             return GetHotreloadErrorByFlag(hCls.fChanges);
98         }
99     }
100 
101     /*
102      * On this point all classes were verified and hotreload recognized like possible
103      * From now all classes should be reloaded anyway and hotreload cannot be reversed
104      */
105     {
106         ScopedSuspendAllThreadsRunning ssat(PandaVM::GetCurrent()->GetRendezvous());
107         ASSERT_MANAGED_CODE();
108         if (Runtime::GetCurrent()->IsJitEnabled()) {
109             /*
110              * In case JIT is enabled while hotreloading we must suspend it and clear compiled methods
111              * Currently JIT is not running with hotreload
112              */
113             UNREACHABLE();
114         }
115         ASSERT(thread_->GetVM()->GetThreadManager() != nullptr);
116         PandaVM::GetCurrent()->GetThreadManager()->EnumerateThreads([this](ManagedThread *thread) {
117             (void)this;  // [[maybe_unused]] in lambda capture list is not possible
118             ASSERT(thread->GetThreadLang() == lang_);
119             thread->GetInterpreterCache()->Clear();
120             return true;
121         });
122         for (auto &hCls : classes_) {
123             ReloadClassNormal(&hCls);
124         }
125 
126         auto classLinker = Runtime::GetCurrent()->GetClassLinker();
127         // Updating VTable should be before adding obsolete classes for performance reason
128         UpdateVtablesInRuntimeClasses(classLinker);
129         AddLoadedPandaFilesToRuntime(classLinker);
130         AddObsoleteClassesToRuntime(classLinker);
131 
132         LangSpecificHotreloadPart();
133     }
134     return Error::NONE;
135 }
136 
ArkHotreloadBase(ManagedThread *mthread, panda_file::SourceLang lang)137 ArkHotreloadBase::ArkHotreloadBase(ManagedThread *mthread, panda_file::SourceLang lang) : lang_(lang), thread_(mthread)
138 {
139     /*
140      * Scoped object that switch to managed code is language dependent
141      * So is should be constructed in superclass
142      */
143     ASSERT_NATIVE_CODE();
144     ASSERT(thread_ != nullptr);
145     ASSERT(thread_ == ManagedThread::GetCurrent());
146     ASSERT(lang_ == thread_->GetThreadLang());
147 }
148 
149 /* virtual */
~ArkHotreloadBase()150 ArkHotreloadBase::~ArkHotreloadBase()
151 {
152     ASSERT_NATIVE_CODE();
153     ASSERT(thread_ == ManagedThread::GetCurrent());
154 
155     auto classLinker = Runtime::GetCurrent()->GetClassLinker();
156     for (auto &hCls : classes_) {
157         if (hCls.tmpClass != nullptr) {
158             classLinker->FreeClass(hCls.tmpClass);
159         }
160     }
161 }
162 
163 // ---------------------------------------------------------
164 // ----------------------- Validators ----------------------
165 // ---------------------------------------------------------
166 
ValidateClassesHotreloadPossibility()167 Error ArkHotreloadBase::ValidateClassesHotreloadPossibility()
168 {
169     ASSERT_MANAGED_CODE();
170     ASSERT(thread_ == ManagedThread::GetCurrent());
171 
172     auto err = this->LangSpecificValidateClasses();
173     if (err != Error::NONE) {
174         return err;
175     }
176 
177     for (const auto &hCls : classes_) {
178         Error returnErr = ValidateClassForHotreload(hCls);
179         if (returnErr != Error::NONE) {
180             return returnErr;
181         }
182     }
183 
184     return Error::NONE;
185 }
186 
GetLockOwnerThreadId(Class *cls)187 std::optional<uint32_t> GetLockOwnerThreadId(Class *cls)
188 {
189     if (Monitor::HoldsLock(cls->GetManagedObject()) != 0U) {
190         return Monitor::GetLockOwnerOsThreadID(cls->GetManagedObject());
191     }
192     return {};
193 }
194 
ValidateClassForHotreload(const ClassContainment &hCls)195 Error ArkHotreloadBase::ValidateClassForHotreload(const ClassContainment &hCls)
196 {
197     Class *clazz = hCls.tmpClass;
198     Class *runtimeClass = hCls.loadedClass;
199     if (clazz == nullptr) {
200         LOG(ERROR, HOTRELOAD) << "Class " << hCls.className_ << " are failed to be initialized";
201         return Error::INTERNAL;
202     }
203 
204     /*
205      * Check if class is not resolved
206      * In case class have a lock holded by the same thread it will lead to deadlock
207      * In case class is not initialized we should initialize it before hotreloading
208      */
209     if (!runtimeClass->IsInitialized()) {
210         if (GetLockOwnerThreadId(runtimeClass) == thread_->GetId()) {
211             LOG(ERROR, HOTRELOAD) << "Class lock " << hCls.className_ << " are already owned by this thread.";
212             return Error::INTERNAL;
213         }
214         auto classLinker = Runtime::GetCurrent()->GetClassLinker();
215         if (!classLinker->InitializeClass(thread_, runtimeClass)) {
216             LOG(ERROR, HOTRELOAD) << "Class " << hCls.className_ << " cannot be initialized in runtime.";
217             return Error::INTERNAL;
218         }
219     }
220 
221     if (clazz->IsInterface()) {
222         LOG(ERROR, HOTRELOAD) << "Class " << hCls.className_ << " is an interface class. Cannot modify.";
223         return Error::CLASS_UNMODIFIABLE;
224     }
225     if (clazz->IsProxy()) {
226         LOG(ERROR, HOTRELOAD) << "Class " << hCls.className_ << " is a proxy class. Cannot modify.";
227         return Error::CLASS_UNMODIFIABLE;
228     }
229     if (clazz->IsArrayClass()) {
230         LOG(ERROR, HOTRELOAD) << "Class " << hCls.className_ << " is an array class. Cannot modify.";
231         return Error::CLASS_UNMODIFIABLE;
232     }
233     if (clazz->IsStringClass()) {
234         LOG(ERROR, HOTRELOAD) << "Class " << hCls.className_ << " is a string class. Cannot modify.";
235         return Error::CLASS_UNMODIFIABLE;
236     }
237     if (clazz->IsPrimitive()) {
238         LOG(ERROR, HOTRELOAD) << "Class " << hCls.className_ << " is a primitive class. Cannot modify.";
239         return Error::CLASS_UNMODIFIABLE;
240     }
241 
242     return Error::NONE;
243 }
244 
RecognizeHotreloadType(ClassContainment *hCls)245 Type ArkHotreloadBase::RecognizeHotreloadType(ClassContainment *hCls)
246 {
247     /*
248      * Checking for the changes:
249      *  - Inheritance changed
250      *  - Access flags changed
251      *  - Field added/deleted
252      *  - Field type changed
253      *  - Method added/deleted
254      *  - Method signature changed
255      *
256      * In case there are any of changes above Type is Structural
257      * Otherwise it's normal changes
258      */
259     if (InheritanceChangesCheck(hCls) == Type::STRUCTURAL) {
260         return Type::STRUCTURAL;
261     }
262     if (FlagsChangesCheck(hCls) == Type::STRUCTURAL) {
263         return Type::STRUCTURAL;
264     }
265     if (FieldChangesCheck(hCls) == Type::STRUCTURAL) {
266         return Type::STRUCTURAL;
267     }
268     if (MethodChangesCheck(hCls) == Type::STRUCTURAL) {
269         return Type::STRUCTURAL;
270     }
271 
272     return Type::NORMAL;
273 }
274 
InheritanceChangesCheck(ClassContainment *hCls)275 Type ArkHotreloadBase::InheritanceChangesCheck(ClassContainment *hCls)
276 {
277     Class *tmpClass = hCls->tmpClass;
278     Class *runtimeClass = hCls->loadedClass;
279     if (tmpClass->GetBase() != runtimeClass->GetBase()) {
280         hCls->fChanges |= ChangesFlags::F_INHERITANCE;
281         return Type::STRUCTURAL;
282     }
283 
284     auto newIfaces = tmpClass->GetInterfaces();
285     auto oldIfaces = runtimeClass->GetInterfaces();
286     if (newIfaces.size() != oldIfaces.size()) {
287         hCls->fChanges |= ChangesFlags::F_INTERFACES;
288         return Type::STRUCTURAL;
289     }
290 
291     PandaUnorderedSet<Class *> ifaces;
292     for (auto iface : oldIfaces) {
293         ifaces.insert(iface);
294     }
295     for (auto iface : newIfaces) {
296         if (ifaces.find(iface) == ifaces.end()) {
297             hCls->fChanges |= ChangesFlags::F_INTERFACES;
298             return Type::STRUCTURAL;
299         }
300         ifaces.erase(iface);
301     }
302     if (!ifaces.empty()) {
303         hCls->fChanges |= ChangesFlags::F_INTERFACES;
304         return Type::STRUCTURAL;
305     }
306 
307     return Type::NORMAL;
308 }
309 
FlagsChangesCheck(ClassContainment *hCls)310 Type ArkHotreloadBase::FlagsChangesCheck(ClassContainment *hCls)
311 {
312     Class *tmpClass = hCls->tmpClass;
313     Class *runtimeClass = hCls->loadedClass;
314 
315     // NOTE(m.strizhak) research that maybe there are flags that can be changed keeping normal type
316     if (tmpClass->GetFlags() != runtimeClass->GetFlags()) {
317         hCls->fChanges |= ChangesFlags::F_ACCESS_FLAGS;
318         return Type::STRUCTURAL;
319     }
320 
321     if (tmpClass->GetAccessFlags() != runtimeClass->GetAccessFlags()) {
322         hCls->fChanges |= ChangesFlags::F_ACCESS_FLAGS;
323         return Type::STRUCTURAL;
324     }
325 
326     return Type::NORMAL;
327 }
328 
FieldChangesCheck(ClassContainment *hCls)329 Type ArkHotreloadBase::FieldChangesCheck(ClassContainment *hCls)
330 {
331     Class *tmpClass = hCls->tmpClass;
332     Class *runtimeClass = hCls->loadedClass;
333 
334     auto oldFields = runtimeClass->GetFields();
335     auto newFields = tmpClass->GetFields();
336     if (newFields.size() != oldFields.size()) {
337         hCls->fChanges |= ChangesFlags::F_FIELDS_AMOUNT;
338         return Type::STRUCTURAL;
339     }
340 
341     FieldIdTable fieldIdTable;
342     PandaUnorderedMap<PandaString, Field *> fieldsTable;
343     for (auto &oldField : oldFields) {
344         PandaString fieldName(utf::Mutf8AsCString(oldField.GetName().data));
345         fieldsTable.insert({fieldName, &oldField});
346     }
347 
348     for (auto &newField : newFields) {
349         PandaString fieldName(utf::Mutf8AsCString(newField.GetName().data));
350         auto oldIt = fieldsTable.find(fieldName);
351         if (oldIt == fieldsTable.end()) {
352             hCls->fChanges |= ChangesFlags::F_FIELDS_AMOUNT;
353             return Type::STRUCTURAL;
354         }
355 
356         if (oldIt->second->GetAccessFlags() != newField.GetAccessFlags() ||
357             oldIt->second->GetTypeId() != newField.GetTypeId()) {
358             hCls->fChanges |= ChangesFlags::F_FIELDS_TYPE;
359             return Type::STRUCTURAL;
360         }
361 
362         fieldIdTable[fieldsTable[fieldName]->GetFileId()] = newField.GetFileId();
363         fieldsTable.erase(fieldName);
364     }
365 
366     fieldsTables_[runtimeClass] = std::move(fieldIdTable);
367 
368     if (!fieldsTable.empty()) {
369         hCls->fChanges |= ChangesFlags::F_FIELDS_AMOUNT;
370         return Type::STRUCTURAL;
371     }
372 
373     return Type::NORMAL;
374 }
375 
GetFileAccessFlags(const Method &method)376 static inline uint32_t GetFileAccessFlags(const Method &method)
377 {
378     return method.GetAccessFlags() & ACC_FILE_MASK;
379 }
380 
MethodChangesCheck(ClassContainment *hCls)381 Type ArkHotreloadBase::MethodChangesCheck(ClassContainment *hCls)
382 {
383     Class *tmpClass = hCls->tmpClass;
384     Class *runtimeClass = hCls->loadedClass;
385 
386     auto oldMethods = runtimeClass->GetMethods();
387     auto newMethods = tmpClass->GetMethods();
388     if (newMethods.size() > oldMethods.size() ||
389         tmpClass->GetNumVirtualMethods() > runtimeClass->GetNumVirtualMethods()) {
390         hCls->fChanges |= ChangesFlags::F_METHOD_ADDED;
391         return Type::STRUCTURAL;
392     }
393 
394     if (newMethods.size() < oldMethods.size() ||
395         tmpClass->GetNumVirtualMethods() < runtimeClass->GetNumVirtualMethods()) {
396         hCls->fChanges |= ChangesFlags::F_METHOD_DELETED;
397         return Type::STRUCTURAL;
398     }
399 
400     for (auto &newMethod : newMethods) {
401         bool isNameFound = false;
402         bool isExactFound = false;
403         for (auto &oldMethod : oldMethods) {
404             PandaString oldName = utf::Mutf8AsCString(oldMethod.GetName().data);
405             PandaString newName = utf::Mutf8AsCString(newMethod.GetName().data);
406             if (oldName != newName) {
407                 continue;
408             }
409             isNameFound = true;
410             if (oldMethod.GetProto() == newMethod.GetProto() &&
411                 GetFileAccessFlags(oldMethod) == GetFileAccessFlags(newMethod)) {
412                 methodsTable_[&oldMethod] = &newMethod;
413                 isExactFound = true;
414                 break;
415             }
416         }
417 
418         if (isNameFound) {
419             if (isExactFound) {
420                 continue;
421             }
422             hCls->fChanges |= ChangesFlags::F_METHOD_SIGN;
423             return Type::STRUCTURAL;
424         }
425         hCls->fChanges |= ChangesFlags::F_METHOD_ADDED;
426         return Type::STRUCTURAL;
427     }
428     return Type::NORMAL;
429 }
430 
431 // ---------------------------------------------------------
432 // ----------------------- Reloaders -----------------------
433 // ---------------------------------------------------------
434 
435 // This method is used under assert. So in release build it's unused
VerifyClassConsistency(const Class *cls)436 [[maybe_unused]] static bool VerifyClassConsistency(const Class *cls)
437 {
438     ASSERT(cls);
439 
440     const auto pf = cls->GetPandaFile();
441     auto methods = cls->GetMethods();
442     auto fields = cls->GetFields();
443     const uint8_t *descriptor = cls->GetDescriptor();
444 
445     if (cls->GetFileId().GetOffset() != pf->GetClassId(descriptor).GetOffset()) {
446         return false;
447     }
448 
449     for (const auto &method : methods) {
450         panda_file::MethodDataAccessor mda(*pf, method.GetFileId());
451         panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
452         if (mda.GetName() != method.GetName()) {
453             return false;
454         }
455         if (method.GetClass() != cls) {
456             return false;
457         }
458     }
459 
460     for (const auto &field : fields) {
461         panda_file::FieldDataAccessor fda(*pf, field.GetFileId());
462         if (field.GetClass() != cls) {
463             return false;
464         }
465     }
466 
467     return true;
468 }
469 
UpdateClassPtrInMethods(Span<Method> methods, Class *cls)470 static void UpdateClassPtrInMethods(Span<Method> methods, Class *cls)
471 {
472     for (auto &method : methods) {
473         method.SetClass(cls);
474     }
475 }
476 
UpdateClassPtrInFields(Span<Field> fields, Class *cls)477 static void UpdateClassPtrInFields(Span<Field> fields, Class *cls)
478 {
479     for (auto &field : fields) {
480         field.SetClass(cls);
481     }
482 }
483 
UpdatePandaFileInClass(Class *runtimeClass, const panda_file::File *pf)484 static void UpdatePandaFileInClass(Class *runtimeClass, const panda_file::File *pf)
485 {
486     const uint8_t *descriptor = runtimeClass->GetDescriptor();
487     panda_file::File::EntityId classId = pf->GetClassId(descriptor);
488     runtimeClass->SetPandaFile(pf);
489     runtimeClass->SetFileId(classId);
490     runtimeClass->SetClassIndex(pf->GetClassIndex(classId));
491     runtimeClass->SetMethodIndex(pf->GetMethodIndex(classId));
492     runtimeClass->SetFieldIndex(pf->GetFieldIndex(classId));
493 }
494 
495 /*
496  * Obsolete methods should be saved by temporary class 'cause it might be continue executing
497  * Updating class pointers in methods to keep it consistent with class and panda file
498  */
UpdateMethods(Class *runtimeClass, Class *tmpClass)499 static void UpdateMethods(Class *runtimeClass, Class *tmpClass)
500 {
501     auto newMethods = tmpClass->GetMethodsWithCopied();
502     auto oldMethods = runtimeClass->GetMethodsWithCopied();
503     uint32_t numVmethods = tmpClass->GetNumVirtualMethods();
504     uint32_t numCmethods = tmpClass->GetNumCopiedMethods();
505     uint32_t numSmethods = newMethods.size() - numVmethods - numCmethods;
506     UpdateClassPtrInMethods(newMethods, runtimeClass);
507     UpdateClassPtrInMethods(oldMethods, tmpClass);
508     runtimeClass->SetMethods(newMethods, numVmethods, numSmethods);
509     tmpClass->SetMethods(oldMethods, numVmethods, numSmethods);
510 }
511 
512 /*
513  * Obsolete fields should be saved by temporary class 'cause it might be used by obselete methods
514  * Updating class pointers in fields to keep it consistent with class and panda file
515  */
UpdateFields(Class *runtimeClass, Class *tmpClass)516 static void UpdateFields(Class *runtimeClass, Class *tmpClass)
517 {
518     auto newFields = tmpClass->GetFields();
519     auto oldFields = runtimeClass->GetFields();
520     uint32_t numSfields = tmpClass->GetNumStaticFields();
521     UpdateClassPtrInFields(newFields, runtimeClass);
522     UpdateClassPtrInFields(oldFields, tmpClass);
523     runtimeClass->SetFields(newFields, numSfields);
524     tmpClass->SetFields(oldFields, numSfields);
525 }
526 
UpdateIfaces(Class *runtimeClass, Class *tmpClass)527 static void UpdateIfaces(Class *runtimeClass, Class *tmpClass)
528 {
529     auto newIfaces = tmpClass->GetInterfaces();
530     auto oldIfaces = runtimeClass->GetInterfaces();
531     runtimeClass->SetInterfaces(newIfaces);
532     tmpClass->SetInterfaces(oldIfaces);
533 }
534 
535 /*
536  * Tables is being copied to runtime class to be possible to resolve virtual calls
537  * No need to copy tables to temporary class 'cause new virtual calls should be resolved from runtime class
538  */
UpdateTables(Class *runtimeClass, Class *tmpClass)539 static void UpdateTables(Class *runtimeClass, Class *tmpClass)
540 {
541     ASSERT(tmpClass->GetIMTSize() == runtimeClass->GetIMTSize());
542     ASSERT(tmpClass->GetVTableSize() == runtimeClass->GetVTableSize());
543     ITable oldItable = runtimeClass->GetITable();
544     Span<Method *> oldVtable = runtimeClass->GetVTable();
545     Span<Method *> newVtable = tmpClass->GetVTable();
546     Span<Method *> oldImt = runtimeClass->GetIMT();
547     Span<Method *> newImt = tmpClass->GetIMT();
548     runtimeClass->SetITable(tmpClass->GetITable());
549     tmpClass->SetITable(oldItable);
550     if (!oldVtable.empty() && memcpy_s(oldVtable.begin(), oldVtable.size() * sizeof(void *), newVtable.begin(),
551                                        oldVtable.size() * sizeof(void *)) != EOK) {
552         LOG(FATAL, RUNTIME) << __func__ << " memcpy_s failed";
553     }
554     if (!oldImt.empty() && memcpy_s(oldImt.begin(), oldImt.size() * sizeof(void *), newImt.begin(),
555                                     oldImt.size() * sizeof(void *)) != EOK) {
556         LOG(FATAL, RUNTIME) << __func__ << " memcpy_s failed";
557     }
558 }
559 
ReloadClassNormal(const ClassContainment *hCls)560 void ArkHotreloadBase::ReloadClassNormal(const ClassContainment *hCls)
561 {
562     ASSERT(thread_->GetVM()->GetThreadManager() != nullptr);
563     ASSERT(!thread_->GetVM()->GetThreadManager()->IsRunningThreadExist());
564 
565     /*
566      * Take class loading lock
567      * Clear interpreter cache
568      * Update runtime class header:
569      *   - Panda file and its id
570      *   - methods
571      *   - fields
572      *   - ifaces
573      *   - tables
574      *
575      * Then adding obsolete classes to special area in class linker
576      * to be able to continue executing obsolete methods after hotreloading
577      */
578     Class *tmpClass = hCls->tmpClass;
579     Class *runtimeClass = hCls->loadedClass;
580 
581     // Locking class
582     HandleScope<ObjectHeader *> scope(thread_);
583     VMHandle<ObjectHeader> managedClassObjHandle(thread_, runtimeClass->GetManagedObject());
584     ::ark::ObjectLock lock(managedClassObjHandle.GetPtr());
585     const panda_file::File *newPf = hCls->pf;
586     const panda_file::File *oldPf = runtimeClass->GetPandaFile();
587 
588     oldPf->GetPandaCache()->Clear();
589 
590     reloadedClasses_.insert(runtimeClass);
591     UpdatePandaFileInClass(tmpClass, oldPf);
592     UpdatePandaFileInClass(runtimeClass, newPf);
593     UpdateMethods(runtimeClass, tmpClass);
594     UpdateFields(runtimeClass, tmpClass);
595     UpdateIfaces(runtimeClass, tmpClass);
596     UpdateTables(runtimeClass, tmpClass);
597 
598     ASSERT(VerifyClassConsistency(runtimeClass));
599     ASSERT(VerifyClassConsistency(tmpClass));
600 }
601 
UpdateVtablesInRuntimeClasses(ClassLinker *classLinker)602 void ArkHotreloadBase::UpdateVtablesInRuntimeClasses(ClassLinker *classLinker)
603 {
604     ASSERT(thread_->GetVM()->GetThreadManager() != nullptr);
605     ASSERT(!thread_->GetVM()->GetThreadManager()->IsRunningThreadExist());
606 
607     auto updateVtable = [this](Class *cls) {
608         auto vtable = cls->GetVTable();
609 
610         if (reloadedClasses_.find(cls) != reloadedClasses_.end()) {
611             // This table is already actual
612             return true;
613         }
614 
615         for (auto &methodPtr : vtable) {
616             if (methodsTable_.find(methodPtr) != methodsTable_.end()) {
617                 methodPtr = methodsTable_[methodPtr];
618             }
619         }
620         return true;
621     };
622     classLinker->GetExtension(lang_)->EnumerateClasses(updateVtable);
623 }
624 
AddLoadedPandaFilesToRuntime(ClassLinker *classLinker)625 void ArkHotreloadBase::AddLoadedPandaFilesToRuntime(ClassLinker *classLinker)
626 {
627     ASSERT(thread_->GetVM()->GetThreadManager() != nullptr);
628     ASSERT(!thread_->GetVM()->GetThreadManager()->IsRunningThreadExist());
629 
630     for (auto &ptrPf : pandaFiles_) {
631         classLinker->AddPandaFile(std::move(ptrPf));
632     }
633     pandaFiles_.clear();
634 }
635 
AddObsoleteClassesToRuntime(ClassLinker *classLinker)636 void ArkHotreloadBase::AddObsoleteClassesToRuntime(ClassLinker *classLinker)
637 {
638     ASSERT(thread_->GetVM()->GetThreadManager() != nullptr);
639     ASSERT(!thread_->GetVM()->GetThreadManager()->IsRunningThreadExist());
640 
641     /*
642      * Sending all classes in one vector to avoid holding lock for every single class
643      * It should be faster because all threads are still stopped
644      */
645     PandaVector<Class *> obsoleteClasses;
646     for (const auto &hCls : classes_) {
647         if (hCls.tmpClass != nullptr) {
648             ASSERT(hCls.tmpClass->GetSourceLang() == lang_);
649             obsoleteClasses.push_back(hCls.tmpClass);
650         }
651     }
652     classLinker->GetExtension(lang_)->AddObsoleteClass(obsoleteClasses);
653     classes_.clear();
654 }
655 
656 }  // namespace ark::hotreload
657