diff --git a/CHANGELOG.md b/CHANGELOG.md index b44975b0f..ab16bd0b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ * Support `Credentials.function` for login user with Custom Function Authentication Provider. ([#742](https://github.com/realm/realm-dart/pull/742)) * Added `update` flag on `Realm.add` and `Realm.addAll` to support upserts. ([#668](https://github.com/realm/realm-dart/pull/668)) +### Fixed +* Use Dart 2.17 `Finalizable` to ensure lexically scoped lifetime of finalizable resources (Realm, App, etc.). ([#754](https://github.com/realm/realm-dart/pull/754)) + ### Internal * Added a command to `realm_dart` for deleting Atlas App Services applications. Usage: `dart run realm_dart delete-apps`. By default it will delete apps from `http://localhost:9090` which is the endpoint of the local docker image. If `--atlas-cluster` is provided, it will authenticate, delete the application from the provided cluster. (PR [#663](https://github.com/realm/realm-dart/pull/663)) * Uses Realm Core v12.5.0 diff --git a/ffigen/config.yaml b/ffigen/config.yaml index 1149eba83..f53419687 100644 --- a/ffigen/config.yaml +++ b/ffigen/config.yaml @@ -28,6 +28,7 @@ functions: symbol-address: include: - 'realm_dart_.*' + - 'realm_release' structs: exclude: - '_.*' diff --git a/lib/src/app.dart b/lib/src/app.dart index fba68ebcc..7eb3822ca 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -15,6 +15,7 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// +import 'dart:ffi'; import 'dart:io'; import 'package:meta/meta.dart'; @@ -108,7 +109,7 @@ class AppConfiguration { /// * Register uses and perform various user-related operations through authentication providers /// * Synchronize data between the local device and a remote Realm App with Synchronized Realms /// {@category Application} -class App { +class App implements Finalizable { final AppHandle _handle; /// The id of this application. This is the same as the appId in the [AppConfiguration] used to @@ -179,6 +180,11 @@ enum MetadataPersistenceMode { /// @nodoc extension AppInternal on App { + @pragma('vm:never-inline') + void keepAlive() { + _handle.keepAlive(); + } + AppHandle get handle => _handle; static App create(AppHandle handle) => App._(handle); diff --git a/lib/src/collections.dart b/lib/src/collections.dart index f62a30d10..7fa13088b 100644 --- a/lib/src/collections.dart +++ b/lib/src/collections.dart @@ -1,9 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2022 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +import 'dart:ffi'; import 'native/realm_core.dart'; /// Contains index information about objects that moved within the same collection. class Move { /// The index in the old version of the collection. final int from; + /// The index in the new version of the collection. final int to; @@ -22,10 +42,10 @@ class CollectionChanges { } /// Describes the changes in a Realm collection since the last time the notification callback was invoked. -class RealmCollectionChanges { +class RealmCollectionChanges implements Finalizable { final RealmCollectionChangesHandle _handle; CollectionChanges? _values; - + RealmCollectionChanges(this._handle); CollectionChanges get _changes => _values ??= realmCore.getCollectionChanges(_handle); @@ -38,11 +58,18 @@ class RealmCollectionChanges { /// The indexes of the objects in the new collection which were modified in this version. List get modified => _changes.modifications; - + /// The indexes of the objects in the collection which moved. List get moved => _changes.moves; - /// The indexes in the new version of the collection which were modified. Conceptually, it contains the same entries as [modified] but after the + /// The indexes in the new version of the collection which were modified. Conceptually, it contains the same entries as [modified] but after the /// insertions and deletions have been accounted for. List get newModified => _changes.modificationsAfter; -} \ No newline at end of file +} + +extension RealmCollectionChangesInternal on RealmCollectionChanges { + @pragma('vm:never-inline') + void keepAlive() { + _handle.keepAlive(); + } +} diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index 5f53d8b09..b17edf289 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -16,6 +16,7 @@ // //////////////////////////////////////////////////////////////////////////////// +import 'dart:ffi'; import 'dart:io'; import 'package:path/path.dart' as _path; @@ -23,6 +24,7 @@ import 'package:path/path.dart' as _path; import 'native/realm_core.dart'; import 'realm_class.dart'; import 'init.dart'; +import 'user.dart'; /// The signature of a callback used to determine if compaction /// should be attempted. @@ -47,7 +49,7 @@ typedef InitialDataCallback = void Function(Realm realm); /// Configuration used to create a [Realm] instance /// {@category Configuration} -abstract class Configuration { +abstract class Configuration implements Finalizable { /// The default realm filename to be used. static String get defaultRealmName => _path.basename(defaultRealmPath); static set defaultRealmName(String name) => defaultRealmPath = _path.join(_path.dirname(defaultRealmPath), _path.basename(name)); @@ -278,6 +280,11 @@ class FlexibleSyncConfiguration extends Configuration { } extension FlexibleSyncConfigurationInternal on FlexibleSyncConfiguration { + @pragma('vm:never-inline') + void keepAlive() { + user.keepAlive(); + } + SessionStopPolicy get sessionStopPolicy => _sessionStopPolicy; set sessionStopPolicy(SessionStopPolicy value) => _sessionStopPolicy = value; } diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index 8643b2f20..b44276cf9 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////////////////// import 'dart:convert'; +import 'dart:ffi'; import 'native/realm_core.dart'; import 'app.dart'; @@ -55,7 +56,7 @@ enum AuthProviderType { /// A class, representing the credentials used for authenticating a [User] /// {@category Application} -class Credentials { +class Credentials implements Finalizable { final RealmAppCredentialsHandle _handle; final AuthProviderType provider; @@ -98,8 +99,8 @@ class Credentials { /// Returns a [Credentials] object that can be used to authenticate a user with a Google account using an id token. Credentials.googleIdToken(String idToken) : _handle = realmCore.createAppCredentialsGoogleIdToken(idToken), - provider = AuthProviderType.google; - + provider = AuthProviderType.google; + /// Returns a [Credentials] object that can be used to authenticate a user with a custom Function. /// [Custom Function Authentication Docs](https://www.mongodb.com/docs/atlas/app-services/authentication/custom-function/) Credentials.function(String payload) @@ -109,13 +110,18 @@ class Credentials { /// @nodoc extension CredentialsInternal on Credentials { + @pragma('vm:never-inline') + void keepAlive() { + _handle.keepAlive(); + } + RealmAppCredentialsHandle get handle => _handle; } /// A class, encapsulating functionality for users, logged in with [Credentials.emailPassword()]. /// It is always scoped to a particular app. /// {@category Application} -class EmailPasswordAuthProvider { +class EmailPasswordAuthProvider implements Finalizable { final App app; /// Create a new EmailPasswordAuthProvider for the [app] @@ -164,5 +170,10 @@ class EmailPasswordAuthProvider { } extension EmailPasswordAuthProviderInternal on EmailPasswordAuthProvider { + @pragma('vm:never-inline') + void keepAlive() { + app.keepAlive(); + } + static EmailPasswordAuthProvider create(App app) => EmailPasswordAuthProvider(app); } diff --git a/lib/src/list.dart b/lib/src/list.dart index c55f35aea..ec7ad3f64 100644 --- a/lib/src/list.dart +++ b/lib/src/list.dart @@ -18,6 +18,7 @@ import 'dart:async'; import 'dart:collection' as collection; +import 'dart:ffi'; import 'collections.dart'; import 'native/realm_core.dart'; @@ -29,7 +30,7 @@ import 'results.dart'; /// added to or deleted from the collection or from the Realm. /// /// {@category Realm} -abstract class RealmList with RealmEntity implements List { +abstract class RealmList with RealmEntity implements List, Finalizable { /// Gets a value indicating whether this collection is still valid to use. /// /// Indicates whether the [Realm] instance hasn't been closed, @@ -145,6 +146,15 @@ extension RealmListOfObject on RealmList { /// @nodoc extension RealmListInternal on RealmList { + @pragma('vm:never-inline') + void keepAlive() { + final self = this; + if (self is ManagedRealmList) { + realm.keepAlive(); + self._handle.keepAlive(); + } + } + ManagedRealmList asManaged() => this is ManagedRealmList ? this as ManagedRealmList : throw RealmStateError('$this is not managed'); RealmListHandle get handle => asManaged()._handle; diff --git a/lib/src/native/realm_bindings.dart b/lib/src/native/realm_bindings.dart index 36728bafa..a59e2c555 100644 --- a/lib/src/native/realm_bindings.dart +++ b/lib/src/native/realm_bindings.dart @@ -2963,27 +2963,6 @@ class RealmLibrary { ffi.Pointer Function( ffi.Pointer)>(); - Dart_FinalizableHandle realm_dart_attach_finalizer( - Object handle, - ffi.Pointer realmPtr, - int size, - ) { - return _realm_dart_attach_finalizer( - handle, - realmPtr, - size, - ); - } - - late final _realm_dart_attach_finalizerPtr = _lookup< - ffi.NativeFunction< - Dart_FinalizableHandle Function(ffi.Handle, ffi.Pointer, - ffi.Int)>>('realm_dart_attach_finalizer'); - late final _realm_dart_attach_finalizer = - _realm_dart_attach_finalizerPtr.asFunction< - Dart_FinalizableHandle Function( - Object, ffi.Pointer, int)>(); - ffi.Pointer realm_dart_create_scheduler( int isolateId, int port, @@ -3001,23 +2980,6 @@ class RealmLibrary { late final _realm_dart_create_scheduler = _realm_dart_create_schedulerPtr .asFunction Function(int, int)>(); - void realm_dart_delete_finalizable( - Dart_FinalizableHandle finalizable_handle, - Object handle, - ) { - return _realm_dart_delete_finalizable( - finalizable_handle, - handle, - ); - } - - late final _realm_dart_delete_finalizablePtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(Dart_FinalizableHandle, - ffi.Handle)>>('realm_dart_delete_finalizable'); - late final _realm_dart_delete_finalizable = _realm_dart_delete_finalizablePtr - .asFunction(); - void realm_dart_delete_persistent_handle( ffi.Pointer handle, ) { @@ -9584,22 +9546,11 @@ class RealmLibrary { class _SymbolAddresses { final RealmLibrary _library; _SymbolAddresses(this._library); - ffi.Pointer< - ffi.NativeFunction< - Dart_FinalizableHandle Function( - ffi.Handle, ffi.Pointer, ffi.Int)>> - get realm_dart_attach_finalizer => - _library._realm_dart_attach_finalizerPtr; ffi.Pointer< ffi.NativeFunction< ffi.Pointer Function(ffi.Uint64, Dart_Port)>> get realm_dart_create_scheduler => _library._realm_dart_create_schedulerPtr; - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(Dart_FinalizableHandle, ffi.Handle)>> - get realm_dart_delete_finalizable => - _library._realm_dart_delete_finalizablePtr; ffi.Pointer)>> get realm_dart_delete_persistent_handle => _library._realm_dart_delete_persistent_handlePtr; @@ -9674,10 +9625,10 @@ class _SymbolAddresses { ffi.Pointer)>> get realm_dart_weak_handle_to_object => _library._realm_dart_weak_handle_to_objectPtr; + ffi.Pointer)>> + get realm_release => _library._realm_releasePtr; } -typedef Dart_FinalizableHandle = ffi.Pointer<_Dart_FinalizableHandle>; - /// A port is used to send or receive inter-isolate messages typedef Dart_Port = ffi.Int64; @@ -9720,8 +9671,6 @@ class UnnamedUnion2 extends ffi.Union { external int logic_error_kind; } -class _Dart_FinalizableHandle extends ffi.Opaque {} - class realm_app extends ffi.Opaque {} class realm_app_config extends ffi.Opaque {} diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index f1212f126..8407e6c74 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -979,7 +979,7 @@ class _RealmCore { return RealmAppCredentialsHandle._(_realmLib.realm_app_credentials_new_google_auth_code(authCodePtr)); }); } - + RealmAppCredentialsHandle createAppCredentialsFunction(String payload) { return using((arena) { final payloadPtr = payload.toCharPtr(arena); @@ -1694,15 +1694,44 @@ class LastError { } } -abstract class HandleBase { +// Flag to enable trace on finalization. +// +// Be aware that the trace is likely late, and it might in rare case be missing, +// as there are no absolute guarantees with Finalizer. +// +// It is often beneficial to also instrument the native realm_release to +// print the address released to get the exact time. +const _enableFinalizerTrace = false; + +// Level used for finalization trace, if enabled. +const _finalizerTraceLevel = RealmLogLevel.trace; + +void _traceFinalization(Object o) { + Realm.logger.log(_finalizerTraceLevel, 'Finalizing: $o'); +} + +final _debugFinalizer = Finalizer(_traceFinalization); + +void _setupFinalizationTrace(Object value, Object finalizationToken) { + _debugFinalizer.attach(value, finalizationToken, detach: value); +} + +void _tearDownFinalizationTrace(Object value, Object finalizationToken) { + _debugFinalizer.detach(value); + _traceFinalization(finalizationToken); +} + +final _nativeFinalizer = NativeFinalizer(_realmLib.addresses.realm_release); + +abstract class HandleBase implements Finalizable { final Pointer _pointer; - late final Dart_FinalizableHandle _finalizableHandle; + + @pragma('vm:never-inline') + void keepAlive() {} HandleBase(this._pointer, int size) { - _finalizableHandle = _realmLib.realm_dart_attach_finalizer(this, _pointer.cast(), size); - if (_finalizableHandle == nullptr) { - throw Exception("Error creating $runtimeType"); - } + _nativeFinalizer.attach(this, _pointer.cast(), detach: this, externalSize: size); + if (_enableFinalizerTrace) _setupFinalizationTrace(this, _pointer); } HandleBase.unowned(this._pointer); @@ -1760,10 +1789,10 @@ class ReleasableHandle extends HandleBase { if (released) { return; } - - _realmLib.realm_dart_delete_finalizable(_finalizableHandle, this); + _nativeFinalizer.detach(this); _realmLib.realm_release(_pointer.cast()); released = true; + if (_enableFinalizerTrace) _tearDownFinalizationTrace(this, _pointer); } } diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index b8e5c01f5..61e1bada4 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////////////////// import 'dart:async'; +import 'dart:ffi'; import 'dart:io'; import 'package:logging/logging.dart'; @@ -84,7 +85,7 @@ export 'session.dart' show Session, SessionState, ConnectionState, ProgressDirec /// A [Realm] instance represents a `Realm` database. /// /// {@category Realm} -class Realm { +class Realm implements Finalizable { final Map _metadata = {}; final RealmHandle _handle; @@ -376,6 +377,15 @@ class Transaction { /// @nodoc extension RealmInternal on Realm { + @pragma('vm:never-inline') + void keepAlive() { + _handle.keepAlive(); + final c = config; + if (c is FlexibleSyncConfiguration) { + c.keepAlive(); + } + } + RealmHandle get handle => _handle; static Realm getUnowned(Configuration config, RealmHandle handle) { @@ -408,7 +418,7 @@ extension RealmInternal on Realm { } /// @nodoc -abstract class NotificationsController { +abstract class NotificationsController implements Finalizable { RealmNotificationTokenHandle? handle; RealmNotificationTokenHandle subscribe(); diff --git a/lib/src/realm_object.dart b/lib/src/realm_object.dart index ebfc42a8e..9136904bc 100644 --- a/lib/src/realm_object.dart +++ b/lib/src/realm_object.dart @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////////////////// import 'dart:async'; +import 'dart:ffi'; import 'list.dart'; import 'native/realm_core.dart'; @@ -193,7 +194,7 @@ extension RealmEntityInternal on RealmEntity { /// /// [RealmObject] should not be used directly as it is part of the generated class hierarchy. ex: `MyClass extends _MyClass with RealmObject`. /// {@category Realm} -mixin RealmObject on RealmEntity { +mixin RealmObject on RealmEntity implements Finalizable { RealmObjectHandle? _handle; RealmAccessor _accessor = RealmValuesAccessor(); static final Map _factories = {}; @@ -262,6 +263,12 @@ mixin RealmObject on RealmEntity { /// @nodoc //RealmObject package internal members extension RealmObjectInternal on RealmObject { + @pragma('vm:never-inline') + void keepAlive() { + _realm?.keepAlive(); + _handle?.keepAlive(); + } + void manage(Realm realm, RealmObjectHandle handle, RealmCoreAccessor accessor, bool update) { if (_handle != null) { //most certainly a bug hence we throw an Error @@ -308,7 +315,7 @@ class RealmException implements Exception { } /// Describes the changes in on a single RealmObject since the last time the notification callback was invoked. -class RealmObjectChanges { +class RealmObjectChanges implements Finalizable { // ignore: unused_field final RealmObjectChangesHandle _handle; @@ -327,6 +334,14 @@ class RealmObjectChanges { const RealmObjectChanges._(this._handle, this.object); } +/// @nodoc +extension RealmObjectChangesInternal on RealmObjectChanges { + @pragma('vm:never-inline') + void keepAlive() { + _handle.keepAlive(); + } +} + /// @nodoc class RealmObjectNotificationsController extends NotificationsController { T realmObject; diff --git a/lib/src/results.dart b/lib/src/results.dart index 1cd2f6e32..5c6c770fd 100644 --- a/lib/src/results.dart +++ b/lib/src/results.dart @@ -18,6 +18,7 @@ import 'dart:async'; import 'dart:collection' as collection; +import 'dart:ffi'; import 'collections.dart'; import 'native/realm_core.dart'; @@ -27,7 +28,7 @@ import 'realm_class.dart'; /// added to or deleted from the Realm that match the underlying query. /// /// {@category Realm} -class RealmResults extends collection.IterableBase { +class RealmResults extends collection.IterableBase implements Finalizable { final RealmResultsHandle _handle; /// The Realm instance this collection belongs to. @@ -81,6 +82,11 @@ class RealmResults extends collection.IterableBase { /// @nodoc //RealmResults package internal members extension RealmResultsInternal on RealmResults { + @pragma('vm:never-inline') + void keepAlive() { + _handle.keepAlive(); + } + RealmResultsHandle get handle => _handle; static RealmResults create(RealmResultsHandle handle, Realm realm) { diff --git a/lib/src/session.dart b/lib/src/session.dart index 23fe4345a..08f9284e1 100644 --- a/lib/src/session.dart +++ b/lib/src/session.dart @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////////////////// import 'dart:async'; +import 'dart:ffi'; import '../realm.dart'; import 'native/realm_core.dart'; @@ -27,7 +28,7 @@ import 'user.dart'; /// server. Sessions are always created by the SDK and vended out through various /// APIs. The lifespans of sessions associated with Realms are managed automatically. /// {@category Sync} -class Session { +class Session implements Finalizable { final SessionHandle _handle; /// The on-disk path of the file backing the [Realm] this [Session] represents @@ -150,6 +151,11 @@ class ConnectionStateChange { } extension SessionInternal on Session { + @pragma('vm:never-inline') + void keepAlive() { + _handle.keepAlive(); + } + static Session create(SessionHandle handle) => Session._(handle); SessionHandle get handle => _handle; diff --git a/lib/src/subscription.dart b/lib/src/subscription.dart index e63da7e96..3e21855de 100644 --- a/lib/src/subscription.dart +++ b/lib/src/subscription.dart @@ -18,6 +18,7 @@ import 'dart:core'; import 'dart:collection'; +import 'dart:ffi'; import 'native/realm_core.dart'; import 'realm_class.dart'; @@ -26,7 +27,7 @@ import 'realm_class.dart'; /// evaluate the query that the app subscribed to and will send data /// that matches it as well as remove data that no longer does. /// {@category Sync} -class Subscription { +class Subscription implements Finalizable { final SubscriptionHandle _handle; Subscription._(this._handle); @@ -64,6 +65,11 @@ class Subscription { } extension SubscriptionInternal on Subscription { + @pragma('vm:never-inline') + void keepAlive() { + _handle.keepAlive(); + } + SubscriptionHandle get handle => _handle; ObjectId get id => _id; } @@ -121,7 +127,7 @@ enum SubscriptionSetState { /// Realm is an expensive operation serverside, even if there's very little data that needs /// downloading. /// {@category Sync} -abstract class SubscriptionSet with IterableMixin { +abstract class SubscriptionSet with IterableMixin implements Finalizable { final Realm _realm; SubscriptionSetHandle _handle; @@ -204,6 +210,12 @@ abstract class SubscriptionSet with IterableMixin { } extension SubscriptionSetInternal on SubscriptionSet { + @pragma('vm:never-inline') + void keepAlive() { + _realm.keepAlive(); + _handle.keepAlive(); + } + Realm get realm => _realm; SubscriptionSetHandle get handle => _handle; diff --git a/lib/src/user.dart b/lib/src/user.dart index ba7a92f3e..2fbf2f173 100644 --- a/lib/src/user.dart +++ b/lib/src/user.dart @@ -29,14 +29,14 @@ import './app.dart'; /// locally on the device, and should be treated as sensitive data. /// {@category Application} class User { - final App? _app; + App? _app; final UserHandle _handle; /// The [App] with which the [User] is associated with. App get app { // The _app field may be null when we're retrieving a user from the session // rather than from the app. - return _app ?? AppInternal.create(realmCore.userGetApp(_handle)); + return _app ??= AppInternal.create(realmCore.userGetApp(_handle)); } User._(this._handle, this._app); @@ -190,6 +190,12 @@ extension UserIdentityInternal on UserIdentity { /// @nodoc extension UserInternal on User { + @pragma('vm:never-inline') + void keepAlive() { + _handle.keepAlive(); + _app?.keepAlive(); + } + UserHandle get handle => _handle; static User create(UserHandle handle, [App? app]) => User._(handle, app); diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index 90f69bc4e..a7292bdb3 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -28,18 +28,6 @@ RLM_API void realm_dart_initializeDartApiDL(void* data) { Dart_InitializeApiDL(data); } -static void handle_finalizer(void* isolate_callback_data, void* realmPtr) { - realm_release(realmPtr); -} - -RLM_API Dart_FinalizableHandle realm_dart_attach_finalizer(Dart_Handle handle, void* realmPtr, int size) { - return Dart_NewFinalizableHandle_DL(handle, realmPtr, size, handle_finalizer); -} - -RLM_API void realm_dart_delete_finalizable(Dart_FinalizableHandle finalizable_handle, Dart_Handle handle) { - Dart_DeleteFinalizableHandle_DL(finalizable_handle, handle); -} - class WeakHandle { public: WeakHandle(Dart_Handle handle) : m_weakHandle(Dart_NewWeakPersistentHandle_DL(handle, this, 1, finalize_handle)) { diff --git a/src/realm_dart.h b/src/realm_dart.h index d79e89878..3c42ac043 100644 --- a/src/realm_dart.h +++ b/src/realm_dart.h @@ -24,10 +24,6 @@ RLM_API void realm_dart_initializeDartApiDL(void* data); -RLM_API Dart_FinalizableHandle realm_dart_attach_finalizer(Dart_Handle handle, void* realmPtr, int size); - -RLM_API void realm_dart_delete_finalizable(Dart_FinalizableHandle finalizable_handle, Dart_Handle handle); - RLM_API void* realm_dart_object_to_weak_handle(Dart_Handle handle); RLM_API Dart_Handle realm_dart_weak_handle_to_object(void* handle);