[vm] Native API: Introduce Dart_FinalizableHandle

Introduces Dart_NewFinalizableHandle which does auto delete itself,
but does not allow accessing the weak referenced object.

Issue: https://github.com/dart-lang/sdk/issues/42312

Change-Id: I24ea732925122c453213c4fa3f629761c352f838
Cq-Include-Trybots:dart/try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,app-kernel-linux-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-precomp-linux-debug-x64-try,vm-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-precomp-msan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,dart-sdk-linux-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,front-end-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-mac-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-nnbd-linux-debug-x64-try,analyzer-nnbd-linux-release-try,front-end-nnbd-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/154695
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Daco Harkes 2020-07-23 11:30:39 +00:00
parent debb4dddea
commit 202e32afeb
33 changed files with 884 additions and 153 deletions

View file

@ -1,3 +1,10 @@
## 2.10.0
### Dart VM
* Introduces `Dart_FinalizableHandle`s. They do auto-delete, and the weakly
referred object cannot be accessed through them.
## 2.9.0
### Language
@ -134,7 +141,7 @@ Updated the Linter to `0.1.117`, which includes:
* Preserve Windows line endings in `pubspec.lock` if they are already there
([#2489](https://github.com/dart-lang/pub/pull/2489)).
* Better terminal color-detection. Use colors in terminals on Windows.
* Fix git folder names in cache, allowing for ssh-style git
* Fix git folder names in cache, allowing for ssh-style git
dependencies.
* Fix: Avoid precompilation of dependencies of global packages.

View file

@ -5430,10 +5430,11 @@ class MemoryUsage extends Response {
/// The amount of non-Dart memory that is retained by Dart objects. For
/// example, memory associated with Dart objects through APIs such as
/// Dart_NewWeakPersistentHandle and Dart_NewExternalTypedData. This usage is
/// only as accurate as the values supplied to these APIs from the VM embedder
/// or native extensions. This external memory applies GC pressure, but is
/// separate from heapUsage and heapCapacity.
/// Dart_NewFinalizableHandle, Dart_NewWeakPersistentHandle and
/// Dart_NewExternalTypedData. This usage is only as accurate as the values
/// supplied to these APIs from the VM embedder or native extensions. This
/// external memory applies GC pressure, but is separate from heapUsage and
/// heapCapacity.
int externalUsage;
/// The total capacity of the heap in bytes. This is the amount of memory used

View file

@ -232,9 +232,7 @@ void FUNCTION_NAME(Directory_GetAsyncDirectoryListerPointer)(
}
}
static void ReleaseListing(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
static void ReleaseListing(void* isolate_callback_data, void* peer) {
AsyncDirectoryListing* listing =
reinterpret_cast<AsyncDirectoryListing*>(peer);
listing->Release();
@ -247,8 +245,8 @@ void FUNCTION_NAME(Directory_SetAsyncDirectoryListerPointer)(
DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1));
AsyncDirectoryListing* listing =
reinterpret_cast<AsyncDirectoryListing*>(listing_pointer);
Dart_NewWeakPersistentHandle(dart_this, reinterpret_cast<void*>(listing),
sizeof(*listing), ReleaseListing);
Dart_NewFinalizableHandle(dart_this, reinterpret_cast<void*>(listing),
sizeof(*listing), ReleaseListing);
Dart_Handle result = Dart_SetNativeInstanceField(
dart_this, kAsyncDirectoryListerFieldIndex, listing_pointer);
ThrowIfError(result);

View file

@ -68,9 +68,7 @@ void FUNCTION_NAME(File_GetPointer)(Dart_NativeArguments args) {
Dart_SetIntegerReturnValue(args, file_pointer);
}
static void ReleaseFile(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
static void ReleaseFile(void* isolate_callback_data, void* peer) {
File* file = reinterpret_cast<File*>(peer);
file->Release();
}
@ -79,9 +77,9 @@ void FUNCTION_NAME(File_SetPointer)(Dart_NativeArguments args) {
Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
intptr_t file_pointer = DartUtils::GetNativeIntptrArgument(args, 1);
File* file = reinterpret_cast<File*>(file_pointer);
Dart_WeakPersistentHandle handle = Dart_NewWeakPersistentHandle(
Dart_FinalizableHandle handle = Dart_NewFinalizableHandle(
dart_this, reinterpret_cast<void*>(file), sizeof(*file), ReleaseFile);
file->SetWeakHandle(handle);
file->SetFinalizableHandle(handle);
SetFile(dart_this, file_pointer);
}
@ -148,7 +146,7 @@ void FUNCTION_NAME(File_Close)(Dart_NativeArguments args) {
return;
}
file->Close();
file->DeleteWeakHandle(Dart_CurrentIsolate());
file->DeleteFinalizableHandle(Dart_CurrentIsolate(), dart_this);
file->Release();
ThrowIfError(

View file

@ -184,21 +184,23 @@ class File : public ReferenceCounted<File> {
// Calls the platform-specific functions to close the file.
void Close();
// Returns the weak persistent handle for the File's Dart wrapper.
Dart_WeakPersistentHandle WeakHandle() const { return weak_handle_; }
// Set the weak persistent handle for the File's Dart wrapper.
void SetWeakHandle(Dart_WeakPersistentHandle handle) {
ASSERT(weak_handle_ == NULL);
weak_handle_ = handle;
// Returns the finalizable handle for the File's Dart wrapper.
Dart_FinalizableHandle FinalizableHandle() const {
return finalizable_handle_;
}
// Deletes the weak persistent handle for the File's Dart wrapper. Call
// Set the finalizable handle for the File's Dart wrapper.
void SetFinalizableHandle(Dart_FinalizableHandle handle) {
ASSERT(finalizable_handle_ == NULL);
finalizable_handle_ = handle;
}
// Deletes the finalizable handle for the File's Dart wrapper. Call
// when the file is explicitly closed and the finalizer is no longer
// needed.
void DeleteWeakHandle(Dart_Isolate isolate) {
Dart_DeleteWeakPersistentHandle(weak_handle_);
weak_handle_ = NULL;
void DeleteFinalizableHandle(Dart_Isolate isolate, Dart_Handle strong_ref) {
Dart_DeleteFinalizableHandle(finalizable_handle_, strong_ref);
finalizable_handle_ = NULL;
}
// Open the file with the given path. The file is always opened for
@ -319,7 +321,7 @@ class File : public ReferenceCounted<File> {
private:
explicit File(FileHandle* handle)
: ReferenceCounted(), handle_(handle), weak_handle_(NULL) {}
: ReferenceCounted(), handle_(handle), finalizable_handle_(NULL) {}
~File();
@ -330,10 +332,10 @@ class File : public ReferenceCounted<File> {
// FileHandle is an OS specific class which stores data about the file.
FileHandle* handle_; // OS specific handle for the file.
// We retain the weak handle because we can do cleanup eagerly when Dart code
// calls closeSync(). In that case, we delete the weak handle so that the
// finalizer doesn't run.
Dart_WeakPersistentHandle weak_handle_;
// We retain the finalizable handle because we can do cleanup eagerly when
// Dart code calls closeSync(). In that case, we delete the finalizable
// handle so that the finalizer doesn't run.
Dart_FinalizableHandle finalizable_handle_;
friend class ReferenceCounted<File>;
DISALLOW_COPY_AND_ASSIGN(File);

View file

@ -256,9 +256,7 @@ void FUNCTION_NAME(Filter_Processed)(Dart_NativeArguments args) {
}
}
static void DeleteFilter(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* filter_pointer) {
static void DeleteFilter(void* isolate_data, void* filter_pointer) {
Filter* filter = reinterpret_cast<Filter*>(filter_pointer);
delete filter;
}
@ -272,8 +270,8 @@ Dart_Handle Filter::SetFilterAndCreateFinalizer(Dart_Handle filter,
if (Dart_IsError(err)) {
return err;
}
Dart_NewWeakPersistentHandle(filter, reinterpret_cast<void*>(filter_pointer),
size, DeleteFilter);
Dart_NewFinalizableHandle(filter, reinterpret_cast<void*>(filter_pointer),
size, DeleteFilter);
return err;
}

View file

@ -145,9 +145,7 @@ Dart_Handle Loader::LibraryTagHandler(Dart_LibraryTag tag,
return Dart_Null();
}
#else
static void MallocFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
static void MallocFinalizer(void* isolate_callback_data, void* peer) {
free(peer);
}
@ -185,8 +183,8 @@ Dart_Handle Loader::LibraryTagHandler(Dart_LibraryTag tag,
}
result = Dart_NewExternalTypedData(Dart_TypedData_kUint8, kernel_buffer,
kernel_buffer_size);
Dart_NewWeakPersistentHandle(result, kernel_buffer, kernel_buffer_size,
MallocFinalizer);
Dart_NewFinalizableHandle(result, kernel_buffer, kernel_buffer_size,
MallocFinalizer);
return result;
}
if (tag == Dart_kImportExtensionTag) {

View file

@ -16,9 +16,7 @@ namespace bin {
static const int kNamespaceNativeFieldIndex = 0;
static void ReleaseNamespace(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
static void ReleaseNamespace(void* isolate_callback_data, void* peer) {
Namespace* namespc = reinterpret_cast<Namespace*>(peer);
ASSERT(namespc != NULL);
namespc->Release();
@ -84,8 +82,8 @@ void FUNCTION_NAME(Namespace_Create)(Dart_NativeArguments args) {
// Set up a finalizer for the Dart object so that we can do any necessary
// platform-specific cleanup for the namespc.
Dart_NewWeakPersistentHandle(namespc_obj, reinterpret_cast<void*>(namespc),
sizeof(*namespc), ReleaseNamespace);
Dart_NewFinalizableHandle(namespc_obj, reinterpret_cast<void*>(namespc),
sizeof(*namespc), ReleaseNamespace);
Dart_SetReturnValue(args, namespc_obj);
}

View file

@ -62,9 +62,7 @@ static SSLFilter* GetFilter(Dart_NativeArguments args) {
return filter;
}
static void DeleteFilter(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* context_pointer) {
static void DeleteFilter(void* isolate_data, void* context_pointer) {
SSLFilter* filter = reinterpret_cast<SSLFilter*>(context_pointer);
filter->Release();
}
@ -78,8 +76,8 @@ static Dart_Handle SetFilter(Dart_NativeArguments args, SSLFilter* filter) {
dart_this, SSLFilter::kSSLFilterNativeFieldIndex,
reinterpret_cast<intptr_t>(filter));
RETURN_IF_ERROR(err);
Dart_NewWeakPersistentHandle(dart_this, reinterpret_cast<void*>(filter),
SSLFilter::kApproximateSize, DeleteFilter);
Dart_NewFinalizableHandle(dart_this, reinterpret_cast<void*>(filter),
SSLFilter::kApproximateSize, DeleteFilter);
return Dart_Null();
}

View file

@ -93,9 +93,7 @@ SSLCertContext* SSLCertContext::GetSecurityContext(Dart_NativeArguments args) {
return context;
}
static void DeleteSecurityContext(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* context_pointer) {
static void DeleteSecurityContext(void* isolate_data, void* context_pointer) {
SSLCertContext* context = static_cast<SSLCertContext*>(context_pointer);
context->Release();
}
@ -109,15 +107,13 @@ static Dart_Handle SetSecurityContext(Dart_NativeArguments args,
dart_this, SSLCertContext::kSecurityContextNativeFieldIndex,
reinterpret_cast<intptr_t>(context));
RETURN_IF_ERROR(err);
Dart_NewWeakPersistentHandle(dart_this, context,
SSLCertContext::kApproximateSize,
DeleteSecurityContext);
Dart_NewFinalizableHandle(dart_this, context,
SSLCertContext::kApproximateSize,
DeleteSecurityContext);
return Dart_Null();
}
static void ReleaseCertificate(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* context_pointer) {
static void ReleaseCertificate(void* isolate_data, void* context_pointer) {
X509* cert = reinterpret_cast<X509*>(context_pointer);
X509_free(cert);
}
@ -158,9 +154,9 @@ Dart_Handle X509Helper::WrappedX509Certificate(X509* certificate) {
const intptr_t approximate_size_of_certificate =
sizeof(*certificate) + EstimateX509Size(certificate);
ASSERT(approximate_size_of_certificate > 0);
Dart_NewWeakPersistentHandle(result, reinterpret_cast<void*>(certificate),
approximate_size_of_certificate,
ReleaseCertificate);
Dart_NewFinalizableHandle(result, reinterpret_cast<void*>(certificate),
approximate_size_of_certificate,
ReleaseCertificate);
return result;
}

View file

@ -1253,9 +1253,7 @@ void FUNCTION_NAME(Socket_AvailableDatagram)(Dart_NativeArguments args) {
Dart_SetBooleanReturnValue(args, available);
}
static void NormalSocketFinalizer(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* data) {
static void NormalSocketFinalizer(void* isolate_data, void* data) {
Socket* socket = reinterpret_cast<Socket*>(data);
if (socket->fd() >= 0) {
const int64_t flags = 1 << kCloseCommand;
@ -1266,9 +1264,7 @@ static void NormalSocketFinalizer(void* isolate_data,
socket->Release();
}
static void ListeningSocketFinalizer(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* data) {
static void ListeningSocketFinalizer(void* isolate_data, void* data) {
Socket* socket = reinterpret_cast<Socket*>(data);
if (socket->fd() >= 0) {
const int64_t flags = (1 << kListeningSocket) | (1 << kCloseCommand);
@ -1279,9 +1275,7 @@ static void ListeningSocketFinalizer(void* isolate_data,
socket->Release();
}
static void StdioSocketFinalizer(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* data) {
static void StdioSocketFinalizer(void* isolate_data, void* data) {
Socket* socket = reinterpret_cast<Socket*>(data);
if (socket->fd() >= 0) {
socket->CloseFd();
@ -1289,9 +1283,7 @@ static void StdioSocketFinalizer(void* isolate_data,
socket->Release();
}
static void SignalSocketFinalizer(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* data) {
static void SignalSocketFinalizer(void* isolate_data, void* data) {
Socket* socket = reinterpret_cast<Socket*>(data);
if (socket->fd() >= 0) {
Process::ClearSignalHandlerByFd(socket->fd(), socket->isolate_port());
@ -1312,7 +1304,7 @@ void Socket::ReuseSocketIdNativeField(Dart_Handle handle,
if (Dart_IsError(err)) {
Dart_PropagateError(err);
}
Dart_WeakPersistentHandleFinalizer callback;
Dart_HandleFinalizer callback;
switch (finalizer) {
case kFinalizerNormal:
callback = NormalSocketFinalizer;
@ -1332,8 +1324,8 @@ void Socket::ReuseSocketIdNativeField(Dart_Handle handle,
break;
}
if (callback != NULL) {
Dart_NewWeakPersistentHandle(handle, reinterpret_cast<void*>(socket),
sizeof(Socket), callback);
Dart_NewFinalizableHandle(handle, reinterpret_cast<void*>(socket),
sizeof(Socket), callback);
}
}

View file

@ -313,9 +313,7 @@ void FUNCTION_NAME(SynchronousSocket_GetRemotePeer)(Dart_NativeArguments args) {
delete addr;
}
static void SynchronousSocketFinalizer(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* data) {
static void SynchronousSocketFinalizer(void* isolate_data, void* data) {
SynchronousSocket* socket = reinterpret_cast<SynchronousSocket*>(data);
if (socket->fd() >= 0) {
SynchronousSocket::Close(socket->fd());
@ -334,9 +332,9 @@ Dart_Handle SynchronousSocket::SetSocketIdNativeField(
return error;
}
Dart_NewWeakPersistentHandle(handle, reinterpret_cast<void*>(socket),
sizeof(SynchronousSocket),
SynchronousSocketFinalizer);
Dart_NewFinalizableHandle(handle, reinterpret_cast<void*>(socket),
sizeof(SynchronousSocket),
SynchronousSocketFinalizer);
return error;
}

View file

@ -239,17 +239,26 @@ typedef struct _Dart_IsolateGroup* Dart_IsolateGroup;
* The type Dart_PersistentHandle is a Dart_Handle and it is used to
* document that a persistent handle is expected as a parameter to a call
* or the return value from a call is a persistent handle.
*
* FinalizableHandles are persistent handles which are auto deleted when
* 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.
*/
typedef struct _Dart_Handle* Dart_Handle;
typedef Dart_Handle Dart_PersistentHandle;
typedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle;
// These three structs are versioned by DART_API_DL_MAJOR_VERSION, bump the
typedef struct _Dart_FinalizableHandle* Dart_FinalizableHandle;
// 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);
/**
* Is this an error handle?
@ -444,6 +453,8 @@ DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1,
/**
* Deallocates a persistent handle.
*
* Requires there to be a current isolate group.
*/
DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object);
@ -457,16 +468,17 @@ DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object);
* 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 not have a current isolate, and 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 from the callback. If the handle is deleted before the object becomes
* unreachable, the callback is never invoked.
* 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.
*
* Requires there to be a current isolate.
*
@ -488,12 +500,86 @@ Dart_NewWeakPersistentHandle(Dart_Handle object,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback);
/**
* Deletes the given weak persistent [object] handle.
*
* Requires there to be a current isolate group.
*/
DART_EXPORT void Dart_DeleteWeakPersistentHandle(
Dart_WeakPersistentHandle object);
/**
* Updates the external memory size for the given weak persistent handle.
*
* May trigger garbage collection.
*/
DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object,
intptr_t external_allocation_size);
/**
* Allocates a finalizable handle for an object.
*
* This handle has the lifetime of the current isolate group 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_DeleteFinalizableHandle.
*
* If the object becomes unreachable the callback is invoked with the
* the peer as argument. 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.
* 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.
*
* Requires there to be a current isolate.
*
* \param object An object.
* \param peer A pointer to a native object or NULL. This value is
* provided to callback when it is invoked.
* \param external_allocation_size The number of externally allocated
* bytes for peer. Used to inform the garbage collector.
* \param callback A function pointer that will be invoked sometime
* after the object is garbage collected, unless the handle has been deleted.
* A valid callback needs to be specified it cannot be NULL.
*
* \return The finalizable handle or NULL. NULL is returned in case of bad
* parameters.
*/
DART_EXPORT Dart_FinalizableHandle
Dart_NewFinalizableHandle(Dart_Handle object,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback);
/**
* Deletes the given finalizable [object] handle.
*
* The caller has to provide the actual Dart object the handle was created from
* to prove the object (and therefore the finalizable handle) is still alive.
*
* Requires there to be a current isolate.
*/
DART_EXPORT void Dart_DeleteFinalizableHandle(Dart_FinalizableHandle object,
Dart_Handle strong_ref_to_object);
/**
* Updates the external memory size for the given finalizable handle.
*
* The caller has to provide the actual Dart object the handle was created from
* to prove the object (and therefore the finalizable handle) is still alive.
*
* May trigger garbage collection.
*/
DART_EXPORT void Dart_UpdateFinalizableExternalSize(
Dart_FinalizableHandle object,
Dart_Handle strong_ref_to_object,
intptr_t external_allocation_size);
/*
* ==========================
* Initialization and Globals

View file

@ -108,6 +108,25 @@ DART_EXTERN_C Dart_WeakPersistentHandle (*Dart_NewWeakPersistentHandle_DL)(
DART_EXTERN_C void (*Dart_DeleteWeakPersistentHandle_DL)(
Dart_WeakPersistentHandle object);
DART_EXTERN_C void (*Dart_UpdateExternalSize_DL)(
Dart_WeakPersistentHandle object,
intptr_t external_allocation_size);
DART_EXTERN_C Dart_FinalizableHandle (*Dart_NewFinalizableHandle_DL)(
Dart_Handle object,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback);
DART_EXTERN_C void (*Dart_DeleteFinalizableHandle_DL)(
Dart_FinalizableHandle object,
Dart_Handle strong_ref_to_object);
DART_EXTERN_C void (*Dart_UpdateFinalizableExternalSize_DL)(
Dart_FinalizableHandle object,
Dart_Handle strong_ref_to_object,
intptr_t external_allocation_size);
DART_EXTERN_C bool (*Dart_Post_DL)(Dart_Port_DL port_id, Dart_Handle object);
DART_EXTERN_C Dart_Handle (*Dart_NewSendPort_DL)(Dart_Port_DL port_id);
@ -123,4 +142,4 @@ DART_EXTERN_C void (*Dart_ExitScope_DL)();
//
// End of verbatim copy.
#endif /* RUNTIME_INCLUDE_DART_API_DL_H_ */ /* NOLINT */
#endif /* RUNTIME_INCLUDE_DART_API_DL_H_ */ /* NOLINT */

View file

@ -11,6 +11,6 @@
// 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 0
#define DART_API_DL_MINOR_VERSION 1
#endif /* RUNTIME_INCLUDE_DART_VERSION_H_ */ /* NOLINT */

View file

@ -41,6 +41,10 @@
F(Dart_NewWeakPersistentHandle) \
F(Dart_HandleFromWeakPersistent) \
F(Dart_DeleteWeakPersistentHandle) \
F(Dart_UpdateExternalSize) \
F(Dart_NewFinalizableHandle) \
F(Dart_DeleteFinalizableHandle) \
F(Dart_UpdateFinalizableExternalSize) \
/* Dart_Port */ \
F(Dart_Post) \
F(Dart_NewSendPort) \

View file

@ -615,7 +615,6 @@ DEFINE_NATIVE_ENTRY(Isolate_sendOOB, 0, 2) {
}
static void ExternalTypedDataFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
free(peer);
}
@ -725,7 +724,8 @@ DEFINE_NATIVE_ENTRY(TransferableTypedData_materialize, 0, 1) {
thread->heap()->SpaceForExternal(length)));
FinalizablePersistentHandle::New(thread->isolate(), typed_data,
/* peer= */ data,
&ExternalTypedDataFinalizer, length);
&ExternalTypedDataFinalizer, length,
/*auto_delete=*/true);
return typed_data.raw();
}

View file

@ -218,7 +218,7 @@ abstract class SnapshotObject {
int get internalSize;
/// The sum of all external allocations associated with this object.
/// See Dart_NewWeakPersistentHandle.
/// See Dart_NewFinalizableHandle and Dart_NewWeakPersistentHandle.
int get externalSize;
/// The [shallowSize] of this object, plus the retainedSize of all its

View file

@ -10,6 +10,7 @@
#include "lib/stacktrace.h"
#include "platform/assert.h"
#include "platform/unicode.h"
#include "vm/class_finalizer.h"
#include "vm/clustered_snapshot.h"
#include "vm/compilation_trace.h"
@ -22,7 +23,6 @@
#include "vm/debugger.h"
#include "vm/dwarf.h"
#include "vm/elf.h"
#include "platform/unicode.h"
#include "vm/exceptions.h"
#include "vm/flags.h"
#include "vm/growable_array.h"
@ -690,6 +690,14 @@ FinalizablePersistentHandle* FinalizablePersistentHandle::Cast(
#endif
return reinterpret_cast<FinalizablePersistentHandle*>(handle);
}
FinalizablePersistentHandle* FinalizablePersistentHandle::Cast(
Dart_FinalizableHandle handle) {
#if defined(DEBUG)
ApiState* state = IsolateGroup::Current()->api_state();
ASSERT(state->IsValidFinalizableHandle(handle));
#endif
return reinterpret_cast<FinalizablePersistentHandle*>(handle);
}
void FinalizablePersistentHandle::Finalize(
IsolateGroup* isolate_group,
@ -697,13 +705,24 @@ void FinalizablePersistentHandle::Finalize(
if (!handle->raw()->IsHeapObject()) {
return; // Free handle.
}
Dart_WeakPersistentHandleFinalizer callback = handle->callback();
ASSERT(callback != NULL);
void* peer = handle->peer();
Dart_WeakPersistentHandle object = handle->apiHandle();
(*callback)(isolate_group->embedder_data(), object, 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);
}
state->FreeWeakPersistentHandle(handle);
}
@ -920,6 +939,19 @@ Dart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object) {
return Api::NewHandle(thread, weak_ref->raw());
}
static Dart_Handle HandleFromFinalizable(Dart_FinalizableHandle object) {
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
CHECK_ISOLATE(isolate);
ApiState* state = isolate->group()->api_state();
ASSERT(state != NULL);
TransitionNativeToVM transition(thread);
NoSafepointScope no_safepoint_scope;
FinalizablePersistentHandle* weak_ref =
FinalizablePersistentHandle::Cast(object);
return Api::NewHandle(thread, weak_ref->raw());
}
DART_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object) {
DARTSCOPE(Thread::Current());
Isolate* I = T->isolate();
@ -943,7 +975,7 @@ DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1,
obj1_ref->set_raw(obj2_ref);
}
static Dart_WeakPersistentHandle AllocateFinalizableHandle(
static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
Thread* thread,
const Object& ref,
void* peer,
@ -954,11 +986,12 @@ static Dart_WeakPersistentHandle AllocateFinalizableHandle(
}
FinalizablePersistentHandle* finalizable_ref =
FinalizablePersistentHandle::New(thread->isolate(), ref, peer, callback,
external_allocation_size);
return finalizable_ref->apiHandle();
external_allocation_size,
/*auto_delete=*/true);
return finalizable_ref->ApiWeakPersistentHandle();
}
static Dart_WeakPersistentHandle AllocateFinalizableHandle(
static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
Thread* thread,
Dart_Handle object,
void* peer,
@ -967,6 +1000,36 @@ static Dart_WeakPersistentHandle AllocateFinalizableHandle(
REUSABLE_OBJECT_HANDLESCOPE(thread);
Object& ref = thread->ObjectHandle();
ref = Api::UnwrapHandle(object);
return AllocateWeakPersistentHandle(thread, ref, peer,
external_allocation_size, callback);
}
static Dart_FinalizableHandle AllocateFinalizableHandle(
Thread* thread,
const Object& ref,
void* peer,
intptr_t external_allocation_size,
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);
return finalizable_ref->ApiFinalizableHandle();
}
static Dart_FinalizableHandle AllocateFinalizableHandle(
Thread* thread,
Dart_Handle object,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback) {
REUSABLE_OBJECT_HANDLESCOPE(thread);
Object& ref = thread->ObjectHandle();
ref = Api::UnwrapHandle(object);
return AllocateFinalizableHandle(thread, ref, peer, external_allocation_size,
callback);
}
@ -982,6 +1045,22 @@ Dart_NewWeakPersistentHandle(Dart_Handle object,
return NULL;
}
TransitionNativeToVM transition(thread);
return AllocateWeakPersistentHandle(thread, object, peer,
external_allocation_size, callback);
}
DART_EXPORT Dart_FinalizableHandle
Dart_NewFinalizableHandle(Dart_Handle object,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback) {
Thread* thread = Thread::Current();
CHECK_ISOLATE(thread->isolate());
if (callback == nullptr) {
return nullptr;
}
TransitionNativeToVM transition(thread);
return AllocateFinalizableHandle(thread, object, peer,
external_allocation_size, callback);
}
@ -998,6 +1077,21 @@ DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object,
weak_ref->UpdateExternalSize(external_size, isolate_group);
}
DART_EXPORT void Dart_UpdateFinalizableExternalSize(
Dart_FinalizableHandle object,
Dart_Handle strong_ref_to_object,
intptr_t external_allocation_size) {
if (!Dart_IdentityEquals(strong_ref_to_object,
HandleFromFinalizable(object))) {
FATAL1(
"%s expects arguments 'object' and 'strong_ref_to_object' to point to "
"the same object.",
CURRENT_FUNC);
}
auto wph_object = reinterpret_cast<Dart_WeakPersistentHandle>(object);
Dart_UpdateExternalSize(wph_object, external_allocation_size);
}
DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object) {
IsolateGroup* isolate_group = IsolateGroup::Current();
CHECK_ISOLATE_GROUP(isolate_group);
@ -1025,6 +1119,22 @@ DART_EXPORT void Dart_DeleteWeakPersistentHandle(
state->FreeWeakPersistentHandle(weak_ref);
}
DART_EXPORT void Dart_DeleteFinalizableHandle(
Dart_FinalizableHandle object,
Dart_Handle strong_ref_to_object) {
if (!Dart_IdentityEquals(strong_ref_to_object,
HandleFromFinalizable(object))) {
FATAL1(
"%s expects arguments 'object' and 'strong_ref_to_object' to point to "
"the same object.",
CURRENT_FUNC);
}
auto wph_object = reinterpret_cast<Dart_WeakPersistentHandle>(object);
return Dart_DeleteWeakPersistentHandle(wph_object);
}
// --- Initialization and Globals ---
DART_EXPORT const char* Dart_VersionString() {
@ -3814,8 +3924,8 @@ static Dart_Handle NewExternalTypedData(
result = ExternalTypedData::New(cid, reinterpret_cast<uint8_t*>(data), length,
thread->heap()->SpaceForExternal(bytes));
if (callback != nullptr) {
AllocateFinalizableHandle(thread, result, peer, external_allocation_size,
callback);
AllocateWeakPersistentHandle(thread, result, peer, external_allocation_size,
callback);
}
return Api::NewHandle(thread, result.raw());
}

View file

@ -2792,12 +2792,16 @@ TEST_CASE(DartAPI_ExternalUint8ClampedArrayAccess) {
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,
@ -2826,9 +2830,7 @@ TEST_CASE(DartAPI_ExternalTypedDataCallback) {
}
}
static void SlowFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
static void SlowFinalizer(void* isolate_callback_data, void* peer) {
OS::Sleep(10);
intptr_t* count = reinterpret_cast<intptr_t*>(peer);
(*count)++;
@ -2839,9 +2841,36 @@ TEST_CASE(DartAPI_SlowFinalizer) {
for (intptr_t i = 0; i < 10; i++) {
Dart_EnterScope();
Dart_Handle str1 = Dart_NewStringFromCString("Live fast");
Dart_NewWeakPersistentHandle(str1, &count, 0, SlowFinalizer);
Dart_NewFinalizableHandle(str1, &count, 0, SlowFinalizer);
Dart_Handle str2 = Dart_NewStringFromCString("Die young");
Dart_NewWeakPersistentHandle(str2, &count, 0, SlowFinalizer);
Dart_NewFinalizableHandle(str2, &count, 0, SlowFinalizer);
Dart_ExitScope();
{
TransitionNativeToVM transition(thread);
GCTestHelper::CollectAllGarbage();
}
}
EXPECT_EQ(20, count);
}
static void SlowWeakPersistentHandle(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
OS::Sleep(10);
intptr_t* count = reinterpret_cast<intptr_t*>(peer);
(*count)++;
}
TEST_CASE(DartAPI_SlowWeakPersistenhandle) {
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);
Dart_Handle str2 = Dart_NewStringFromCString("Die young");
Dart_NewWeakPersistentHandle(str2, &count, 0, SlowWeakPersistentHandle);
Dart_ExitScope();
{
@ -3219,6 +3248,122 @@ TEST_CASE(DartAPI_WeakPersistentHandle) {
}
}
static Dart_FinalizableHandle finalizable_new_ref = nullptr;
static void* finalizable_new_ref_peer = 0;
static Dart_FinalizableHandle finalizable_old_ref = nullptr;
static void* finalizable_old_ref_peer = 0;
static void FinalizableHandleCallback(void* isolate_callback_data, void* peer) {
if (peer == finalizable_new_ref_peer) {
finalizable_new_ref_peer = 0;
finalizable_new_ref = nullptr;
} else if (peer == finalizable_old_ref_peer) {
finalizable_old_ref_peer = 0;
finalizable_old_ref = nullptr;
}
}
TEST_CASE(DartAPI_FinalizableHandle) {
// GCs due to allocations or weak handle creation can cause early promotion
// and interfer with the scenario this test is verifying.
NoHeapGrowthControlScope force_growth;
void* peer = reinterpret_cast<void*>(0);
Dart_Handle local_new_ref = Dart_Null();
finalizable_new_ref = Dart_NewFinalizableHandle(local_new_ref, peer, 0,
FinalizableHandleCallback);
finalizable_new_ref_peer = peer;
peer = reinterpret_cast<void*>(1);
Dart_Handle local_old_ref = Dart_Null();
finalizable_old_ref = Dart_NewFinalizableHandle(local_old_ref, peer, 0,
FinalizableHandleCallback);
finalizable_old_ref_peer = peer;
{
Dart_EnterScope();
// Create an object in new space.
Dart_Handle new_ref = AllocateNewString("new string");
EXPECT_VALID(new_ref);
// Create an object in old space.
Dart_Handle old_ref = AllocateOldString("old string");
EXPECT_VALID(old_ref);
// Create a weak ref to the new space object.
peer = reinterpret_cast<void*>(2);
finalizable_new_ref =
Dart_NewFinalizableHandle(new_ref, peer, 0, FinalizableHandleCallback);
finalizable_new_ref_peer = peer;
// Create a weak ref to the old space object.
peer = reinterpret_cast<void*>(3);
finalizable_old_ref =
Dart_NewFinalizableHandle(old_ref, peer, 0, FinalizableHandleCallback);
finalizable_old_ref_peer = peer;
{
TransitionNativeToVM transition(thread);
// Garbage collect new space.
GCTestHelper::CollectNewSpace();
}
// Nothing should be invalidated or cleared.
EXPECT_VALID(new_ref);
EXPECT(!Dart_IsNull(new_ref));
EXPECT_VALID(old_ref);
EXPECT(!Dart_IsNull(old_ref));
{
TransitionNativeToVM transition(thread);
// Garbage collect old space.
GCTestHelper::CollectOldSpace();
}
// Nothing should be invalidated or cleared.
EXPECT_VALID(new_ref);
EXPECT(!Dart_IsNull(new_ref));
EXPECT_VALID(old_ref);
EXPECT(!Dart_IsNull(old_ref));
// Delete local (strong) references.
Dart_ExitScope();
}
{
TransitionNativeToVM transition(thread);
// Garbage collect new space again.
GCTestHelper::CollectNewSpace();
}
{
Dart_EnterScope();
// Weak ref to new space object should now be cleared.
EXPECT(finalizable_new_ref == nullptr);
Dart_ExitScope();
}
{
TransitionNativeToVM transition(thread);
// Garbage collect old space again.
GCTestHelper::CollectOldSpace();
}
{
Dart_EnterScope();
// Weak ref to old space object should now be cleared.
EXPECT(finalizable_new_ref == nullptr);
EXPECT(finalizable_old_ref == nullptr);
Dart_ExitScope();
}
{
TransitionNativeToVM transition(thread);
GCTestHelper::CollectAllGarbage();
}
}
TEST_CASE(DartAPI_WeakPersistentHandleErrors) {
Dart_EnterScope();
@ -3239,6 +3384,26 @@ TEST_CASE(DartAPI_WeakPersistentHandleErrors) {
Dart_ExitScope();
}
TEST_CASE(DartAPI_FinalizableHandleErrors) {
Dart_EnterScope();
// NULL callback.
Dart_Handle obj1 = NewString("new string");
EXPECT_VALID(obj1);
Dart_FinalizableHandle ref1 =
Dart_NewFinalizableHandle(obj1, nullptr, 0, nullptr);
EXPECT_EQ(ref1, static_cast<void*>(nullptr));
// Immediate object.
Dart_Handle obj2 = Dart_NewInteger(0);
EXPECT_VALID(obj2);
Dart_FinalizableHandle ref2 =
Dart_NewFinalizableHandle(obj2, nullptr, 0, FinalizableHandleCallback);
EXPECT_EQ(ref2, static_cast<void*>(nullptr));
Dart_ExitScope();
}
static Dart_PersistentHandle persistent_handle1;
static Dart_WeakPersistentHandle weak_persistent_handle2;
static Dart_WeakPersistentHandle weak_persistent_handle3;
@ -3285,6 +3450,44 @@ TEST_CASE(DartAPI_WeakPersistentHandleCleanupFinalizer) {
Dart_ExitScope();
}
static Dart_FinalizableHandle finalizable_handle3;
static void FinalizableHandlePeerCleanupFinalizer(void* isolate_callback_data,
void* peer) {
Dart_DeletePersistentHandle(persistent_handle1);
Dart_DeleteWeakPersistentHandle(weak_persistent_handle2);
*static_cast<int*>(peer) = 42;
}
TEST_CASE(DartAPI_FinalizableHandleCleanupFinalizer) {
Heap* heap = Isolate::Current()->heap();
const char* kTestString1 = "Test String1";
Dart_EnterScope();
CHECK_API_SCOPE(thread);
Dart_Handle ref1 = Dart_NewStringFromCString(kTestString1);
persistent_handle1 = Dart_NewPersistentHandle(ref1);
Dart_Handle ref2 = Dart_NewStringFromCString(kTestString1);
int peer2 = 0;
weak_persistent_handle2 =
Dart_NewWeakPersistentHandle(ref2, &peer2, 0, NopCallback);
int peer3 = 0;
{
Dart_EnterScope();
Dart_Handle ref3 = Dart_NewStringFromCString(kTestString1);
finalizable_handle3 = Dart_NewFinalizableHandle(
ref3, &peer3, 0, FinalizableHandlePeerCleanupFinalizer);
Dart_ExitScope();
}
{
TransitionNativeToVM transition(thread);
GCTestHelper::CollectAllGarbage();
EXPECT(heap->ExternalInWords(Heap::kOld) == 0);
EXPECT(peer3 == 42);
}
Dart_ExitScope();
}
static void WeakPersistentHandlePeerFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
@ -3313,6 +3516,32 @@ TEST_CASE(DartAPI_WeakPersistentHandleCallback) {
}
}
static void FinalizableHandlePeerFinalizer(void* isolate_callback_data,
void* peer) {
*static_cast<int*>(peer) = 42;
}
TEST_CASE(DartAPI_FinalizableHandleCallback) {
Dart_FinalizableHandle weak_ref = nullptr;
int peer = 0;
{
Dart_EnterScope();
Dart_Handle obj = NewString("new string");
EXPECT_VALID(obj);
weak_ref = Dart_NewFinalizableHandle(obj, &peer, 0,
FinalizableHandlePeerFinalizer);
EXPECT(peer == 0);
Dart_ExitScope();
}
{
TransitionNativeToVM transition(thread);
GCTestHelper::CollectOldSpace();
EXPECT(peer == 0);
GCTestHelper::CollectNewSpace();
EXPECT(peer == 42);
}
}
TEST_CASE(DartAPI_WeakPersistentHandleNoCallback) {
Dart_WeakPersistentHandle weak_ref = NULL;
int peer = 0;
@ -3337,6 +3566,35 @@ TEST_CASE(DartAPI_WeakPersistentHandleNoCallback) {
}
}
TEST_CASE(DartAPI_FinalizableHandleNoCallback) {
Dart_FinalizableHandle weak_ref = nullptr;
Dart_PersistentHandle strong_ref = nullptr;
int peer = 0;
{
Dart_EnterScope();
Dart_Handle obj = NewString("new string");
EXPECT_VALID(obj);
weak_ref = Dart_NewFinalizableHandle(obj, &peer, 0,
FinalizableHandlePeerFinalizer);
strong_ref = Dart_NewPersistentHandle(obj);
Dart_ExitScope();
}
// A finalizer is not invoked on a deleted handle. Therefore, the
// peer value should not change after the referent is collected.
Dart_DeleteFinalizableHandle(weak_ref, strong_ref);
Dart_DeletePersistentHandle(strong_ref);
EXPECT(peer == 0);
{
TransitionNativeToVM transition(thread);
GCTestHelper::CollectOldSpace();
EXPECT(peer == 0);
GCTestHelper::CollectNewSpace();
EXPECT(peer == 0);
}
}
Dart_WeakPersistentHandle delete_on_finalization;
VM_UNIT_TEST_CASE(DartAPI_WeakPersistentHandlesCallbackShutdown) {
TestCase::CreateTestIsolate();
Dart_EnterScope();
@ -3349,6 +3607,17 @@ VM_UNIT_TEST_CASE(DartAPI_WeakPersistentHandlesCallbackShutdown) {
EXPECT(peer == 42);
}
VM_UNIT_TEST_CASE(DartAPI_FinalizableHandlesCallbackShutdown) {
TestCase::CreateTestIsolate();
Dart_EnterScope();
Dart_Handle ref = Dart_True();
int peer = 1234;
Dart_NewFinalizableHandle(ref, &peer, 0, FinalizableHandlePeerFinalizer);
Dart_ExitScope();
Dart_ShutdownIsolate();
EXPECT(peer == 42);
}
TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSize) {
Heap* heap = Isolate::Current()->heap();
EXPECT(heap->ExternalInWords(Heap::kNew) == 0);
@ -3397,6 +3666,52 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSize) {
}
}
TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSize) {
Heap* heap = Isolate::Current()->heap();
EXPECT(heap->ExternalInWords(Heap::kNew) == 0);
EXPECT(heap->ExternalInWords(Heap::kOld) == 0);
Dart_FinalizableHandle weak1 = nullptr;
static const intptr_t kWeak1ExternalSize = 1 * KB;
{
Dart_EnterScope();
Dart_Handle obj = NewString("weakly referenced string");
EXPECT_VALID(obj);
weak1 = Dart_NewFinalizableHandle(obj, nullptr, kWeak1ExternalSize,
NopCallback);
Dart_ExitScope();
}
Dart_PersistentHandle strong_ref = nullptr;
Dart_FinalizableHandle weak2 = nullptr;
static const intptr_t kWeak2ExternalSize = 2 * KB;
{
Dart_EnterScope();
Dart_Handle obj = NewString("strongly referenced string");
EXPECT_VALID(obj);
strong_ref = Dart_NewPersistentHandle(obj);
weak2 = Dart_NewFinalizableHandle(obj, nullptr, kWeak2ExternalSize,
NopCallback);
EXPECT_VALID(AsHandle(strong_ref));
Dart_ExitScope();
}
{
TransitionNativeToVM transition(thread);
GCTestHelper::CollectOldSpace();
EXPECT(heap->ExternalInWords(Heap::kNew) ==
(kWeak1ExternalSize + kWeak2ExternalSize) / kWordSize);
// Collect weakly referenced string, and promote strongly referenced string.
GCTestHelper::CollectNewSpace();
GCTestHelper::CollectNewSpace();
EXPECT(heap->ExternalInWords(Heap::kNew) == 0);
EXPECT(heap->ExternalInWords(Heap::kOld) == kWeak2ExternalSize / kWordSize);
}
Dart_DeletePersistentHandle(strong_ref);
{
TransitionNativeToVM transition(thread);
GCTestHelper::CollectOldSpace();
EXPECT(heap->ExternalInWords(Heap::kOld) == 0);
}
}
TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeNewspaceGC) {
Heap* heap = Isolate::Current()->heap();
Dart_WeakPersistentHandle weak1 = NULL;
@ -3439,6 +3754,49 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeNewspaceGC) {
}
}
TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeNewspaceGC) {
Heap* heap = Isolate::Current()->heap();
Dart_FinalizableHandle weak1 = nullptr;
Dart_PersistentHandle strong1 = nullptr;
// Large enough to exceed any new space limit. Not actually allocated.
const intptr_t kWeak1ExternalSize = 500 * MB;
{
Dart_EnterScope();
Dart_Handle obj = NewString("weakly referenced string");
EXPECT_VALID(obj);
// Triggers a scavenge immediately, since kWeak1ExternalSize is above limit.
weak1 = Dart_NewFinalizableHandle(obj, nullptr, kWeak1ExternalSize,
NopCallback);
strong1 = Dart_NewPersistentHandle(obj);
// ... but the object is still alive and not yet promoted, so external size
// in new space is still above the limit. Thus, even the following tiny
// external allocation will trigger another scavenge.
Dart_FinalizableHandle trigger =
Dart_NewFinalizableHandle(obj, nullptr, 1, NopCallback);
Dart_DeleteFinalizableHandle(trigger, obj);
// After the two scavenges above, 'obj' should now be promoted, hence its
// external size charged to old space.
{
CHECK_API_SCOPE(thread);
TransitionNativeToVM transition(thread);
HANDLESCOPE(thread);
String& handle = String::Handle(thread->zone());
handle ^= Api::UnwrapHandle(obj);
EXPECT(handle.IsOld());
}
EXPECT(heap->ExternalInWords(Heap::kNew) == 0);
EXPECT(heap->ExternalInWords(Heap::kOld) == kWeak1ExternalSize / kWordSize);
Dart_ExitScope();
}
Dart_DeleteFinalizableHandle(weak1, strong1);
Dart_DeletePersistentHandle(strong1);
{
TransitionNativeToVM transition(thread);
GCTestHelper::CollectOldSpace();
EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld));
}
}
TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOldspaceGC) {
// Check that external allocation in old space can trigger GC.
Isolate* isolate = Isolate::Current();
@ -3446,6 +3804,7 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOldspaceGC) {
Dart_Handle live = AllocateOldString("live");
EXPECT_VALID(live);
Dart_WeakPersistentHandle weak = NULL;
Dart_WeakPersistentHandle weak2 = NULL;
{
TransitionNativeToVM transition(thread);
GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size.
@ -3469,7 +3828,48 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOldspaceGC) {
}
// 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);
weak2 =
Dart_NewWeakPersistentHandle(live, NULL, kHugeExternalSize, NopCallback);
{
TransitionNativeToVM transition(thread);
GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size.
// Expect small garbage to be collected.
EXPECT_EQ(kHugeExternalSize,
isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
}
Dart_ExitScope();
}
TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeOldspaceGC) {
// Check that external allocation in old space can trigger GC.
Isolate* isolate = Isolate::Current();
Dart_EnterScope();
Dart_Handle live = AllocateOldString("live");
EXPECT_VALID(live);
Dart_FinalizableHandle weak = NULL;
{
TransitionNativeToVM transition(thread);
GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size.
EXPECT_EQ(0, isolate->heap()->ExternalInWords(Heap::kOld));
}
const intptr_t kSmallExternalSize = 1 * KB;
{
Dart_EnterScope();
Dart_Handle dead = AllocateOldString("dead");
EXPECT_VALID(dead);
weak = Dart_NewFinalizableHandle(dead, nullptr, kSmallExternalSize,
NopCallback);
Dart_ExitScope();
}
{
TransitionNativeToVM transition(thread);
GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size.
EXPECT_EQ(kSmallExternalSize,
isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
}
// Large enough to trigger GC in old space. Not actually allocated.
const intptr_t kHugeExternalSize = (kWordSize == 4) ? 513 * MB : 1025 * MB;
Dart_NewFinalizableHandle(live, nullptr, kHugeExternalSize, NopCallback);
{
TransitionNativeToVM transition(thread);
GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size.
@ -3514,6 +3914,44 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOddReferents) {
}
}
TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeOddReferents) {
Heap* heap = Isolate::Current()->heap();
Dart_FinalizableHandle weak1 = nullptr;
Dart_PersistentHandle strong1 = nullptr;
static const intptr_t kWeak1ExternalSize = 1 * KB;
Dart_FinalizableHandle weak2 = nullptr;
Dart_PersistentHandle strong2 = nullptr;
static const intptr_t kWeak2ExternalSize = 2 * KB;
EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld));
{
Dart_EnterScope();
Dart_Handle dart_true = Dart_True(); // VM heap object.
EXPECT_VALID(dart_true);
weak1 = Dart_NewFinalizableHandle(dart_true, nullptr, kWeak1ExternalSize,
UnreachedCallback);
strong1 = Dart_NewPersistentHandle(dart_true);
Dart_Handle zero = Dart_False(); // VM heap object.
EXPECT_VALID(zero);
weak2 = Dart_NewFinalizableHandle(zero, nullptr, kWeak2ExternalSize,
UnreachedCallback);
strong2 = Dart_NewPersistentHandle(zero);
// Both should be charged to old space.
EXPECT(heap->ExternalInWords(Heap::kOld) ==
(kWeak1ExternalSize + kWeak2ExternalSize) / kWordSize);
Dart_ExitScope();
}
Dart_DeleteFinalizableHandle(weak1, strong1);
Dart_DeletePersistentHandle(strong1);
Dart_DeleteFinalizableHandle(weak2, strong2);
Dart_DeletePersistentHandle(strong2);
EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld));
{
TransitionNativeToVM transition(thread);
GCTestHelper::CollectOldSpace();
EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld));
}
}
#define EXAMPLE_RESOURCE_NATIVE_LIST(V) \
V(ExampleResource_Allocate, 1) \
V(ExampleResource_Use, 1) \

View file

@ -190,12 +190,29 @@ class PersistentHandle {
// 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);
intptr_t external_size,
bool auto_delete);
static FinalizablePersistentHandle* New(Isolate* isolate,
const Object& object,
void* peer,
Dart_HandleFinalizer callback,
intptr_t external_size,
bool auto_delete);
// Accessors.
ObjectPtr raw() const { return raw_; }
@ -204,10 +221,26 @@ class FinalizablePersistentHandle {
return OFFSET_OF(FinalizablePersistentHandle, raw_);
}
void* peer() const { return peer_; }
Dart_WeakPersistentHandleFinalizer callback() const { return callback_; }
Dart_WeakPersistentHandle apiHandle() {
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_WeakPersistentHandle ApiWeakPersistentHandle() {
return reinterpret_cast<Dart_WeakPersistentHandle>(this);
}
Dart_FinalizableHandle ApiFinalizableHandle() {
return reinterpret_cast<Dart_FinalizableHandle>(this);
}
bool auto_delete() const { return auto_delete_; }
intptr_t external_size() const {
return ExternalSizeInWordsBits::decode(external_data_) * kWordSize;
@ -256,6 +289,7 @@ class FinalizablePersistentHandle {
}
static FinalizablePersistentHandle* Cast(Dart_WeakPersistentHandle handle);
static FinalizablePersistentHandle* Cast(Dart_FinalizableHandle handle);
private:
enum {
@ -264,6 +298,15 @@ class FinalizablePersistentHandle {
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,
@ -277,7 +320,10 @@ class FinalizablePersistentHandle {
friend class FinalizablePersistentHandles;
FinalizablePersistentHandle()
: raw_(nullptr), peer_(NULL), external_data_(0), callback_(NULL) {}
: raw_(nullptr),
peer_(NULL),
external_data_(0),
callback_(HandleFinalizer()) {}
~FinalizablePersistentHandle() {}
static void Finalize(IsolateGroup* isolate_group,
@ -293,6 +339,7 @@ class FinalizablePersistentHandle {
raw_ = static_cast<ObjectPtr>(reinterpret_cast<uword>(free_list));
ASSERT(!raw_->IsHeapObject());
}
void FreeHandle(FinalizablePersistentHandle* free_list) {
Clear();
SetNext(free_list);
@ -302,7 +349,9 @@ class FinalizablePersistentHandle {
raw_ = Object::null();
peer_ = NULL;
external_data_ = 0;
callback_ = NULL;
callback_ = HandleFinalizer();
auto_delete_ = false;
callback_signature_ = CallbackSignature::kWeakPersistentHandleFinalizer;
}
void set_raw(ObjectPtr raw) { raw_ = raw; }
@ -311,10 +360,14 @@ class FinalizablePersistentHandle {
void set_peer(void* peer) { peer_ = peer; }
void set_callback(Dart_WeakPersistentHandleFinalizer callback) {
callback_ = callback;
void set_callback_signature(CallbackSignature callback_signature) {
callback_signature_ = callback_signature;
}
void set_callback(HandleFinalizer callback) { callback_ = callback; }
void set_auto_delete(bool auto_delete) { auto_delete_ = auto_delete; }
void set_external_size(intptr_t size) {
intptr_t size_in_words = Utils::RoundUp(size, kObjectAlignment) / kWordSize;
ASSERT(ExternalSizeInWordsBits::is_valid(size_in_words));
@ -343,7 +396,9 @@ class FinalizablePersistentHandle {
ObjectPtr raw_;
void* peer_;
uword external_data_;
Dart_WeakPersistentHandleFinalizer callback_;
HandleFinalizer callback_;
bool auto_delete_;
CallbackSignature callback_signature_;
DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods.
DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandle);
@ -563,6 +618,10 @@ class FinalizablePersistentHandles
return IsValidScopedHandle(reinterpret_cast<uword>(object));
}
bool IsValidHandle(Dart_FinalizableHandle object) const {
return IsValidScopedHandle(reinterpret_cast<uword>(object));
}
bool IsFreeHandle(Dart_WeakPersistentHandle object) const {
FinalizablePersistentHandle* handle = free_list_;
while (handle != NULL) {
@ -746,6 +805,7 @@ class ApiState {
MutexLocker ml(&mutex_);
return weak_persistent_handles_.AllocateHandle();
}
void FreeWeakPersistentHandle(FinalizablePersistentHandle* weak_ref) {
MutexLocker ml(&mutex_);
weak_persistent_handles_.FreeHandle(weak_ref);
@ -767,6 +827,11 @@ class ApiState {
return weak_persistent_handles_.IsValidHandle(object);
}
bool IsValidFinalizableHandle(Dart_FinalizableHandle object) {
MutexLocker ml(&mutex_);
return weak_persistent_handles_.IsValidHandle(object);
}
bool IsActiveWeakPersistentHandle(Dart_WeakPersistentHandle object) {
MutexLocker ml(&mutex_);
return weak_persistent_handles_.IsValidHandle(object) &&
@ -832,13 +897,38 @@ inline FinalizablePersistentHandle* FinalizablePersistentHandle::New(
const Object& object,
void* peer,
Dart_WeakPersistentHandleFinalizer callback,
intptr_t external_size) {
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) {
ApiState* state = isolate->group()->api_state();
ASSERT(state != NULL);
FinalizablePersistentHandle* ref = state->AllocateWeakPersistentHandle();
ref->set_raw(object);
ref->set_peer(peer);
ref->set_callback(callback);
ref->set_callback_signature(CallbackSignature::kHandleFinalizer);
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;

View file

@ -595,7 +595,7 @@ void Heap::CollectAllGarbage(GCReason reason) {
EvacuateNewSpace(thread, reason);
if (thread->is_marking()) {
// If incremental marking is happening, we need to finish the GC cycle
// and perform a follow-up GC to pruge any "floating garbage" that may be
// and perform a follow-up GC to purge any "floating garbage" that may be
// retained by the incremental barrier.
CollectOldSpaceGarbage(thread, kMarkSweep, reason);
}

View file

@ -61,7 +61,7 @@ class Heap {
kOldSpace, // Old space limit crossed.
kFinalize, // Concurrent marking finished.
kFull, // Heap::CollectAllGarbage
kExternal, // Dart_NewWeakPersistentHandle
kExternal, // Dart_NewFinalizableHandle Dart_NewWeakPersistentHandle
kIdle, // Dart_NotifyIdle
kLowMemory, // Dart_NotifyLowMemory
kDebugging, // service request, etc.

View file

@ -571,9 +571,7 @@ ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveOldToNewChain) {
EXPECT(size_before < size_after);
}
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(ExternalPromotion) {
Isolate* isolate = Isolate::Current();
@ -587,7 +585,8 @@ ISOLATE_UNIT_TEST_CASE(ExternalPromotion) {
Array& neu = Array::Handle();
for (intptr_t i = 0; i < 100; i++) {
neu = Array::New(1, Heap::kNew);
FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB);
FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB,
/*auto_delete=*/true);
old.SetAt(i, neu);
}
@ -753,7 +752,8 @@ ISOLATE_UNIT_TEST_CASE(ExternalAllocationStats) {
Array& neu = Array::Handle();
for (intptr_t i = 0; i < 100; i++) {
neu = Array::New(1, Heap::kNew);
FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB);
FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB,
/*auto_delete=*/true);
old.SetAt(i, neu);
if ((i % 4) == 0) {

View file

@ -22421,7 +22421,8 @@ static FinalizablePersistentHandle* AddFinalizer(
intptr_t external_size) {
ASSERT(callback != NULL);
return FinalizablePersistentHandle::New(Isolate::Current(), referent, peer,
callback, external_size);
callback, external_size,
/*auto_delete=*/true);
}
StringPtr String::Transform(int32_t (*mapping)(int32_t ch),
@ -24052,7 +24053,6 @@ const char* SendPort::ToCString() const {
}
static void TransferableTypedDataFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
delete (reinterpret_cast<TransferableTypedDataPeer*>(peer));
}
@ -24075,8 +24075,8 @@ TransferableTypedDataPtr TransferableTypedData::New(uint8_t* data,
// Set up finalizer so it frees allocated memory if handle is
// garbage-collected.
peer->set_handle(FinalizablePersistentHandle::New(
thread->isolate(), result, peer, &TransferableTypedDataFinalizer,
length));
thread->isolate(), result, peer, &TransferableTypedDataFinalizer, length,
/*auto_delete=*/true));
return result.raw();
}

View file

@ -982,7 +982,7 @@ class Pass2Visitor : public ObjectVisitor,
writer_->WriteUnsigned(weak_persistent_handle->external_size());
// Attempt to include a native symbol name.
auto const name = NativeSymbolResolver::LookupSymbolName(
reinterpret_cast<uword>(weak_persistent_handle->callback()), nullptr);
weak_persistent_handle->callback_address(), nullptr);
writer_->WriteUtf8((name == nullptr) ? "Unknown native function" : name);
if (name != nullptr) {
NativeSymbolResolver::FreeSymbolName(name);

View file

@ -4269,12 +4269,11 @@ class PersistentHandleVisitor : public HandleVisitor {
obj.AddPropertyF(
"peer", "0x%" Px "",
reinterpret_cast<uintptr_t>(weak_persistent_handle->peer()));
obj.AddPropertyF(
"callbackAddress", "0x%" Px "",
reinterpret_cast<uintptr_t>(weak_persistent_handle->callback()));
obj.AddPropertyF("callbackAddress", "0x%" Px "",
weak_persistent_handle->callback_address());
// Attempt to include a native symbol name.
char* name = NativeSymbolResolver::LookupSymbolName(
reinterpret_cast<uword>(weak_persistent_handle->callback()), nullptr);
weak_persistent_handle->callback_address(), nullptr);
obj.AddProperty("callbackSymbolName", (name == nullptr) ? "" : name);
if (name != nullptr) {
NativeSymbolResolver::FreeSymbolName(name);

View file

@ -3098,10 +3098,11 @@ class MapAssociation {
class MemoryUsage extends Response {
// The amount of non-Dart memory that is retained by Dart objects. For
// example, memory associated with Dart objects through APIs such as
// Dart_NewWeakPersistentHandle and Dart_NewExternalTypedData. This usage is
// only as accurate as the values supplied to these APIs from the VM embedder or
// native extensions. This external memory applies GC pressure, but is separate
// from heapUsage and heapCapacity.
// Dart_NewFinalizableHandle, Dart_NewWeakPersistentHandle and
// Dart_NewExternalTypedData. This usage is only as accurate as the values
// supplied to these APIs from the VM embedder or native extensions. This
// external memory applies GC pressure, but is separate from heapUsage and
// heapCapacity.
int externalUsage;
// The total capacity of the heap in bytes. This is the amount of memory used

View file

@ -27,7 +27,7 @@ main() async {
print("C = C T1 or C T2.");
print("Dart: Setup.");
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 0);
Expect.isTrue(NativeApi.minorVersion >= 1);
final initializeApi = dl.lookupFunction<IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");
Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);

View file

@ -36,7 +36,7 @@ main() async {
print("C = C T1 or C T2.");
print("Dart: Setup.");
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 0);
Expect.isTrue(NativeApi.minorVersion >= 1);
final initializeApi = dl.lookupFunction<IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");
Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);

View file

@ -54,7 +54,7 @@ final closureCallbackPointer =
void doDynamicLinking() {
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 0);
Expect.isTrue(NativeApi.minorVersion >= 1);
final initializeApi = testLibrary.lookupFunction<
IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");

View file

@ -17,7 +17,7 @@ void main() {
void doDynamicLinking() {
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 0);
Expect.isTrue(NativeApi.minorVersion >= 1);
final initializeApi = testLibrary.lookupFunction<
IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");

View file

@ -17,7 +17,7 @@ void main() {
void doDynamicLinking() {
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 0);
Expect.isTrue(NativeApi.minorVersion >= 1);
final initializeApi = testLibrary.lookupFunction<
IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");