[vm/finalize] Ensure weak finalizers are invoked with isolate group still available.

Fixes https://github.com/dart-lang/sdk/issues/48321

Tested: DartAPI_WeakPersistentHandleCleanupFinalizerAtShutdown
Change-Id: If437dff4e524cb74ac8d0b3c80ec6bb56dd24e84
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/235280
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 73c4f52..4d7f959 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -3633,6 +3633,23 @@
 static Dart_WeakPersistentHandle weak_persistent_handle2;
 static Dart_WeakPersistentHandle weak_persistent_handle3;
 
+static void WeakPersistentHandlePeerCleanupEnsuresIGFinalizer(
+    void* isolate_callback_data,
+    void* peer) {
+  ASSERT(IsolateGroup::Current() != nullptr);
+}
+
+TEST_CASE(DartAPI_WeakPersistentHandleCleanupFinalizerAtShutdown) {
+  const char* kTestString1 = "Test String1";
+  int peer3 = 0;
+  Dart_EnterScope();
+  CHECK_API_SCOPE(thread);
+  Dart_Handle ref3 = Dart_NewStringFromCString(kTestString1);
+  weak_persistent_handle3 = Dart_NewWeakPersistentHandle(
+      ref3, &peer3, 0, WeakPersistentHandlePeerCleanupEnsuresIGFinalizer);
+  Dart_ExitScope();
+}
+
 static void WeakPersistentHandlePeerCleanupFinalizer(
     void* isolate_callback_data,
     void* peer) {
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index c6e70aa..260c293 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -412,10 +412,6 @@
 }
 
 IsolateGroup::~IsolateGroup() {
-  // Finalize any weak persistent handles with a non-null referent.
-  FinalizeWeakPersistentHandlesVisitor visitor(this);
-  api_state()->VisitWeakHandlesUnlocked(&visitor);
-
   // Ensure we destroy the heap before the other members.
   heap_ = nullptr;
   ASSERT(marking_stack_ == nullptr);
@@ -2658,14 +2654,20 @@
   if (shutdown_group) {
     KernelIsolate::NotifyAboutIsolateGroupShutdown(isolate_group);
 
-#if !defined(DART_PRECOMPILED_RUNTIME)
     if (!is_vm_isolate) {
       Thread::EnterIsolateGroupAsHelper(isolate_group, Thread::kUnknownTask,
                                         /*bypass_safepoint=*/false);
+#if !defined(DART_PRECOMPILED_RUNTIME)
       BackgroundCompiler::Stop(isolate_group);
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
+
+      // Finalize any weak persistent handles with a non-null referent with
+      // isolate group still being available.
+      FinalizeWeakPersistentHandlesVisitor visitor(isolate_group);
+      isolate_group->api_state()->VisitWeakHandlesUnlocked(&visitor);
+
       Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/false);
     }
-#endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
     // The "vm-isolate" does not have a thread pool.
     ASSERT(is_vm_isolate == (isolate_group->thread_pool() == nullptr));