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