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);
+ }
+