blob: a96cdfb99c65dcb325aaea5c3857548b24ed81ab [file] [log] [blame]
// Copyright (c) 2013, 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.async;
typedef R ZoneCallback<R>();
typedef R ZoneUnaryCallback<R, T>(T arg);
typedef R ZoneBinaryCallback<R, T1, T2>(T1 arg1, T2 arg2);
/// The type of a custom [Zone.handleUncaughtError] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The [error] and [stackTrace] are the error and stack trace that
/// was uncaught in [zone].
typedef HandleUncaughtErrorHandler = void Function(Zone self,
ZoneDelegate parent, Zone zone, Object error, StackTrace stackTrace);
/// The type of a custom [Zone.run] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The function [f] is the function which was passed to the
/// [Zone.run] of [zone].
///
/// The default behavior of [Zone.run] is
/// to call [f] in the current zone, [zone].
/// A custom handler can do things before, after or instead of
/// calling [f].
typedef RunHandler = R Function<R>(
Zone self, ZoneDelegate parent, Zone zone, R Function() f);
/// The type of a custom [Zone.runUnary] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The function [f] and value [arg] are the function and argument
/// which was passed to the [Zone.runUnary] of [zone].
///
/// The default behavior of [Zone.runUnary] is
/// to call [f] with argument [arg] in the current zone, [zone].
/// A custom handler can do things before, after or instead of
/// calling [f].
typedef RunUnaryHandler = R Function<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T arg) f, T arg);
/// The type of a custom [Zone.runBinary] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The function [f] and values [arg1] and [arg2] are the function and arguments
/// which was passed to the [Zone.runBinary] of [zone].
///
/// The default behavior of [Zone.runUnary] is
/// to call [f] with arguments [arg1] and [arg2] in the current zone, [zone].
/// A custom handler can do things before, after or instead of
/// calling [f].
typedef RunBinaryHandler = R Function<R, T1, T2>(Zone self, ZoneDelegate parent,
Zone zone, R Function(T1 arg1, T2 arg2) f, T1 arg1, T2 arg2);
/// The type of a custom [Zone.registerCallback] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The function [f] is the function which was passed to the
/// [Zone.registerCallback] of [zone].
///
/// The handler should return either the function [f]
/// or another function replacing [f],
/// typically by wrapping [f] in a function
/// which does something extra before and after invoking [f]
typedef RegisterCallbackHandler = ZoneCallback<R> Function<R>(
Zone self, ZoneDelegate parent, Zone zone, R Function() f);
/// The type of a custom [Zone.registerUnaryCallback] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The function [f] is the function which was passed to the
/// which was passed to the [Zone.registerUnaryCallback] of [zone].
///
/// The handler should return either the function [f]
/// or another function replacing [f],
/// typically by wrapping [f] in a function
/// which does something extra before and after invoking [f]
typedef RegisterUnaryCallbackHandler = ZoneUnaryCallback<R, T> Function<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T arg) f);
/// The type of a custom [Zone.registerBinaryCallback] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The function [f] is the function which was passed to the
/// which was passed to the [Zone.registerBinaryCallback] of [zone].
///
/// The handler should return either the function [f]
/// or another function replacing [f],
/// typically by wrapping [f] in a function
/// which does something extra before and after invoking [f]
typedef RegisterBinaryCallbackHandler
= ZoneBinaryCallback<R, T1, T2> Function<R, T1, T2>(Zone self,
ZoneDelegate parent, Zone zone, R Function(T1 arg1, T2 arg2) f);
/// The type of a custom [Zone.errorCallback] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The [error] and [stackTrace] are the error and stack trace
/// passed to [Zone.errorCallback] of [zone].
///
/// The function should return either `null` if it doesn't want
/// to replace the original error and stack trace,
/// or an [AsyncError] containg a replacement error and stack trace
/// which will be used to replace the originals.
typedef AsyncError? ErrorCallbackHandler(Zone self, ZoneDelegate parent,
Zone zone, Object error, StackTrace? stackTrace);
/// The type of a custom [Zone.scheduleMicrotask] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The function [f] is the function which was
/// passed to [Zone.scheduleMicrotask] of [zone].
///
/// The custom handler can choose to replace the function [f]
/// with one that does something before, after or instead of calling [f],
/// and then call `parent.scheduleMicrotask(zone, replacement)`.
/// or it can implement its own microtask scheduling queue, which typically
/// still depends on `parent.scheduleMicrotask` to as a way to get started.
typedef void ScheduleMicrotaskHandler(
Zone self, ZoneDelegate parent, Zone zone, void f());
/// The type of a custom [Zone.createTimer] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The callback function [f] and [duration] are the ones which were
/// passed to [Zone.createTimer] of [zone]
/// (possibly through the [Timer] constructor).
///
/// The custom handler can choose to replace the function [f]
/// with one that does something before, after or instead of calling [f],
/// and then call `parent.createTimer(zone, replacement)`.
/// or it can implement its own timer queue, which typically
/// still depends on `parent.createTimer` to as a way to get started.
///
/// The function should return a [Timer] object which can be used
/// to inspect and control the scheduled timer callback.
typedef Timer CreateTimerHandler(
Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f());
/// The type of a custom [Zone.createPeriodicTimer] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The callback function [f] and [period] are the ones which were
/// passed to [Zone.createPeriodicTimer] of [zone]
/// (possibly through the [Timer.periodic] constructor).
///
/// The custom handler can choose to replace the function [f]
/// with one that does something before, after or instead of calling [f],
/// and then call `parent.createTimer(zone, replacement)`.
/// or it can implement its own timer queue, which typically
/// still depends on `parent.createTimer` to as a way to get started.
///
/// The function should return a [Timer] object which can be used
/// to inspect and control the scheduled timer callbacks.
typedef Timer CreatePeriodicTimerHandler(Zone self, ZoneDelegate parent,
Zone zone, Duration period, void f(Timer timer));
/// The type of a custom [Zone.print] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The string [line] is the one which was passed to [Zone.print] of [zone],
/// (possibly through the global [print] function).
///
/// The custom handler can intercept print operations and
/// redirect them to other targets than the console.
typedef void PrintHandler(
Zone self, ZoneDelegate parent, Zone zone, String line);
/// The type of a custom [Zone.fork] implementation function.
///
/// Receives the [Zone] that the handler was registered on as [self],
/// a delegate forwarding to the handlers of [self]'s parent zone as [parent],
/// and the current zone where the error was uncaught as [zone],
/// which will have [self] as a parent zone.
///
/// The handler should create a new zone with [zone] as its
/// immediate parent zone.
///
/// The [specification] and [zoneValues] are the ones which were
/// passed to [Zone.fork] of [zone]. They specify the custom zone
/// handlers and zone variables that the new zone should have.
///
/// The custom handler can change the specification or zone
/// values before calling `parent.fork(zone, specification, zoneValues)`,
/// but it has to call the [parent]'s [ZoneDelegate.fork] in order
/// to create a valid [Zone] object.
typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
ZoneSpecification? specification, Map<Object?, Object?>? zoneValues);
class _ZoneFunction<T extends Function> {
final _Zone zone;
final T function;
const _ZoneFunction(this.zone, this.function);
}
class _RunNullaryZoneFunction {
final _Zone zone;
final RunHandler function;
const _RunNullaryZoneFunction(this.zone, this.function);
}
class _RunUnaryZoneFunction {
final _Zone zone;
final RunUnaryHandler function;
const _RunUnaryZoneFunction(this.zone, this.function);
}
class _RunBinaryZoneFunction {
final _Zone zone;
final RunBinaryHandler function;
const _RunBinaryZoneFunction(this.zone, this.function);
}
class _RegisterNullaryZoneFunction {
final _Zone zone;
final RegisterCallbackHandler function;
const _RegisterNullaryZoneFunction(this.zone, this.function);
}
class _RegisterUnaryZoneFunction {
final _Zone zone;
final RegisterUnaryCallbackHandler function;
const _RegisterUnaryZoneFunction(this.zone, this.function);
}
class _RegisterBinaryZoneFunction {
final _Zone zone;
final RegisterBinaryCallbackHandler function;
const _RegisterBinaryZoneFunction(this.zone, this.function);
}
/// A parameter object with custom zone function handlers for [Zone.fork].
///
/// A zone specification is a parameter object passed to [Zone.fork]
/// and any underlying [ForkHandler] custom implementations.
/// The individual handlers, if set to a non-null value,
/// will be the implementation of the correpsonding [Zone] methods
/// for a forked zone created using this zone specification.
///
/// Handlers have the same signature as the same-named methods on [Zone],
/// but receive three additional arguments:
///
/// 1. The zone the handlers are attached to (the "self" zone).
/// This is the zone created by [Zone.fork] where the handler is
/// passed as part of the zone delegation.
/// 2. A [ZoneDelegate] to the parent zone.
/// 3. The "current" zone at the time the request was made.
/// The self zone is always a parent zone of the current zone.
///
/// Handlers can either stop propagating the request (by simply not calling the
/// parent handler), or forward to the parent zone, potentially modifying the
/// arguments on the way.
abstract class ZoneSpecification {
/// Creates a specification with the provided handlers.
///
/// If the [handleUncaughtError] is provided, the new zone will be a new
/// "error zone" which will prevent errors from flowing into other
/// error zones (see [Zone.errorZone], [Zone.inSameErrorZone]).
const factory ZoneSpecification(
{HandleUncaughtErrorHandler? handleUncaughtError,
RunHandler? run,
RunUnaryHandler? runUnary,
RunBinaryHandler? runBinary,
RegisterCallbackHandler? registerCallback,
RegisterUnaryCallbackHandler? registerUnaryCallback,
RegisterBinaryCallbackHandler? registerBinaryCallback,
ErrorCallbackHandler? errorCallback,
ScheduleMicrotaskHandler? scheduleMicrotask,
CreateTimerHandler? createTimer,
CreatePeriodicTimerHandler? createPeriodicTimer,
PrintHandler? print,
ForkHandler? fork}) = _ZoneSpecification;
/// Creates a specification from [other] and provided handlers.
///
/// The created zone specification has the handlers of [other]
/// and any individually provided handlers.
/// If a handler is provided both through [other] and individually,
/// the individually provided handler overries the one from [other].
factory ZoneSpecification.from(ZoneSpecification other,
{HandleUncaughtErrorHandler? handleUncaughtError,
RunHandler? run,
RunUnaryHandler? runUnary,
RunBinaryHandler? runBinary,
RegisterCallbackHandler? registerCallback,
RegisterUnaryCallbackHandler? registerUnaryCallback,
RegisterBinaryCallbackHandler? registerBinaryCallback,
ErrorCallbackHandler? errorCallback,
ScheduleMicrotaskHandler? scheduleMicrotask,
CreateTimerHandler? createTimer,
CreatePeriodicTimerHandler? createPeriodicTimer,
PrintHandler? print,
ForkHandler? fork}) {
return new ZoneSpecification(
handleUncaughtError: handleUncaughtError ?? other.handleUncaughtError,
run: run ?? other.run,
runUnary: runUnary ?? other.runUnary,
runBinary: runBinary ?? other.runBinary,
registerCallback: registerCallback ?? other.registerCallback,
registerUnaryCallback:
registerUnaryCallback ?? other.registerUnaryCallback,
registerBinaryCallback:
registerBinaryCallback ?? other.registerBinaryCallback,
errorCallback: errorCallback ?? other.errorCallback,
scheduleMicrotask: scheduleMicrotask ?? other.scheduleMicrotask,
createTimer: createTimer ?? other.createTimer,
createPeriodicTimer: createPeriodicTimer ?? other.createPeriodicTimer,
print: print ?? other.print,
fork: fork ?? other.fork);
}
/// A custom [Zone.handleUncaughtError] implementation for a new zone.
HandleUncaughtErrorHandler? get handleUncaughtError;
/// A custom [Zone.run] implementation for a new zone.
RunHandler? get run;
/// A custom [Zone.runUnary] implementation for a new zone.
RunUnaryHandler? get runUnary;
/// A custom [Zone.runBinary] implementation for a new zone.
RunBinaryHandler? get runBinary;
/// A custom [Zone.registerCallback] implementation for a new zone.
RegisterCallbackHandler? get registerCallback;
/// A custom [Zone.registerUnaryCallback] implementation for a new zone.
RegisterUnaryCallbackHandler? get registerUnaryCallback;
/// A custom [Zone.registerBinaryCallback] implementation for a new zone.
RegisterBinaryCallbackHandler? get registerBinaryCallback;
/// A custom [Zone.errorCallback] implementation for a new zone.
ErrorCallbackHandler? get errorCallback;
/// A custom [Zone.scheduleMicrotask] implementation for a new zone.
ScheduleMicrotaskHandler? get scheduleMicrotask;
/// A custom [Zone.createTimer] implementation for a new zone.
CreateTimerHandler? get createTimer;
/// A custom [Zone.createPeriodicTimer] implementation for a new zone.
CreatePeriodicTimerHandler? get createPeriodicTimer;
/// A custom [Zone.print] implementation for a new zone.
PrintHandler? get print;
/// A custom [Zone.handleUncaughtError] implementation for a new zone.
ForkHandler? get fork;
}
/// Internal [ZoneSpecification] class.
///
/// The implementation wants to rely on the fact that the getters cannot change
/// dynamically. We thus require users to go through the redirecting
/// [ZoneSpecification] constructor which instantiates this class.
class _ZoneSpecification implements ZoneSpecification {
const _ZoneSpecification(
{this.handleUncaughtError,
this.run,
this.runUnary,
this.runBinary,
this.registerCallback,
this.registerUnaryCallback,
this.registerBinaryCallback,
this.errorCallback,
this.scheduleMicrotask,
this.createTimer,
this.createPeriodicTimer,
this.print,
this.fork});
final HandleUncaughtErrorHandler? handleUncaughtError;
final RunHandler? run;
final RunUnaryHandler? runUnary;
final RunBinaryHandler? runBinary;
final RegisterCallbackHandler? registerCallback;
final RegisterUnaryCallbackHandler? registerUnaryCallback;
final RegisterBinaryCallbackHandler? registerBinaryCallback;
final ErrorCallbackHandler? errorCallback;
final ScheduleMicrotaskHandler? scheduleMicrotask;
final CreateTimerHandler? createTimer;
final CreatePeriodicTimerHandler? createPeriodicTimer;
final PrintHandler? print;
final ForkHandler? fork;
}
/// An adapted view of the parent zone.
///
/// This class allows the implementation of a zone method to invoke methods on
/// the parent zone while retaining knowledge of the originating zone.
///
/// Custom zones (created through [Zone.fork] or [runZoned]) can provide
/// implementations of most methods of zones. This is similar to overriding
/// methods on [Zone], except that this mechanism doesn't require subclassing.
///
/// A custom zone function (provided through a [ZoneSpecification]) typically
/// records or wraps its parameters and then delegates the operation to its
/// parent zone using the provided [ZoneDelegate].
///
/// While zones have access to their parent zone (through [Zone.parent]) it is
/// recommended to call the methods on the provided parent delegate for two
/// reasons:
/// 1. the delegate methods take an additional `zone` argument which is the
/// zone the action has been initiated in.
/// 2. delegate calls are more efficient, since the implementation knows how
/// to skip zones that would just delegate to their parents.
abstract class ZoneDelegate {
// Invoke the [HandleUncaughtErrorHandler] of the zone with a current zone.
void handleUncaughtError(Zone zone, Object error, StackTrace stackTrace);
// Invokes the [RunHandler] of the zone with a current zone.
R run<R>(Zone zone, R f());
// Invokes the [RunUnaryHandler] of the zone with a current zone.
R runUnary<R, T>(Zone zone, R f(T arg), T arg);
// Invokes the [RunBinaryHandler] of the zone with a current zone.
R runBinary<R, T1, T2>(Zone zone, R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2);
// Invokes the [RegisterCallbackHandler] of the zone with a current zone.
ZoneCallback<R> registerCallback<R>(Zone zone, R f());
// Invokes the [RegisterUnaryHandler] of the zone with a current zone.
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(Zone zone, R f(T arg));
// Invokes the [RegisterBinaryHandler] of the zone with a current zone.
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
Zone zone, R f(T1 arg1, T2 arg2));
// Invokes the [ErrorCallbackHandler] of the zone with a current zone.
AsyncError? errorCallback(Zone zone, Object error, StackTrace? stackTrace);
// Invokes the [ScheduleMicrotaskHandler] of the zone with a current zone.
void scheduleMicrotask(Zone zone, void f());
// Invokes the [CreateTimerHandler] of the zone with a current zone.
Timer createTimer(Zone zone, Duration duration, void f());
// Invokes the [CreatePeriodicTimerHandler] of the zone with a current zone.
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer));
// Invokes the [PrintHandler] of the zone with a current zone.
void print(Zone zone, String line);
// Invokes the [ForkHandler] of the zone with a current zone.
Zone fork(Zone zone, ZoneSpecification? specification, Map? zoneValues);
}
/// A zone represents an environment that remains stable across asynchronous
/// calls.
///
/// Code is always executed in the context of a zone, available as
/// [Zone.current]. The initial `main` function runs in the context of the
/// default zone ([Zone.root]). Code can be run in a different zone using either
/// [runZoned], to create a new zone, or [Zone.run] to run code in the context of
/// an existing zone which was created earlier using [Zone.fork].
///
/// Developers can create a new zone that overrides some of the functionality of
/// an existing zone. For example, custom zones can replace of modify the
/// behavior of `print`, timers, microtasks or how uncaught errors are handled.
///
/// The [Zone] class is not subclassable, but users can provide custom zones by
/// forking an existing zone (usually [Zone.current]) with a [ZoneSpecification].
/// This is similar to creating a new class that extends the base `Zone` class
/// and that overrides some methods, except without actually creating a new
/// class. Instead the overriding methods are provided as functions that
/// explicitly take the equivalent of their own class, the "super" class and the
/// `this` object as parameters.
///
/// Asynchronous callbacks always run in the context of the zone where they were
/// scheduled. This is implemented using two steps:
/// 1. the callback is first registered using one of [registerCallback],
/// [registerUnaryCallback], or [registerBinaryCallback]. This allows the zone
/// to record that a callback exists and potentially modify it (by returning a
/// different callback). The code doing the registration (e.g., `Future.then`)
/// also remembers the current zone so that it can later run the callback in
/// that zone.
/// 2. At a later point the registered callback is run in the remembered zone,
/// using one of [run], [runUnary] or [runBinary].
///
/// This is all handled internally by the platform code and most users don't need
/// to worry about it. However, developers of new asynchronous operations,
/// provided by the underlying system or through native extensions, must follow
/// the protocol to be zone compatible.
///
/// For convenience, zones provide [bindCallback] (and the corresponding
/// [bindUnaryCallback] and [bindBinaryCallback]) to make it easier to respect
/// the zone contract: these functions first invoke the corresponding `register`
/// functions and then wrap the returned function so that it runs in the current
/// zone when it is later asynchronously invoked.
///
/// Similarly, zones provide [bindCallbackGuarded] (and the corresponding
/// [bindUnaryCallbackGuarded] and [bindBinaryCallbackGuarded]), when the
/// callback should be invoked through [Zone.runGuarded].
abstract class Zone {
// Private constructor so that it is not possible instantiate a Zone class.
Zone._();
/// The root zone.
///
/// All isolate entry functions (`main` or spawned functions) start running in
/// the root zone (that is, [Zone.current] is identical to [Zone.root] when the
/// entry function is called). If no custom zone is created, the rest of the
/// program always runs in the root zone.
///
/// The root zone implements the default behavior of all zone operations.
/// Many methods, like [registerCallback] do the bare minimum required of the
/// function, and are only provided as a hook for custom zones. Others, like
/// [scheduleMicrotask], interact with the underlying system to implement the
/// desired behavior.
static const Zone root = _rootZone;
/// The currently running zone.
static _Zone _current = _rootZone;
/// The zone that is currently active.
static Zone get current => _current;
/// Handles uncaught asynchronous errors.
///
/// There are two kind of asynchronous errors that are handled by this
/// function:
/// 1. Uncaught errors that were thrown in asynchronous callbacks, for example,
/// a `throw` in the function passed to [Timer.run].
/// 2. Asynchronous errors that are pushed through [Future] and [Stream]
/// chains, but for which nobody registered an error handler.
/// Most asynchronous classes, like [Future] or [Stream] push errors to their
/// listeners. Errors are propagated this way until either a listener handles
/// the error (for example with [Future.catchError]), or no listener is
/// available anymore. In the latter case, futures and streams invoke the
/// zone's [handleUncaughtError].
///
/// By default, when handled by the root zone, uncaught asynchronous errors are
/// treated like uncaught synchronous exceptions.
void handleUncaughtError(Object error, StackTrace stackTrace);
/// The parent zone of the this zone.
///
/// Is `null` if `this` is the [root] zone.
///
/// Zones are created by [fork] on an existing zone, or by [runZoned] which
/// forks the [current] zone. The new zone's parent zone is the zone it was
/// forked from.
Zone? get parent;
/// The error zone is responsible for dealing with uncaught errors.
///
/// This is the closest parent zone of this zone that provides a
/// [handleUncaughtError] method.
///
/// Asynchronous errors never cross zone boundaries between zones with
/// different error handlers.
///
/// Example:
/// ```
/// import 'dart:async';
///
/// main() {
/// var future;
/// runZoned(() {
/// // The asynchronous error is caught by the custom zone which prints
/// // 'asynchronous error'.
/// future = Future.error("asynchronous error");
/// }, onError: (e) { print(e); }); // Creates a zone with an error handler.
/// // The following `catchError` handler is never invoked, because the
/// // custom zone created by the call to `runZoned` provides an
/// // error handler.
/// future.catchError((e) { throw "is never reached"; });
/// }
/// ```
///
/// Note that errors cannot enter a child zone with a different error handler
/// either:
/// ```
/// import 'dart:async';
///
/// main() {
/// runZoned(() {
/// // The following asynchronous error is *not* caught by the `catchError`
/// // in the nested zone, since errors are not to cross zone boundaries
/// // with different error handlers.
/// // Instead the error is handled by the current error handler,
/// // printing "Caught by outer zone: asynchronous error".
/// var future = Future.error("asynchronous error");
/// runZoned(() {
/// future.catchError((e) { throw "is never reached"; });
/// }, onError: (e) { throw "is never reached"; });
/// }, onError: (e) { print("Caught by outer zone: $e"); });
/// }
/// ```
Zone get errorZone;
/// Whether this zone and [otherZone] are in the same error zone.
///
/// Two zones are in the same error zone if they have the same [errorZone].
bool inSameErrorZone(Zone otherZone);
/// Creates a new zone as a child zone of this zone.
///
/// The new zone uses the closures in the given [specification] to override
/// the current's zone behavior. All specification entries that are `null`
/// inherit the behavior from the parent zone (`this`).
///
/// The new zone inherits the stored values (accessed through [operator []])
/// of this zone and updates them with values from [zoneValues], which either
/// adds new values or overrides existing ones.
///
/// Note that the fork operation is interceptible. A zone can thus change
/// the zone specification (or zone values), giving the forking zone full
/// control over the child zone.
Zone fork(
{ZoneSpecification? specification, Map<Object?, Object?>? zoneValues});
/// Executes [action] in this zone.
///
/// By default (as implemented in the [root] zone), runs [action]
/// with [current] set to this zone.
///
/// If [action] throws, the synchronous exception is not caught by the zone's
/// error handler. Use [runGuarded] to achieve that.
///
/// Since the root zone is the only zone that can modify the value of
/// [current], custom zones intercepting run should always delegate to their
/// parent zone. They may take actions before and after the call.
R run<R>(R action());
/// Executes the given [action] with [argument] in this zone.
///
/// As [run] except that [action] is called with one [argument] instead of
/// none.
R runUnary<R, T>(R action(T argument), T argument);
/// Executes the given [action] with [argument1] and [argument2] in this
/// zone.
///
/// As [run] except that [action] is called with two arguments instead of none.
R runBinary<R, T1, T2>(
R action(T1 argument1, T2 argument2), T1 argument1, T2 argument2);
/// Executes the given [action] in this zone and catches synchronous
/// errors.
///
/// This function is equivalent to:
/// ```
/// try {
/// this.run(action);
/// } catch (e, s) {
/// this.handleUncaughtError(e, s);
/// }
/// ```
///
/// See [run].
void runGuarded(void action());
/// Executes the given [action] with [argument] in this zone and
/// catches synchronous errors.
///
/// See [runGuarded].
void runUnaryGuarded<T>(void action(T argument), T argument);
/// Executes the given [action] with [argument1] and [argument2] in this
/// zone and catches synchronous errors.
///
/// See [runGuarded].
void runBinaryGuarded<T1, T2>(
void action(T1 argument1, T2 argument2), T1 argument1, T2 argument2);
/// Registers the given callback in this zone.
///
/// When implementing an asynchronous primitive that uses callbacks, the
/// callback must be registered using [registerCallback] at the point where the
/// user provides the callback. This allows zones to record other information
/// that they need at the same time, perhaps even wrapping the callback, so
/// that the callback is prepared when it is later run in the same zones
/// (using [run]). For example, a zone may decide
/// to store the stack trace (at the time of the registration) with the
/// callback.
///
/// Returns the callback that should be used in place of the provided
/// [callback]. Frequently zones simply return the original callback.
///
/// Custom zones may intercept this operation. The default implementation in
/// [Zone.root] returns the original callback unchanged.
ZoneCallback<R> registerCallback<R>(R callback());
/// Registers the given callback in this zone.
///
/// Similar to [registerCallback] but with a unary callback.
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R callback(T arg));
/// Registers the given callback in this zone.
///
/// Similar to [registerCallback] but with a unary callback.
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
R callback(T1 arg1, T2 arg2));
/// Registers the provided [callback] and returns a function that will
/// execute in this zone.
///
/// Equivalent to:
/// ```dart
/// ZoneCallback registered = this.registerCallback(callback);
/// return () => this.run(registered);
/// ```
ZoneCallback<R> bindCallback<R>(R callback());
/// Registers the provided [callback] and returns a function that will
/// execute in this zone.
///
/// Equivalent to:
/// ```dart
/// ZoneCallback registered = this.registerUnaryCallback(callback);
/// return (arg) => thin.runUnary(registered, arg);
/// ```
ZoneUnaryCallback<R, T> bindUnaryCallback<R, T>(R callback(T argument));
/// Registers the provided [callback] and returns a function that will
/// execute in this zone.
///
/// Equivalent to:
/// ```dart
/// ZoneCallback registered = registerBinaryCallback(callback);
/// return (arg1, arg2) => thin.runBinary(registered, arg1, arg2);
/// ```
ZoneBinaryCallback<R, T1, T2> bindBinaryCallback<R, T1, T2>(
R callback(T1 argument1, T2 argument2));
/// Registers the provided [callback] and returns a function that will
/// execute in this zone.
///
/// When the function executes, errors are caught and treated as uncaught
/// errors.
///
/// Equivalent to:
/// ```dart
/// ZoneCallback registered = this.registerCallback(callback);
/// return () => this.runGuarded(registered);
/// ```
void Function() bindCallbackGuarded(void Function() callback);
/// Registers the provided [callback] and returns a function that will
/// execute in this zone.
///
/// When the function executes, errors are caught and treated as uncaught
/// errors.
///
/// Equivalent to:
/// ```dart
/// ZoneCallback registered = this.registerUnaryCallback(callback);
/// return (arg) => this.runUnaryGuarded(registered, arg);
/// ```
void Function(T) bindUnaryCallbackGuarded<T>(void callback(T argument));
/// Registers the provided [callback] and returns a function that will
/// execute in this zone.
///
/// Equivalent to:
/// ```dart
/// ZoneCallback registered = registerBinaryCallback(callback);
/// return (arg1, arg2) => this.runBinaryGuarded(registered, arg1, arg2);
/// ```
void Function(T1, T2) bindBinaryCallbackGuarded<T1, T2>(
void callback(T1 argument1, T2 argument2));
/// Intercepts errors when added programmatically to a [Future] or [Stream].
///
/// When calling [Completer.completeError], [StreamController.addError],
/// or some [Future] constructors, the current zone is allowed to intercept
/// and replace the error.
///
/// Future constructors invoke this function when the error is received
/// directly, for example with [Future.error], or when the error is caught
/// synchronously, for example with [Future.sync].
///
/// There is no guarantee that an error is only sent through [errorCallback]
/// once. Libraries that use intermediate controllers or completers might
/// end up invoking [errorCallback] multiple times.
///
/// Returns `null` if no replacement is desired. Otherwise returns an instance
/// of [AsyncError] holding the new pair of error and stack trace.
///
/// Custom zones may intercept this operation.
///
/// Implementations of a new asynchronous primitive that converts synchronous
/// errors to asynchronous errors rarely need to invoke [errorCallback], since
/// errors are usually reported through future completers or stream
/// controllers.
AsyncError? errorCallback(Object error, StackTrace? stackTrace);
/// Runs [callback] asynchronously in this zone.
///
/// The global `scheduleMicrotask` delegates to the [current] zone's
/// [scheduleMicrotask]. The root zone's implementation interacts with the
/// underlying system to schedule the given callback as a microtask.
///
/// Custom zones may intercept this operation (for example to wrap the given
/// [callback]), or to implement their own microtask scheduler.
/// In the latter case, they will usually still use the parent zone's
/// [ZoneDelegate.scheduleMicrotask] to attach themselves to the existing
/// event loop.
void scheduleMicrotask(void Function() callback);
/// Creates a [Timer] where the callback is executed in this zone.
Timer createTimer(Duration duration, void Function() callback);
/// Creates a periodic [Timer] where the callback is executed in this zone.
Timer createPeriodicTimer(Duration period, void callback(Timer timer));
/// Prints the given [line].
///
/// The global `print` function delegates to the current zone's [print]
/// function which makes it possible to intercept printing.
///
/// Example:
/// ```
/// import 'dart:async';
///
/// main() {
/// runZoned(() {
/// // Ends up printing: "Intercepted: in zone".
/// print("in zone");
/// }, zoneSpecification: new ZoneSpecification(
/// print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
/// parent.print(zone, "Intercepted: $line");
/// }));
/// }
/// ```
void print(String line);
/// Call to enter the [Zone].
///
/// The previous current zone is returned.
static _Zone _enter(_Zone zone) {
assert(!identical(zone, _current));
_Zone previous = _current;
_current = zone;
return previous;
}
/// Call to leave the [Zone].
///
/// The previous [Zone] must be provided as `previous`.
static void _leave(_Zone previous) {
assert(previous != null);
Zone._current = previous;
}
/// Retrieves the zone-value associated with [key].
///
/// If this zone does not contain the value looks up the same key in the
/// parent zone. If the [key] is not found returns `null`.
///
/// Any object can be used as key, as long as it has compatible `operator ==`
/// and `hashCode` implementations.
/// By controlling access to the key, a zone can grant or deny access to the
/// zone value.
dynamic operator [](Object? key);
}
class _ZoneDelegate implements ZoneDelegate {
final _Zone _delegationTarget;
_ZoneDelegate(this._delegationTarget);
void handleUncaughtError(Zone zone, Object error, StackTrace stackTrace) {
var implementation = _delegationTarget._handleUncaughtError;
_Zone implZone = implementation.zone;
HandleUncaughtErrorHandler handler = implementation.function;
return handler(implZone, implZone._parentDelegate, zone, error, stackTrace);
}
R run<R>(Zone zone, R f()) {
var implementation = _delegationTarget._run;
_Zone implZone = implementation.zone;
var handler = implementation.function as RunHandler;
return handler(implZone, implZone._parentDelegate, zone, f);
}
R runUnary<R, T>(Zone zone, R f(T arg), T arg) {
var implementation = _delegationTarget._runUnary;
_Zone implZone = implementation.zone;
var handler = implementation.function as RunUnaryHandler;
return handler(implZone, implZone._parentDelegate, zone, f, arg);
}
R runBinary<R, T1, T2>(Zone zone, R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
var implementation = _delegationTarget._runBinary;
_Zone implZone = implementation.zone;
var handler = implementation.function as RunBinaryHandler;
return handler(implZone, implZone._parentDelegate, zone, f, arg1, arg2);
}
ZoneCallback<R> registerCallback<R>(Zone zone, R f()) {
var implementation = _delegationTarget._registerCallback;
_Zone implZone = implementation.zone;
var handler = implementation.function as RegisterCallbackHandler;
return handler(implZone, implZone._parentDelegate, zone, f);
}
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(Zone zone, R f(T arg)) {
var implementation = _delegationTarget._registerUnaryCallback;
_Zone implZone = implementation.zone;
var handler = implementation.function as RegisterUnaryCallbackHandler;
return handler(implZone, implZone._parentDelegate, zone, f);
}
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
Zone zone, R f(T1 arg1, T2 arg2)) {
var implementation = _delegationTarget._registerBinaryCallback;
_Zone implZone = implementation.zone;
var handler = implementation.function as RegisterBinaryCallbackHandler;
return handler(implZone, implZone._parentDelegate, zone, f);
}
AsyncError? errorCallback(Zone zone, Object error, StackTrace? stackTrace) {
checkNotNullable(error, "error");
var implementation = _delegationTarget._errorCallback;
_Zone implZone = implementation.zone;
if (identical(implZone, _rootZone)) return null;
ErrorCallbackHandler handler = implementation.function;
return handler(implZone, implZone._parentDelegate, zone, error, stackTrace);
}
void scheduleMicrotask(Zone zone, f()) {
var implementation = _delegationTarget._scheduleMicrotask;
_Zone implZone = implementation.zone;
ScheduleMicrotaskHandler handler = implementation.function;
handler(implZone, implZone._parentDelegate, zone, f);
}
Timer createTimer(Zone zone, Duration duration, void f()) {
var implementation = _delegationTarget._createTimer;
_Zone implZone = implementation.zone;
CreateTimerHandler handler = implementation.function;
return handler(implZone, implZone._parentDelegate, zone, duration, f);
}
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer)) {
var implementation = _delegationTarget._createPeriodicTimer;
_Zone implZone = implementation.zone;
CreatePeriodicTimerHandler handler = implementation.function;
return handler(implZone, implZone._parentDelegate, zone, period, f);
}
void print(Zone zone, String line) {
var implementation = _delegationTarget._print;
_Zone implZone = implementation.zone;
PrintHandler handler = implementation.function;
handler(implZone, implZone._parentDelegate, zone, line);
}
Zone fork(Zone zone, ZoneSpecification? specification,
Map<Object?, Object?>? zoneValues) {
var implementation = _delegationTarget._fork;
_Zone implZone = implementation.zone;
ForkHandler handler = implementation.function;
return handler(
implZone, implZone._parentDelegate, zone, specification, zoneValues);
}
}
/// Base class for Zone implementations.
abstract class _Zone implements Zone {
const _Zone();
// TODO(floitsch): the types of the `_ZoneFunction`s should have a type for
// all fields.
_RunNullaryZoneFunction get _run;
_RunUnaryZoneFunction get _runUnary;
_RunBinaryZoneFunction get _runBinary;
_RegisterNullaryZoneFunction get _registerCallback;
_RegisterUnaryZoneFunction get _registerUnaryCallback;
_RegisterBinaryZoneFunction get _registerBinaryCallback;
_ZoneFunction<ErrorCallbackHandler> get _errorCallback;
_ZoneFunction<ScheduleMicrotaskHandler> get _scheduleMicrotask;
_ZoneFunction<CreateTimerHandler> get _createTimer;
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer;
_ZoneFunction<PrintHandler> get _print;
_ZoneFunction<ForkHandler> get _fork;
_ZoneFunction<HandleUncaughtErrorHandler> get _handleUncaughtError;
// Parent zone. Only `null` for the root zone.
_Zone? get parent;
ZoneDelegate get _delegate;
ZoneDelegate get _parentDelegate;
Map<Object?, Object?> get _map;
bool inSameErrorZone(Zone otherZone) {
return identical(this, otherZone) ||
identical(errorZone, otherZone.errorZone);
}
}
class _CustomZone extends _Zone {
// The actual zone and implementation of each of these
// inheritable zone functions.
// TODO(floitsch): the types of the `_ZoneFunction`s should have a type for
// all fields, but we can't use generic function types as type arguments.
_RunNullaryZoneFunction _run;
_RunUnaryZoneFunction _runUnary;
_RunBinaryZoneFunction _runBinary;
_RegisterNullaryZoneFunction _registerCallback;
_RegisterUnaryZoneFunction _registerUnaryCallback;
_RegisterBinaryZoneFunction _registerBinaryCallback;
_ZoneFunction<ErrorCallbackHandler> _errorCallback;
_ZoneFunction<ScheduleMicrotaskHandler> _scheduleMicrotask;
_ZoneFunction<CreateTimerHandler> _createTimer;
_ZoneFunction<CreatePeriodicTimerHandler> _createPeriodicTimer;
_ZoneFunction<PrintHandler> _print;
_ZoneFunction<ForkHandler> _fork;
_ZoneFunction<HandleUncaughtErrorHandler> _handleUncaughtError;
// A cached delegate to this zone.
ZoneDelegate? _delegateCache;
/// The parent zone.
final _Zone parent;
/// The zone's scoped value declaration map.
///
/// This is always a [HashMap].
final Map<Object?, Object?> _map;
ZoneDelegate get _delegate => _delegateCache ??= _ZoneDelegate(this);
ZoneDelegate get _parentDelegate => parent._delegate;
_CustomZone(this.parent, ZoneSpecification specification, this._map)
: _run = parent._run,
_runUnary = parent._runUnary,
_runBinary = parent._runBinary,
_registerCallback = parent._registerCallback,
_registerUnaryCallback = parent._registerUnaryCallback,
_registerBinaryCallback = parent._registerBinaryCallback,
_errorCallback = parent._errorCallback,
_scheduleMicrotask = parent._scheduleMicrotask,
_createTimer = parent._createTimer,
_createPeriodicTimer = parent._createPeriodicTimer,
_print = parent._print,
_fork = parent._fork,
_handleUncaughtError = parent._handleUncaughtError {
// The root zone will have implementations of all parts of the
// specification, so it will never try to access the (null) parent.
// All other zones have a non-null parent.
var run = specification.run;
if (run != null) {
_run = _RunNullaryZoneFunction(this, run);
}
var runUnary = specification.runUnary;
if (runUnary != null) {
_runUnary = _RunUnaryZoneFunction(this, runUnary);
}
var runBinary = specification.runBinary;
if (runBinary != null) {
_runBinary = _RunBinaryZoneFunction(this, runBinary);
}
var registerCallback = specification.registerCallback;
if (registerCallback != null) {
_registerCallback = _RegisterNullaryZoneFunction(this, registerCallback);
}
var registerUnaryCallback = specification.registerUnaryCallback;
if (registerUnaryCallback != null) {
_registerUnaryCallback =
_RegisterUnaryZoneFunction(this, registerUnaryCallback);
}
var registerBinaryCallback = specification.registerBinaryCallback;
if (registerBinaryCallback != null) {
_registerBinaryCallback =
_RegisterBinaryZoneFunction(this, registerBinaryCallback);
}
var errorCallback = specification.errorCallback;
if (errorCallback != null) {
_errorCallback = _ZoneFunction<ErrorCallbackHandler>(this, errorCallback);
}
var scheduleMicrotask = specification.scheduleMicrotask;
if (scheduleMicrotask != null) {
_scheduleMicrotask =
_ZoneFunction<ScheduleMicrotaskHandler>(this, scheduleMicrotask);
}
var createTimer = specification.createTimer;
if (createTimer != null) {
_createTimer = _ZoneFunction<CreateTimerHandler>(this, createTimer);
}
var createPeriodicTimer = specification.createPeriodicTimer;
if (createPeriodicTimer != null) {
_createPeriodicTimer =
_ZoneFunction<CreatePeriodicTimerHandler>(this, createPeriodicTimer);
}
var print = specification.print;
if (print != null) {
_print = _ZoneFunction<PrintHandler>(this, print);
}
var fork = specification.fork;
if (fork != null) {
_fork = _ZoneFunction<ForkHandler>(this, fork);
}
var handleUncaughtError = specification.handleUncaughtError;
if (handleUncaughtError != null) {
_handleUncaughtError =
_ZoneFunction<HandleUncaughtErrorHandler>(this, handleUncaughtError);
}
}
/// The closest error-handling zone.
///
/// Returns this zone if it has an error-handler. Otherwise returns the
/// parent's error-zone.
Zone get errorZone => _handleUncaughtError.zone;
void runGuarded(void f()) {
try {
run(f);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
void runUnaryGuarded<T>(void f(T arg), T arg) {
try {
runUnary(f, arg);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
void runBinaryGuarded<T1, T2>(void f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
try {
runBinary(f, arg1, arg2);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
ZoneCallback<R> bindCallback<R>(R f()) {
var registered = registerCallback(f);
return () => this.run(registered);
}
ZoneUnaryCallback<R, T> bindUnaryCallback<R, T>(R f(T arg)) {
var registered = registerUnaryCallback(f);
return (arg) => this.runUnary(registered, arg);
}
ZoneBinaryCallback<R, T1, T2> bindBinaryCallback<R, T1, T2>(
R f(T1 arg1, T2 arg2)) {
var registered = registerBinaryCallback(f);
return (arg1, arg2) => this.runBinary(registered, arg1, arg2);
}
void Function() bindCallbackGuarded(void f()) {
var registered = registerCallback(f);
return () => this.runGuarded(registered);
}
void Function(T) bindUnaryCallbackGuarded<T>(void f(T arg)) {
var registered = registerUnaryCallback(f);
return (arg) => this.runUnaryGuarded(registered, arg);
}
void Function(T1, T2) bindBinaryCallbackGuarded<T1, T2>(
void f(T1 arg1, T2 arg2)) {
var registered = registerBinaryCallback(f);
return (arg1, arg2) => this.runBinaryGuarded(registered, arg1, arg2);
}
dynamic operator [](Object? key) {
var result = _map[key];
if (result != null || _map.containsKey(key)) return result;
// If we are not the root zone, look up in the parent zone.
if (parent != null) {
// We do not optimize for repeatedly looking up a key which isn't
// there. That would require storing the key and keeping it alive.
// Copying the key/value from the parent does not keep any new values
// alive.
var value = parent[key];
if (value != null) {
_map[key] = value;
}
return value;
}
assert(this == _rootZone);
return null;
}
// Methods that can be customized by the zone specification.
void handleUncaughtError(Object error, StackTrace stackTrace) {
var implementation = this._handleUncaughtError;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
HandleUncaughtErrorHandler handler = implementation.function;
return handler(
implementation.zone, parentDelegate, this, error, stackTrace);
}
Zone fork(
{ZoneSpecification? specification, Map<Object?, Object?>? zoneValues}) {
var implementation = this._fork;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
ForkHandler handler = implementation.function;
return handler(
implementation.zone, parentDelegate, this, specification, zoneValues);
}
R run<R>(R f()) {
var implementation = this._run;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
var handler = implementation.function as RunHandler;
return handler(implementation.zone, parentDelegate, this, f);
}
R runUnary<R, T>(R f(T arg), T arg) {
var implementation = this._runUnary;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
var handler = implementation.function as RunUnaryHandler;
return handler(implementation.zone, parentDelegate, this, f, arg);
}
R runBinary<R, T1, T2>(R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
var implementation = this._runBinary;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
var handler = implementation.function as RunBinaryHandler;
return handler(implementation.zone, parentDelegate, this, f, arg1, arg2);
}
ZoneCallback<R> registerCallback<R>(R callback()) {
var implementation = this._registerCallback;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
var handler = implementation.function as RegisterCallbackHandler;
return handler(implementation.zone, parentDelegate, this, callback);
}
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R callback(T arg)) {
var implementation = this._registerUnaryCallback;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
var handler = implementation.function as RegisterUnaryCallbackHandler;
return handler(implementation.zone, parentDelegate, this, callback);
}
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
R callback(T1 arg1, T2 arg2)) {
var implementation = this._registerBinaryCallback;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
var handler = implementation.function as RegisterBinaryCallbackHandler;
return handler(implementation.zone, parentDelegate, this, callback);
}
AsyncError? errorCallback(Object error, StackTrace? stackTrace) {
checkNotNullable(error, "error");
var implementation = this._errorCallback;
final _Zone implementationZone = implementation.zone;
if (identical(implementationZone, _rootZone)) return null;
final ZoneDelegate parentDelegate = implementationZone._parentDelegate;
ErrorCallbackHandler handler = implementation.function;
return handler(implementationZone, parentDelegate, this, error, stackTrace);
}
void scheduleMicrotask(void f()) {
var implementation = this._scheduleMicrotask;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
ScheduleMicrotaskHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, f);
}
Timer createTimer(Duration duration, void f()) {
var implementation = this._createTimer;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
CreateTimerHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, duration, f);
}
Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
var implementation = this._createPeriodicTimer;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
CreatePeriodicTimerHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, duration, f);
}
void print(String line) {
var implementation = this._print;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
PrintHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, line);
}
}
void _rootHandleUncaughtError(Zone? self, ZoneDelegate? parent, Zone zone,
Object error, StackTrace stackTrace) {
_schedulePriorityAsyncCallback(() {
_rethrow(error, stackTrace);
});
}
external void _rethrow(Object error, StackTrace stackTrace);
R _rootRun<R>(Zone? self, ZoneDelegate? parent, Zone zone, R f()) {
if (identical(Zone._current, zone)) return f();
if (zone is! _Zone) {
throw ArgumentError.value(zone, "zone", "Can only run in platform zones");
}
_Zone old = Zone._enter(zone);
try {
return f();
} finally {
Zone._leave(old);
}
}
R _rootRunUnary<R, T>(
Zone? self, ZoneDelegate? parent, Zone zone, R f(T arg), T arg) {
if (identical(Zone._current, zone)) return f(arg);
if (zone is! _Zone) {
throw ArgumentError.value(zone, "zone", "Can only run in platform zones");
}
_Zone old = Zone._enter(zone);
try {
return f(arg);
} finally {
Zone._leave(old);
}
}
R _rootRunBinary<R, T1, T2>(Zone? self, ZoneDelegate? parent, Zone zone,
R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
if (identical(Zone._current, zone)) return f(arg1, arg2);
if (zone is! _Zone) {
throw ArgumentError.value(zone, "zone", "Can only run in platform zones");
}
_Zone old = Zone._enter(zone);
try {
return f(arg1, arg2);
} finally {
Zone._leave(old);
}
}
ZoneCallback<R> _rootRegisterCallback<R>(
Zone self, ZoneDelegate parent, Zone zone, R f()) {
return f;
}
ZoneUnaryCallback<R, T> _rootRegisterUnaryCallback<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R f(T arg)) {
return f;
}
ZoneBinaryCallback<R, T1, T2> _rootRegisterBinaryCallback<R, T1, T2>(
Zone self, ZoneDelegate parent, Zone zone, R f(T1 arg1, T2 arg2)) {
return f;
}
AsyncError? _rootErrorCallback(Zone self, ZoneDelegate parent, Zone zone,
Object error, StackTrace? stackTrace) =>
null;
void _rootScheduleMicrotask(
Zone? self, ZoneDelegate? parent, Zone zone, void f()) {
if (!identical(_rootZone, zone)) {
bool hasErrorHandler = !_rootZone.inSameErrorZone(zone);
if (hasErrorHandler) {
f = zone.bindCallbackGuarded(f);
} else {
f = zone.bindCallback(f);
}
}
_scheduleAsyncCallback(f);
}
Timer _rootCreateTimer(Zone self, ZoneDelegate parent, Zone zone,
Duration duration, void Function() callback) {
if (!identical(_rootZone, zone)) {
callback = zone.bindCallback(callback);
}
return Timer._createTimer(duration, callback);
}
Timer _rootCreatePeriodicTimer(Zone self, ZoneDelegate parent, Zone zone,
Duration duration, void callback(Timer timer)) {
if (!identical(_rootZone, zone)) {
callback = zone.bindUnaryCallback<void, Timer>(callback);
}
return Timer._createPeriodicTimer(duration, callback);
}
void _rootPrint(Zone self, ZoneDelegate parent, Zone zone, String line) {
printToConsole(line);
}
void _printToZone(String line) {
Zone.current.print(line);
}
Zone _rootFork(Zone? self, ZoneDelegate? parent, Zone zone,
ZoneSpecification? specification, Map<Object?, Object?>? zoneValues) {
if (zone is! _Zone) {
throw ArgumentError.value(zone, "zone", "Can only fork a platform zone");
}
// TODO(floitsch): it would be nice if we could get rid of this hack.
// Change the static zoneOrDirectPrint function to go through zones
// from now on.
printToZone = _printToZone;
if (specification == null) {
specification = const ZoneSpecification();
} else if (specification is! _ZoneSpecification) {
specification = ZoneSpecification.from(specification);
}
Map<Object?, Object?> valueMap;
if (zoneValues == null) {
valueMap = zone._map;
} else {
valueMap = HashMap<Object?, Object?>.from(zoneValues);
}
if (specification == null)
throw "unreachable"; // TODO(lrn): Remove when type promotion works.
return _CustomZone(zone, specification, valueMap);
}
class _RootZone extends _Zone {
const _RootZone();
_RunNullaryZoneFunction get _run =>
const _RunNullaryZoneFunction(_rootZone, _rootRun);
_RunUnaryZoneFunction get _runUnary =>
const _RunUnaryZoneFunction(_rootZone, _rootRunUnary);
_RunBinaryZoneFunction get _runBinary =>
const _RunBinaryZoneFunction(_rootZone, _rootRunBinary);
_RegisterNullaryZoneFunction get _registerCallback =>
const _RegisterNullaryZoneFunction(_rootZone, _rootRegisterCallback);
_RegisterUnaryZoneFunction get _registerUnaryCallback =>
const _RegisterUnaryZoneFunction(_rootZone, _rootRegisterUnaryCallback);
_RegisterBinaryZoneFunction get _registerBinaryCallback =>
const _RegisterBinaryZoneFunction(_rootZone, _rootRegisterBinaryCallback);
_ZoneFunction<ErrorCallbackHandler> get _errorCallback =>
const _ZoneFunction<ErrorCallbackHandler>(_rootZone, _rootErrorCallback);
_ZoneFunction<ScheduleMicrotaskHandler> get _scheduleMicrotask =>
const _ZoneFunction<ScheduleMicrotaskHandler>(
_rootZone, _rootScheduleMicrotask);
_ZoneFunction<CreateTimerHandler> get _createTimer =>
const _ZoneFunction<CreateTimerHandler>(_rootZone, _rootCreateTimer);
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer =>
const _ZoneFunction<CreatePeriodicTimerHandler>(
_rootZone, _rootCreatePeriodicTimer);
_ZoneFunction<PrintHandler> get _print =>
const _ZoneFunction<PrintHandler>(_rootZone, _rootPrint);
_ZoneFunction<ForkHandler> get _fork =>
const _ZoneFunction<ForkHandler>(_rootZone, _rootFork);
_ZoneFunction<HandleUncaughtErrorHandler> get _handleUncaughtError =>
const _ZoneFunction<HandleUncaughtErrorHandler>(
_rootZone, _rootHandleUncaughtError);
// The parent zone.
_Zone? get parent => null;
/// The zone's scoped value declaration map.
///
/// This is always a [HashMap].
Map<Object?, Object?> get _map => _rootMap;
static final _rootMap = HashMap();
static ZoneDelegate? _rootDelegate;
ZoneDelegate get _delegate => _rootDelegate ??= new _ZoneDelegate(this);
// It's a lie, but the root zone never uses the parent delegate.
ZoneDelegate get _parentDelegate => _delegate;
/// The closest error-handling zone.
///
/// Returns `this` if `this` has an error-handler. Otherwise returns the
/// parent's error-zone.
Zone get errorZone => this;
// Zone interface.
void runGuarded(void f()) {
try {
if (identical(_rootZone, Zone._current)) {
f();
return;
}
_rootRun(null, null, this, f);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
void runUnaryGuarded<T>(void f(T arg), T arg) {
try {
if (identical(_rootZone, Zone._current)) {
f(arg);
return;
}
_rootRunUnary(null, null, this, f, arg);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
void runBinaryGuarded<T1, T2>(void f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
try {
if (identical(_rootZone, Zone._current)) {
f(arg1, arg2);
return;
}
_rootRunBinary(null, null, this, f, arg1, arg2);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
ZoneCallback<R> bindCallback<R>(R f()) {
return () => this.run<R>(f);
}
ZoneUnaryCallback<R, T> bindUnaryCallback<R, T>(R f(T arg)) {
return (arg) => this.runUnary<R, T>(f, arg);
}
ZoneBinaryCallback<R, T1, T2> bindBinaryCallback<R, T1, T2>(
R f(T1 arg1, T2 arg2)) {
return (arg1, arg2) => this.runBinary<R, T1, T2>(f, arg1, arg2);
}
void Function() bindCallbackGuarded(void f()) {
return () => this.runGuarded(f);
}
void Function(T) bindUnaryCallbackGuarded<T>(void f(T arg)) {
return (arg) => this.runUnaryGuarded(f, arg);
}
void Function(T1, T2) bindBinaryCallbackGuarded<T1, T2>(
void f(T1 arg1, T2 arg2)) {
return (arg1, arg2) => this.runBinaryGuarded(f, arg1, arg2);
}
dynamic operator [](Object? key) => null;
// Methods that can be customized by the zone specification.
void handleUncaughtError(Object error, StackTrace stackTrace) {
_rootHandleUncaughtError(null, null, this, error, stackTrace);
}
Zone fork(
{ZoneSpecification? specification, Map<Object?, Object?>? zoneValues}) {
return _rootFork(null, null, this, specification, zoneValues);
}
R run<R>(R f()) {
if (identical(Zone._current, _rootZone)) return f();
return _rootRun(null, null, this, f);
}
@pragma("vm:recognized", "other")
R runUnary<R, T>(R f(T arg), T arg) {
if (identical(Zone._current, _rootZone)) return f(arg);
return _rootRunUnary(null, null, this, f, arg);
}
R runBinary<R, T1, T2>(R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
if (identical(Zone._current, _rootZone)) return f(arg1, arg2);
return _rootRunBinary(null, null, this, f, arg1, arg2);
}
ZoneCallback<R> registerCallback<R>(R f()) => f;
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R f(T arg)) => f;
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
R f(T1 arg1, T2 arg2)) =>
f;
AsyncError? errorCallback(Object error, StackTrace? stackTrace) => null;
void scheduleMicrotask(void f()) {
_rootScheduleMicrotask(null, null, this, f);
}
Timer createTimer(Duration duration, void f()) {
return Timer._createTimer(duration, f);
}
Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
return Timer._createPeriodicTimer(duration, f);
}
void print(String line) {
printToConsole(line);
}
}
const _Zone _rootZone = const _RootZone();
/// Runs [body] in its own zone.
///
/// Creates a new zone using [Zone.fork] based on [zoneSpecification] and
/// [zoneValues], then runs [body] in that zone and returns the result.
///
/// If [onError] is provided, it must have one of the types
/// * `void Function(Object)`
/// * `void Function(Object, StackTrace)`
/// and the [onError] handler is used *both* to handle asynchronous errors
/// by overriding [ZoneSpecification.handleUncaughtError] in [zoneSpecification],
/// if any, *and* to handle errors thrown synchronously by the call to [body].
///
/// If an error occurs synchronously in [body],
/// then throwing in the [onError] handler
/// makes the call to `runZone` throw that error,
/// and otherwise the call to `runZoned` attempt to return `null`.
///
/// If the zone specification has a `handleUncaughtError` value or the [onError]
/// parameter is provided, the zone becomes an error-zone.
///
/// Errors will never cross error-zone boundaries by themselves.
/// Errors that try to cross error-zone boundaries are considered uncaught in
/// their originating error zone.
/// ```dart
/// var future = Future.value(499);
/// runZoned(() {
/// var future2 = future.then((_) { throw "error in first error-zone"; });
/// runZoned(() {
/// var future3 = future2.catchError((e) { print("Never reached!"); });
/// }, onError: (e, s) { print("unused error handler"); });
/// }, onError: (e, s) { print("catches error of first error-zone."); });
/// ```
/// Example:
/// ```dart
/// runZoned(() {
/// Future(() { throw "asynchronous error"; });
/// }, onError: (e, s) => print(e)); // Will print "asynchronous error".
/// ```
/// It is possible to manually pass an error from one error zone to another
/// by re-throwing it in the new zone. If [onError] throws, that error will
/// occur in the original zone where [runZoned] was called.
R runZoned<R>(R body(),
{Map<Object?, Object?>? zoneValues,
ZoneSpecification? zoneSpecification,
@Deprecated("Use runZonedGuarded instead") Function? onError}) {
checkNotNullable(body, "body");
if (onError != null) {
// TODO: Remove this when code have been migrated off using [onError].
if (onError is! void Function(Object, StackTrace)) {
if (onError is void Function(Object)) {
var originalOnError = onError;
onError = (Object error, StackTrace stack) => originalOnError(error);
} else {
throw ArgumentError.value(onError, "onError",
"Must be Function(Object) or Function(Object, StackTrace)");
}
}
return runZonedGuarded(body, onError,
zoneSpecification: zoneSpecification, zoneValues: zoneValues) as R;
}
return _runZoned<R>(body, zoneValues, zoneSpecification);
}
/// Runs [body] in its own error zone.
///
/// Creates a new zone using [Zone.fork] based on [zoneSpecification] and
/// [zoneValues], then runs [body] in that zone and returns the result.
///
/// The [onError] function is used *both* to handle asynchronous errors
/// by overriding [ZoneSpecification.handleUncaughtError] in [zoneSpecification],
/// if any, *and* to handle errors thrown synchronously by the call to [body].
///
/// If an error occurs synchronously in [body],
/// then throwing in the [onError] handler
/// makes the call to `runZonedGuarded` throw that error,
/// and otherwise the call to `runZonedGuarded` returns `null`.
///
/// The zone will always be an error-zone.
///
/// Errors will never cross error-zone boundaries by themselves.
/// Errors that try to cross error-zone boundaries are considered uncaught in
/// their originating error zone.
/// ```dart
/// var future = Future.value(499);
/// runZonedGuarded(() {
/// var future2 = future.then((_) { throw "error in first error-zone"; });
/// runZonedGuarded(() {
/// var future3 = future2.catchError((e) { print("Never reached!"); });
/// }, (e, s) { print("unused error handler"); });
/// }, (e, s) { print("catches error of first error-zone."); });
/// ```
/// Example:
/// ```dart
/// runZonedGuarded(() {
/// Future(() { throw "asynchronous error"; });
/// }, (e, s) => print(e)); // Will print "asynchronous error".
/// ```
/// It is possible to manually pass an error from one error zone to another
/// by re-throwing it in the new zone. If [onError] throws, that error will
/// occur in the original zone where [runZoned] was called.
@Since("2.8")
R? runZonedGuarded<R>(R body(), void onError(Object error, StackTrace stack),
{Map<Object?, Object?>? zoneValues, ZoneSpecification? zoneSpecification}) {
checkNotNullable(body, "body");
checkNotNullable(onError, "onError");
_Zone parentZone = Zone._current;
HandleUncaughtErrorHandler errorHandler = (Zone self, ZoneDelegate parent,
Zone zone, Object error, StackTrace stackTrace) {
try {
parentZone.runBinary(onError, error, stackTrace);
} catch (e, s) {
if (identical(e, error)) {
parent.handleUncaughtError(zone, error, stackTrace);
} else {
parent.handleUncaughtError(zone, e, s);
}
}
};
if (zoneSpecification == null) {
zoneSpecification =
new ZoneSpecification(handleUncaughtError: errorHandler);
} else {
zoneSpecification = ZoneSpecification.from(zoneSpecification,
handleUncaughtError: errorHandler);
}
try {
return _runZoned<R>(body, zoneValues, zoneSpecification);
} catch (error, stackTrace) {
onError(error, stackTrace);
}
return null;
}
/// Runs [body] in a new zone based on [zoneValues] and [specification].
R _runZoned<R>(R body(), Map<Object?, Object?>? zoneValues,
ZoneSpecification? specification) =>
Zone.current
.fork(specification: specification, zoneValues: zoneValues)
.run<R>(body);