[vm/reload] Make sure classes in saved class table are visited by GC

Previously, IsolateReloadContext::CheckpointClasses() was filling
local_saved_class_table with raw pointers to classes and at the same
time it was adding classes to the set which is based on Array.
After that local_saved_class_table was saved into saved_class_table_
were it becomes visible to GC. However, adding an element to the set
may trigger GC in the middle and pointers for already processed
classes in local_saved_class_table are not visited and are not updated
if GC moves a Class object.

Also, IsolateReloadContext::VisitObjectPointers was not visiting
classes in saved_class_table_ properly as it was interpreting it as
an array of saved_num_cids_ pointers (while it is an array of
ClassAndSize elements).

This change fixes flaky crashes in vm/dart/trigger_gc_in_native_test.

Change-Id: I25b2e50995cf580bc9347f06875d344f75a1a950
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100728
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/class_table.h b/runtime/vm/class_table.h
index fd2bdc1..9404617 100644
--- a/runtime/vm/class_table.h
+++ b/runtime/vm/class_table.h
@@ -36,6 +36,7 @@
   intptr_t size_;
 
   friend class ClassTable;
+  friend class IsolateReloadContext;  // For VisitObjectPointers.
 };
 
 #if defined(ARCH_IS_32_BIT)
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 76e8e9d..6413b63 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -987,25 +987,40 @@
   ClassAndSize* local_saved_class_table = reinterpret_cast<ClassAndSize*>(
       malloc(sizeof(ClassAndSize) * saved_num_cids_));
 
+  // Copy classes into saved_class_table_ first. Make sure there are no
+  // safepoints until saved_class_table_ is filled up and saved so class raw
+  // pointers in saved_class_table_ are properly visited by GC.
+  {
+    NoSafepointScope no_safepoint_scope(Thread::Current());
+
+    for (intptr_t i = 0; i < saved_num_cids_; i++) {
+      if (class_table->IsValidIndex(i) && class_table->HasValidClassAt(i)) {
+        // Copy the class into the saved class table.
+        local_saved_class_table[i] = class_table->PairAt(i);
+      } else {
+        // No class at this index, mark it as NULL.
+        local_saved_class_table[i] = ClassAndSize(NULL);
+      }
+    }
+
+    // Elements of saved_class_table_ are now visible to GC.
+    saved_class_table_ = local_saved_class_table;
+  }
+
+  // Add classes to the set. Set is stored in the Array, so adding an element
+  // may allocate Dart object on the heap and trigger GC.
   Class& cls = Class::Handle();
   UnorderedHashSet<ClassMapTraits> old_classes_set(old_classes_set_storage_);
   for (intptr_t i = 0; i < saved_num_cids_; i++) {
     if (class_table->IsValidIndex(i) && class_table->HasValidClassAt(i)) {
-      // Copy the class into the saved class table and add it to the set.
-      local_saved_class_table[i] = class_table->PairAt(i);
       if (i != kFreeListElement && i != kForwardingCorpse) {
         cls = class_table->At(i);
         bool already_present = old_classes_set.Insert(cls);
         ASSERT(!already_present);
       }
-    } else {
-      // No class at this index, mark it as NULL.
-      local_saved_class_table[i] = ClassAndSize(NULL);
     }
   }
   old_classes_set_storage_ = old_classes_set.Release().raw();
-  // Assigning the field must be done after saving the class table.
-  saved_class_table_ = local_saved_class_table;
   TIR_Print("---- System had %" Pd " classes\n", saved_num_cids_);
 }
 
@@ -1840,8 +1855,10 @@
 void IsolateReloadContext::VisitObjectPointers(ObjectPointerVisitor* visitor) {
   visitor->VisitPointers(from(), to());
   if (saved_class_table_ != NULL) {
-    visitor->VisitPointers(
-        reinterpret_cast<RawObject**>(&saved_class_table_[0]), saved_num_cids_);
+    for (intptr_t i = 0; i < saved_num_cids_; i++) {
+      visitor->VisitPointer(
+          reinterpret_cast<RawObject**>(&(saved_class_table_[i].class_)));
+    }
   }
 }