blob: 01ef31c1eab188dc46d76471013829411a57fe49 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart.core;
/// An [Expando] allows adding new properties to objects.
///
/// Does not work on numbers, strings, booleans, `null`, `dart:ffi` pointers,
/// `dart:ffi` structs, or `dart:ffi` unions.
///
/// An `Expando` does not hold on to the added property value after an object
/// becomes inaccessible.
///
/// Since you can always create a new number that is identical to an existing
/// number, it means that an expando property on a number could never be
/// released. To avoid this, expando properties cannot be added to numbers.
/// The same argument applies to strings, booleans and `null`, which also have
/// literals that evaluate to identical values when they occur more than once.
///
/// There is no restriction on other classes, even for compile time constant
/// objects. Be careful if adding expando properties to compile time constants,
/// since they will stay alive forever.
class Expando<T extends Object> {
/// The name of the this [Expando] as passed to the constructor.
///
/// If no name was passed to the constructor, the value is the `null` value.
final String? name;
/// Creates a new [Expando]. The optional name is only used for
/// debugging purposes and creating two different [Expando]s with the
/// same name yields two [Expando]s that work on different properties
/// of the objects they are used on.
external Expando([String? name]);
/// Expando toString method override.
String toString() => "Expando:$name";
/// Gets the value of this [Expando]'s property on the given object.
///
/// If the object hasn't been expanded, the result is the `null` value.
///
/// The object must not be a number, a string, a boolean, `null`, a
/// `dart:ffi` pointer, a `dart:ffi` struct, or a `dart:ffi` union.
external T? operator [](Object object);
/// Sets this [Expando]'s property value on the given object to [value].
///
/// Properties can effectively be removed again
/// by setting their value to `null`.
///
/// The object must not be a number, a string, a boolean, `null`, a
/// `dart:ffi` pointer, a `dart:ffi` struct, or a `dart:ffi` union.
external void operator []=(Object object, T? value);
}
/// A weak reference to a Dart object.
///
/// A _weak_ reference to the [target] object which may be cleared
/// (set to reference `null` instead) at any time
/// when there is no other way for the program to access the target object.
///
/// _Being the target of a weak reference does not keep an object
/// from being garbage collected._
///
/// There are no guarantees that a weak reference will ever be cleared
/// even if all references to its target are weak references.
///
/// Not all objects are supported as targets for weak references.
/// The [WeakReference] constructor will reject any object that is not
/// supported as an [Expando] key.
@Since("2.17")
abstract class WeakReference<T extends Object> {
/// Creates a [WeakReference] pointing to the given [target].
///
/// The [target] must be an object supported as an [Expando] key,
/// which means [target] cannot be a number, a string, a boolean,
/// the `null` value, or certain other types of special objects.
external factory WeakReference(T target);
/// The current object weakly referenced by [this], if any.
///
/// The value is either the object supplied in the constructor,
/// or `null` if the weak reference has been cleared.
T? get target;
}
/// A finalizer which can be attached to Dart objects.
///
/// A finalizer can create attachments between
/// the finalizer and any number of Dart values,
/// by calling [attach] with the value, along with a
/// _finalization token_ and an optional _attach key_,
/// which are part of the attachment.
///
/// When a Dart value becomes inaccessible to the program,
/// any finalizer that currently has an attachment to
/// the value *may* have its callback function called
/// with the attachment's finalization token.
///
/// Example:
/// ```dart template:none
/// // Keep the finalizer itself reachable, otherwise might not do anything.
/// final Finalizer<DBConnection> _finalizer = Finalizer((connection) {
/// connection.close();
/// });
///
/// /// Access the database.
/// Database connect() {
/// // Wraps the connection in a nicer user-facing API,
/// // *and* closes connection if the user forgets to.
/// var connection = _connectToDatabase();
/// var wrapper = Database._fromConnection(connection, _finalizer);
/// // Get finalizer callback when `wrapper` is no longer reachable.
/// _finalizer.attach(wrapper, connection, detach: wrapper);
/// return wrapper;
/// }
///
/// class Database {
/// final DBConnection _connection;
/// final Finalizer<Connection> _finalizer;
/// Database._fromConnection(this._connection, this._finalizer);
///
/// // Some useful methods.
///
/// void close() {
/// // User requested close.
/// _connection.close();
/// // Detach from finalizer, no longer needed.
/// _finalizer.detach(this);
/// }
/// }
/// ```
/// This example has an example of an external resource that needs clean-up.
/// The finalizer is used to clean up an external connection when the
/// user of the API no longer has access to that connection.
/// The example uses the same object as attached object and detach key,
/// which is a useful approach when each attached object can be detached
/// individually. Being a detachment key doesn't keep an object alive.
///
/// No promises are made that the callback will ever be called.
/// The only thing that is guaranteed is that if a finalizer's callback
/// is called with a specific finalization token as argument,
/// then at least one value with an attachment to to the finalizer
/// that has that finalization token,
/// is no longer accessible to the program.
///
/// If the finalzier *itself* becomes unreachable,
/// it's allowed to be garbage collected
/// and then it won't trigger any further callbacks.
/// Always make sure to keep the finalizer itself reachable while it's needed.
///
/// If multiple finalizers are attached to a single object,
/// or the same finalizer is attached multiple times to an object,
/// and that object becomes inaccessible to the program,
/// then any number (including zero) of those attachments may trigger
/// their associated finalizer's callback.
/// It will not necessarily be all or none of them.
///
/// Finalization callbacks will happen as *events*.
/// They will not happen during execution of other code,
/// and not as a microtask,
/// but as high-level events similar to timer events.
///
/// Finalization callbacks must not throw.
@Since("2.17")
abstract class Finalizer<T> {
/// Creates a finalizer with the given finalization callback.
///
/// The [callback] is bound to the current zone
/// when the [Finalizer] is created, and will run in that zone when called.
external factory Finalizer(void Function(T) callback);
/// Attaches this finalizer to [value].
///
/// When [value] is longer accessible to the program,
/// while still having an attachement to this finalizer,
/// the callback of this finalizer *may* be called
/// with [finalizationToken] as argument.
/// The callback may be called at most once per active attachment,
/// ones which have not been detached by calling [Finalizer.detach].
///
/// If a non-`null` [detach] value is provided, that object can be
/// passed to [Finalizer.detach] to remove the attachment again.
///
/// The [value] and [detach] arguments do not count towards those
/// objects being accessible to the program.
/// Both must be objects supported as an [Expando] key.
/// They may be the *same* object.
///
/// Example:
/// ```dart template:top
/// /// Access the data base.
/// Database connect() {
/// // Wraps the connection in a nice user API,
/// // *and* closes connection if the user forgets to.
/// var connection = _connectToDatabase();
/// var wrapper = Database._fromConnection(connection, _finalizer);
/// // Get finalizer callback when `wrapper` is no longer reachable.
/// _finalizer.attach(wrapper, connection, detach: wrapper);
/// return wrapper;
/// }
/// ````
///
/// Multiple objects may be attached using the same finalization token,
/// and the finalizer can be attached multiple times to the same object
/// with different, or the same, finalization token.
void attach(Object value, T finalizationToken, {Object? detach});
/// Detaches the finalizer from values attached with [detachToken].
///
/// Each attachment between this finalizer and a value,
/// which was created by calling [attach] with the [detachToken] object as
/// `detach` argument, is removed.
///
/// If the finalizer was attached multiple times to the same value
/// with different detachment keys,
/// only those attachments which used [detachToken] are removed.
///
/// After detaching, an attachment won't cause any callbacks to happen
/// if the object become inaccessible.
///
/// Example:
/// ```dart template:none
/// final Finalizer<DBConnection> _finalizer = Finalizer((connection) {
/// connection.close();
/// });
///
/// class Database {
/// final DBConnection _connection;
/// final Finalizer<Connection> _finalizer;
/// Database._fromConnection(this._connection, this._finalizer);
///
/// // Some useful methods.
///
/// void close() {
/// // User requested close.
/// _connection.close();
/// // Detach from finalizer, no longer needed.
/// // Was attached using this object as `detach` token.
/// _finalizer.detach(this);
/// }
/// }
/// ```
void detach(Object detachToken);
}