Version 2.12.0-15.0.dev

Merge commit '52783837369de45d3372cb6c6b7cdd63e71cd829' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index abe1134..4fe4dce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,16 @@
 
 ### Dart VM
 
+*   **Breaking Change** [#42312][]: `Dart_WeakPersistentHandle`s will no longer
+    auto-delete themselves when the referenced object is garbage collected to
+    avoid race conditions, but they are still automatically deleted when the
+    isolate group shuts down.
+*   **Breaking Change** [#42312][]: `Dart_WeakPersistentHandleFinalizer`
+    is renamed to `Dart_HandleFinalizer` and had its `handle` argument removed.
+    All api functions using that type have been updated.
+
+[#42312]: https://github.com/dart-lang/sdk/issues/42312
+
 ### Dart2JS
 
 * Removed `--no-defer-class-types` and `--no-new-deferred-split`.
diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc
index a47775c..4e0f338 100644
--- a/runtime/bin/dartutils.cc
+++ b/runtime/bin/dartutils.cc
@@ -905,11 +905,10 @@
   return cobject;
 }
 
-Dart_CObject* CObject::NewExternalUint8Array(
-    intptr_t length,
-    uint8_t* data,
-    void* peer,
-    Dart_WeakPersistentHandleFinalizer callback) {
+Dart_CObject* CObject::NewExternalUint8Array(intptr_t length,
+                                             uint8_t* data,
+                                             void* peer,
+                                             Dart_HandleFinalizer callback) {
   Dart_CObject* cobject = New(Dart_CObject_kExternalTypedData);
   cobject->value.as_external_typed_data.type = Dart_TypedData_kUint8;
   cobject->value.as_external_typed_data.length = length;
@@ -937,7 +936,7 @@
 void CObject::FreeIOBufferData(Dart_CObject* cobject) {
   ASSERT(cobject->type == Dart_CObject_kExternalTypedData);
   cobject->value.as_external_typed_data.callback(
-      NULL, NULL, cobject->value.as_external_typed_data.peer);
+      NULL, cobject->value.as_external_typed_data.peer);
   cobject->value.as_external_typed_data.data = NULL;
 }
 
diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h
index 65ed5ae..66da23f 100644
--- a/runtime/bin/dartutils.h
+++ b/runtime/bin/dartutils.h
@@ -344,11 +344,10 @@
   static Dart_CObject* NewArray(intptr_t length);
   static Dart_CObject* NewUint8Array(intptr_t length);
   static Dart_CObject* NewUint32Array(intptr_t length);
-  static Dart_CObject* NewExternalUint8Array(
-      intptr_t length,
-      uint8_t* data,
-      void* peer,
-      Dart_WeakPersistentHandleFinalizer callback);
+  static Dart_CObject* NewExternalUint8Array(intptr_t length,
+                                             uint8_t* data,
+                                             void* peer,
+                                             Dart_HandleFinalizer callback);
 
   static Dart_CObject* NewIOBuffer(int64_t length);
   static void FreeIOBufferData(Dart_CObject* object);
@@ -571,7 +570,7 @@
   }
   uint8_t* Data() const { return cobject_->value.as_external_typed_data.data; }
   void* Peer() const { return cobject_->value.as_external_typed_data.peer; }
-  Dart_WeakPersistentHandleFinalizer Callback() const {
+  Dart_HandleFinalizer Callback() const {
     return cobject_->value.as_external_typed_data.callback;
   }
 
diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
index e5762770..ace3747 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
@@ -475,7 +475,7 @@
 
 Dart_Port send_port_;
 
-static void FreeFinalizer(void*, Dart_WeakPersistentHandle, void* value) {
+static void FreeFinalizer(void*, void* value) {
   free(value);
 }
 
@@ -776,9 +776,7 @@
 // vmspecific_handle_test.dart (statically linked).
 // vmspecific_handle_dynamically_linked_test.dart (dynamically linked).
 
-static void RunFinalizer(void* isolate_callback_data,
-                         Dart_WeakPersistentHandle handle,
-                         void* peer) {
+static void RunFinalizer(void* isolate_callback_data, void* peer) {
   printf("Running finalizer for weak handle.\n");
 }
 
diff --git a/runtime/bin/io_buffer.h b/runtime/bin/io_buffer.h
index 2df3086..79b4fb9 100644
--- a/runtime/bin/io_buffer.h
+++ b/runtime/bin/io_buffer.h
@@ -25,9 +25,7 @@
   static void Free(void* buffer) { free(buffer); }
 
   // Function for finalizing external byte arrays used as IO buffers.
-  static void Finalizer(void* isolate_callback_data,
-                        Dart_WeakPersistentHandle handle,
-                        void* buffer) {
+  static void Finalizer(void* isolate_callback_data, void* buffer) {
     Free(buffer);
   }
 
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index d4f0e53..db0e630 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -244,8 +244,9 @@
  * the object is garbage collected. It is never safe to use these handles
  * unless you know the object is still reachable.
  *
- * WeakPersistentHandles are persistent handles which are auto deleted
- * when the object is garbage collected.
+ * WeakPersistentHandles are persistent handles which are automatically set
+ * to point Dart_Null when the object is garbage collected. They are not auto
+ * deleted, so it is safe to use them after the object has become unreachable.
  */
 typedef struct _Dart_Handle* Dart_Handle;
 typedef Dart_Handle Dart_PersistentHandle;
@@ -254,10 +255,6 @@
 // These structs are versioned by DART_API_DL_MAJOR_VERSION, bump the
 // version when changing this struct.
 
-typedef void (*Dart_WeakPersistentHandleFinalizer)(
-    void* isolate_callback_data,
-    Dart_WeakPersistentHandle handle,
-    void* peer);
 typedef void (*Dart_HandleFinalizer)(void* isolate_callback_data, void* peer);
 
 /**
@@ -423,6 +420,8 @@
 
 /**
  * Allocates a handle in the current scope from a weak persistent handle.
+ *
+ * This will be a handle to Dart_Null if the object has been garbage collected.
  */
 DART_EXPORT Dart_Handle
 Dart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object);
@@ -461,24 +460,18 @@
 /**
  * Allocates a weak persistent handle for an object.
  *
- * This handle has the lifetime of the current isolate unless the object
- * pointed to by the handle is garbage collected, in this case the VM
- * automatically deletes the handle after invoking the callback associated
- * with the handle. The handle can also be explicitly deallocated by
- * calling Dart_DeleteWeakPersistentHandle.
+ * This handle has the lifetime of the current isolate. The handle can also be
+ * explicitly deallocated by calling Dart_DeleteWeakPersistentHandle.
  *
- * If the object becomes unreachable the callback is invoked with the weak
- * persistent handle and the peer as arguments. The callback can be executed on
- * any thread, will have an isolate group, but will not have a current isolate.
- * The callback can only call Dart_DeletePersistentHandle or
- * Dart_DeleteWeakPersistentHandle. The callback must not call
- * Dart_DeleteWeakPersistentHandle for the handle being finalized, as it is
- * automatically deleted by the VM after the callback returns. This gives the
- * embedder the ability to cleanup data associated with the object and clear
- * out any cached references to the handle. All references to this handle after
- * the callback will be invalid. It is illegal to call into the VM with any
- * other Dart_* functions from the callback. If the handle is deleted before
- * the object becomes unreachable, the callback is never invoked.
+ * If the object becomes unreachable the callback is invoked with the peer as
+ * argument. The callback can be executed on any thread, will have a current
+ * isolate group, but will not have a current isolate. The callback can only
+ * call Dart_DeletePersistentHandle or Dart_DeleteWeakPersistentHandle. This
+ * gives the embedder the ability to cleanup data associated with the object.
+ * The handle will point to the Dart_Null object after the finalizer has been
+ * run. It is illegal to call into the VM with any other Dart_* functions from
+ * the callback. If the handle is deleted before the object becomes
+ * unreachable, the callback is never invoked.
  *
  * Requires there to be a current isolate.
  *
@@ -498,7 +491,7 @@
 Dart_NewWeakPersistentHandle(Dart_Handle object,
                              void* peer,
                              intptr_t external_allocation_size,
-                             Dart_WeakPersistentHandleFinalizer callback);
+                             Dart_HandleFinalizer callback);
 
 /**
  * Deletes the given weak persistent [object] handle.
@@ -2046,7 +2039,7 @@
                              intptr_t length,
                              void* peer,
                              intptr_t external_allocation_size,
-                             Dart_WeakPersistentHandleFinalizer callback);
+                             Dart_HandleFinalizer callback);
 
 /**
  * Returns a String which references an external array of UTF-16 encoded
@@ -2067,7 +2060,7 @@
                             intptr_t length,
                             void* peer,
                             intptr_t external_allocation_size,
-                            Dart_WeakPersistentHandleFinalizer callback);
+                            Dart_HandleFinalizer callback);
 
 /**
  * Gets the C string representation of a String.
@@ -2440,13 +2433,13 @@
  * \return The TypedData object if no error occurs. Otherwise returns
  *   an error handle.
  */
-DART_EXPORT Dart_Handle Dart_NewExternalTypedDataWithFinalizer(
-    Dart_TypedData_Type type,
-    void* data,
-    intptr_t length,
-    void* peer,
-    intptr_t external_allocation_size,
-    Dart_WeakPersistentHandleFinalizer callback);
+DART_EXPORT Dart_Handle
+Dart_NewExternalTypedDataWithFinalizer(Dart_TypedData_Type type,
+                                       void* data,
+                                       intptr_t length,
+                                       void* peer,
+                                       intptr_t external_allocation_size,
+                                       Dart_HandleFinalizer callback);
 
 /**
  * Returns a ByteBuffer object for the typed data.
diff --git a/runtime/include/dart_api_dl.h b/runtime/include/dart_api_dl.h
index 391f441..0854d71 100644
--- a/runtime/include/dart_api_dl.h
+++ b/runtime/include/dart_api_dl.h
@@ -84,7 +84,7 @@
   F(Dart_DeletePersistentHandle, void, (Dart_PersistentHandle object))         \
   F(Dart_NewWeakPersistentHandle, Dart_WeakPersistentHandle,                   \
     (Dart_Handle object, void* peer, intptr_t external_allocation_size,        \
-     Dart_WeakPersistentHandleFinalizer callback))                             \
+     Dart_HandleFinalizer callback))                                           \
   F(Dart_DeleteWeakPersistentHandle, void, (Dart_WeakPersistentHandle object)) \
   F(Dart_UpdateExternalSize, void,                                             \
     (Dart_WeakPersistentHandle object, intptr_t external_allocation_size))     \
diff --git a/runtime/include/dart_native_api.h b/runtime/include/dart_native_api.h
index 7aeb40d..a40c522 100644
--- a/runtime/include/dart_native_api.h
+++ b/runtime/include/dart_native_api.h
@@ -31,8 +31,7 @@
  * The data for kTypedData is copied on message send and ownership remains with
  * the caller. The ownership of data for kExternalTyped is passed to the VM on
  * message send and returned when the VM invokes the
- * Dart_WeakPersistentHandleFinalizer callback; a non-NULL callback must be
- * provided.
+ * Dart_HandleFinalizer callback; a non-NULL callback must be provided.
  */
 typedef enum {
   Dart_CObject_kNull = 0,
@@ -79,7 +78,7 @@
       intptr_t length;
       uint8_t* data;
       void* peer;
-      Dart_WeakPersistentHandleFinalizer callback;
+      Dart_HandleFinalizer callback;
     } as_external_typed_data;
   } value;
 } Dart_CObject;
diff --git a/runtime/include/dart_version.h b/runtime/include/dart_version.h
index d2d904d..b3b4924 100644
--- a/runtime/include/dart_version.h
+++ b/runtime/include/dart_version.h
@@ -10,7 +10,7 @@
 // On breaking changes the major version is increased.
 // On backwards compatible changes the minor version is increased.
 // The versioning covers the symbols exposed in dart_api_dl.h
-#define DART_API_DL_MAJOR_VERSION 1
-#define DART_API_DL_MINOR_VERSION 1
+#define DART_API_DL_MAJOR_VERSION 2
+#define DART_API_DL_MINOR_VERSION 0
 
 #endif /* RUNTIME_INCLUDE_DART_VERSION_H_ */ /* NOLINT */
diff --git a/runtime/lib/vmservice.cc b/runtime/lib/vmservice.cc
index eae5565..8ffecd6 100644
--- a/runtime/lib/vmservice.cc
+++ b/runtime/lib/vmservice.cc
@@ -72,7 +72,7 @@
 DEFINE_NATIVE_ENTRY(VMService_SendObjectRootServiceMessage, 0, 1) {
 #ifndef PRODUCT
   GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(0));
-    return Service::HandleObjectRootMessage(message);
+  return Service::HandleObjectRootMessage(message);
 #endif
   return Object::null();
 }
@@ -307,16 +307,12 @@
   DISALLOW_COPY_AND_ASSIGN(TarArchive);
 };
 
-static void ContentsFinalizer(void* isolate_callback_data,
-                              Dart_WeakPersistentHandle handle,
-                              void* peer) {
+static void ContentsFinalizer(void* isolate_callback_data, void* peer) {
   uint8_t* data = reinterpret_cast<uint8_t*>(peer);
   delete[] data;
 }
 
-static void FilenameFinalizer(void* isolate_callback_data,
-                              Dart_WeakPersistentHandle handle,
-                              void* peer) {
+static void FilenameFinalizer(void* isolate_callback_data, void* peer) {
   char* filename = reinterpret_cast<char*>(peer);
   delete[] filename;
 }
diff --git a/runtime/vm/benchmark_test.cc b/runtime/vm/benchmark_test.cc
index 551ca97..a1ca4cd 100644
--- a/runtime/vm/benchmark_test.cc
+++ b/runtime/vm/benchmark_test.cc
@@ -201,9 +201,7 @@
   benchmark->set_score(elapsed_time);
 }
 
-static void NoopFinalizer(void* isolate_callback_data,
-                          Dart_WeakPersistentHandle handle,
-                          void* peer) {}
+static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
 
 //
 // Measure time accessing internal and external strings.
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 3218bd7..4776fa5 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -711,25 +711,22 @@
   if (!handle->raw()->IsHeapObject()) {
     return;  // Free handle.
   }
+  Dart_HandleFinalizer callback = handle->callback();
+  ASSERT(callback != NULL);
   void* peer = handle->peer();
   ApiState* state = isolate_group->api_state();
   ASSERT(state != NULL);
 
-  ASSERT(handle->auto_delete());
-
-  if (handle->callback_signature_ == CallbackSignature::kHandleFinalizer) {
-    Dart_HandleFinalizer callback = handle->callback();
-    ASSERT(callback != NULL);
-    (*callback)(isolate_group->embedder_data(), peer);
-  } else {
-    Dart_WeakPersistentHandleFinalizer callback =
-        handle->CallbackWeakFinalizer();
-    ASSERT(callback != NULL);
-    Dart_WeakPersistentHandle object = handle->ApiWeakPersistentHandle();
-    (*callback)(isolate_group->embedder_data(), object, peer);
+  if (!handle->auto_delete()) {
+    // Clear handle before running finalizer, finalizer can free the handle.
+    state->ClearWeakPersistentHandle(handle);
   }
 
-  state->FreeWeakPersistentHandle(handle);
+  (*callback)(isolate_group->embedder_data(), peer);
+
+  if (handle->auto_delete()) {
+    state->FreeWeakPersistentHandle(handle);
+  }
 }
 
 // --- Handles ---
@@ -942,6 +939,9 @@
   NoSafepointScope no_safepoint_scope;
   FinalizablePersistentHandle* weak_ref =
       FinalizablePersistentHandle::Cast(object);
+  if (weak_ref->IsFinalizedNotFreed()) {
+    return Dart_Null();
+  }
   return Api::NewHandle(thread, weak_ref->raw());
 }
 
@@ -986,14 +986,14 @@
     const Object& ref,
     void* peer,
     intptr_t external_allocation_size,
-    Dart_WeakPersistentHandleFinalizer callback) {
+    Dart_HandleFinalizer callback) {
   if (!ref.raw()->IsHeapObject()) {
     return NULL;
   }
   FinalizablePersistentHandle* finalizable_ref =
       FinalizablePersistentHandle::New(thread->isolate(), ref, peer, callback,
                                        external_allocation_size,
-                                       /*auto_delete=*/true);
+                                       /*auto_delete=*/false);
   return finalizable_ref->ApiWeakPersistentHandle();
 }
 
@@ -1002,7 +1002,7 @@
     Dart_Handle object,
     void* peer,
     intptr_t external_allocation_size,
-    Dart_WeakPersistentHandleFinalizer callback) {
+    Dart_HandleFinalizer callback) {
   REUSABLE_OBJECT_HANDLESCOPE(thread);
   Object& ref = thread->ObjectHandle();
   ref = Api::UnwrapHandle(object);
@@ -1044,7 +1044,7 @@
 Dart_NewWeakPersistentHandle(Dart_Handle object,
                              void* peer,
                              intptr_t external_allocation_size,
-                             Dart_WeakPersistentHandleFinalizer callback) {
+                             Dart_HandleFinalizer callback) {
   Thread* thread = Thread::Current();
   CHECK_ISOLATE(thread->isolate());
   if (callback == NULL) {
@@ -2961,7 +2961,7 @@
                              intptr_t length,
                              void* peer,
                              intptr_t external_allocation_size,
-                             Dart_WeakPersistentHandleFinalizer callback) {
+                             Dart_HandleFinalizer callback) {
   DARTSCOPE(Thread::Current());
   API_TIMELINE_DURATION(T);
   if (latin1_array == NULL && length != 0) {
@@ -2983,7 +2983,7 @@
                             intptr_t length,
                             void* peer,
                             intptr_t external_allocation_size,
-                            Dart_WeakPersistentHandleFinalizer callback) {
+                            Dart_HandleFinalizer callback) {
   DARTSCOPE(Thread::Current());
   if (utf16_array == NULL && length != 0) {
     RETURN_NULL_ERROR(utf16_array);
@@ -3933,14 +3933,13 @@
   return Api::NewHandle(thread, TypedData::New(cid, length));
 }
 
-static Dart_Handle NewExternalTypedData(
-    Thread* thread,
-    intptr_t cid,
-    void* data,
-    intptr_t length,
-    void* peer,
-    intptr_t external_allocation_size,
-    Dart_WeakPersistentHandleFinalizer callback) {
+static Dart_Handle NewExternalTypedData(Thread* thread,
+                                        intptr_t cid,
+                                        void* data,
+                                        intptr_t length,
+                                        void* peer,
+                                        intptr_t external_allocation_size,
+                                        Dart_HandleFinalizer callback) {
   CHECK_LENGTH(length, ExternalTypedData::MaxElements(cid));
   Zone* zone = thread->zone();
   intptr_t bytes = length * ExternalTypedData::ElementSizeInBytes(cid);
@@ -3952,19 +3951,18 @@
   result = ExternalTypedData::New(cid, reinterpret_cast<uint8_t*>(data), length,
                                   thread->heap()->SpaceForExternal(bytes));
   if (callback != nullptr) {
-    AllocateWeakPersistentHandle(thread, result, peer, external_allocation_size,
-                                 callback);
+    AllocateFinalizableHandle(thread, result, peer, external_allocation_size,
+                              callback);
   }
   return Api::NewHandle(thread, result.raw());
 }
 
-static Dart_Handle NewExternalByteData(
-    Thread* thread,
-    void* data,
-    intptr_t length,
-    void* peer,
-    intptr_t external_allocation_size,
-    Dart_WeakPersistentHandleFinalizer callback) {
+static Dart_Handle NewExternalByteData(Thread* thread,
+                                       void* data,
+                                       intptr_t length,
+                                       void* peer,
+                                       intptr_t external_allocation_size,
+                                       Dart_HandleFinalizer callback) {
   Zone* zone = thread->zone();
   Dart_Handle ext_data =
       NewExternalTypedData(thread, kExternalTypedDataUint8ArrayCid, data,
@@ -4049,13 +4047,13 @@
                                                 NULL);
 }
 
-DART_EXPORT Dart_Handle Dart_NewExternalTypedDataWithFinalizer(
-    Dart_TypedData_Type type,
-    void* data,
-    intptr_t length,
-    void* peer,
-    intptr_t external_allocation_size,
-    Dart_WeakPersistentHandleFinalizer callback) {
+DART_EXPORT Dart_Handle
+Dart_NewExternalTypedDataWithFinalizer(Dart_TypedData_Type type,
+                                       void* data,
+                                       intptr_t length,
+                                       void* peer,
+                                       intptr_t external_allocation_size,
+                                       Dart_HandleFinalizer callback) {
   DARTSCOPE(Thread::Current());
   if (data == NULL && length != 0) {
     RETURN_NULL_ERROR(data);
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index cf7853f..fea59eb 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -1430,9 +1430,7 @@
   }
 }
 
-static void NoopFinalizer(void* isolate_callback_data,
-                          Dart_WeakPersistentHandle handle,
-                          void* peer) {}
+static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
 
 TEST_CASE(DartAPI_IsString) {
   uint8_t latin1[] = {'o', 'n', 'e', 0xC2, 0xA2};
@@ -1581,7 +1579,6 @@
 }
 
 static void ExternalStringCallbackFinalizer(void* isolate_callback_data,
-                                            Dart_WeakPersistentHandle handle,
                                             void* peer) {
   *static_cast<int*>(peer) *= 2;
 }
@@ -2258,9 +2255,7 @@
 }
 
 static bool byte_data_finalizer_run = false;
-void ByteDataFinalizer(void* isolate_data,
-                       Dart_WeakPersistentHandle handle,
-                       void* peer) {
+void ByteDataFinalizer(void* isolate_data, void* peer) {
   ASSERT(!byte_data_finalizer_run);
   free(peer);
   byte_data_finalizer_run = true;
@@ -2789,22 +2784,13 @@
   EXPECT(value);
 }
 
-static void NopCallback(void* isolate_callback_data,
-                        Dart_WeakPersistentHandle handle,
-                        void* peer) {}
 static void NopCallback(void* isolate_callback_data, void* peer) {}
 
-static void UnreachedCallback(void* isolate_callback_data,
-                              Dart_WeakPersistentHandle handle,
-                              void* peer) {
-  UNREACHABLE();
-}
 static void UnreachedCallback(void* isolate_callback_data, void* peer) {
   UNREACHABLE();
 }
 
 static void ExternalTypedDataFinalizer(void* isolate_callback_data,
-                                       Dart_WeakPersistentHandle handle,
                                        void* peer) {
   *static_cast<int*>(peer) = 42;
 }
@@ -2855,22 +2841,24 @@
   EXPECT_EQ(20, count);
 }
 
-static void SlowWeakPersistentHandle(void* isolate_callback_data,
-                                     Dart_WeakPersistentHandle handle,
-                                     void* peer) {
+static void SlowWeakPersistentHandle(void* isolate_callback_data, void* peer) {
   OS::Sleep(10);
   intptr_t* count = reinterpret_cast<intptr_t*>(peer);
   (*count)++;
 }
 
 TEST_CASE(DartAPI_SlowWeakPersistenhandle) {
+  Dart_WeakPersistentHandle handles[20];
   intptr_t count = 0;
+
   for (intptr_t i = 0; i < 10; i++) {
     Dart_EnterScope();
     Dart_Handle str1 = Dart_NewStringFromCString("Live fast");
-    Dart_NewWeakPersistentHandle(str1, &count, 0, SlowWeakPersistentHandle);
+    handles[i] =
+        Dart_NewWeakPersistentHandle(str1, &count, 0, SlowWeakPersistentHandle);
     Dart_Handle str2 = Dart_NewStringFromCString("Die young");
-    Dart_NewWeakPersistentHandle(str2, &count, 0, SlowWeakPersistentHandle);
+    handles[i + 10] =
+        Dart_NewWeakPersistentHandle(str2, &count, 0, SlowWeakPersistentHandle);
     Dart_ExitScope();
 
     {
@@ -2880,6 +2868,10 @@
   }
 
   EXPECT_EQ(20, count);
+
+  for (intptr_t i = 0; i < 20; i++) {
+    Dart_DeleteWeakPersistentHandle(handles[i]);
+  }
 }
 
 static void CheckFloat32x4Data(Dart_Handle obj) {
@@ -3119,27 +3111,9 @@
   return Dart_HandleFromWeakPersistent(weak);
 }
 
-static Dart_WeakPersistentHandle weak_new_ref = NULL;
-static Dart_WeakPersistentHandle weak_old_ref = NULL;
-
-static void WeakPersistentHandleCallback(void* isolate_callback_data,
-                                         Dart_WeakPersistentHandle handle,
-                                         void* peer) {
-  if (handle == weak_new_ref) {
-    weak_new_ref = NULL;
-  } else if (handle == weak_old_ref) {
-    weak_old_ref = NULL;
-  }
-}
-
 TEST_CASE(DartAPI_WeakPersistentHandle) {
-  Dart_Handle local_new_ref = Dart_Null();
-  weak_new_ref = Dart_NewWeakPersistentHandle(local_new_ref, NULL, 0,
-                                              WeakPersistentHandleCallback);
-
-  Dart_Handle local_old_ref = Dart_Null();
-  weak_old_ref = Dart_NewWeakPersistentHandle(local_old_ref, NULL, 0,
-                                              WeakPersistentHandleCallback);
+  Dart_WeakPersistentHandle weak_new_ref = nullptr;
+  Dart_WeakPersistentHandle weak_old_ref = nullptr;
 
   {
     Dart_EnterScope();
@@ -3159,14 +3133,15 @@
       EXPECT_VALID(old_ref);
 
       // Create a weak ref to the new space object.
-      weak_new_ref = Dart_NewWeakPersistentHandle(new_ref, NULL, 0,
-                                                  WeakPersistentHandleCallback);
+      weak_new_ref =
+          Dart_NewWeakPersistentHandle(new_ref, nullptr, 0, NopCallback);
       EXPECT_VALID(AsHandle(weak_new_ref));
       EXPECT(!Dart_IsNull(AsHandle(weak_new_ref)));
 
       // Create a weak ref to the old space object.
-      weak_old_ref = Dart_NewWeakPersistentHandle(old_ref, NULL, 0,
-                                                  WeakPersistentHandleCallback);
+      weak_old_ref =
+          Dart_NewWeakPersistentHandle(old_ref, nullptr, 0, NopCallback);
+
       EXPECT_VALID(AsHandle(weak_old_ref));
       EXPECT(!Dart_IsNull(AsHandle(weak_old_ref)));
     }
@@ -3224,7 +3199,8 @@
   {
     Dart_EnterScope();
     // Weak ref to new space object should now be cleared.
-    EXPECT(weak_new_ref == NULL);
+    EXPECT_VALID(AsHandle(weak_new_ref));
+    EXPECT(Dart_IsNull(AsHandle(weak_new_ref)));
     EXPECT_VALID(AsHandle(weak_old_ref));
     EXPECT(!Dart_IsNull(AsHandle(weak_old_ref)));
     Dart_ExitScope();
@@ -3239,8 +3215,10 @@
   {
     Dart_EnterScope();
     // Weak ref to old space object should now be cleared.
-    EXPECT(weak_new_ref == NULL);
-    EXPECT(weak_old_ref == NULL);
+    EXPECT_VALID(AsHandle(weak_new_ref));
+    EXPECT(Dart_IsNull(AsHandle(weak_new_ref)));
+    EXPECT_VALID(AsHandle(weak_old_ref));
+    EXPECT(Dart_IsNull(AsHandle(weak_old_ref)));
     Dart_ExitScope();
   }
 
@@ -3249,6 +3227,9 @@
     // Garbage collect one last time to revisit deleted handles.
     GCTestHelper::CollectAllGarbage();
   }
+
+  Dart_DeleteWeakPersistentHandle(weak_new_ref);
+  Dart_DeleteWeakPersistentHandle(weak_old_ref);
 }
 
 static Dart_FinalizableHandle finalizable_new_ref = nullptr;
@@ -3384,7 +3365,7 @@
   Dart_Handle obj2 = Dart_NewInteger(0);
   EXPECT_VALID(obj2);
   Dart_WeakPersistentHandle ref2 =
-      Dart_NewWeakPersistentHandle(obj2, NULL, 0, WeakPersistentHandleCallback);
+      Dart_NewWeakPersistentHandle(obj2, NULL, 0, NopCallback);
   EXPECT_EQ(ref2, static_cast<void*>(NULL));
 
   Dart_ExitScope();
@@ -3416,17 +3397,12 @@
 
 static void WeakPersistentHandlePeerCleanupFinalizer(
     void* isolate_callback_data,
-    Dart_WeakPersistentHandle handle,
     void* peer) {
   Dart_DeletePersistentHandle(persistent_handle1);
   Dart_DeleteWeakPersistentHandle(weak_persistent_handle2);
   *static_cast<int*>(peer) = 42;
 }
 
-static void WeakPersistentHandleNoopCallback(void* isolate_callback_data,
-                                             Dart_WeakPersistentHandle handle,
-                                             void* peer) {}
-
 TEST_CASE(DartAPI_WeakPersistentHandleCleanupFinalizer) {
   Heap* heap = Isolate::Current()->heap();
 
@@ -3437,8 +3413,8 @@
   persistent_handle1 = Dart_NewPersistentHandle(ref1);
   Dart_Handle ref2 = Dart_NewStringFromCString(kTestString1);
   int peer2 = 0;
-  weak_persistent_handle2 = Dart_NewWeakPersistentHandle(
-      ref2, &peer2, 0, WeakPersistentHandleNoopCallback);
+  weak_persistent_handle2 =
+      Dart_NewWeakPersistentHandle(ref2, &peer2, 0, NopCallback);
   int peer3 = 0;
   {
     Dart_EnterScope();
@@ -3454,6 +3430,8 @@
     EXPECT(peer3 == 42);
   }
   Dart_ExitScope();
+
+  Dart_DeleteWeakPersistentHandle(weak_persistent_handle3);
 }
 
 static Dart_FinalizableHandle finalizable_handle3;
@@ -3495,7 +3473,6 @@
 }
 
 static void WeakPersistentHandlePeerFinalizer(void* isolate_callback_data,
-                                              Dart_WeakPersistentHandle handle,
                                               void* peer) {
   *static_cast<int*>(peer) = 42;
 }
@@ -3520,6 +3497,7 @@
     GCTestHelper::CollectNewSpace();
     EXPECT(peer == 42);
   }
+  Dart_DeleteWeakPersistentHandle(weak_ref);
 }
 
 static void FinalizableHandlePeerFinalizer(void* isolate_callback_data,
@@ -3599,13 +3577,53 @@
 
 Dart_WeakPersistentHandle delete_on_finalization;
 
+static void DeleteWeakHandleOnFinalization(void* isolate_callback_data,
+                                           void* peer) {
+  *static_cast<int*>(peer) = 42;
+  Dart_DeleteWeakPersistentHandle(delete_on_finalization);
+  delete_on_finalization = nullptr;
+}
+
+static void DontDeleteWeakHandleOnFinalization(void* isolate_callback_data,
+                                               void* peer) {
+  *static_cast<int*>(peer) = 42;
+  delete_on_finalization = nullptr;
+}
+
+// Mimicking the old handle behavior by deleting the handle itself in the
+// finalizer.
+TEST_CASE(DartAPI_WeakPersistentHandleCallbackSelfDelete) {
+  int peer = 0;
+  {
+    Dart_EnterScope();
+    Dart_Handle obj = NewString("new string");
+    EXPECT_VALID(obj);
+    delete_on_finalization = Dart_NewWeakPersistentHandle(
+        obj, &peer, 0, DeleteWeakHandleOnFinalization);
+    EXPECT_VALID(AsHandle(delete_on_finalization));
+    EXPECT(peer == 0);
+    Dart_ExitScope();
+  }
+  {
+    TransitionNativeToVM transition(thread);
+    GCTestHelper::CollectOldSpace();
+    EXPECT(peer == 0);
+    GCTestHelper::CollectNewSpace();
+    EXPECT(peer == 42);
+    ASSERT(delete_on_finalization == nullptr);
+  }
+}
+
+// Checking that the finalizer gets run on shutdown, but that the delete
+// handle does not get invoked. (The handles have already been deleted by
+// releasing the LocalApiState.)
 VM_UNIT_TEST_CASE(DartAPI_WeakPersistentHandlesCallbackShutdown) {
   TestCase::CreateTestIsolate();
   Dart_EnterScope();
   Dart_Handle ref = Dart_True();
   int peer = 1234;
-  Dart_NewWeakPersistentHandle(ref, &peer, 0,
-                               WeakPersistentHandlePeerFinalizer);
+  delete_on_finalization = Dart_NewWeakPersistentHandle(
+      ref, &peer, 0, DontDeleteWeakHandleOnFinalization);
   Dart_ExitScope();
   Dart_ShutdownIsolate();
   EXPECT(peer == 42);
@@ -3668,6 +3686,8 @@
     GCTestHelper::CollectOldSpace();
     EXPECT(heap->ExternalInWords(Heap::kOld) == 0);
   }
+  Dart_DeleteWeakPersistentHandle(weak1);
+  Dart_DeleteWeakPersistentHandle(weak2);
 }
 
 TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSize) {
@@ -3827,7 +3847,8 @@
   }
   // Large enough to trigger GC in old space. Not actually allocated.
   const intptr_t kHugeExternalSize = (kWordSize == 4) ? 513 * MB : 1025 * MB;
-  Dart_NewWeakPersistentHandle(live, NULL, kHugeExternalSize, NopCallback);
+  Dart_WeakPersistentHandle weak2 =
+      Dart_NewWeakPersistentHandle(live, NULL, kHugeExternalSize, NopCallback);
   {
     TransitionNativeToVM transition(thread);
     GCTestHelper::WaitForGCTasks();  // Finalize GC for accurate live size.
@@ -3836,6 +3857,8 @@
               isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
   }
   Dart_ExitScope();
+  Dart_DeleteWeakPersistentHandle(weak);
+  Dart_DeleteWeakPersistentHandle(weak2);
 }
 
 TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeOldspaceGC) {
@@ -3984,13 +4007,11 @@
 }
 
 struct ExampleResource {
-  Dart_WeakPersistentHandle self;
+  Dart_FinalizableHandle self;
   void* lots_of_memory;
 };
 
-void ExampleResourceFinalizer(void* isolate_peer,
-                              Dart_WeakPersistentHandle handle,
-                              void* peer) {
+void ExampleResourceFinalizer(void* isolate_peer, void* peer) {
   ExampleResource* resource = reinterpret_cast<ExampleResource*>(peer);
   free(resource->lots_of_memory);
   delete resource;
@@ -4001,8 +4022,8 @@
   intptr_t external_size = 10 * MB;
   ExampleResource* resource = new ExampleResource();
   resource->lots_of_memory = malloc(external_size);
-  resource->self = Dart_NewWeakPersistentHandle(
-      receiver, resource, external_size, ExampleResourceFinalizer);
+  resource->self = Dart_NewFinalizableHandle(receiver, resource, external_size,
+                                             ExampleResourceFinalizer);
   EXPECT_VALID(Dart_SetNativeInstanceField(
       receiver, 0, reinterpret_cast<intptr_t>(resource)));
   // Some pretend resource initialization.
@@ -4032,7 +4053,7 @@
   if (resource->lots_of_memory != nullptr) {
     free(resource->lots_of_memory);
     resource->lots_of_memory = nullptr;
-    Dart_UpdateExternalSize(resource->self, 0);
+    Dart_UpdateFinalizableExternalSize(resource->self, receiver, 0);
   }
 }
 
@@ -4072,13 +4093,12 @@
 static Dart_WeakPersistentHandle weak3 = NULL;
 
 static void ImplicitReferencesCallback(void* isolate_callback_data,
-                                       Dart_WeakPersistentHandle handle,
                                        void* peer) {
-  if (handle == weak1) {
+  if (peer == &weak1) {
     weak1 = NULL;
-  } else if (handle == weak2) {
+  } else if (peer == &weak2) {
     weak2 = NULL;
-  } else if (handle == weak3) {
+  } else if (peer == &weak3) {
     weak3 = NULL;
   }
 }
@@ -4103,19 +4123,19 @@
 
     weak1 =
         Dart_NewWeakPersistentHandle(AllocateOldString("weakly reachable 1"),
-                                     NULL, 0, ImplicitReferencesCallback);
+                                     &weak1, 0, ImplicitReferencesCallback);
     EXPECT(!Dart_IsNull(AsHandle(weak1)));
     EXPECT_VALID(AsHandle(weak1));
 
     weak2 =
         Dart_NewWeakPersistentHandle(AllocateOldString("weakly reachable 2"),
-                                     NULL, 0, ImplicitReferencesCallback);
+                                     &weak2, 0, ImplicitReferencesCallback);
     EXPECT(!Dart_IsNull(AsHandle(weak2)));
     EXPECT_VALID(AsHandle(weak2));
 
     weak3 =
         Dart_NewWeakPersistentHandle(AllocateOldString("weakly reachable 3"),
-                                     NULL, 0, ImplicitReferencesCallback);
+                                     &weak3, 0, ImplicitReferencesCallback);
     EXPECT(!Dart_IsNull(AsHandle(weak3)));
     EXPECT_VALID(AsHandle(weak3));
   }
@@ -4144,6 +4164,11 @@
     EXPECT(!Dart_IsNull(AsHandle(weak3)));
     Dart_ExitScope();
   }
+
+  Dart_DeleteWeakPersistentHandle(strong_weak);
+  Dart_DeleteWeakPersistentHandle(weak1);
+  Dart_DeleteWeakPersistentHandle(weak2);
+  Dart_DeleteWeakPersistentHandle(weak3);
 }
 
 TEST_CASE(DartAPI_ImplicitReferencesNewSpace) {
@@ -4166,19 +4191,19 @@
 
     weak1 =
         Dart_NewWeakPersistentHandle(AllocateNewString("weakly reachable 1"),
-                                     NULL, 0, ImplicitReferencesCallback);
+                                     &weak1, 0, ImplicitReferencesCallback);
     EXPECT(!Dart_IsNull(AsHandle(weak1)));
     EXPECT_VALID(AsHandle(weak1));
 
     weak2 =
         Dart_NewWeakPersistentHandle(AllocateNewString("weakly reachable 2"),
-                                     NULL, 0, ImplicitReferencesCallback);
+                                     &weak2, 0, ImplicitReferencesCallback);
     EXPECT(!Dart_IsNull(AsHandle(weak2)));
     EXPECT_VALID(AsHandle(weak2));
 
     weak3 =
         Dart_NewWeakPersistentHandle(AllocateNewString("weakly reachable 3"),
-                                     NULL, 0, ImplicitReferencesCallback);
+                                     &weak3, 0, ImplicitReferencesCallback);
     EXPECT(!Dart_IsNull(AsHandle(weak3)));
     EXPECT_VALID(AsHandle(weak3));
   }
@@ -4206,6 +4231,11 @@
     EXPECT(!Dart_IsNull(AsHandle(weak3)));
     Dart_ExitScope();
   }
+
+  Dart_DeleteWeakPersistentHandle(strong_weak);
+  Dart_DeleteWeakPersistentHandle(weak1);
+  Dart_DeleteWeakPersistentHandle(weak2);
+  Dart_DeleteWeakPersistentHandle(weak3);
 }
 
 // Unit test for creating multiple scopes and local handles within them.
@@ -7379,9 +7409,7 @@
   EXPECT(!success);
 }
 
-static void UnreachableFinalizer(void* isolate_callback_data,
-                                 Dart_WeakPersistentHandle handle,
-                                 void* peer) {
+static void UnreachableFinalizer(void* isolate_callback_data, void* peer) {
   UNREACHABLE();
 }
 
@@ -8970,10 +8998,7 @@
 
   Dart_Handle bytes = Dart_NewExternalTypedDataWithFinalizer(
       Dart_TypedData_kUint8, response_json, response_json_length, response_json,
-      response_json_length,
-      [](void* ignored, Dart_WeakPersistentHandle handle, void* peer) {
-        free(peer);
-      });
+      response_json_length, [](void* ignored, void* peer) { free(peer); });
   EXPECT_VALID(bytes);
 
   // We don't have a C++ JSON decoder so we'll invoke dart to validate the
diff --git a/runtime/vm/dart_api_message.cc b/runtime/vm/dart_api_message.cc
index bc83266..d371645 100644
--- a/runtime/vm/dart_api_message.cc
+++ b/runtime/vm/dart_api_message.cc
@@ -1124,7 +1124,7 @@
       }
       uint8_t* data = object->value.as_external_typed_data.data;
       void* peer = object->value.as_external_typed_data.peer;
-      Dart_WeakPersistentHandleFinalizer callback =
+      Dart_HandleFinalizer callback =
           object->value.as_external_typed_data.callback;
       if (callback == NULL) {
         return false;
diff --git a/runtime/vm/dart_api_state.h b/runtime/vm/dart_api_state.h
index 7b52e98..b9ba995 100644
--- a/runtime/vm/dart_api_state.h
+++ b/runtime/vm/dart_api_state.h
@@ -190,23 +190,6 @@
 // dart API.
 class FinalizablePersistentHandle {
  public:
-  // TODO(http://dartbug.com/42312): Delete this on migrating signature
-  // Dart_NewWeakPersistentHandle to Dart_HandleFinalizer.
-  enum class CallbackSignature {
-    // Uses a Dart_WeakPersistentHandleFinalizer.
-    kWeakPersistentHandleFinalizer = 0,
-    // Uses a Dart_HandleFinalizer.
-    kHandleFinalizer = 1,
-  };
-
-  static FinalizablePersistentHandle* New(
-      Isolate* isolate,
-      const Object& object,
-      void* peer,
-      Dart_WeakPersistentHandleFinalizer callback,
-      intptr_t external_size,
-      bool auto_delete);
-
   static FinalizablePersistentHandle* New(Isolate* isolate,
                                           const Object& object,
                                           void* peer,
@@ -221,18 +204,7 @@
     return OFFSET_OF(FinalizablePersistentHandle, raw_);
   }
   void* peer() const { return peer_; }
-  Dart_WeakPersistentHandleFinalizer CallbackWeakFinalizer() const {
-    ASSERT(callback_signature_ ==
-           CallbackSignature::kWeakPersistentHandleFinalizer);
-    return callback_.weak_persistent;
-  }
-  Dart_HandleFinalizer callback() const {
-    ASSERT(callback_signature_ == CallbackSignature::kHandleFinalizer);
-    return callback_.finalizable;
-  }
-  uword callback_address() const {
-    return reinterpret_cast<uword>(callback_.finalizable);
-  }
+  Dart_HandleFinalizer callback() const { return callback_; }
   Dart_WeakPersistentHandle ApiWeakPersistentHandle() {
     return reinterpret_cast<Dart_WeakPersistentHandle>(this);
   }
@@ -242,6 +214,10 @@
 
   bool auto_delete() const { return auto_delete_; }
 
+  bool IsFinalizedNotFreed() const {
+    return raw_ == static_cast<ObjectPtr>(reinterpret_cast<uword>(this));
+  }
+
   intptr_t external_size() const {
     return ExternalSizeInWordsBits::decode(external_data_) * kWordSize;
   }
@@ -298,15 +274,6 @@
     kExternalSizeBitsSize = (kBitsPerWord - 1),
   };
 
-  union HandleFinalizer {
-    Dart_HandleFinalizer finalizable;
-    Dart_WeakPersistentHandleFinalizer weak_persistent;
-    HandleFinalizer(Dart_HandleFinalizer finalizer) : finalizable(finalizer) {}
-    HandleFinalizer(Dart_WeakPersistentHandleFinalizer finalizer)
-        : weak_persistent(finalizer) {}
-    HandleFinalizer() : finalizable(nullptr) {}
-  };
-
   // This part of external_data_ is the number of externally allocated bytes.
   class ExternalSizeInWordsBits : public BitField<uword,
                                                   intptr_t,
@@ -320,10 +287,7 @@
   friend class FinalizablePersistentHandles;
 
   FinalizablePersistentHandle()
-      : raw_(nullptr),
-        peer_(NULL),
-        external_data_(0),
-        callback_(HandleFinalizer()) {}
+      : raw_(nullptr), peer_(NULL), external_data_(0), callback_(NULL) {}
   ~FinalizablePersistentHandle() {}
 
   static void Finalize(IsolateGroup* isolate_group,
@@ -340,6 +304,11 @@
     ASSERT(!raw_->IsHeapObject());
   }
 
+  void SetFinalizedNotFreed() {
+    // `handle->raw_ != Object::null()` or the GC will finalize again.
+    SetNext(this);
+  }
+
   void FreeHandle(FinalizablePersistentHandle* free_list) {
     Clear();
     SetNext(free_list);
@@ -347,11 +316,10 @@
 
   void Clear() {
     raw_ = Object::null();
-    peer_ = NULL;
+    peer_ = nullptr;
     external_data_ = 0;
-    callback_ = HandleFinalizer();
+    callback_ = nullptr;
     auto_delete_ = false;
-    callback_signature_ = CallbackSignature::kWeakPersistentHandleFinalizer;
   }
 
   void set_raw(ObjectPtr raw) { raw_ = raw; }
@@ -360,11 +328,7 @@
 
   void set_peer(void* peer) { peer_ = peer; }
 
-  void set_callback_signature(CallbackSignature callback_signature) {
-    callback_signature_ = callback_signature;
-  }
-
-  void set_callback(HandleFinalizer callback) { callback_ = callback; }
+  void set_callback(Dart_HandleFinalizer callback) { callback_ = callback; }
 
   void set_auto_delete(bool auto_delete) { auto_delete_ = auto_delete; }
 
@@ -396,9 +360,8 @@
   ObjectPtr raw_;
   void* peer_;
   uword external_data_;
-  HandleFinalizer callback_;
+  Dart_HandleFinalizer callback_;
   bool auto_delete_;
-  CallbackSignature callback_signature_;
 
   DISALLOW_ALLOCATION();  // Allocated through AllocateHandle methods.
   DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandle);
@@ -608,6 +571,11 @@
     return handle;
   }
 
+  void ClearHandle(FinalizablePersistentHandle* handle) {
+    handle->Clear();
+    handle->SetFinalizedNotFreed();
+  }
+
   void FreeHandle(FinalizablePersistentHandle* handle) {
     handle->FreeHandle(free_list());
     set_free_list(handle);
@@ -805,7 +773,10 @@
     MutexLocker ml(&mutex_);
     return weak_persistent_handles_.AllocateHandle();
   }
-
+  void ClearWeakPersistentHandle(FinalizablePersistentHandle* weak_ref) {
+    MutexLocker ml(&mutex_);
+    weak_persistent_handles_.ClearHandle(weak_ref);
+  }
   void FreeWeakPersistentHandle(FinalizablePersistentHandle* weak_ref) {
     MutexLocker ml(&mutex_);
     weak_persistent_handles_.FreeHandle(weak_ref);
@@ -896,28 +867,6 @@
     Isolate* isolate,
     const Object& object,
     void* peer,
-    Dart_WeakPersistentHandleFinalizer callback,
-    intptr_t external_size,
-    bool auto_delete) {
-  ApiState* state = isolate->group()->api_state();
-  ASSERT(state != NULL);
-  ASSERT(callback != NULL);
-  FinalizablePersistentHandle* ref = state->AllocateWeakPersistentHandle();
-  ref->set_raw(object);
-  ref->set_peer(peer);
-  ref->set_callback_signature(
-      CallbackSignature::kWeakPersistentHandleFinalizer);
-  ref->set_callback(HandleFinalizer(callback));
-  ref->set_auto_delete(auto_delete);
-  // This may trigger GC, so it must be called last.
-  ref->SetExternalSize(external_size, isolate->group());
-  return ref;
-}
-
-inline FinalizablePersistentHandle* FinalizablePersistentHandle::New(
-    Isolate* isolate,
-    const Object& object,
-    void* peer,
     Dart_HandleFinalizer callback,
     intptr_t external_size,
     bool auto_delete) {
@@ -926,8 +875,7 @@
   FinalizablePersistentHandle* ref = state->AllocateWeakPersistentHandle();
   ref->set_raw(object);
   ref->set_peer(peer);
-  ref->set_callback_signature(CallbackSignature::kHandleFinalizer);
-  ref->set_callback(HandleFinalizer(callback));
+  ref->set_callback(callback);
   ref->set_auto_delete(auto_delete);
   // This may trigger GC, so it must be called last.
   ref->SetExternalSize(external_size, isolate->group());
diff --git a/runtime/vm/finalizable_data.h b/runtime/vm/finalizable_data.h
index d604a94..170e3aa 100644
--- a/runtime/vm/finalizable_data.h
+++ b/runtime/vm/finalizable_data.h
@@ -14,8 +14,8 @@
 struct FinalizableData {
   void* data;
   void* peer;
-  Dart_WeakPersistentHandleFinalizer callback;
-  Dart_WeakPersistentHandleFinalizer successful_write_callback;
+  Dart_HandleFinalizer callback;
+  Dart_HandleFinalizer successful_write_callback;
 };
 
 class MessageFinalizableData {
@@ -25,19 +25,18 @@
 
   ~MessageFinalizableData() {
     for (intptr_t i = take_position_; i < records_.length(); i++) {
-      records_[i].callback(nullptr, nullptr, records_[i].peer);
+      records_[i].callback(nullptr, records_[i].peer);
     }
   }
 
   /// If [successful_write_callback] is provided, it's invoked when message
   /// was serialized successfully.
   /// [callback] is invoked when serialization failed.
-  void Put(
-      intptr_t external_size,
-      void* data,
-      void* peer,
-      Dart_WeakPersistentHandleFinalizer callback,
-      Dart_WeakPersistentHandleFinalizer successful_write_callback = nullptr) {
+  void Put(intptr_t external_size,
+           void* data,
+           void* peer,
+           Dart_HandleFinalizer callback,
+           Dart_HandleFinalizer successful_write_callback = nullptr) {
     FinalizableData finalizable_data;
     finalizable_data.data = data;
     finalizable_data.peer = peer;
@@ -64,8 +63,7 @@
   void SerializationSucceeded() {
     for (intptr_t i = 0; i < records_.length(); i++) {
       if (records_[i].successful_write_callback != nullptr) {
-        records_[i].successful_write_callback(nullptr, nullptr,
-                                              records_[i].peer);
+        records_[i].successful_write_callback(nullptr, records_[i].peer);
       }
     }
   }
diff --git a/runtime/vm/handles_test.cc b/runtime/vm/handles_test.cc
index 0e6486f..3267089 100644
--- a/runtime/vm/handles_test.cc
+++ b/runtime/vm/handles_test.cc
@@ -77,9 +77,7 @@
   EXPECT_EQ(handle_count, VMHandles::ScopedHandleCount());
 }
 
-static void NoopCallback(void* isolate_callback_data,
-                         Dart_WeakPersistentHandle handle,
-                         void* peer) {}
+static void NoopCallback(void* isolate_callback_data, void* peer) {}
 
 // Unit test for handle validity checks.
 TEST_CASE(CheckHandleValidity) {
diff --git a/runtime/vm/json_stream.cc b/runtime/vm/json_stream.cc
index 041f250..30142f5 100644
--- a/runtime/vm/json_stream.cc
+++ b/runtime/vm/json_stream.cc
@@ -184,9 +184,7 @@
       Message::New(port, Object::null(), Message::kNormalPriority));
 }
 
-static void Finalizer(void* isolate_callback_data,
-                      Dart_WeakPersistentHandle handle,
-                      void* buffer) {
+static void Finalizer(void* isolate_callback_data, void* buffer) {
   free(buffer);
 }
 
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index 3b34856..f8f0d54 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -401,9 +401,7 @@
   delete[] files.value.as_array.values;
 }
 
-static void PassThroughFinalizer(void* isolate_callback_data,
-                                 Dart_WeakPersistentHandle handle,
-                                 void* peer) {}
+static void PassThroughFinalizer(void* isolate_callback_data, void* peer) {}
 
 MallocGrowableArray<char*>* KernelIsolate::experimental_flags_ =
     new MallocGrowableArray<char*>();
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 6a2bed1..ba6d46c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -21845,7 +21845,7 @@
                               intptr_t len,
                               void* peer,
                               intptr_t external_allocation_size,
-                              Dart_WeakPersistentHandleFinalizer callback,
+                              Dart_HandleFinalizer callback,
                               Heap::Space space) {
   return ExternalOneByteString::New(characters, len, peer,
                                     external_allocation_size, callback, space);
@@ -21855,7 +21855,7 @@
                               intptr_t len,
                               void* peer,
                               intptr_t external_allocation_size,
-                              Dart_WeakPersistentHandleFinalizer callback,
+                              Dart_HandleFinalizer callback,
                               Heap::Space space) {
   return ExternalTwoByteString::New(characters, len, peer,
                                     external_allocation_size, callback, space);
@@ -22239,11 +22239,10 @@
   Utf8::Encode(*this, reinterpret_cast<char*>(utf8_array), array_len);
 }
 
-static FinalizablePersistentHandle* AddFinalizer(
-    const Object& referent,
-    void* peer,
-    Dart_WeakPersistentHandleFinalizer callback,
-    intptr_t external_size) {
+static FinalizablePersistentHandle* AddFinalizer(const Object& referent,
+                                                 void* peer,
+                                                 Dart_HandleFinalizer callback,
+                                                 intptr_t external_size) {
   ASSERT(callback != NULL);
   return FinalizablePersistentHandle::New(Isolate::Current(), referent, peer,
                                           callback, external_size,
@@ -22856,7 +22855,7 @@
     intptr_t len,
     void* peer,
     intptr_t external_allocation_size,
-    Dart_WeakPersistentHandleFinalizer callback,
+    Dart_HandleFinalizer callback,
     Heap::Space space) {
   ASSERT(Isolate::Current()->object_store()->external_one_byte_string_class() !=
          Class::null());
@@ -22885,7 +22884,7 @@
     intptr_t len,
     void* peer,
     intptr_t external_allocation_size,
-    Dart_WeakPersistentHandleFinalizer callback,
+    Dart_HandleFinalizer callback,
     Heap::Space space) {
   ASSERT(Isolate::Current()->object_store()->external_two_byte_string_class() !=
          Class::null());
@@ -23658,7 +23657,7 @@
 
 FinalizablePersistentHandle* ExternalTypedData::AddFinalizer(
     void* peer,
-    Dart_WeakPersistentHandleFinalizer callback,
+    Dart_HandleFinalizer callback,
     intptr_t external_size) const {
   return dart::AddFinalizer(*this, peer, callback, external_size);
 }
@@ -23696,10 +23695,7 @@
   ExternalTypedData& result = ExternalTypedData::Handle(ExternalTypedData::New(
       kExternalTypedDataUint8ArrayCid, data, len, Heap::kOld));
   result.AddFinalizer(
-      data,
-      [](void* isolate_callback_data, Dart_WeakPersistentHandle handle,
-         void* data) { free(data); },
-      len);
+      data, [](void* isolate_callback_data, void* data) { free(data); }, len);
   return result.raw();
 }
 
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index c489355..28e2443 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -8765,7 +8765,7 @@
                                intptr_t array_len,
                                void* peer,
                                intptr_t external_allocation_size,
-                               Dart_WeakPersistentHandleFinalizer callback,
+                               Dart_HandleFinalizer callback,
                                Heap::Space = Heap::kNew);
 
   // Creates a new External String object using the specified array of
@@ -8774,7 +8774,7 @@
                                intptr_t array_len,
                                void* peer,
                                intptr_t external_allocation_size,
-                               Dart_WeakPersistentHandleFinalizer callback,
+                               Dart_HandleFinalizer callback,
                                Heap::Space = Heap::kNew);
 
   static void Copy(const String& dst,
@@ -9214,13 +9214,12 @@
     return String::RoundedAllocationSize(sizeof(ExternalOneByteStringLayout));
   }
 
-  static ExternalOneByteStringPtr New(
-      const uint8_t* characters,
-      intptr_t len,
-      void* peer,
-      intptr_t external_allocation_size,
-      Dart_WeakPersistentHandleFinalizer callback,
-      Heap::Space space);
+  static ExternalOneByteStringPtr New(const uint8_t* characters,
+                                      intptr_t len,
+                                      void* peer,
+                                      intptr_t external_allocation_size,
+                                      Dart_HandleFinalizer callback,
+                                      Heap::Space space);
 
   static ExternalOneByteStringPtr null() {
     return static_cast<ExternalOneByteStringPtr>(Object::null());
@@ -9313,13 +9312,12 @@
     return String::RoundedAllocationSize(sizeof(ExternalTwoByteStringLayout));
   }
 
-  static ExternalTwoByteStringPtr New(
-      const uint16_t* characters,
-      intptr_t len,
-      void* peer,
-      intptr_t external_allocation_size,
-      Dart_WeakPersistentHandleFinalizer callback,
-      Heap::Space space = Heap::kNew);
+  static ExternalTwoByteStringPtr New(const uint16_t* characters,
+                                      intptr_t len,
+                                      void* peer,
+                                      intptr_t external_allocation_size,
+                                      Dart_HandleFinalizer callback,
+                                      Heap::Space space = Heap::kNew);
 
   static ExternalTwoByteStringPtr null() {
     return static_cast<ExternalTwoByteStringPtr>(Object::null());
@@ -10103,10 +10101,9 @@
 
 #undef TYPED_GETTER_SETTER
 
-  FinalizablePersistentHandle* AddFinalizer(
-      void* peer,
-      Dart_WeakPersistentHandleFinalizer callback,
-      intptr_t external_size) const;
+  FinalizablePersistentHandle* AddFinalizer(void* peer,
+                                            Dart_HandleFinalizer callback,
+                                            intptr_t external_size) const;
 
   static intptr_t data_offset() {
     return OFFSET_OF(ExternalTypedDataLayout, data_);
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index 2cd2590..ac498cc 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -990,7 +990,7 @@
     writer_->WriteUnsigned(weak_persistent_handle->external_size());
     // Attempt to include a native symbol name.
     auto const name = NativeSymbolResolver::LookupSymbolName(
-        weak_persistent_handle->callback_address(), nullptr);
+        reinterpret_cast<uword>(weak_persistent_handle->callback()), nullptr);
     writer_->WriteUtf8((name == nullptr) ? "Unknown native function" : name);
     if (name != nullptr) {
       NativeSymbolResolver::FreeSymbolName(name);
diff --git a/runtime/vm/object_graph_test.cc b/runtime/vm/object_graph_test.cc
index a072650..882c685 100644
--- a/runtime/vm/object_graph_test.cc
+++ b/runtime/vm/object_graph_test.cc
@@ -142,9 +142,7 @@
   }
 }
 
-static void WeakHandleFinalizer(void* isolate_callback_data,
-                                Dart_WeakPersistentHandle handle,
-                                void* peer) {}
+static void WeakHandleFinalizer(void* isolate_callback_data, void* peer) {}
 
 ISOLATE_UNIT_TEST_CASE(RetainingPathGCRoot) {
   Dart_PersistentHandle persistent_handle;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 4d4ba5b..01a7024 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -1624,9 +1624,7 @@
   EXPECT(!th_str.Equals(chars, 3));
 }
 
-static void NoopFinalizer(void* isolate_callback_data,
-                          Dart_WeakPersistentHandle handle,
-                          void* peer) {}
+static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
 
 ISOLATE_UNIT_TEST_CASE(ExternalOneByteString) {
   uint8_t characters[] = {0xF6, 0xF1, 0xE9};
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 0ec7de3..4b011d9 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -1374,7 +1374,6 @@
 
 // This function's name can appear in Observatory.
 static void IsolateMessageTypedDataFinalizer(void* isolate_callback_data,
-                                             Dart_WeakPersistentHandle handle,
                                              void* buffer) {
   free(buffer);
 }
@@ -1707,9 +1706,9 @@
       length, data, tpeer,
       // Finalizer does nothing - in case of failure to serialize,
       // [data] remains wrapped in sender's [TransferableTypedData].
-      [](void* data, Dart_WeakPersistentHandle handle, void* peer) {},
+      [](void* data, void* peer) {},
       // This is invoked on successful serialization of the message
-      [](void* data, Dart_WeakPersistentHandle handle, void* peer) {
+      [](void* data, void* peer) {
         TransferableTypedDataPeer* tpeer =
             reinterpret_cast<TransferableTypedDataPeer*>(peer);
         tpeer->handle()->EnsureFreedExternal(IsolateGroup::Current());
diff --git a/runtime/vm/regexp_test.cc b/runtime/vm/regexp_test.cc
index 01eca0e..7afcd5f 100644
--- a/runtime/vm/regexp_test.cc
+++ b/runtime/vm/regexp_test.cc
@@ -64,9 +64,7 @@
   EXPECT_EQ(3, smi_2.Value());
 }
 
-static void NoopFinalizer(void* isolate_callback_data,
-                          Dart_WeakPersistentHandle handle,
-                          void* peer) {}
+static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
 
 ISOLATE_UNIT_TEST_CASE(RegExp_ExternalOneByteString) {
   uint8_t chars[] = {'a', 'b', 'c', 'b', 'a'};
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index d12772e..ab78015 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -974,9 +974,7 @@
   return MaybePause(isolate, error);
 }
 
-static void Finalizer(void* isolate_callback_data,
-                      Dart_WeakPersistentHandle handle,
-                      void* buffer) {
+static void Finalizer(void* isolate_callback_data, void* buffer) {
   free(buffer);
 }
 
@@ -4356,11 +4354,12 @@
     obj.AddPropertyF(
         "peer", "0x%" Px "",
         reinterpret_cast<uintptr_t>(weak_persistent_handle->peer()));
-    obj.AddPropertyF("callbackAddress", "0x%" Px "",
-                     weak_persistent_handle->callback_address());
+    obj.AddPropertyF(
+        "callbackAddress", "0x%" Px "",
+        reinterpret_cast<uintptr_t>(weak_persistent_handle->callback()));
     // Attempt to include a native symbol name.
     char* name = NativeSymbolResolver::LookupSymbolName(
-        weak_persistent_handle->callback_address(), nullptr);
+        reinterpret_cast<uword>(weak_persistent_handle->callback()), nullptr);
     obj.AddProperty("callbackSymbolName", (name == nullptr) ? "" : name);
     if (name != nullptr) {
       NativeSymbolResolver::FreeSymbolName(name);
diff --git a/runtime/vm/service_test.cc b/runtime/vm/service_test.cc
index 9520585..515a2b2 100644
--- a/runtime/vm/service_test.cc
+++ b/runtime/vm/service_test.cc
@@ -485,9 +485,7 @@
   EXPECT_SUBSTRING("\"members\":[", handler.msg());
 }
 
-static void WeakHandleFinalizer(void* isolate_callback_data,
-                                Dart_WeakPersistentHandle handle,
-                                void* peer) {}
+static void WeakHandleFinalizer(void* isolate_callback_data, void* peer) {}
 
 ISOLATE_UNIT_TEST_CASE(Service_PersistentHandles) {
   const char* kScript =
diff --git a/samples/ffi/async/sample_async_callback.dart b/samples/ffi/async/sample_async_callback.dart
index 2c4b7d9..5e6d79a 100644
--- a/samples/ffi/async/sample_async_callback.dart
+++ b/samples/ffi/async/sample_async_callback.dart
@@ -28,8 +28,8 @@
   print("C T2 = Some C thread executing C.");
   print("C    = C T1 or C T2.");
   print("Dart: Setup.");
-  Expect.isTrue(NativeApi.majorVersion == 1);
-  Expect.isTrue(NativeApi.minorVersion >= 1);
+  Expect.isTrue(NativeApi.majorVersion == 2);
+  Expect.isTrue(NativeApi.minorVersion >= 0);
   final initializeApi = dl.lookupFunction<IntPtr Function(Pointer<Void>),
       int Function(Pointer<Void>)>("InitDartApiDL");
   Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);
diff --git a/samples/ffi/async/sample_native_port_call.dart b/samples/ffi/async/sample_native_port_call.dart
index 177ad4f..a2c3ee4 100644
--- a/samples/ffi/async/sample_native_port_call.dart
+++ b/samples/ffi/async/sample_native_port_call.dart
@@ -37,8 +37,8 @@
   print("C T2 = Some C thread executing C.");
   print("C    = C T1 or C T2.");
   print("Dart: Setup.");
-  Expect.isTrue(NativeApi.majorVersion == 1);
-  Expect.isTrue(NativeApi.minorVersion >= 1);
+  Expect.isTrue(NativeApi.majorVersion == 2);
+  Expect.isTrue(NativeApi.minorVersion >= 0);
   final initializeApi = dl.lookupFunction<IntPtr Function(Pointer<Void>),
       int Function(Pointer<Void>)>("InitDartApiDL");
   Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);
diff --git a/samples/ffi/sample_ffi_functions_callbacks_closures.dart b/samples/ffi/sample_ffi_functions_callbacks_closures.dart
index 36740d1..a6ce9a6 100644
--- a/samples/ffi/sample_ffi_functions_callbacks_closures.dart
+++ b/samples/ffi/sample_ffi_functions_callbacks_closures.dart
@@ -55,8 +55,8 @@
     Pointer.fromFunction<Void Function(Handle)>(doClosureCallback);
 
 void doDynamicLinking() {
-  Expect.isTrue(NativeApi.majorVersion == 1);
-  Expect.isTrue(NativeApi.minorVersion >= 1);
+  Expect.isTrue(NativeApi.majorVersion == 2);
+  Expect.isTrue(NativeApi.minorVersion >= 0);
   final initializeApi = testLibrary.lookupFunction<
       IntPtr Function(Pointer<Void>),
       int Function(Pointer<Void>)>("InitDartApiDL");
diff --git a/tests/ffi/vmspecific_handle_dynamically_linked_test.dart b/tests/ffi/vmspecific_handle_dynamically_linked_test.dart
index 6422958..2b6e834 100644
--- a/tests/ffi/vmspecific_handle_dynamically_linked_test.dart
+++ b/tests/ffi/vmspecific_handle_dynamically_linked_test.dart
@@ -16,8 +16,8 @@
 }
 
 void doDynamicLinking() {
-  Expect.isTrue(NativeApi.majorVersion == 1);
-  Expect.isTrue(NativeApi.minorVersion >= 1);
+  Expect.isTrue(NativeApi.majorVersion == 2);
+  Expect.isTrue(NativeApi.minorVersion >= 0);
   final initializeApi = testLibrary.lookupFunction<
       IntPtr Function(Pointer<Void>),
       int Function(Pointer<Void>)>("InitDartApiDL");
diff --git a/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart b/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart
index 6422958..2b6e834 100644
--- a/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart
+++ b/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart
@@ -16,8 +16,8 @@
 }
 
 void doDynamicLinking() {
-  Expect.isTrue(NativeApi.majorVersion == 1);
-  Expect.isTrue(NativeApi.minorVersion >= 1);
+  Expect.isTrue(NativeApi.majorVersion == 2);
+  Expect.isTrue(NativeApi.minorVersion >= 0);
   final initializeApi = testLibrary.lookupFunction<
       IntPtr Function(Pointer<Void>),
       int Function(Pointer<Void>)>("InitDartApiDL");
diff --git a/tools/VERSION b/tools/VERSION
index f971f7f..5ffc271 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 14
+PRERELEASE 15
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/patches/flutter-engine/d2577410a5016eadc111919355e19d42dd45d816.patch b/tools/patches/flutter-engine/d2577410a5016eadc111919355e19d42dd45d816.patch
new file mode 100644
index 0000000..c7446e9
--- /dev/null
+++ b/tools/patches/flutter-engine/d2577410a5016eadc111919355e19d42dd45d816.patch
@@ -0,0 +1,804 @@
+diff --git a/BUILD.gn b/BUILD.gn
+index edbeb6803..e280ae901 100644
+--- a/BUILD.gn
++++ b/BUILD.gn
+@@ -98,6 +98,7 @@ group("flutter") {
+       "//flutter/shell/platform/embedder:embedder_proctable_unittests",
+       "//flutter/shell/platform/embedder:embedder_unittests",
+       "//flutter/testing:testing_unittests",
++      "//flutter/third_party/tonic/tests:tonic_unittests",
+       "//flutter/third_party/txt:txt_unittests",
+     ]
+ 
+diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
+index f13271aa9..6b84f6a83 100755
+--- a/ci/licenses_golden/licenses_flutter
++++ b/ci/licenses_golden/licenses_flutter
+@@ -1463,6 +1463,8 @@ FILE: ../../../flutter/third_party/tonic/dart_persistent_value.cc
+ FILE: ../../../flutter/third_party/tonic/dart_persistent_value.h
+ FILE: ../../../flutter/third_party/tonic/dart_state.cc
+ FILE: ../../../flutter/third_party/tonic/dart_state.h
++FILE: ../../../flutter/third_party/tonic/dart_weak_persistent_value.cc
++FILE: ../../../flutter/third_party/tonic/dart_weak_persistent_value.h
+ FILE: ../../../flutter/third_party/tonic/dart_wrappable.cc
+ FILE: ../../../flutter/third_party/tonic/dart_wrappable.h
+ FILE: ../../../flutter/third_party/tonic/dart_wrapper_info.h
+diff --git a/lib/ui/painting/image_decoder.cc b/lib/ui/painting/image_decoder.cc
+index d6c4a668f..b6ffb20f5 100644
+--- a/lib/ui/painting/image_decoder.cc
++++ b/lib/ui/painting/image_decoder.cc
+@@ -224,12 +224,13 @@ void ImageDecoder::Decode(fml::RefPtr<ImageDescriptor> descriptor,
+   FML_DCHECK(callback);
+   FML_DCHECK(runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
+ 
+-  // Always service the callback on the UI thread.
+-  auto result = [callback, ui_runner = runners_.GetUITaskRunner()](
++  // Always service the callback (and cleanup the descriptor) on the UI thread.
++  auto result = [callback, descriptor, ui_runner = runners_.GetUITaskRunner()](
+                     SkiaGPUObject<SkImage> image,
+                     fml::tracing::TraceFlow flow) {
+-    ui_runner->PostTask(fml::MakeCopyable(
+-        [callback, image = std::move(image), flow = std::move(flow)]() mutable {
++    ui_runner->PostTask(
++        fml::MakeCopyable([callback, descriptor, image = std::move(image),
++                           flow = std::move(flow)]() mutable {
+           // We are going to terminate the trace flow here. Flows cannot
+           // terminate without a base trace. Add one explicitly.
+           TRACE_EVENT0("flutter", "ImageDecodeCallback");
+diff --git a/lib/ui/painting/image_encoding.cc b/lib/ui/painting/image_encoding.cc
+index c17a784c5..8a7653f0b 100644
+--- a/lib/ui/painting/image_encoding.cc
++++ b/lib/ui/painting/image_encoding.cc
+@@ -35,9 +35,7 @@ enum ImageByteFormat {
+   kPNG,
+ };
+ 
+-void FinalizeSkData(void* isolate_callback_data,
+-                    Dart_WeakPersistentHandle handle,
+-                    void* peer) {
++void FinalizeSkData(void* isolate_callback_data, void* peer) {
+   SkData* buffer = reinterpret_cast<SkData*>(peer);
+   buffer->unref();
+ }
+diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc
+index b834957a9..8a6215110 100644
+--- a/runtime/dart_isolate.cc
++++ b/runtime/dart_isolate.cc
+@@ -981,6 +981,11 @@ void DartIsolate::AddIsolateShutdownCallback(const fml::closure& closure) {
+ }
+ 
+ void DartIsolate::OnShutdownCallback() {
++  tonic::DartState* state = tonic::DartState::Current();
++  if (state != nullptr) {
++    state->SetIsShuttingDown();
++  }
++
+   {
+     tonic::DartApiScope api_scope;
+     Dart_Handle sticky_error = Dart_GetStickyError();
+diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc
+index ae9849a5a..b0c61a1b5 100644
+--- a/shell/platform/embedder/embedder.cc
++++ b/shell/platform/embedder/embedder.cc
+@@ -1922,8 +1922,7 @@ FlutterEngineResult FlutterEnginePostDartObject(
+         dart_object.value.as_external_typed_data.data = buffer;
+         dart_object.value.as_external_typed_data.peer = peer;
+         dart_object.value.as_external_typed_data.callback =
+-            +[](void* unused_isolate_callback_data,
+-                Dart_WeakPersistentHandle unused_handle, void* peer) {
++            +[](void* unused_isolate_callback_data, void* peer) {
+               auto typed_peer = reinterpret_cast<ExternalTypedDataPeer*>(peer);
+               typed_peer->trampoline(typed_peer->user_data);
+               delete typed_peer;
+diff --git a/shell/platform/fuchsia/dart_runner/dart_runner.cc b/shell/platform/fuchsia/dart_runner/dart_runner.cc
+index 76d85405a..415d2885c 100644
+--- a/shell/platform/fuchsia/dart_runner/dart_runner.cc
++++ b/shell/platform/fuchsia/dart_runner/dart_runner.cc
+@@ -86,6 +86,10 @@ void IsolateShutdownCallback(void* isolate_group_data, void* isolate_data) {
+     tonic::DartMicrotaskQueue::GetForCurrentThread()->Destroy();
+     async_loop_quit(loop);
+   }
++
++  auto state =
++      static_cast<std::shared_ptr<tonic::DartState>*>(isolate_group_data);
++  state->get()->SetIsShuttingDown();
+ }
+ 
+ void IsolateGroupCleanupCallback(void* isolate_group_data) {
+diff --git a/testing/run_tests.py b/testing/run_tests.py
+index 06b8256d0..8bdd114f4 100755
+--- a/testing/run_tests.py
++++ b/testing/run_tests.py
+@@ -132,6 +132,8 @@ def RunCCTests(build_dir, filter):
+ 
+   RunEngineExecutable(build_dir, 'runtime_unittests', filter, shuffle_flags)
+ 
++  RunEngineExecutable(build_dir, 'tonic_unittests', filter, shuffle_flags)
++
+   if not IsWindows():
+     # https://github.com/flutter/flutter/issues/36295
+     RunEngineExecutable(build_dir, 'shell_unittests', filter, shuffle_flags)
+diff --git a/third_party/tonic/BUILD.gn b/third_party/tonic/BUILD.gn
+index f5bedda8d..9a6abda8b 100644
+--- a/third_party/tonic/BUILD.gn
++++ b/third_party/tonic/BUILD.gn
+@@ -35,6 +35,8 @@ source_set("tonic") {
+     "dart_persistent_value.h",
+     "dart_state.cc",
+     "dart_state.h",
++    "dart_weak_persistent_value.cc",
++    "dart_weak_persistent_value.h",
+     "dart_wrappable.cc",
+     "dart_wrappable.h",
+     "dart_wrapper_info.h",
+diff --git a/third_party/tonic/dart_state.cc b/third_party/tonic/dart_state.cc
+index b711a2297..3f37685c5 100644
+--- a/third_party/tonic/dart_state.cc
++++ b/third_party/tonic/dart_state.cc
+@@ -27,7 +27,8 @@ DartState::DartState(int dirfd,
+       message_handler_(new DartMessageHandler()),
+       file_loader_(new FileLoader(dirfd)),
+       message_epilogue_(message_epilogue),
+-      has_set_return_code_(false) {}
++      has_set_return_code_(false),
++      is_shutting_down_(false) {}
+ 
+ DartState::~DartState() {}
+ 
+diff --git a/third_party/tonic/dart_state.h b/third_party/tonic/dart_state.h
+index 845914937..1984a66bf 100644
+--- a/third_party/tonic/dart_state.h
++++ b/third_party/tonic/dart_state.h
+@@ -5,6 +5,7 @@
+ #ifndef LIB_TONIC_DART_STATE_H_
+ #define LIB_TONIC_DART_STATE_H_
+ 
++#include <atomic>
+ #include <functional>
+ #include <memory>
+ 
+@@ -68,6 +69,9 @@ class DartState : public std::enable_shared_from_this<DartState> {
+   void SetReturnCodeCallback(std::function<void(uint32_t)> callback);
+   bool has_set_return_code() const { return has_set_return_code_; }
+ 
++  void SetIsShuttingDown() { is_shutting_down_ = true; }
++  bool IsShuttingDown() { return is_shutting_down_; }
++
+   virtual void DidSetIsolate();
+ 
+   static Dart_Handle HandleLibraryTag(Dart_LibraryTag tag,
+@@ -83,6 +87,7 @@ class DartState : public std::enable_shared_from_this<DartState> {
+   std::function<void(Dart_Handle)> message_epilogue_;
+   std::function<void(uint32_t)> set_return_code_callback_;
+   bool has_set_return_code_;
++  std::atomic<bool> is_shutting_down_;
+ 
+  protected:
+   TONIC_DISALLOW_COPY_AND_ASSIGN(DartState);
+diff --git a/third_party/tonic/dart_weak_persistent_value.cc b/third_party/tonic/dart_weak_persistent_value.cc
+new file mode 100644
+index 000000000..a2664d3e0
+--- /dev/null
++++ b/third_party/tonic/dart_weak_persistent_value.cc
+@@ -0,0 +1,70 @@
++// Copyright 2013 The Flutter Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "tonic/dart_weak_persistent_value.h"
++
++#include "tonic/dart_state.h"
++#include "tonic/scopes/dart_isolate_scope.h"
++
++namespace tonic {
++
++DartWeakPersistentValue::DartWeakPersistentValue() : handle_(nullptr) {}
++
++DartWeakPersistentValue::~DartWeakPersistentValue() {
++  Clear();
++}
++
++void DartWeakPersistentValue::Set(DartState* dart_state,
++                                  Dart_Handle object,
++                                  void* peer,
++                                  intptr_t external_allocation_size,
++                                  Dart_HandleFinalizer callback) {
++  TONIC_DCHECK(is_empty());
++  dart_state_ = dart_state->GetWeakPtr();
++  handle_ = Dart_NewWeakPersistentHandle(object, peer, external_allocation_size,
++                                         callback);
++}
++
++void DartWeakPersistentValue::Clear() {
++  if (!handle_) {
++    return;
++  }
++
++  auto dart_state = dart_state_.lock();
++  if (!dart_state) {
++    // The DartVM that the handle used to belong to has been shut down and that
++    // handle has already been deleted.
++    handle_ = nullptr;
++    return;
++  }
++
++  // The DartVM frees the handles during shutdown and calls the finalizers.
++  // Freeing handles during shutdown would fail.
++  if (!dart_state->IsShuttingDown()) {
++    if (Dart_CurrentIsolateGroup()) {
++      Dart_DeleteWeakPersistentHandle(handle_);
++    } else {
++      // If we are not on the mutator thread, this will fail. The caller must
++      // ensure to be on the mutator thread.
++      DartIsolateScope scope(dart_state->isolate());
++      Dart_DeleteWeakPersistentHandle(handle_);
++    }
++  }
++  // If it's shutting down, the handle will be deleted already.
++
++  dart_state_.reset();
++  handle_ = nullptr;
++}
++
++Dart_Handle DartWeakPersistentValue::Get() {
++  auto dart_state = dart_state_.lock();
++  TONIC_DCHECK(dart_state);
++  TONIC_DCHECK(!dart_state->IsShuttingDown());
++  if (!handle_) {
++    return nullptr;
++  }
++  return Dart_HandleFromWeakPersistent(handle_);
++}
++
++}  // namespace tonic
+diff --git a/third_party/tonic/dart_weak_persistent_value.h b/third_party/tonic/dart_weak_persistent_value.h
+new file mode 100644
+index 000000000..5f8aed5ee
+--- /dev/null
++++ b/third_party/tonic/dart_weak_persistent_value.h
+@@ -0,0 +1,48 @@
++// Copyright 2013 The Flutter Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef LIB_TONIC_DART_WEAK_PERSISTENT_VALUE_H_
++#define LIB_TONIC_DART_WEAK_PERSISTENT_VALUE_H_
++
++#include <memory>
++
++#include "third_party/dart/runtime/include/dart_api.h"
++#include "tonic/common/macros.h"
++
++namespace tonic {
++class DartState;
++
++// DartWeakPersistentValue is a bookkeeping class to help pair calls to
++// Dart_NewWeakPersistentHandle with Dart_DeleteWeakPersistentHandle even in
++// the case if IsolateGroup shutdown. Consider using this class instead of
++// holding a Dart_PersistentHandle directly so that you don't leak the
++// Dart_WeakPersistentHandle.
++class DartWeakPersistentValue {
++ public:
++  DartWeakPersistentValue();
++  ~DartWeakPersistentValue();
++
++  Dart_WeakPersistentHandle value() const { return handle_; }
++  bool is_empty() const { return handle_ == nullptr; }
++
++  void Set(DartState* dart_state,
++           Dart_Handle object,
++           void* peer,
++           intptr_t external_allocation_size,
++           Dart_HandleFinalizer callback);
++  void Clear();
++  Dart_Handle Get();
++
++  const std::weak_ptr<DartState>& dart_state() const { return dart_state_; }
++
++ private:
++  std::weak_ptr<DartState> dart_state_;
++  Dart_WeakPersistentHandle handle_;
++
++  TONIC_DISALLOW_COPY_AND_ASSIGN(DartWeakPersistentValue);
++};
++
++}  // namespace tonic
++
++#endif  // LIB_TONIC_DART_WEAK_PERSISTENT_VALUE_H_
+diff --git a/third_party/tonic/dart_wrappable.cc b/third_party/tonic/dart_wrappable.cc
+index 3bdfe3e6e..858215c11 100644
+--- a/third_party/tonic/dart_wrappable.cc
++++ b/third_party/tonic/dart_wrappable.cc
+@@ -12,12 +12,17 @@
+ namespace tonic {
+ 
+ DartWrappable::~DartWrappable() {
+-  TONIC_CHECK(!dart_wrapper_);
++  // Calls the destructor of dart_wrapper_ to delete WeakPersistentHandle.
+ }
+ 
+ // TODO(dnfield): Delete this. https://github.com/flutter/flutter/issues/50997
+ Dart_Handle DartWrappable::CreateDartWrapper(DartState* dart_state) {
+-  TONIC_DCHECK(!dart_wrapper_);
++  if (!dart_wrapper_.is_empty()) {
++    // Any previously given out wrapper must have been GCed.
++    TONIC_DCHECK(Dart_IsNull(dart_wrapper_.Get()));
++    dart_wrapper_.Clear();
++  }
++
+   const DartWrapperInfo& info = GetDartWrapperInfo();
+ 
+   Dart_PersistentHandle type = dart_state->class_library().GetClass(info);
+@@ -36,14 +41,19 @@ Dart_Handle DartWrappable::CreateDartWrapper(DartState* dart_state) {
+   TONIC_DCHECK(!LogIfError(res));
+ 
+   this->RetainDartWrappableReference();  // Balanced in FinalizeDartWrapper.
+-  dart_wrapper_ = Dart_NewWeakPersistentHandle(
+-      wrapper, this, GetAllocationSize(), &FinalizeDartWrapper);
++  dart_wrapper_.Set(dart_state, wrapper, this, GetAllocationSize(),
++                    &FinalizeDartWrapper);
+ 
+   return wrapper;
+ }
+ 
+ void DartWrappable::AssociateWithDartWrapper(Dart_Handle wrapper) {
+-  TONIC_DCHECK(!dart_wrapper_);
++  if (!dart_wrapper_.is_empty()) {
++    // Any previously given out wrapper must have been GCed.
++    TONIC_DCHECK(Dart_IsNull(dart_wrapper_.Get()));
++    dart_wrapper_.Clear();
++  }
++
+   TONIC_CHECK(!LogIfError(wrapper));
+ 
+   const DartWrapperInfo& info = GetDartWrapperInfo();
+@@ -54,26 +64,25 @@ void DartWrappable::AssociateWithDartWrapper(Dart_Handle wrapper) {
+       wrapper, kWrapperInfoIndex, reinterpret_cast<intptr_t>(&info))));
+ 
+   this->RetainDartWrappableReference();  // Balanced in FinalizeDartWrapper.
+-  dart_wrapper_ = Dart_NewWeakPersistentHandle(
+-      wrapper, this, GetAllocationSize(), &FinalizeDartWrapper);
++
++  DartState* dart_state = DartState::Current();
++  dart_wrapper_.Set(dart_state, wrapper, this, GetAllocationSize(),
++                    &FinalizeDartWrapper);
+ }
+ 
+ void DartWrappable::ClearDartWrapper() {
+-  TONIC_DCHECK(dart_wrapper_);
+-  Dart_Handle wrapper = Dart_HandleFromWeakPersistent(dart_wrapper_);
++  TONIC_DCHECK(!dart_wrapper_.is_empty());
++  Dart_Handle wrapper = dart_wrapper_.Get();
+   TONIC_CHECK(!LogIfError(Dart_SetNativeInstanceField(wrapper, kPeerIndex, 0)));
+   TONIC_CHECK(
+       !LogIfError(Dart_SetNativeInstanceField(wrapper, kWrapperInfoIndex, 0)));
+-  Dart_DeleteWeakPersistentHandle(dart_wrapper_);
+-  dart_wrapper_ = nullptr;
++  dart_wrapper_.Clear();
+   this->ReleaseDartWrappableReference();
+ }
+ 
+ void DartWrappable::FinalizeDartWrapper(void* isolate_callback_data,
+-                                        Dart_WeakPersistentHandle wrapper,
+                                         void* peer) {
+   DartWrappable* wrappable = reinterpret_cast<DartWrappable*>(peer);
+-  wrappable->dart_wrapper_ = nullptr;
+   wrappable->ReleaseDartWrappableReference();  // Balanced in CreateDartWrapper.
+ }
+ 
+diff --git a/third_party/tonic/dart_wrappable.h b/third_party/tonic/dart_wrappable.h
+index a036abb85..f944dacef 100644
+--- a/third_party/tonic/dart_wrappable.h
++++ b/third_party/tonic/dart_wrappable.h
+@@ -9,6 +9,7 @@
+ #include "tonic/common/macros.h"
+ #include "tonic/converter/dart_converter.h"
+ #include "tonic/dart_state.h"
++#include "tonic/dart_weak_persistent_value.h"
+ #include "tonic/dart_wrapper_info.h"
+ #include "tonic/logging/dart_error.h"
+ 
+@@ -26,7 +27,7 @@ class DartWrappable {
+     kNumberOfNativeFields,
+   };
+ 
+-  DartWrappable() : dart_wrapper_(nullptr) {}
++  DartWrappable() : dart_wrapper_(DartWeakPersistentValue()) {}
+ 
+   // Subclasses that wish to expose a new interface must override this function
+   // and provide information about their wrapper. There is no need to call your
+@@ -49,7 +50,9 @@ class DartWrappable {
+   Dart_Handle CreateDartWrapper(DartState* dart_state);
+   void AssociateWithDartWrapper(Dart_Handle wrappable);
+   void ClearDartWrapper();  // Warning: Might delete this.
+-  Dart_WeakPersistentHandle dart_wrapper() const { return dart_wrapper_; }
++  Dart_WeakPersistentHandle dart_wrapper() const {
++    return dart_wrapper_.value();
++  }
+ 
+  protected:
+   virtual ~DartWrappable();
+@@ -59,11 +62,9 @@ class DartWrappable {
+       const tonic::DartWrapperInfo& wrapper_info);
+ 
+  private:
+-  static void FinalizeDartWrapper(void* isolate_callback_data,
+-                                  Dart_WeakPersistentHandle wrapper,
+-                                  void* peer);
++  static void FinalizeDartWrapper(void* isolate_callback_data, void* peer);
+ 
+-  Dart_WeakPersistentHandle dart_wrapper_;
++  DartWeakPersistentValue dart_wrapper_;
+ 
+   TONIC_DISALLOW_COPY_AND_ASSIGN(DartWrappable);
+ };
+@@ -103,22 +104,36 @@ struct DartConverter<
+     typename std::enable_if<
+         std::is_convertible<T*, const DartWrappable*>::value>::type> {
+   static Dart_Handle ToDart(DartWrappable* val) {
+-    if (!val)
++    if (!val) {
+       return Dart_Null();
+-    if (Dart_WeakPersistentHandle wrapper = val->dart_wrapper())
+-      return Dart_HandleFromWeakPersistent(wrapper);
++    }
++    if (Dart_WeakPersistentHandle wrapper = val->dart_wrapper()) {
++      auto strong_handle = Dart_HandleFromWeakPersistent(wrapper);
++      if (!Dart_IsNull(strong_handle)) {
++        return strong_handle;
++      }
++      // After the weak referenced object has been GCed, the handle points to
++      // Dart_Null(). Fall through create a new wrapper object.
++    }
+     return val->CreateDartWrapper(DartState::Current());
+   }
+ 
+   static void SetReturnValue(Dart_NativeArguments args,
+                              DartWrappable* val,
+                              bool auto_scope = true) {
+-    if (!val)
++    if (!val) {
+       Dart_SetReturnValue(args, Dart_Null());
+-    else if (Dart_WeakPersistentHandle wrapper = val->dart_wrapper())
+-      Dart_SetWeakHandleReturnValue(args, wrapper);
+-    else
+-      Dart_SetReturnValue(args, val->CreateDartWrapper(DartState::Current()));
++      return;
++    } else if (Dart_WeakPersistentHandle wrapper = val->dart_wrapper()) {
++      auto strong_handle = Dart_HandleFromWeakPersistent(wrapper);
++      if (!Dart_IsNull(strong_handle)) {
++        Dart_SetReturnValue(args, strong_handle);
++        return;
++      }
++      // After the weak referenced object has been GCed, the handle points to
++      // Dart_Null(). Fall through create a new wrapper object.
++    }
++    Dart_SetReturnValue(args, val->CreateDartWrapper(DartState::Current()));
+   }
+ 
+   static T* FromDart(Dart_Handle handle) {
+diff --git a/third_party/tonic/tests/BUILD.gn b/third_party/tonic/tests/BUILD.gn
+new file mode 100644
+index 000000000..359deb13b
+--- /dev/null
++++ b/third_party/tonic/tests/BUILD.gn
+@@ -0,0 +1,33 @@
++# Copyright 2013 The Flutter Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++import("//flutter/testing/testing.gni")
++
++test_fixtures("tonic_fixtures") {
++  dart_main = "fixtures/tonic_test.dart"
++  fixtures = []
++}
++
++executable("tonic_unittests") {
++  testonly = true
++
++  public_configs = [ "//flutter:export_dynamic_symbols" ]
++
++  sources = [
++    "dart_state_unittest.cc",
++    "dart_weak_persistent_handle_unittest.cc",
++  ]
++
++  public_deps = [
++    ":tonic_fixtures",
++    "//flutter/runtime:libdart",
++    "//flutter/runtime:runtime",
++    "//flutter/testing",
++    "//flutter/testing:fixture_test",
++    "//third_party/dart/runtime:dart_api",
++    "//third_party/googletest:gtest",
++  ]
++
++  deps = [ "../:tonic" ]
++}
+diff --git a/third_party/tonic/tests/dart_state_unittest.cc b/third_party/tonic/tests/dart_state_unittest.cc
+new file mode 100644
+index 000000000..3d053de40
+--- /dev/null
++++ b/third_party/tonic/tests/dart_state_unittest.cc
+@@ -0,0 +1,61 @@
++// Copyright 2013 The Flutter Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "flutter/common/task_runners.h"
++#include "flutter/runtime/dart_vm_lifecycle.h"
++#include "flutter/runtime/isolate_configuration.h"
++#include "flutter/testing/fixture_test.h"
++
++namespace flutter {
++namespace testing {
++
++using DartState = FixtureTest;
++
++TEST_F(DartState, IsShuttingDown) {
++  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
++  auto settings = CreateSettingsForFixture();
++  auto vm_ref = DartVMRef::Create(settings);
++  ASSERT_TRUE(vm_ref);
++  auto vm_data = vm_ref.GetVMData();
++  ASSERT_TRUE(vm_data);
++  TaskRunners task_runners(GetCurrentTestName(),    //
++                           GetCurrentTaskRunner(),  //
++                           GetCurrentTaskRunner(),  //
++                           GetCurrentTaskRunner(),  //
++                           GetCurrentTaskRunner()   //
++  );
++  auto isolate_configuration =
++      IsolateConfiguration::InferFromSettings(settings);
++  auto weak_isolate = DartIsolate::CreateRunningRootIsolate(
++      vm_data->GetSettings(),              // settings
++      vm_data->GetIsolateSnapshot(),       // isolate snapshot
++      std::move(task_runners),             // task runners
++      nullptr,                             // window
++      {},                                  // snapshot delegate
++      {},                                  // hint freed delegate
++      {},                                  // io manager
++      {},                                  // unref queue
++      {},                                  // image decoder
++      "main.dart",                         // advisory uri
++      "main",                              // advisory entrypoint
++      DartIsolate::Flags{},                // flags
++      settings.isolate_create_callback,    // isolate create callback
++      settings.isolate_shutdown_callback,  // isolate shutdown callback
++      "main",                              // dart entrypoint
++      std::nullopt,                        // dart entrypoint library
++      std::move(isolate_configuration)     // isolate configuration
++  );
++  auto root_isolate = weak_isolate.lock();
++  ASSERT_TRUE(root_isolate);
++
++  tonic::DartState* dart_state = root_isolate.get();
++  ASSERT_FALSE(dart_state->IsShuttingDown());
++
++  ASSERT_TRUE(root_isolate->Shutdown());
++
++  ASSERT_TRUE(dart_state->IsShuttingDown());
++}
++
++}  // namespace testing
++}  // namespace flutter
+diff --git a/third_party/tonic/tests/dart_weak_persistent_handle_unittest.cc b/third_party/tonic/tests/dart_weak_persistent_handle_unittest.cc
+new file mode 100644
+index 000000000..65d5e116c
+--- /dev/null
++++ b/third_party/tonic/tests/dart_weak_persistent_handle_unittest.cc
+@@ -0,0 +1,167 @@
++// Copyright 2013 The Flutter Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "flutter/testing/dart_isolate_runner.h"
++#include "flutter/testing/fixture_test.h"
++
++namespace flutter {
++namespace testing {
++
++class DartWeakPersistentHandle : public FixtureTest {
++ public:
++  DartWeakPersistentHandle()
++      : settings_(CreateSettingsForFixture()),
++        vm_(DartVMRef::Create(settings_)) {}
++
++  ~DartWeakPersistentHandle() = default;
++
++  [[nodiscard]] bool RunWithEntrypoint(const std::string& entrypoint) {
++    if (running_isolate_) {
++      return false;
++    }
++    auto thread = CreateNewThread();
++    TaskRunners single_threaded_task_runner(GetCurrentTestName(), thread,
++                                            thread, thread, thread);
++    auto isolate =
++        RunDartCodeInIsolate(vm_, settings_, single_threaded_task_runner,
++                             entrypoint, {}, GetFixturesPath());
++    if (!isolate || isolate->get()->GetPhase() != DartIsolate::Phase::Running) {
++      return false;
++    }
++
++    running_isolate_ = std::move(isolate);
++    return true;
++  }
++
++  [[nodiscard]] bool RunInIsolateScope(std::function<bool(void)> closure) {
++    return running_isolate_->RunInIsolateScope(closure);
++  }
++
++ private:
++  Settings settings_;
++  DartVMRef vm_;
++  std::unique_ptr<AutoIsolateShutdown> running_isolate_;
++  FML_DISALLOW_COPY_AND_ASSIGN(DartWeakPersistentHandle);
++};
++
++void NopFinalizer(void* isolate_callback_data, void* peer) {}
++
++TEST_F(DartWeakPersistentHandle, ClearImmediately) {
++  auto weak_persistent_value = tonic::DartWeakPersistentValue();
++
++  fml::AutoResetWaitableEvent event;
++
++  AddNativeCallback(
++      "GiveObjectToNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
++        auto handle = Dart_GetNativeArgument(args, 0);
++
++        auto dart_state = tonic::DartState::Current();
++        ASSERT_TRUE(dart_state);
++        ASSERT_TRUE(tonic::DartState::Current());
++        weak_persistent_value.Set(dart_state, handle, nullptr, 0, NopFinalizer);
++
++        weak_persistent_value.Clear();
++
++        event.Signal();
++      }));
++
++  ASSERT_TRUE(RunWithEntrypoint("callGiveObjectToNative"));
++
++  event.Wait();
++}
++
++TEST_F(DartWeakPersistentHandle, ClearLaterCc) {
++  auto weak_persistent_value = tonic::DartWeakPersistentValue();
++
++  fml::AutoResetWaitableEvent event;
++
++  AddNativeCallback(
++      "GiveObjectToNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
++        auto handle = Dart_GetNativeArgument(args, 0);
++
++        auto dart_state = tonic::DartState::Current();
++        ASSERT_TRUE(dart_state);
++        ASSERT_TRUE(tonic::DartState::Current());
++        weak_persistent_value.Set(dart_state, handle, nullptr, 0, NopFinalizer);
++
++        // Do not clear handle immediately.
++
++        event.Signal();
++      }));
++
++  ASSERT_TRUE(RunWithEntrypoint("callGiveObjectToNative"));
++
++  event.Wait();
++
++  ASSERT_TRUE(RunInIsolateScope([&weak_persistent_value]() -> bool {
++    // Clear on initiative of native.
++    weak_persistent_value.Clear();
++    return true;
++  }));
++}
++
++TEST_F(DartWeakPersistentHandle, ClearLaterDart) {
++  auto weak_persistent_value = tonic::DartWeakPersistentValue();
++
++  fml::AutoResetWaitableEvent event;
++
++  AddNativeCallback(
++      "GiveObjectToNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
++        auto handle = Dart_GetNativeArgument(args, 0);
++
++        auto dart_state = tonic::DartState::Current();
++        ASSERT_TRUE(dart_state);
++        ASSERT_TRUE(tonic::DartState::Current());
++        weak_persistent_value.Set(dart_state, handle, nullptr, 0, NopFinalizer);
++
++        // Do not clear handle immediately.
++      }));
++
++  AddNativeCallback("SignalDone",
++                    CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
++                      // Clear on initiative of Dart.
++                      weak_persistent_value.Clear();
++
++                      event.Signal();
++                    }));
++
++  ASSERT_TRUE(RunWithEntrypoint("testClearLater"));
++
++  event.Wait();
++}
++
++// Handle outside the test body scope so it survives until isolate shutdown.
++tonic::DartWeakPersistentValue global_weak_persistent_value =
++    tonic::DartWeakPersistentValue();
++
++TEST_F(DartWeakPersistentHandle, ClearOnShutdown) {
++  fml::AutoResetWaitableEvent event;
++
++  AddNativeCallback("GiveObjectToNative",
++                    CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
++                      auto handle = Dart_GetNativeArgument(args, 0);
++
++                      auto dart_state = tonic::DartState::Current();
++                      ASSERT_TRUE(dart_state);
++                      ASSERT_TRUE(tonic::DartState::Current());
++
++                      // The test is repeated, ensure the global var is
++                      // cleared before use.
++                      global_weak_persistent_value.Clear();
++
++                      global_weak_persistent_value.Set(
++                          dart_state, handle, nullptr, 0, NopFinalizer);
++
++                      // Do not clear handle, so it is cleared on shutdown.
++
++                      event.Signal();
++                    }));
++
++  ASSERT_TRUE(RunWithEntrypoint("callGiveObjectToNative"));
++
++  event.Wait();
++}
++
++}  // namespace testing
++}  // namespace flutter
+diff --git a/third_party/tonic/tests/fixtures/tonic_test.dart b/third_party/tonic/tests/fixtures/tonic_test.dart
+new file mode 100644
+index 000000000..83e92d4dc
+--- /dev/null
++++ b/third_party/tonic/tests/fixtures/tonic_test.dart
+@@ -0,0 +1,25 @@
++// Copyright 2013 The Flutter Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++void main() {}
++
++class SomeClass {
++  int i;
++  SomeClass(this.i);
++}
++
++void giveObjectToNative(Object someObject) native 'GiveObjectToNative';
++
++void signalDone() native 'SignalDone';
++
++@pragma('vm:entry-point')
++void callGiveObjectToNative() {
++  giveObjectToNative(SomeClass(123));
++}
++
++@pragma('vm:entry-point')
++void testClearLater() {
++  giveObjectToNative(SomeClass(123));
++  signalDone();
++}
+diff --git a/third_party/tonic/typed_data/dart_byte_data.cc b/third_party/tonic/typed_data/dart_byte_data.cc
+index dc312de3f..cfb07399f 100644
+--- a/third_party/tonic/typed_data/dart_byte_data.cc
++++ b/third_party/tonic/typed_data/dart_byte_data.cc
+@@ -16,9 +16,7 @@ namespace {
+ // with a buffer allocated outside the Dart heap.
+ const int kExternalSizeThreshold = 1000;
+ 
+-void FreeFinalizer(void* isolate_callback_data,
+-                   Dart_WeakPersistentHandle handle,
+-                   void* peer) {
++void FreeFinalizer(void* isolate_callback_data, void* peer) {
+   free(peer);
+ }
+