Revert "Make Zone functions not call `register*`, but properly catch errors."

This reverts commit fc2c46ae82cf9c34a7c20bfaecaa759782ba1533.

Reason for revert: broke Flutter tests:
https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8724303399273084113/+/u/Run_framework_coverage_tests/stdout
https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8724303399273084097/+/u/Run_framework_tests_libraries_tests/stdout
https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8724303399273084081/+/u/Run_framework_tests_misc_tests/stdout
https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8724303399273084049/+/u/Run_framework_tests_widgets_tests/stdout
https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8724303399273084033/+/u/Run_tool_tests_tests/stdout

Original change's description:
> Make Zone functions not call `register*`, but properly catch errors.
>
> Make `Zone.createTimer`, `Zone.createPeriodicTimer` and `Zone.scheduleMicrotask`
> not call register on the same zone.
> These are low-level functions that should not build on top
> of other low-level functions.
>
> Instead the function is registered in the `Timer` constructors and
> top-level `scheduleMicrotask` code, and the root zone's `createTimer`
> and `scheduleMicrotask` just make sure that the callback will run
> in its original zone, and that uncaught errors are handled in the
> correct zone.
> Avoids a double-registration that timers did.
>
> Significant clean-up.
> - The private `_Zone`, `_ZoneSpecification` and `_ZoneDelegate`
>   subclasses are not necessary now that their superclasses are `final`.
> - Stopped using type aliases instead of function types
>   and old-style function arguments.
> - Renamed some parameters to not be, fx, `f`.
>
> Avoided some closure allocations for microtasks by putting the zone
> into the queue entry, instead of wrapping the callback in a closure calling `zone.run`.
>
> This is a potentially visible change if any code uses the zone functions
> directly and expect them to invoke `Zone.register*`.
> (Suspiciously no test failed with this change. May need to add some.)
>
> Fixes #59913.
>
> CoreLibraryReviewExempt: Have had all +1s, just not at the same time.
> Bug: https://dartbug.com/59913
> Change-Id: Ie3c87f5f22aedcbe30ab04718df98e1c0f0659c3
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405020
> Reviewed-by: Martin Kustermann <kustermann@google.com>
> Commit-Queue: Lasse Nielsen <lrn@google.com>

Bug: https://dartbug.com/59913
Change-Id: I386b2b6d2594a50a7597363e13fe24111ea72ade
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/406685
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Jake Macdonald <jakemac@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fcb06d7..df2531b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,27 +4,6 @@
 
 ### Libraries
 
-#### `dart:async`
-
-- The `Timer` and `Timer.periodic` constructors and the top-level
-  `scheduleMicrotask` function no longer *bind* and *guard* their callback
-  in the current zone, using `Zone.bindCallbackGuarded` and
-  `Zone.bindUnaryCallbackGuarded`. They only registers the callback
-  before calling the zone's `Zone.createTimer`, `Zone.createPeriodicTimer`
-  or `Zone.scheduleMicrotask` with the registered function.
-- The default `Zone.createTimer` and `Zone.createPeriodicTimer`,
-  no longer registers the callback in the current zone, which would
-  be a second registration if called through `Timer` or `Timer.periodic`.
-- The default (root) zone's `Zone.createTimer`, `Zone.createPeriodicTimer`
-  and `Zone.scheduleMicrotask` now always run the callback using
-  `Zone.run`/`Zone.runUnary`, and catch and reports errors from that
-  to the same zone, rather than relying on the callback
-  having been bound (and guarded) in the zone.
-- Together these changes ensure that if a custom zone's
-  `Zone.registerCallback` (or similar for a unary or binary function)
-  returns a new function which can throw, that thrown error is reported
-  in the correct error zone.
-
 #### `dart:core`
 
 - Added `Iterable.withIterator` constructor.
diff --git a/pkg/front_end/testcases/nnbd/return_async.dart.strong.expect b/pkg/front_end/testcases/nnbd/return_async.dart.strong.expect
index 5a89414..3c04d2a 100644
--- a/pkg/front_end/testcases/nnbd/return_async.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/return_async.dart.strong.expect
@@ -21,7 +21,7 @@
   self::throwSync();
 }
 static method customErrorZone() → asy::Future<void> async /* emittedValueType= void */ {
-  final asy::Completer<void> completer = new asy::_AsyncCompleter::•<void>();
+  final asy::Completer<void> completer = asy::Completer::•<void>();
   asy::runZonedGuarded<asy::Future<Null>>(() → asy::Future<Null> async /* emittedValueType= Null */ {
     await self::allYield();
     completer.{asy::Completer::complete}(null){([FutureOr<void>?]) → void};
diff --git a/pkg/front_end/testcases/nnbd/return_async.dart.strong.modular.expect b/pkg/front_end/testcases/nnbd/return_async.dart.strong.modular.expect
index 5a89414..3c04d2a 100644
--- a/pkg/front_end/testcases/nnbd/return_async.dart.strong.modular.expect
+++ b/pkg/front_end/testcases/nnbd/return_async.dart.strong.modular.expect
@@ -21,7 +21,7 @@
   self::throwSync();
 }
 static method customErrorZone() → asy::Future<void> async /* emittedValueType= void */ {
-  final asy::Completer<void> completer = new asy::_AsyncCompleter::•<void>();
+  final asy::Completer<void> completer = asy::Completer::•<void>();
   asy::runZonedGuarded<asy::Future<Null>>(() → asy::Future<Null> async /* emittedValueType= Null */ {
     await self::allYield();
     completer.{asy::Completer::complete}(null){([FutureOr<void>?]) → void};
diff --git a/pkg/front_end/testcases/nnbd/return_async.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/return_async.dart.strong.transformed.expect
index 5a89414..3c04d2a 100644
--- a/pkg/front_end/testcases/nnbd/return_async.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/return_async.dart.strong.transformed.expect
@@ -21,7 +21,7 @@
   self::throwSync();
 }
 static method customErrorZone() → asy::Future<void> async /* emittedValueType= void */ {
-  final asy::Completer<void> completer = new asy::_AsyncCompleter::•<void>();
+  final asy::Completer<void> completer = asy::Completer::•<void>();
   asy::runZonedGuarded<asy::Future<Null>>(() → asy::Future<Null> async /* emittedValueType= Null */ {
     await self::allYield();
     completer.{asy::Completer::complete}(null){([FutureOr<void>?]) → void};
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 3f3d98e..d1ce62c 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -235,7 +235,7 @@
   static final _Future<void> _nullFuture = nullFuture as _Future<void>;
 
   /// A `Future<bool>` completed with `false`.
-  static final _Future<bool> _falseFuture = _Future<bool>.zoneValue(
+  static final _Future<bool> _falseFuture = new _Future<bool>.zoneValue(
     false,
     _rootZone,
   );
@@ -253,7 +253,7 @@
   /// If a non-future value is returned, the returned future is completed
   /// with that value.
   factory Future(FutureOr<T> computation()) {
-    _Future<T> result = _Future<T>();
+    _Future<T> result = new _Future<T>();
     Timer.run(() {
       FutureOr<T> computationResult;
       try {
@@ -280,7 +280,7 @@
   /// If calling [computation] returns a non-future value,
   /// the returned future is completed with that value.
   factory Future.microtask(FutureOr<T> computation()) {
-    _Future<T> result = _Future<T>();
+    _Future<T> result = new _Future<T>();
     scheduleMicrotask(() {
       FutureOr<T> computationResult;
       try {
@@ -314,7 +314,7 @@
     try {
       result = computation();
     } catch (error, stackTrace) {
-      var future = _Future<T>();
+      var future = new _Future<T>();
       _asyncCompleteWithErrorCallback(future, error, stackTrace);
       return future;
     }
@@ -349,7 +349,7 @@
   @pragma("vm:entry-point")
   @pragma("vm:prefer-inline")
   factory Future.value([FutureOr<T>? value]) {
-    return _Future<T>.immediate(value == null ? value as T : value);
+    return new _Future<T>.immediate(value == null ? value as T : value);
   }
 
   /// Creates a future that completes with an error.
@@ -371,7 +371,7 @@
   /// ```
   factory Future.error(Object error, [StackTrace? stackTrace]) {
     AsyncError(:error, :stackTrace) = _interceptUserError(error, stackTrace);
-    return _Future<T>.immediateError(error, stackTrace);
+    return new _Future<T>.immediateError(error, stackTrace);
   }
 
   /// Creates a future that runs its computation after a delay.
@@ -390,7 +390,7 @@
   /// If [computation] is omitted,
   /// it will be treated as if [computation] was `() => null`,
   /// and the future will eventually complete with the `null` value.
-  /// In that case, [T] **must** be nullable.
+  /// In that case, [T] must be nullable.
   ///
   /// If calling [computation] throws, the created future will complete with the
   /// error.
@@ -405,32 +405,28 @@
   /// });
   /// ```
   factory Future.delayed(Duration duration, [FutureOr<T> computation()?]) {
+    if (computation == null && !typeAcceptsNull<T>()) {
+      throw ArgumentError.value(
+        null,
+        "computation",
+        "The type parameter is not nullable",
+      );
+    }
     _Future<T> result = _Future<T>();
-    if (computation == null) {
-      const T? defaultResult = null;
-      if (defaultResult is T) {
-        result._zone.createTimer(duration, () {
-          result._complete(defaultResult);
-        });
+    new Timer(duration, () {
+      if (computation == null) {
+        result._complete(null as T);
       } else {
-        throw ArgumentError(
-          "Required when the type parameter is not nullable",
-          "computation",
-        );
-      }
-    } else {
-      var registeredComputation = result._zone.registerCallback(computation);
-      result._zone.createTimer(duration, () {
         FutureOr<T> computationResult;
         try {
-          computationResult = result._zone.run(registeredComputation);
+          computationResult = computation();
         } catch (e, s) {
           _completeWithErrorCallback(result, e, s);
           return;
         }
         result._complete(computationResult);
-      });
-    }
+      }
+    });
     return result;
   }
 
@@ -708,63 +704,31 @@
   /// // Outputs: 'Finished with 3'
   /// ```
   static Future<void> doWhile(FutureOr<bool> action()) {
-    var zone = Zone.current;
-    _Future<void> doneSignal = _Future<void>.zone(zone);
-
-    /// As a function instead of a tear-off because some compilers
-    /// prefer that. (Same overhead for creating, but tear-offs have
-    /// more complicated equality, which isn't used anyway.)
-    void completeError(Object e, StackTrace s) {
-      doneSignal._completeError(e, s);
-    }
-
-    // Bind/register the callbacks only once, and reuse them on every iteration.
-    // This avoids, e.g., deeply nested stack traces from the `stack_trace`
+    _Future<void> doneSignal = new _Future<void>();
+    late void Function(bool) nextIteration;
+    // Bind this callback explicitly so that each iteration isn't bound in the
+    // context of all the previous iterations' callbacks.
+    // This avoids, e.g., deeply nested stack traces from the stack trace
     // package.
-    void Function(bool)? registeredNextIteration;
-    void Function(Object, StackTrace)? registeredCompleteError;
-
-    /// True until the first asynchronous gap.
-    bool sync = true;
-    void nextIteration(bool keepGoing) {
+    nextIteration = Zone.current.bindUnaryCallbackGuarded((bool keepGoing) {
       while (keepGoing) {
         FutureOr<bool> result;
         try {
           result = action();
         } catch (error, stackTrace) {
-          if (sync) {
-            // Complete asynchronously if still running synchronously.
-            _asyncCompleteWithErrorCallback(doneSignal, error, stackTrace);
-          } else {
-            _completeWithErrorCallback(doneSignal, error, stackTrace);
-          }
+          // Cannot use _completeWithErrorCallback because it completes
+          // the future synchronously.
+          _asyncCompleteWithErrorCallback(doneSignal, error, stackTrace);
           return;
         }
         if (result is Future<bool>) {
-          if (result is _Future<bool>) {
-            var onValue =
-                registeredNextIteration ??= zone.registerUnaryCallback(
-                  nextIteration,
-                );
-            var onError =
-                registeredCompleteError ??= zone.registerBinaryCallback(
-                  completeError,
-                );
-            // Reuse registered callbacks when it's a `_Future`.
-            result._addThenListener(zone, onValue, onError);
-          } else {
-            // Let `then` do callback registration for non-native futures.
-            // It will do that anyway.
-            result.then(nextIteration, onError: completeError);
-          }
-          sync = false;
+          result.then(nextIteration, onError: doneSignal._completeError);
           return;
         }
         keepGoing = result;
       }
       doneSignal._complete(null);
-    }
-
+    });
     nextIteration(true);
     return doneSignal;
   }
@@ -934,7 +898,7 @@
 
   /// Stop waiting for this future after [timeLimit] has passed.
   ///
-  /// Creates a _timeout future_ that completes
+  /// Creates a new _timeout future_ that completes
   /// with the same result as this future, the _source future_,
   /// *if* the source future completes in time.
   ///
@@ -1195,7 +1159,7 @@
 /// one — you can use a Completer as follows:
 /// ```dart
 /// class AsyncOperation {
-///   final Completer _completer = Completer();
+///   final Completer _completer = new Completer();
 ///
 ///   Future<T> doOperation() {
 ///     _startOperation();
@@ -1234,7 +1198,7 @@
   ///   completer.complete('completion value');
   /// }
   /// ```
-  factory Completer() = _AsyncCompleter<T>;
+  factory Completer() => new _AsyncCompleter<T>();
 
   /// Completes the future synchronously.
   ///
@@ -1285,7 +1249,7 @@
   ///   foo();  // In this case, foo() runs after bar().
   /// });
   /// ```
-  factory Completer.sync() = _SyncCompleter<T>;
+  factory Completer.sync() => new _SyncCompleter<T>();
 
   /// The future that is completed by this completer.
   ///
@@ -1367,7 +1331,7 @@
   bool get isCompleted;
 }
 
-// Helper function completing a `_Future` with error, but checking the zone
+// Helper function completing a _Future with error, but checking the zone
 // for error replacement and missing stack trace first.
 // Only used for errors that are *caught*.
 // A user provided error object should use `_interceptUserError` which
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 928814f..e82eafb 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -56,14 +56,14 @@
 abstract class _Completer<T> implements Completer<T> {
   @pragma("wasm:entry-point")
   @pragma("vm:entry-point")
-  final _Future<T> future = _Future<T>();
+  final _Future<T> future = new _Future<T>();
 
   // Overridden by either a synchronous or asynchronous implementation.
   void complete([FutureOr<T>? value]);
 
   @pragma("wasm:entry-point")
   void completeError(Object error, [StackTrace? stackTrace]) {
-    if (!future._mayComplete) throw StateError("Future already completed");
+    if (!future._mayComplete) throw new StateError("Future already completed");
     AsyncError(:error, :stackTrace) = _interceptUserError(error, stackTrace);
     _completeError(error, stackTrace);
   }
@@ -81,7 +81,7 @@
 class _AsyncCompleter<T> extends _Completer<T> {
   @pragma("wasm:entry-point")
   void complete([FutureOr<T>? value]) {
-    if (!future._mayComplete) throw StateError("Future already completed");
+    if (!future._mayComplete) throw new StateError("Future already completed");
     future._asyncComplete(value == null ? value as dynamic : value);
   }
 
@@ -96,7 +96,7 @@
 @pragma("vm:entry-point")
 class _SyncCompleter<T> extends _Completer<T> {
   void complete([FutureOr<T>? value]) {
-    if (!future._mayComplete) throw StateError("Future already completed");
+    if (!future._mayComplete) throw new StateError("Future already completed");
     future._complete(value == null ? value as dynamic : value);
   }
 
@@ -170,7 +170,7 @@
     : errorCallback = null,
       state = stateWhenComplete;
 
-  Zone get _zone => result._zone;
+  _Zone get _zone => result._zone;
 
   bool get handlesValue => (state & maskValue != 0);
   bool get handlesError => (state & maskError != 0);
@@ -317,7 +317,7 @@
   ///
   /// Until the future is completed, the field may hold the zone that
   /// listener callbacks used to create this future should be run in.
-  final Zone _zone;
+  final _Zone _zone;
 
   /// Either the result, a list of listeners or another future.
   ///
@@ -396,19 +396,8 @@
         onError = _registerErrorHandler(onError, currentZone);
       }
     }
-    return _addThenListener<R>(currentZone, f, onError);
-  }
-
-  /// Adds listener without registering or binding callbacks in the [zone].
-  ///
-  /// Caller should have registered functions already.
-  _Future<R> _addThenListener<R>(
-    Zone zone,
-    FutureOr<R> Function(T) onValue,
-    Function? onError,
-  ) {
-    _Future<R> result = _Future<R>.zone(zone);
-    _addListener(_FutureListener<T, R>.then(result, onValue, onError));
+    _Future<R> result = new _Future<R>();
+    _addListener(new _FutureListener<T, R>.then(result, f, onError));
     return result;
   }
 
@@ -417,8 +406,8 @@
   /// Used by the implementation of `await` to listen to a future.
   /// The system created listeners are not registered in the zone.
   Future<E> _thenAwait<E>(FutureOr<E> f(T value), Function onError) {
-    _Future<E> result = _Future<E>();
-    _addListener(_FutureListener<T, E>.thenAwait(result, f, onError));
+    _Future<E> result = new _Future<E>();
+    _addListener(new _FutureListener<T, E>.thenAwait(result, f, onError));
     return result;
   }
 
@@ -438,12 +427,12 @@
   }
 
   Future<T> catchError(Function onError, {bool test(Object error)?}) {
-    _Future<T> result = _Future<T>();
+    _Future<T> result = new _Future<T>();
     if (!identical(result._zone, _rootZone)) {
       onError = _registerErrorHandler(onError, result._zone);
       if (test != null) test = result._zone.registerUnaryCallback(test);
     }
-    _addListener(_FutureListener<T, T>.catchError(result, onError, test));
+    _addListener(new _FutureListener<T, T>.catchError(result, onError, test));
     return result;
   }
 
@@ -465,15 +454,15 @@
   }
 
   Future<T> whenComplete(dynamic action()) {
-    _Future<T> result = _Future<T>();
+    _Future<T> result = new _Future<T>();
     if (!identical(result._zone, _rootZone)) {
       action = result._zone.registerCallback<dynamic>(action);
     }
-    _addListener(_FutureListener<T, T>.whenComplete(result, action));
+    _addListener(new _FutureListener<T, T>.whenComplete(result, action));
     return result;
   }
 
-  Stream<T> asStream() => Stream<T>.fromFuture(this);
+  Stream<T> asStream() => new Stream<T>.fromFuture(this);
 
   void _setPendingComplete() {
     assert(_mayComplete); // Aka. _stateIncomplete
@@ -509,7 +498,7 @@
   }
 
   void _setError(Object error, StackTrace stackTrace) {
-    _setErrorObject(AsyncError(error, stackTrace));
+    _setErrorObject(new AsyncError(error, stackTrace));
   }
 
   /// Copy the completion result of [source] into this future.
@@ -874,7 +863,7 @@
       // expensive, branch. Here we'll enter/leave the zone. Many futures
       // don't have callbacks, so this is a significant optimization.
       if (hasError || listener.handlesValue || listener.handlesComplete) {
-        Zone zone = listener._zone;
+        _Zone zone = listener._zone;
         if (hasError && !source._zone.inSameErrorZone(zone)) {
           // Don't cross zone boundaries with errors.
           AsyncError asyncError = source._error;
@@ -885,7 +874,7 @@
           return;
         }
 
-        Zone? oldZone;
+        _Zone? oldZone;
         if (!identical(Zone._current, zone)) {
           // Change zone if it's not current.
           oldZone = Zone._enter(zone);
@@ -906,7 +895,7 @@
             if (hasError && identical(source._error.error, e)) {
               listenerValueOrError = source._error;
             } else {
-              listenerValueOrError = AsyncError(e, s);
+              listenerValueOrError = new AsyncError(e, s);
             }
             listenerHasError = true;
             return;
@@ -942,7 +931,7 @@
           try {
             listenerValueOrError = listener.handleValue(sourceResult);
           } catch (e, s) {
-            listenerValueOrError = AsyncError(e, s);
+            listenerValueOrError = new AsyncError(e, s);
             listenerHasError = true;
           }
         }
@@ -959,7 +948,7 @@
             if (identical(source._error.error, e)) {
               listenerValueOrError = source._error;
             } else {
-              listenerValueOrError = AsyncError(e, s);
+              listenerValueOrError = new AsyncError(e, s);
             }
             listenerHasError = true;
           }
@@ -1020,19 +1009,19 @@
   /// New uncompleted future with same type.
   ///
   /// In same zone or other [zone] if specified.
-  _Future<T> _newFutureWithSameType([Zone? zone]) =>
+  _Future<T> _newFutureWithSameType([_Zone? zone]) =>
       _Future<T>.zone(zone ?? _zone);
 
   @pragma("vm:entry-point")
   Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()?}) {
-    if (_isComplete) return _Future.immediate(this);
+    if (_isComplete) return new _Future.immediate(this);
     @pragma('vm:awaiter-link')
-    _Future<T> _future = _Future<T>();
+    _Future<T> _future = new _Future<T>();
     Timer timer;
     if (onTimeout == null) {
-      timer = Timer(timeLimit, () {
+      timer = new Timer(timeLimit, () {
         _future._completeError(
-          TimeoutException("Future not completed", timeLimit),
+          new TimeoutException("Future not completed", timeLimit),
           StackTrace.current,
         );
       });
@@ -1042,7 +1031,7 @@
         onTimeout,
       );
 
-      timer = Timer(timeLimit, () {
+      timer = new Timer(timeLimit, () {
         try {
           _future._complete(zone.run(onTimeoutHandler));
         } catch (e, s) {
diff --git a/sdk/lib/async/schedule_microtask.dart b/sdk/lib/async/schedule_microtask.dart
index 37e606a..4245d6c 100644
--- a/sdk/lib/async/schedule_microtask.dart
+++ b/sdk/lib/async/schedule_microtask.dart
@@ -4,12 +4,12 @@
 
 part of dart.async;
 
-/// Entry of linked list of scheduled microtasks.
+typedef void _AsyncCallback();
+
 class _AsyncCallbackEntry {
-  final Zone zone;
-  final void Function() callback;
+  final _AsyncCallback callback;
   _AsyncCallbackEntry? next;
-  _AsyncCallbackEntry(this.zone, this.callback);
+  _AsyncCallbackEntry(this.callback);
 }
 
 /// Head of single linked list of pending callbacks.
@@ -21,9 +21,8 @@
 /// Tail of priority callbacks added by the currently executing callback.
 ///
 /// Priority callbacks are put at the beginning of the
-/// callback queue, after prior priority callbacks, so that if one callback
-/// schedules more than one priority callback, they are still enqueued
-/// in scheduling order.
+/// callback queue, so that if one callback schedules more than one
+/// priority callback, they are still enqueued in scheduling order.
 _AsyncCallbackEntry? _lastPriorityCallback;
 
 /// Whether we are currently inside the callback loop.
@@ -38,19 +37,7 @@
     var next = entry.next;
     _nextCallback = next;
     if (next == null) _lastCallback = null;
-    var zone = entry.zone;
-    var callback = entry.callback;
-    if (identical(_rootZone, zone)) {
-      callback();
-    } else if (_rootZone.inSameErrorZone(zone)) {
-      // Let an error reach event loop synchronously,
-      // gives better error reporting. The surrounding `_startMicrotaskLoop`
-      // will reschedule as the next microtask to continue.
-      zone.run(callback);
-    } else {
-      // If it's an error zone, handle its own errors.
-      _runGuardedInZone(zone, callback);
-    }
+    (entry.callback)();
   }
 }
 
@@ -73,8 +60,8 @@
 ///
 /// The microtask is called after all other currently scheduled
 /// microtasks, but as part of the current system event.
-void _scheduleAsyncCallback(Zone zone, void Function() callback) {
-  _AsyncCallbackEntry newEntry = _AsyncCallbackEntry(zone, callback);
+void _scheduleAsyncCallback(_AsyncCallback callback) {
+  _AsyncCallbackEntry newEntry = new _AsyncCallbackEntry(callback);
   _AsyncCallbackEntry? lastCallback = _lastCallback;
   if (lastCallback == null) {
     _nextCallback = _lastCallback = newEntry;
@@ -93,13 +80,13 @@
 /// It is only used internally to give higher priority to error reporting.
 ///
 /// Is always run in the root zone.
-void _schedulePriorityAsyncCallback(Zone zone, void Function() callback) {
+void _schedulePriorityAsyncCallback(_AsyncCallback callback) {
   if (_nextCallback == null) {
-    _scheduleAsyncCallback(zone, callback);
+    _scheduleAsyncCallback(callback);
     _lastPriorityCallback = _lastCallback;
     return;
   }
-  _AsyncCallbackEntry entry = _AsyncCallbackEntry(zone, callback);
+  _AsyncCallbackEntry entry = new _AsyncCallbackEntry(callback);
   _AsyncCallbackEntry? lastPriorityCallback = _lastPriorityCallback;
   if (lastPriorityCallback == null) {
     entry.next = _nextCallback;
@@ -140,16 +127,28 @@
 /// better asynchronous code with fewer surprises.
 @pragma('vm:entry-point', 'call')
 void scheduleMicrotask(void Function() callback) {
-  Zone currentZone = Zone._current;
+  _Zone currentZone = Zone._current;
   if (identical(_rootZone, currentZone)) {
-    _rootScheduleMicrotask(null, null, currentZone, callback);
-  } else {
-    currentZone.scheduleMicrotask(currentZone.registerCallback(callback));
+    // No need to bind the callback. We know that the root's scheduleMicrotask
+    // will be invoked in the root zone.
+    _rootScheduleMicrotask(null, null, _rootZone, callback);
+    return;
   }
+  _ZoneFunction implementation = currentZone._scheduleMicrotask;
+  if (identical(_rootZone, implementation.zone) &&
+      _rootZone.inSameErrorZone(currentZone)) {
+    _rootScheduleMicrotask(
+      null,
+      null,
+      currentZone,
+      currentZone.registerCallback(callback),
+    );
+    return;
+  }
+  Zone.current.scheduleMicrotask(Zone.current.bindCallbackGuarded(callback));
 }
 
 class _AsyncRun {
   /// Schedule the given callback before any other event in the event-loop.
-  /// Interface to the platform's microtask event loop.
   external static void _scheduleImmediate(void Function() callback);
 }
diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart
index 767fd51..acc04c1 100644
--- a/sdk/lib/async/stream_impl.dart
+++ b/sdk/lib/async/stream_impl.dart
@@ -339,7 +339,7 @@
   // Hooks called when the input is paused, unpaused or canceled.
   // These must not throw. If overwritten to call user code, include suitable
   // try/catch wrapping and send any errors to
-  // [Zone.current.handleUncaughtError].
+  // [_Zone.current.handleUncaughtError].
   void _onPause() {
     assert(_isInputPaused);
   }
diff --git a/sdk/lib/async/timer.dart b/sdk/lib/async/timer.dart
index 1cb13ac..5ff3711 100644
--- a/sdk/lib/async/timer.dart
+++ b/sdk/lib/async/timer.dart
@@ -33,14 +33,6 @@
   ///
   /// The [callback] function is invoked after the given [duration].
   ///
-  /// The [callback] is registered in the current zone using
-  /// [Zone.registerCallback], and the timer is created using
-  /// that zone's [Zone.createTimer].
-  /// The default timer implementation will run the registered callback
-  /// in that zone using [Zone.run] when the timer expires,
-  /// and if the callback throws, errors will be reported as an
-  /// uncaught asynchronous error using the zone's [Zone.handleUncaughtError].
-  ///
   /// Example:
   /// ```dart
   /// final timer =
@@ -48,11 +40,15 @@
   /// // Outputs after 5 seconds: "Timer finished".
   /// ```
   factory Timer(Duration duration, void Function() callback) {
-    var zone = Zone._current;
-    if (!identical(zone, _rootZone)) {
-      callback = zone.registerCallback(callback);
+    if (Zone.current == Zone.root) {
+      // No need to bind the callback. We know that the root's timer will
+      // be invoked in the root zone.
+      return Zone.current.createTimer(duration, callback);
     }
-    return zone.createTimer(duration, callback);
+    return Zone.current.createTimer(
+      duration,
+      Zone.current.bindCallbackGuarded(callback),
+    );
   }
 
   /// Creates a new repeating timer.
@@ -60,19 +56,8 @@
   /// The [callback] is invoked repeatedly with [duration] intervals until
   /// canceled with the [cancel] function.
   ///
-  /// The [callback] is registered in the current zone using
-  /// [Zone.registerUnaryCallback], and the timer is created using
-  /// that zone's [Zone.createPeriodicTimer].
-  /// The default timer implementation will run the registered callback
-  /// in that zone using [Zone.runUnary] when the timer ticks,
-  /// and if the callback throws, errors will be reported as an
-  /// uncaught asynchronous error using the zone's [Zone.handleUncaughtError].
-  ///
-  /// If the callback throws, that will not cancel the timer.
-  ///
   /// The exact timing depends on the underlying timer implementation.
   /// No more than `n` callbacks will be made in `duration * n` time,
-  /// within the precision of the timer's granularity,
   /// but the time between two consecutive callbacks
   /// can be shorter and longer than `duration`.
   ///
@@ -101,23 +86,25 @@
   /// // "Cancel timer"
   /// ```
   factory Timer.periodic(Duration duration, void callback(Timer timer)) {
-    Zone zone = Zone._current;
-    if (!identical(zone, _rootZone)) {
-      callback = zone.registerUnaryCallback(callback);
+    if (Zone.current == Zone.root) {
+      // No need to bind the callback. We know that the root's timer will
+      // be invoked in the root zone.
+      return Zone.current.createPeriodicTimer(duration, callback);
     }
-    return zone.createPeriodicTimer(duration, callback);
+    var boundCallback = Zone.current.bindUnaryCallbackGuarded<Timer>(callback);
+    return Zone.current.createPeriodicTimer(duration, boundCallback);
   }
 
   /// Runs the given [callback] asynchronously as soon as possible.
   ///
-  /// This function is equivalent to `Timer(Duration.zero, callback)`.
+  /// This function is equivalent to `new Timer(Duration.zero, callback)`.
   ///
   /// Example:
   /// ```dart
   /// Timer.run(() => print('timer run'));
   /// ```
   static void run(void Function() callback) {
-    Timer(Duration.zero, callback);
+    new Timer(Duration.zero, callback);
   }
 
   /// Cancels the timer.
diff --git a/sdk/lib/async/zone.dart b/sdk/lib/async/zone.dart
index 2780b33..6404070 100644
--- a/sdk/lib/async/zone.dart
+++ b/sdk/lib/async/zone.dart
@@ -4,13 +4,8 @@
 
 part of dart.async;
 
-/// A zero argument function.
 typedef ZoneCallback<R> = R Function();
-
-/// A one argument function.
 typedef ZoneUnaryCallback<R, T> = R Function(T);
-
-/// A two argument function.
 typedef ZoneBinaryCallback<R, T1, T2> = R Function(T1, T2);
 
 /// The type of a custom [Zone.handleUncaughtError] implementation function.
@@ -47,24 +42,19 @@
 /// and the current zone where the error was uncaught as [zone],
 /// which will have [self] as a parent zone.
 ///
-/// The function [callback] is the function which was passed to the
+/// The function [f] is the function which was passed to the
 /// [Zone.run] of [zone].
 ///
 /// The default behavior of [Zone.run] is
-/// to call [callback] in the current zone, [zone].
+/// to call [f] in the current zone, [zone].
 /// A custom handler can do things before, after or instead of
-/// calling [callback].
+/// calling [f].
 ///
 /// The function must only access zone-related functionality through
 /// [self], [parent] or [zone].
 /// It should not depend on the current zone ([Zone.current]).
 typedef RunHandler =
-    R Function<R>(
-      Zone self,
-      ZoneDelegate parent,
-      Zone zone,
-      R Function() callback,
-    );
+    R Function<R>(Zone self, ZoneDelegate parent, Zone zone, R Function() f);
 
 /// The type of a custom [Zone.runUnary] implementation function.
 ///
@@ -73,13 +63,13 @@
 /// and the current zone where the error was uncaught as [zone],
 /// which will have [self] as a parent zone.
 ///
-/// The function [callback] and value [argument] are the function and argument
+/// 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 [callback] with argument [argument] in the current zone, [zone].
+/// to call [f] with argument [arg] in the current zone, [zone].
 /// A custom handler can do things before, after or instead of
-/// calling [callback].
+/// calling [f].
 ///
 /// The function must only access zone-related functionality through
 /// [self], [parent] or [zone].
@@ -89,8 +79,8 @@
       Zone self,
       ZoneDelegate parent,
       Zone zone,
-      R Function(T argument) callback,
-      T argument,
+      R Function(T arg) f,
+      T arg,
     );
 
 /// The type of a custom [Zone.runBinary] implementation function.
@@ -100,13 +90,13 @@
 /// and the current zone where the error was uncaught as [zone],
 /// which will have [self] as a parent zone.
 ///
-/// The function [callback] and values [argument1] and [argument2] are the function and arguments
+/// 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 [callback] with arguments [argument1] and [argument2] in the current zone, [zone].
+/// 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 [callback].
+/// calling [f].
 ///
 /// The function must only access zone-related functionality through
 /// [self], [parent] or [zone].
@@ -116,9 +106,9 @@
       Zone self,
       ZoneDelegate parent,
       Zone zone,
-      R Function(T1, T2) callback,
-      T1 argument1,
-      T2 argument2,
+      R Function(T1 arg1, T2 arg2) f,
+      T1 arg1,
+      T2 arg2,
     );
 
 /// The type of a custom [Zone.registerCallback] implementation function.
@@ -128,23 +118,23 @@
 /// and the current zone where the error was uncaught as [zone],
 /// which will have [self] as a parent zone.
 ///
-/// The function [callback] is the function which was passed to the
+/// The function [f] is the function which was passed to the
 /// [Zone.registerCallback] of [zone].
 ///
-/// The handler should return either the function [callback]
-/// or another function replacing [callback],
-/// typically by wrapping [callback] in a function
-/// which does something extra before and after invoking [callback]
+/// 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]
 ///
 /// The function must only access zone-related functionality through
 /// [self], [parent] or [zone].
 /// It should not depend on the current zone ([Zone.current]).
 typedef RegisterCallbackHandler =
-    R Function() Function<R>(
+    ZoneCallback<R> Function<R>(
       Zone self,
       ZoneDelegate parent,
       Zone zone,
-      R Function() callback,
+      R Function() f,
     );
 
 /// The type of a custom [Zone.registerUnaryCallback] implementation function.
@@ -154,23 +144,23 @@
 /// and the current zone where the error was uncaught as [zone],
 /// which will have [self] as a parent zone.
 ///
-/// The function [callback] is the function which was passed to the
+/// 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 [callback]
-/// or another function replacing [callback],
-/// typically by wrapping [callback] in a function
-/// which does something extra before and after invoking [callback]
+/// 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]
 ///
 /// The function must only access zone-related functionality through
 /// [self], [parent] or [zone].
 /// It should not depend on the current zone ([Zone.current]).
 typedef RegisterUnaryCallbackHandler =
-    R Function(T) Function<R, T>(
+    ZoneUnaryCallback<R, T> Function<R, T>(
       Zone self,
       ZoneDelegate parent,
       Zone zone,
-      R Function(T argument) callback,
+      R Function(T arg) f,
     );
 
 /// The type of a custom [Zone.registerBinaryCallback] implementation function.
@@ -180,19 +170,19 @@
 /// and the current zone where the error was uncaught as [zone],
 /// which will have [self] as a parent zone.
 ///
-/// The function [callback] is the function which was passed to the
+/// 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 [callback]
-/// or another function replacing [callback],
-/// typically by wrapping [callback] in a function
-/// which does something extra before and after invoking [callback]
+/// 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 =
-    R Function(T1, T2) Function<R, T1, T2>(
+    ZoneBinaryCallback<R, T1, T2> Function<R, T1, T2>(
       Zone self,
       ZoneDelegate parent,
       Zone zone,
-      R Function(T1, T2) callback,
+      R Function(T1 arg1, T2 arg2) f,
     );
 
 /// The type of a custom [Zone.errorCallback] implementation function.
@@ -213,8 +203,8 @@
 /// say with `error2` and `stackTrace2`, it should still allow the
 /// parent zone to intercept those errors, for examples as:
 /// ```dart
-///   return parent.errorCallback(zone, error2, stackTrace2) ??
-///       AsyncError(error2, stackTrace2);
+///   return parent.errorCallback(zone, error, stackTrace) ??
+///       AsyncError(error, stackTrace);
 /// ```
 ///
 /// The function returns either `null` if the original error and stack trace
@@ -248,11 +238,11 @@
 /// and the current zone where the error was uncaught as [zone],
 /// which will have [self] as a parent zone.
 ///
-/// The function [callback] is the function which was
+/// The function [f] is the function which was
 /// passed to [Zone.scheduleMicrotask] of [zone].
 ///
-/// The custom handler can choose to replace the function [callback]
-/// with one that does something before, after or instead of calling [callback],
+/// 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.
@@ -261,12 +251,7 @@
 /// [self], [parent] or [zone].
 /// It should not depend on the current zone ([Zone.current]).
 typedef ScheduleMicrotaskHandler =
-    void Function(
-      Zone self,
-      ZoneDelegate parent,
-      Zone zone,
-      void Function() callback,
-    );
+    void Function(Zone self, ZoneDelegate parent, Zone zone, void Function() f);
 
 /// The type of a custom [Zone.createTimer] implementation function.
 ///
@@ -275,12 +260,12 @@
 /// and the current zone where the error was uncaught as [zone],
 /// which will have [self] as a parent zone.
 ///
-/// The callback function [callback] and [duration] are the ones which were
+/// 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 [callback]
-/// with one that does something before, after or instead of calling [callback],
+/// 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.
@@ -297,7 +282,7 @@
       ZoneDelegate parent,
       Zone zone,
       Duration duration,
-      void Function() callback,
+      void Function() f,
     );
 
 /// The type of a custom [Zone.createPeriodicTimer] implementation function.
@@ -307,12 +292,12 @@
 /// and the current zone where the error was uncaught as [zone],
 /// which will have [self] as a parent zone.
 ///
-/// The callback function [callback] and [period] are the ones which were
+/// 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 [callback]
-/// with one that does something before, after or instead of calling [callback],
+/// 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.
@@ -329,7 +314,7 @@
       ZoneDelegate parent,
       Zone zone,
       Duration period,
-      void Function(Timer timer) callback,
+      void Function(Timer timer) f,
     );
 
 /// The type of a custom [Zone.print] implementation function.
@@ -383,7 +368,7 @@
     );
 
 class _ZoneFunction<T extends Function> {
-  final Zone zone;
+  final _Zone zone;
   final T function;
   const _ZoneFunction(this.zone, this.function);
 }
@@ -409,27 +394,27 @@
 /// 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.
-final class ZoneSpecification {
+abstract final 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 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,
-  });
+  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.
   ///
@@ -473,42 +458,79 @@
   }
 
   /// A custom [Zone.handleUncaughtError] implementation for a new zone.
-  final HandleUncaughtErrorHandler? handleUncaughtError;
+  HandleUncaughtErrorHandler? get handleUncaughtError;
 
   /// A custom [Zone.run] implementation for a new zone.
-  final RunHandler? run;
+  RunHandler? get run;
 
   /// A custom [Zone.runUnary] implementation for a new zone.
-  final RunUnaryHandler? runUnary;
+  RunUnaryHandler? get runUnary;
 
   /// A custom [Zone.runBinary] implementation for a new zone.
-  final RunBinaryHandler? runBinary;
+  RunBinaryHandler? get runBinary;
 
   /// A custom [Zone.registerCallback] implementation for a new zone.
-  final RegisterCallbackHandler? registerCallback;
+  RegisterCallbackHandler? get registerCallback;
 
   /// A custom [Zone.registerUnaryCallback] implementation for a new zone.
-  final RegisterUnaryCallbackHandler? registerUnaryCallback;
+  RegisterUnaryCallbackHandler? get registerUnaryCallback;
 
   /// A custom [Zone.registerBinaryCallback] implementation for a new zone.
-  final RegisterBinaryCallbackHandler? registerBinaryCallback;
+  RegisterBinaryCallbackHandler? get registerBinaryCallback;
 
   /// A custom [Zone.errorCallback] implementation for a new zone.
-  final ErrorCallbackHandler? errorCallback;
+  ErrorCallbackHandler? get errorCallback;
 
   /// A custom [Zone.scheduleMicrotask] implementation for a new zone.
-  final ScheduleMicrotaskHandler? scheduleMicrotask;
+  ScheduleMicrotaskHandler? get scheduleMicrotask;
 
   /// A custom [Zone.createTimer] implementation for a new zone.
-  final CreateTimerHandler? createTimer;
+  CreateTimerHandler? get createTimer;
 
   /// A custom [Zone.createPeriodicTimer] implementation for a new zone.
-  final CreatePeriodicTimerHandler? createPeriodicTimer;
+  CreatePeriodicTimerHandler? get createPeriodicTimer;
 
   /// A custom [Zone.print] implementation for a new zone.
-  final PrintHandler? print;
+  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.
+base 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;
 }
 
@@ -532,156 +554,48 @@
 ///   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.
-final class ZoneDelegate {
-  const ZoneDelegate._(this._delegationTarget);
-
-  final Zone _delegationTarget;
-
-  // Invokes the [HandleUncaughtErrorHandler] of the zone with a current zone.
-  void handleUncaughtError(Zone zone, Object error, StackTrace stackTrace) {
-    _delegationTarget._processUncaughtError(zone, error, stackTrace);
-  }
+abstract final 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 Function() callback) {
-    var implementation = _delegationTarget._run;
-    Zone implZone = implementation.zone;
-    var handler = implementation.function;
-    return handler(implZone, implZone._parentDelegate, zone, callback);
-  }
+  R run<R>(Zone zone, R f());
 
   // Invokes the [RunUnaryHandler] of the zone with a current zone.
-  R runUnary<R, T>(Zone zone, R Function(T) callback, T argument) {
-    var implementation = _delegationTarget._runUnary;
-    Zone implZone = implementation.zone;
-    var handler = implementation.function;
-    return handler(
-      implZone,
-      implZone._parentDelegate,
-      zone,
-      callback,
-      argument,
-    );
-  }
+  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 Function(T1, T2) callback,
-    T1 argument1,
-    T2 argument2,
-  ) {
-    var implementation = _delegationTarget._runBinary;
-    Zone implZone = implementation.zone;
-    var handler = implementation.function;
-    return handler(
-      implZone,
-      implZone._parentDelegate,
-      zone,
-      callback,
-      argument1,
-      argument2,
-    );
-  }
+  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.
-  R Function() registerCallback<R>(Zone zone, R Function() callback) {
-    var implementation = _delegationTarget._registerCallback;
-    Zone implZone = implementation.zone;
-    if (identical(implZone, _rootZone)) return callback;
-    var handler = implementation.function;
-    return handler(implZone, implZone._parentDelegate, zone, callback);
-  }
+  ZoneCallback<R> registerCallback<R>(Zone zone, R f());
 
   // Invokes the [RegisterUnaryHandler] of the zone with a current zone.
-  R Function(T) registerUnaryCallback<R, T>(Zone zone, R Function(T) callback) {
-    var implementation = _delegationTarget._registerUnaryCallback;
-    Zone implZone = implementation.zone;
-    if (identical(implZone, _rootZone)) return callback;
-    var handler = implementation.function;
-    return handler(implZone, implZone._parentDelegate, zone, callback);
-  }
+  ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(Zone zone, R f(T arg));
 
   // Invokes the [RegisterBinaryHandler] of the zone with a current zone.
-  R Function(T1, T2) registerBinaryCallback<R, T1, T2>(
+  ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
     Zone zone,
-    R Function(T1, T2) callback,
-  ) {
-    var implementation = _delegationTarget._registerBinaryCallback;
-    Zone implZone = implementation.zone;
-    if (identical(implZone, _rootZone)) return callback;
-    var handler = implementation.function;
-    return handler(implZone, implZone._parentDelegate, zone, callback);
-  }
+    R f(T1 arg1, T2 arg2),
+  );
 
   // Invokes the [ErrorCallbackHandler] of the zone with a current zone.
-  AsyncError? errorCallback(Zone zone, Object error, StackTrace? stackTrace) {
-    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);
-  }
+  AsyncError? errorCallback(Zone zone, Object error, StackTrace? stackTrace);
 
   // Invokes the [ScheduleMicrotaskHandler] of the zone with a current zone.
-  void scheduleMicrotask(Zone zone, Function() callback) {
-    var implementation = _delegationTarget._scheduleMicrotask;
-    Zone implZone = implementation.zone;
-    ScheduleMicrotaskHandler handler = implementation.function;
-    handler(implZone, implZone._parentDelegate, zone, callback);
-  }
+  void scheduleMicrotask(Zone zone, void f());
 
   // Invokes the [CreateTimerHandler] of the zone with a current zone.
-  Timer createTimer(Zone zone, Duration duration, void Function() callback) {
-    var implementation = _delegationTarget._createTimer;
-    Zone implZone = implementation.zone;
-    CreateTimerHandler handler = implementation.function;
-    return handler(
-      implZone,
-      implZone._parentDelegate,
-      zone,
-      duration,
-      callback,
-    );
-  }
+  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 Function(Timer timer) callback,
-  ) {
-    var implementation = _delegationTarget._createPeriodicTimer;
-    Zone implZone = implementation.zone;
-    CreatePeriodicTimerHandler handler = implementation.function;
-    return handler(implZone, implZone._parentDelegate, zone, period, callback);
-  }
+  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) {
-    var implementation = _delegationTarget._print;
-    Zone implZone = implementation.zone;
-    PrintHandler handler = implementation.function;
-    handler(implZone, implZone._parentDelegate, zone, line);
-  }
+  void print(Zone zone, String line);
 
   // Invokes the [ForkHandler] of the zone with a current zone.
-  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,
-    );
-  }
+  Zone fork(Zone zone, ZoneSpecification? specification, Map? zoneValues);
 }
 
 /// A zone represents an environment that remains stable across asynchronous
@@ -708,8 +622,8 @@
 /// explicitly take the equivalent of their own class, the "super" class and the
 /// `this` object as parameters.
 ///
-/// Asynchronous callbacks should always run in the context of the zone where
-/// they were created. This is implemented using two steps:
+/// 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
@@ -719,26 +633,16 @@
 /// 2. At a later point the registered callback is run in the remembered zone,
 ///    using one of [run], [runUnary] or [runBinary].
 ///
-/// A function returned by one of the [registerCallback] family of functions
-/// must *always* be run using the same zone's corresponding [run] function.
-/// The returned function may assume that it is running in that particular
-/// zone, and will fail if run outside of that zone.
-///
-/// This registration and running all handled internally by the platform code
-/// when using the top-level functions like `scheduledMicrotask` or
-/// constructors like [Timer.periodic], and most users don't need to worry
-/// about it.
-/// Developers creating custom zones should interact correctly with these
-/// expectations, and developers of new asynchronous operations
-/// must follow the protocol to interact correctly with custom zones.
+/// 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, 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.
-/// The function returned by one of these [bindCallback] operations
-/// should not be run using the [Zone.run] functions.
 ///
 /// Similarly, zones provide [bindCallbackGuarded] (and the corresponding
 /// [bindUnaryCallbackGuarded] and [bindBinaryCallbackGuarded]), when the
@@ -746,7 +650,7 @@
 @vmIsolateUnsendable
 abstract final class Zone {
   // Private constructor so that it is not possible instantiate a Zone class.
-  const Zone._();
+  Zone._();
 
   /// The root zone.
   ///
@@ -757,13 +661,13 @@
   ///
   /// The root zone implements the default behavior of all zone operations.
   /// Many methods, like [registerCallback] do the bare minimum required of the
-  /// callback, and are only provided as a hook for custom zones. Others, like
+  /// 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;
+  static _Zone _current = _rootZone;
 
   /// The zone that is currently active.
   static Zone get current => _current;
@@ -845,10 +749,7 @@
   /// 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) {
-    return identical(this, otherZone) ||
-        identical(_handleUncaughtError, otherZone._handleUncaughtError);
-  }
+  bool inSameErrorZone(Zone otherZone);
 
   /// Creates a new zone as a child zone of this zone.
   ///
@@ -928,44 +829,34 @@
     T2 argument2,
   );
 
-  /// Registers the given [callback] in this zone.
+  /// Registers the given callback in this zone.
   ///
-  /// This informs the zone that the [callback] will later be run
-  /// using this zone's [run] method. A custom zone can use this opportunity
-  /// to record information available at the time of registration,
-  /// for example the current stack trace, so that it can be made
-  /// available again when the callback is run. Doing so allows a zone to bridge
-  /// the asynchronous gap between a callback being created and being run.
+  /// 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.
   ///
-  /// When implementing an asynchronous primitive that uses callbacks
-  /// to be run at a later timer, the callback should be registered using
-  /// [registerCallback] at the point where the user provides the callback,
-  /// and then later run using [run] in the same zone.
+  /// 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.
-  ///
-  /// Returns the callback that should be used in place of the provided
-  /// [callback], which is often just the original callback function.
-  ///
-  /// The returned function should *only* be run using this zone's [run]
-  /// function. The [callback] function may have been modified,
-  /// by a custom [registerCallback] implementation, into a function that
-  /// assumes that it runs in this zone. Such a function may fail
-  /// if invoked directly from another zone, or even in the same
-  /// zone without going through a corresponding custom [run] implementation.
-  R Function() registerCallback<R>(R callback());
+  ZoneCallback<R> registerCallback<R>(R callback());
 
   /// Registers the given callback in this zone.
   ///
   /// Similar to [registerCallback] but with a unary callback.
-  R Function(T) registerUnaryCallback<R, T>(R callback(T argument));
+  ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R callback(T arg));
 
   /// Registers the given callback in this zone.
   ///
   /// Similar to [registerCallback] but with a binary callback.
-  R Function(T1, T2) registerBinaryCallback<R, T1, T2>(
-    R callback(T1 argument1, T2 argument2),
+  ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
+    R callback(T1 arg1, T2 arg2),
   );
 
   /// Registers the provided [callback] and returns a function that will
@@ -976,7 +867,7 @@
   /// ZoneCallback registered = this.registerCallback(callback);
   /// return () => this.run(registered);
   /// ```
-  R Function() bindCallback<R>(R callback());
+  ZoneCallback<R> bindCallback<R>(R callback());
 
   /// Registers the provided [callback] and returns a function that will
   /// execute in this zone.
@@ -984,9 +875,9 @@
   /// Equivalent to:
   /// ```dart
   /// ZoneCallback registered = this.registerUnaryCallback(callback);
-  /// return (argument) => this.runUnary(registered, argument);
+  /// return (arg) => this.runUnary(registered, arg);
   /// ```
-  R Function(T) bindUnaryCallback<R, T>(R callback(T argument));
+  ZoneUnaryCallback<R, T> bindUnaryCallback<R, T>(R callback(T argument));
 
   /// Registers the provided [callback] and returns a function that will
   /// execute in this zone.
@@ -994,18 +885,17 @@
   /// Equivalent to:
   /// ```dart
   /// ZoneCallback registered = registerBinaryCallback(callback);
-  /// return (argument1, argument2) =>
-  ///     runBinary(registered, argument1, argument2);
+  /// return (arg1, arg2) => this.runBinary(registered, arg1, arg2);
   /// ```
-  R Function(T1, T2) bindBinaryCallback<R, T1, T2>(
+  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.
   ///
-  /// If the [callback] throws when run, the error is reported as an uncaught
-  /// error in this zone.
+  /// When the function executes, errors are caught and treated as uncaught
+  /// errors.
   ///
   /// Equivalent to:
   /// ```dart
@@ -1017,27 +907,23 @@
   /// Registers the provided [callback] and returns a function that will
   /// execute in this zone.
   ///
-  /// If the [callback] throws when run, the error is reported as an uncaught
-  /// error in this zone.
+  /// When the function executes, errors are caught and treated as uncaught
+  /// errors.
   ///
   /// Equivalent to:
   /// ```dart
   /// ZoneCallback registered = this.registerUnaryCallback(callback);
-  /// return (argument) => this.runUnaryGuarded(registered, argument);
+  /// 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.
+  ///  Registers the provided [callback] and returns a function that will
+  ///  execute in this zone.
   ///
-  /// If the [callback] throws when run, the error is reported as an uncaught
-  /// error in this zone.
-  ///
-  /// Equivalent to:
+  ///  Equivalent to:
   /// ```dart
-  /// ZoneCallback registered = registerBinaryCallback(callback);
-  /// return (argument1, argument2) =>
-  ///     runBinaryGuarded(registered, argument1, argument2);
+  ///  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),
@@ -1059,15 +945,13 @@
   ///
   /// Returns `null` if no replacement is desired. Otherwise returns an instance
   /// of [AsyncError] holding the new pair of error and stack trace.
-  /// The default implementation of the [root] zone performs not replacement
-  /// and always return `null`.
   ///
   /// 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, and are therefore already asynchronous errors.
+  /// controllers.
   AsyncError? errorCallback(Object error, StackTrace? stackTrace);
 
   /// Runs [callback] asynchronously in this zone.
@@ -1081,50 +965,28 @@
   /// In the latter case, they will usually still use the parent zone's
   /// [ZoneDelegate.scheduleMicrotask] to attach themselves to the existing
   /// event loop.
-  ///
-  /// The default implementation does not register the callback in the current
-  /// zone, the caller is expected to do that first if necessary, and will
-  /// eventually run the callback using this zone's [run],
-  /// and pass any thrown error to its [handleUncaughtError].
   void scheduleMicrotask(void Function() callback);
 
   /// Creates a [Timer] where the callback is executed in this zone.
-  ///
-  /// The [Timer.new] constructor delegates to this function
-  /// of the [current] zone.
-  ///
-  /// The default implementation does not register the callback in the current
-  /// zone, the caller is expected to do that first if necessary, and will
-  /// eventually run the callback using this zone's [run],
-  /// and pass any thrown error to its [handleUncaughtError].
   Timer createTimer(Duration duration, void Function() callback);
 
   /// Creates a periodic [Timer] where the callback is executed in this zone.
-  ///
-  /// The [Timer.periodic] constructor delegates to this function
-  /// of the [current] zone.
-  ///
-  /// The default implementation does not register the callback in the current
-  /// zone, the caller is expected to do that first if necessary, and will
-  /// run each the callback for each timer tick using this zone's [runUnary],
-  /// and pass any thrown error to its [handleUncaughtError].
   Timer createPeriodicTimer(Duration period, void callback(Timer timer));
 
   /// Prints the given [line].
   ///
   /// The global `print` function delegates to the current zone's [print]
-  /// function, after converting its argument to a [String],
-  /// which makes it possible to intercept printing.
+  /// function which makes it possible to intercept printing.
   ///
   /// Example:
   /// ```dart
   /// import 'dart:async';
   ///
-  /// void main() {
+  /// main() {
   ///   runZoned(() {
   ///     // Ends up printing: "Intercepted: in zone".
   ///     print("in zone");
-  ///   }, zoneSpecification: ZoneSpecification(
+  ///   }, zoneSpecification: new ZoneSpecification(
   ///       print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
   ///     parent.print(zone, "Intercepted: $line");
   ///   }));
@@ -1135,9 +997,9 @@
   /// Call to enter the [Zone].
   ///
   /// The previous current zone is returned.
-  static Zone _enter(Zone zone) {
+  static _Zone _enter(_Zone zone) {
     assert(!identical(zone, _current));
-    Zone previous = _current;
+    _Zone previous = _current;
     _current = zone;
     return previous;
   }
@@ -1145,7 +1007,8 @@
   /// Call to leave the [Zone].
   ///
   /// The previous [Zone] must be provided as `previous`.
-  static void _leave(Zone previous) {
+  static void _leave(_Zone previous) {
+    assert(previous != null);
     Zone._current = previous;
   }
 
@@ -1159,10 +1022,122 @@
   /// By controlling access to the key, a zone can grant or deny access to the
   /// zone value.
   dynamic operator [](Object? key);
+}
 
-  Map<Object?, Object?>? get _map;
+base class _ZoneDelegate implements ZoneDelegate {
+  final _Zone _delegationTarget;
 
-  // Internal implementation.
+  _ZoneDelegate(this._delegationTarget);
+
+  void handleUncaughtError(Zone zone, Object error, StackTrace stackTrace) {
+    _delegationTarget._processUncaughtError(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) {
+    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 base class _Zone implements Zone {
+  const _Zone();
+
+  // TODO(floitsch): the types of the `_ZoneFunction`s should have a type for
+  // all fields.
   _ZoneFunction<RunHandler> get _run;
   _ZoneFunction<RunUnaryHandler> get _runUnary;
   _ZoneFunction<RunBinaryHandler> get _runBinary;
@@ -1176,21 +1151,28 @@
   _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);
+  }
 
   void _processUncaughtError(Zone zone, Object error, StackTrace stackTrace) {
     var implementation = _handleUncaughtError;
-    Zone implZone = implementation.zone;
+    _Zone implZone = implementation.zone;
     if (identical(implZone, _rootZone)) {
       _rootHandleError(error, stackTrace);
       return;
     }
     HandleUncaughtErrorHandler handler = implementation.function;
     ZoneDelegate parentDelegate = implZone._parentDelegate;
-    Zone parentZone = implZone.parent!; // Not null for non-root zones.
-    Zone currentZone = Zone._current;
+    _Zone parentZone = implZone.parent!; // Not null for non-root zones.
+    _Zone currentZone = Zone._current;
     try {
       Zone._current = parentZone;
       handler(implZone, parentDelegate, zone, error, stackTrace);
@@ -1206,9 +1188,11 @@
   }
 }
 
-base class _CustomZone extends Zone {
+base 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.
   _ZoneFunction<RunHandler> _run;
   _ZoneFunction<RunUnaryHandler> _runUnary;
   _ZoneFunction<RunBinaryHandler> _runBinary;
@@ -1223,26 +1207,21 @@
   _ZoneFunction<ForkHandler> _fork;
   _ZoneFunction<HandleUncaughtErrorHandler> _handleUncaughtError;
 
-  /// Remembers the first delegate created for the zone, so it can be reused for
-  /// other sub-zones.
+  // A cached delegate to this zone.
   ZoneDelegate? _delegateCache;
 
   /// The parent zone.
-  final Zone parent;
+  final _Zone parent;
 
   /// The zone's scoped value declaration map.
   ///
   /// This is always a [HashMap].
-  final Map<Object?, Object?>? _map;
+  final Map<Object?, Object?> _map;
 
-  /// Lazily created and cached delegate object.
-  ///
-  /// Only used if the zone has a function handler that is called.
-  ZoneDelegate get _delegate => _delegateCache ??= ZoneDelegate._(this);
-  // Shorthand.
+  ZoneDelegate get _delegate => _delegateCache ??= _ZoneDelegate(this);
   ZoneDelegate get _parentDelegate => parent._delegate;
 
-  _CustomZone(this.parent, ZoneSpecification? specification, this._map)
+  _CustomZone(this.parent, ZoneSpecification specification, this._map)
     : _run = parent._run,
       _runUnary = parent._runUnary,
       _runBinary = parent._runBinary,
@@ -1255,143 +1234,162 @@
       _createPeriodicTimer = parent._createPeriodicTimer,
       _print = parent._print,
       _fork = parent._fork,
-      _handleUncaughtError = parent._handleUncaughtError,
-      super._() {
-    if (specification != null) {
-      // 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.
-      if (specification.run case var run?) {
-        _run = _ZoneFunction<RunHandler>(this, run);
-      }
-      if (specification.runUnary case var runUnary?) {
-        _runUnary = _ZoneFunction<RunUnaryHandler>(this, runUnary);
-      }
-      if (specification.runBinary case var runBinary?) {
-        _runBinary = _ZoneFunction<RunBinaryHandler>(this, runBinary);
-      }
-      if (specification.registerCallback case var registerCallback?) {
-        _registerCallback = _ZoneFunction<RegisterCallbackHandler>(
-          this,
-          registerCallback,
-        );
-      }
-      if (specification.registerUnaryCallback case var registerUnaryCallback?) {
-        _registerUnaryCallback = _ZoneFunction<RegisterUnaryCallbackHandler>(
-          this,
-          registerUnaryCallback,
-        );
-      }
-      if (specification.registerBinaryCallback
-          case var registerBinaryCallback?) {
-        _registerBinaryCallback = _ZoneFunction<RegisterBinaryCallbackHandler>(
-          this,
-          registerBinaryCallback,
-        );
-      }
-      if (specification.errorCallback case var errorCallback?) {
-        _errorCallback = _ZoneFunction<ErrorCallbackHandler>(
-          this,
-          errorCallback,
-        );
-      }
-      if (specification.scheduleMicrotask case var scheduleMicrotask?) {
-        _scheduleMicrotask = _ZoneFunction<ScheduleMicrotaskHandler>(
-          this,
-          scheduleMicrotask,
-        );
-      }
-      if (specification.createTimer case var createTimer?) {
-        _createTimer = _ZoneFunction<CreateTimerHandler>(this, createTimer);
-      }
-      if (specification.createPeriodicTimer case var createPeriodicTimer?) {
-        _createPeriodicTimer = _ZoneFunction<CreatePeriodicTimerHandler>(
-          this,
-          createPeriodicTimer,
-        );
-      }
-      if (specification.print case var print?) {
-        _print = _ZoneFunction<PrintHandler>(this, print);
-      }
-      if (specification.fork case var fork?) {
-        _fork = _ZoneFunction<ForkHandler>(this, fork);
-      }
-      if (specification.handleUncaughtError case var handleUncaughtError?) {
-        _handleUncaughtError = _ZoneFunction<HandleUncaughtErrorHandler>(
-          this,
-          handleUncaughtError,
-        );
-      }
+      _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 = _ZoneFunction<RunHandler>(this, run);
+    }
+    var runUnary = specification.runUnary;
+    if (runUnary != null) {
+      _runUnary = _ZoneFunction<RunUnaryHandler>(this, runUnary);
+    }
+    var runBinary = specification.runBinary;
+    if (runBinary != null) {
+      _runBinary = _ZoneFunction<RunBinaryHandler>(this, runBinary);
+    }
+    var registerCallback = specification.registerCallback;
+    if (registerCallback != null) {
+      _registerCallback = _ZoneFunction<RegisterCallbackHandler>(
+        this,
+        registerCallback,
+      );
+    }
+    var registerUnaryCallback = specification.registerUnaryCallback;
+    if (registerUnaryCallback != null) {
+      _registerUnaryCallback = _ZoneFunction<RegisterUnaryCallbackHandler>(
+        this,
+        registerUnaryCallback,
+      );
+    }
+    var registerBinaryCallback = specification.registerBinaryCallback;
+    if (registerBinaryCallback != null) {
+      _registerBinaryCallback = _ZoneFunction<RegisterBinaryCallbackHandler>(
+        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.
   ///
-  /// This zone if it has an error-handler, otherwise the
-  /// [parent] zone's error-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 Function() callback) {
-    _runGuardedInZone(this, callback);
+  void runGuarded(void f()) {
+    try {
+      run(f);
+    } catch (e, s) {
+      handleUncaughtError(e, s);
+    }
   }
 
-  void runUnaryGuarded<T>(void Function(T) callback, T argument) {
-    _runUnaryGuardedInZone<T>(this, callback, argument);
+  void runUnaryGuarded<T>(void f(T arg), T arg) {
+    try {
+      runUnary(f, arg);
+    } catch (e, s) {
+      handleUncaughtError(e, s);
+    }
   }
 
-  void runBinaryGuarded<T1, T2>(
-    void Function(T1, T2) callback,
-    T1 argument1,
-    T2 argument2,
-  ) {
-    _runBinaryGuardedInZone<T1, T2>(this, callback, argument1, argument2);
+  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);
+    }
   }
 
-  R Function() bindCallback<R>(R Function() callback) {
-    var registered = registerCallback(callback);
+  ZoneCallback<R> bindCallback<R>(R f()) {
+    var registered = registerCallback(f);
     return () => this.run(registered);
   }
 
-  R Function(T) bindUnaryCallback<R, T>(R Function(T) callback) {
-    var registered = registerUnaryCallback(callback);
-    return (T argument) => this.runUnary(registered, argument);
+  ZoneUnaryCallback<R, T> bindUnaryCallback<R, T>(R f(T arg)) {
+    var registered = registerUnaryCallback(f);
+    return (arg) => this.runUnary(registered, arg);
   }
 
-  R Function(T1, T2) bindBinaryCallback<R, T1, T2>(
-    R Function(T1, T2) callback,
+  ZoneBinaryCallback<R, T1, T2> bindBinaryCallback<R, T1, T2>(
+    R f(T1 arg1, T2 arg2),
   ) {
-    var registered = registerBinaryCallback(callback);
-    return (T1 argument1, T2 argument2) =>
-        this.runBinary(registered, argument1, argument2);
+    var registered = registerBinaryCallback(f);
+    return (arg1, arg2) => this.runBinary(registered, arg1, arg2);
   }
 
-  void Function() bindCallbackGuarded(void Function() callback) =>
-      _guardCallbackInZone(this, registerCallback(callback));
+  void Function() bindCallbackGuarded(void f()) {
+    var registered = registerCallback(f);
+    return () => this.runGuarded(registered);
+  }
 
-  void Function(T) bindUnaryCallbackGuarded<T>(void Function(T) callback) =>
-      _guardUnaryCallbackInZone(this, registerUnaryCallback(callback));
+  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 Function(T1, T2) callback,
-  ) => _guardBinaryCallbackInZone(this, registerBinaryCallback(callback));
+    void f(T1 arg1, T2 arg2),
+  ) {
+    var registered = registerBinaryCallback(f);
+    return (arg1, arg2) => this.runBinaryGuarded(registered, arg1, arg2);
+  }
 
   dynamic operator [](Object? key) {
-    var map = _map;
-    if (map == null) return null;
-    var result = map[key];
-    if (result != null || map.containsKey(key)) return result;
+    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 (!identical(parent, _rootZone)) {
+    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;
+        _map[key] = value;
       }
       return value;
     }
+    assert(this == _rootZone);
     return null;
   }
 
@@ -1406,108 +1404,96 @@
     Map<Object?, Object?>? zoneValues,
   }) {
     var implementation = this._fork;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, specification, zoneValues);
+    ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
+    ForkHandler handler = implementation.function;
+    return handler(
+      implementation.zone,
+      parentDelegate,
+      this,
+      specification,
+      zoneValues,
+    );
   }
 
-  R run<R>(R Function() callback) {
+  R run<R>(R f()) {
     var implementation = this._run;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, callback);
+    ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
+    var handler = implementation.function as RunHandler;
+    return handler(implementation.zone, parentDelegate, this, f);
   }
 
-  R runUnary<R, T>(R Function(T) callback, T argument) {
+  R runUnary<R, T>(R f(T arg), T arg) {
     var implementation = this._runUnary;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, callback, argument);
+    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 Function(T1, T2) callback,
-    T1 argument1,
-    T2 argument2,
-  ) {
+  R runBinary<R, T1, T2>(R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
     var implementation = this._runBinary;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, callback, argument1, argument2);
+    ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
+    var handler = implementation.function as RunBinaryHandler;
+    return handler(implementation.zone, parentDelegate, this, f, arg1, arg2);
   }
 
-  R Function() registerCallback<R>(R callback()) {
+  ZoneCallback<R> registerCallback<R>(R callback()) {
     var implementation = this._registerCallback;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, callback);
+    ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
+    var handler = implementation.function as RegisterCallbackHandler;
+    return handler(implementation.zone, parentDelegate, this, callback);
   }
 
-  R Function(T) registerUnaryCallback<R, T>(R callback(T argument)) {
+  ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R callback(T arg)) {
     var implementation = this._registerUnaryCallback;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, callback);
+    ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
+    var handler = implementation.function as RegisterUnaryCallbackHandler;
+    return handler(implementation.zone, parentDelegate, this, callback);
   }
 
-  R Function(T1, T2) registerBinaryCallback<R, T1, T2>(
-    R callback(T1 argument1, T2 argument2),
+  ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
+    R callback(T1 arg1, T2 arg2),
   ) {
     var implementation = this._registerBinaryCallback;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, callback);
+    ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
+    var handler = implementation.function as RegisterBinaryCallbackHandler;
+    return handler(implementation.zone, parentDelegate, this, callback);
   }
 
   AsyncError? errorCallback(Object error, StackTrace? stackTrace) {
     var implementation = this._errorCallback;
-    var implementationZone = implementation.zone;
+    final _Zone implementationZone = implementation.zone;
     if (identical(implementationZone, _rootZone)) return null;
-    var parentDelegate = implementationZone._parentDelegate;
-    var handler = implementation.function;
+    final ZoneDelegate parentDelegate = implementationZone._parentDelegate;
+    ErrorCallbackHandler handler = implementation.function;
     return handler(implementationZone, parentDelegate, this, error, stackTrace);
   }
 
-  void scheduleMicrotask(void Function() callback) {
+  void scheduleMicrotask(void f()) {
     var implementation = this._scheduleMicrotask;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, callback);
+    ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
+    ScheduleMicrotaskHandler handler = implementation.function;
+    return handler(implementation.zone, parentDelegate, this, f);
   }
 
-  Timer createTimer(Duration duration, void Function() callback) {
+  Timer createTimer(Duration duration, void f()) {
     var implementation = this._createTimer;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, duration, callback);
+    ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
+    CreateTimerHandler handler = implementation.function;
+    return handler(implementation.zone, parentDelegate, this, duration, f);
   }
 
-  Timer createPeriodicTimer(
-    Duration duration,
-    void Function(Timer timer) callback,
-  ) {
+  Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
     var implementation = this._createPeriodicTimer;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, duration, callback);
+    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;
-    var zone = implementation.zone;
-    var parentDelegate = zone._parentDelegate;
-    var handler = implementation.function;
-    return handler(zone, parentDelegate, this, line);
+    ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
+    PrintHandler handler = implementation.function;
+    return handler(implementation.zone, parentDelegate, this, line);
   }
 }
 
@@ -1522,22 +1508,21 @@
 }
 
 void _rootHandleError(Object error, StackTrace stackTrace) {
-  _schedulePriorityAsyncCallback(_rootZone, () {
+  _schedulePriorityAsyncCallback(() {
     Error.throwWithStackTrace(error, stackTrace);
   });
 }
 
-R _rootRun<R>(
-  Zone? self,
-  ZoneDelegate? parent,
-  Zone zone,
-  R Function() callback,
-) {
-  if (identical(Zone._current, zone)) return callback();
+R _rootRun<R>(Zone? self, ZoneDelegate? parent, Zone zone, R f()) {
+  if (identical(Zone._current, zone)) return f();
 
-  var old = Zone._enter(zone);
+  if (zone is! _Zone) {
+    throw ArgumentError.value(zone, "zone", "Can only run in platform zones");
+  }
+
+  _Zone old = Zone._enter(zone);
   try {
-    return callback();
+    return f();
   } finally {
     Zone._leave(old);
   }
@@ -1547,14 +1532,18 @@
   Zone? self,
   ZoneDelegate? parent,
   Zone zone,
-  R Function(T) callback,
-  T argument,
+  R f(T arg),
+  T arg,
 ) {
-  if (identical(Zone._current, zone)) return callback(argument);
+  if (identical(Zone._current, zone)) return f(arg);
 
-  var old = Zone._enter(zone);
+  if (zone is! _Zone) {
+    throw ArgumentError.value(zone, "zone", "Can only run in platform zones");
+  }
+
+  _Zone old = Zone._enter(zone);
   try {
-    return callback(argument);
+    return f(arg);
   } finally {
     Zone._leave(old);
   }
@@ -1564,45 +1553,49 @@
   Zone? self,
   ZoneDelegate? parent,
   Zone zone,
-  R Function(T1, T2) callback,
-  T1 argument1,
-  T2 argument2,
+  R f(T1 arg1, T2 arg2),
+  T1 arg1,
+  T2 arg2,
 ) {
-  if (identical(Zone._current, zone)) return callback(argument1, argument2);
+  if (identical(Zone._current, zone)) return f(arg1, arg2);
 
-  var old = Zone._enter(zone);
+  if (zone is! _Zone) {
+    throw ArgumentError.value(zone, "zone", "Can only run in platform zones");
+  }
+
+  _Zone old = Zone._enter(zone);
   try {
-    return callback(argument1, argument2);
+    return f(arg1, arg2);
   } finally {
     Zone._leave(old);
   }
 }
 
-R Function() _rootRegisterCallback<R>(
+ZoneCallback<R> _rootRegisterCallback<R>(
   Zone self,
   ZoneDelegate parent,
   Zone zone,
-  R Function() callback,
+  R f(),
 ) {
-  return callback;
+  return f;
 }
 
-R Function(T) _rootRegisterUnaryCallback<R, T>(
+ZoneUnaryCallback<R, T> _rootRegisterUnaryCallback<R, T>(
   Zone self,
   ZoneDelegate parent,
   Zone zone,
-  R Function(T) callback,
+  R f(T arg),
 ) {
-  return callback;
+  return f;
 }
 
-R Function(T1, T2) _rootRegisterBinaryCallback<R, T1, T2>(
+ZoneBinaryCallback<R, T1, T2> _rootRegisterBinaryCallback<R, T1, T2>(
   Zone self,
   ZoneDelegate parent,
   Zone zone,
-  R Function(T1, T2) callback,
+  R f(T1 arg1, T2 arg2),
 ) {
-  return callback;
+  return f;
 }
 
 AsyncError? _rootErrorCallback(
@@ -1617,9 +1610,17 @@
   Zone? self,
   ZoneDelegate? parent,
   Zone zone,
-  void Function() callback,
+  void f(),
 ) {
-  _scheduleAsyncCallback(zone, callback);
+  if (!identical(_rootZone, zone)) {
+    bool hasErrorHandler = !_rootZone.inSameErrorZone(zone);
+    if (hasErrorHandler) {
+      f = zone.bindCallbackGuarded(f);
+    } else {
+      f = zone.bindCallback(f);
+    }
+  }
+  _scheduleAsyncCallback(f);
 }
 
 Timer _rootCreateTimer(
@@ -1629,10 +1630,8 @@
   Duration duration,
   void Function() callback,
 ) {
-  // A timer callback will run in the root zone, and an uncaught error from that
-  // will be reported just as if reported to the root zone.
   if (!identical(_rootZone, zone)) {
-    callback = _guardCallbackInZone(zone, callback);
+    callback = zone.bindCallback(callback);
   }
   return Timer._createTimer(duration, callback);
 }
@@ -1645,7 +1644,7 @@
   void callback(Timer timer),
 ) {
   if (!identical(_rootZone, zone)) {
-    callback = _guardUnaryCallbackInZone<Timer>(zone, callback);
+    callback = zone.bindUnaryCallback<void, Timer>(callback);
   }
   return Timer._createPeriodicTimer(duration, callback);
 }
@@ -1665,22 +1664,32 @@
   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;
 
-  Map<Object?, Object?>? valueMap;
+  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);
 }
 
-base class _RootZone extends Zone {
-  const _RootZone() : super._();
+base class _RootZone extends _Zone {
+  const _RootZone();
 
   _ZoneFunction<RunHandler> get _run =>
       const _ZoneFunction<RunHandler>(_rootZone, _rootRun);
@@ -1727,92 +1736,95 @@
         _rootHandleUncaughtError,
       );
 
-  Zone? get parent => null;
+  // The parent zone.
+  _Zone? get parent => null;
 
-  static const _rootDelegate = const ZoneDelegate._(_rootZone);
+  /// The zone's scoped value declaration map.
+  ///
+  /// This is always a [HashMap].
+  Map<Object?, Object?> get _map => _rootMap;
 
-  ZoneDelegate get _delegate => _rootDelegate;
-  // It's a lie, but the root zone functions never uses the parent delegate.
-  ZoneDelegate get _parentDelegate => _rootDelegate;
+  static final _rootMap = HashMap();
 
+  static ZoneDelegate? _rootDelegate;
+
+  ZoneDelegate get _delegate => _rootDelegate ??= _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.
-  // All other zones have a different behavior, and so does calling
-  // the root zone as a parent delegate.
 
-  void runGuarded(void Function() callback) {
+  void runGuarded(void f()) {
     try {
       if (identical(_rootZone, Zone._current)) {
-        callback();
-      } else {
-        _rootRun(null, null, this, callback);
+        f();
+        return;
       }
+      _rootRun(null, null, this, f);
     } catch (e, s) {
-      _rootHandleError(e, s);
+      handleUncaughtError(e, s);
     }
   }
 
-  void runUnaryGuarded<T>(void Function(T) callback, T argument) {
+  void runUnaryGuarded<T>(void f(T arg), T arg) {
     try {
       if (identical(_rootZone, Zone._current)) {
-        callback(argument);
-      } else {
-        _rootRunUnary(null, null, this, callback, argument);
+        f(arg);
+        return;
       }
+      _rootRunUnary(null, null, this, f, arg);
     } catch (e, s) {
-      _rootHandleError(e, s);
+      handleUncaughtError(e, s);
     }
   }
 
-  void runBinaryGuarded<T1, T2>(
-    void Function(T1, T2) callback,
-    T1 argument1,
-    T2 argument2,
+  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),
   ) {
-    try {
-      if (identical(_rootZone, Zone._current)) {
-        callback(argument1, argument2);
-      } else {
-        _rootRunBinary(null, null, this, callback, argument1, argument2);
-      }
-    } catch (e, s) {
-      _rootHandleError(e, s);
-    }
+    return (arg1, arg2) => this.runBinary<R, T1, T2>(f, arg1, arg2);
   }
 
-  R Function() bindCallback<R>(R Function() callback) {
-    return () => _rootZone.run<R>(callback);
+  void Function() bindCallbackGuarded(void f()) {
+    return () => this.runGuarded(f);
   }
 
-  R Function(T) bindUnaryCallback<R, T>(R Function(T) callback) {
-    return (argument) => _rootZone.runUnary<R, T>(callback, argument);
-  }
-
-  R Function(T1, T2) bindBinaryCallback<R, T1, T2>(
-    R Function(T1, T2) callback,
-  ) {
-    return (argument1, argument2) =>
-        _rootZone.runBinary<R, T1, T2>(callback, argument1, argument2);
-  }
-
-  void Function() bindCallbackGuarded(void Function() callback) {
-    return () => _rootZone.runGuarded(callback);
-  }
-
-  void Function(T) bindUnaryCallbackGuarded<T>(void Function(T) callback) {
-    return (argument) => _rootZone.runUnaryGuarded(callback, argument);
+  void Function(T) bindUnaryCallbackGuarded<T>(void f(T arg)) {
+    return (arg) => this.runUnaryGuarded(f, arg);
   }
 
   void Function(T1, T2) bindBinaryCallbackGuarded<T1, T2>(
-    void Function(T1, T2) callback,
+    void f(T1 arg1, T2 arg2),
   ) {
-    return (argument1, argument2) =>
-        _rootZone.runBinaryGuarded(callback, argument1, argument2);
+    return (arg1, arg2) => this.runBinaryGuarded(f, arg1, arg2);
   }
 
   dynamic operator [](Object? key) => null;
-  Map<Object?, Object?>? get _map => null;
 
   // Methods that can be customized by the zone specification.
 
@@ -1827,51 +1839,42 @@
     return _rootFork(null, null, this, specification, zoneValues);
   }
 
-  R run<R>(R Function() callback) {
-    if (identical(Zone._current, _rootZone)) return callback();
-    return _rootRun(null, null, this, callback);
+  R run<R>(R f()) {
+    if (identical(Zone._current, _rootZone)) return f();
+    return _rootRun(null, null, this, f);
   }
 
   @pragma('vm:invisible')
-  R runUnary<R, T>(R Function(T) callback, T argument) {
-    if (identical(Zone._current, _rootZone)) return callback(argument);
-    return _rootRunUnary(null, null, this, callback, argument);
+  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 Function(T1, T2) callback,
-    T1 argument1,
-    T2 argument2,
-  ) {
-    if (identical(Zone._current, _rootZone)) {
-      return callback(argument1, argument2);
-    }
-    return _rootRunBinary(null, null, this, callback, argument1, argument2);
+  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);
   }
 
-  R Function() registerCallback<R>(R Function() callback) => callback;
+  ZoneCallback<R> registerCallback<R>(R f()) => f;
 
-  R Function(T) registerUnaryCallback<R, T>(R Function(T) callback) => callback;
+  ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R f(T arg)) => f;
 
   ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
-    R Function(T1, T2) callback,
-  ) => callback;
+    R f(T1 arg1, T2 arg2),
+  ) => f;
 
   AsyncError? errorCallback(Object error, StackTrace? stackTrace) => null;
 
-  void scheduleMicrotask(void Function() callback) {
-    _rootScheduleMicrotask(null, null, this, callback);
+  void scheduleMicrotask(void f()) {
+    _rootScheduleMicrotask(null, null, this, f);
   }
 
-  Timer createTimer(Duration duration, void Function() callback) {
-    return Timer._createTimer(duration, callback);
+  Timer createTimer(Duration duration, void f()) {
+    return Timer._createTimer(duration, f);
   }
 
-  Timer createPeriodicTimer(
-    Duration duration,
-    void Function(Timer timer) callback,
-  ) {
-    return Timer._createPeriodicTimer(duration, callback);
+  Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
+    return Timer._createPeriodicTimer(duration, f);
   }
 
   void print(String line) {
@@ -1879,7 +1882,7 @@
   }
 }
 
-const Zone _rootZone = _RootZone();
+const _Zone _rootZone = _RootZone();
 
 /// Runs [body] in its own zone.
 ///
@@ -1922,7 +1925,7 @@
 /// inaccessible will cause the error to be reported *again* in it's original
 /// error zone.
 ///
-/// See [runZonedGuarded] in place of using the deprecated [onError] argument.
+/// See [runZonedGuarded] in place of using the deprected [onError] argument.
 /// If [onError] is provided this function also tries to catch and handle
 /// synchronous errors from [body], but may throw an error anyway returning
 /// `null` if the generic argument [R] is not nullable.
@@ -1933,7 +1936,7 @@
   @Deprecated("Use runZonedGuarded instead") Function? onError,
 }) {
   if (onError != null) {
-    // TODO: Remove this when code has been migrated off using [onError].
+    // 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;
@@ -1987,7 +1990,7 @@
   Map<Object?, Object?>? zoneValues,
   ZoneSpecification? zoneSpecification,
 }) {
-  Zone parentZone = Zone._current;
+  _Zone parentZone = Zone._current;
   HandleUncaughtErrorHandler errorHandler = (
     Zone self,
     ZoneDelegate parent,
@@ -2029,67 +2032,3 @@
 ) => Zone.current
     .fork(specification: specification, zoneValues: zoneValues)
     .run<R>(body);
-
-/// Wraps [callback] to be run in [zone], and with errors reported in zone.
-void Function() _guardCallbackInZone(Zone zone, void Function() callback) =>
-    () {
-      _runGuardedInZone<void>(zone, callback);
-    };
-
-/// Wraps [callback] to be run in [zone], and with errors reported in zone.
-void Function(P) _guardUnaryCallbackInZone<P>(
-  Zone zone,
-  void Function(P) callback,
-) => (P argument) {
-  _runUnaryGuardedInZone<P>(zone, callback, argument);
-};
-
-/// Wraps [callback] to be run in [zone], and with errors reported in zone.
-void Function(P1, P2) _guardBinaryCallbackInZone<P1, P2>(
-  Zone zone,
-  void Function(P1, P2) callback,
-) => (P1 argument1, P2 argument2) {
-  _runBinaryGuardedInZone<P1, P2>(zone, callback, argument1, argument2);
-};
-
-void _runGuardedInZone<R>(Zone zone, void Function() callback) {
-  try {
-    zone.run<void>(callback);
-  } catch (e, s) {
-    _handleErrorWithCallback(zone, e, s);
-  }
-}
-
-void _runUnaryGuardedInZone<P>(
-  Zone zone,
-  void Function(P) callback,
-  P argument,
-) {
-  try {
-    zone.runUnary<void, P>(callback, argument);
-  } catch (e, s) {
-    _handleErrorWithCallback(zone, e, s);
-  }
-}
-
-void _runBinaryGuardedInZone<P1, P2>(
-  Zone zone,
-  void Function(P1, P2) callback,
-  P1 argument1,
-  P2 argument2,
-) {
-  try {
-    zone.runBinary<void, P1, P2>(callback, argument1, argument2);
-  } catch (e, s) {
-    _handleErrorWithCallback(zone, e, s);
-  }
-}
-
-void _handleErrorWithCallback(Zone zone, Object error, StackTrace stackTrace) {
-  var replacement = zone.errorCallback(error, stackTrace);
-  if (replacement != null) {
-    error = replacement.error;
-    stackTrace = replacement.stackTrace;
-  }
-  zone.handleUncaughtError(error, stackTrace);
-}
diff --git a/tests/lib/async/zone_register_callback_throws_test.dart b/tests/lib/async/zone_register_callback_throws_test.dart
deleted file mode 100644
index e68bf58..0000000
--- a/tests/lib/async/zone_register_callback_throws_test.dart
+++ /dev/null
@@ -1,381 +0,0 @@
-// Copyright (c) 2025, 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.
-
-import 'package:expect/async_helper.dart';
-import 'package:expect/expect.dart';
-import 'dart:async';
-
-// Regression test for https://dartbug.com/59913
-//
-// The zone registration for `Timer` constructors was not as intended.
-// - The constructor itself called `bind*CallbackGuarded`.
-// - The root zone's `createTimer`/`createPeriodicTimer` did another
-//   `zone.bind*Callback` on the already registered function,
-//   but did not guard it, which is really the most important part.
-// - If a zone intercepts a callback and makes it throw,
-//   the latter `bind` would trigger first and the error would not
-//   be caught, hitting the event loop in the root zone.
-//
-// After fixing this, the `Timer` constructors register the callback,
-// and the root zone's `create*Timer` doesn't register the callback,
-// but it does ensure that it runs in the correct zone, and that
-// uncaught errors are reported in that zone.
-
-void main() {
-  asyncStart();
-  int safeRun = 0;
-  // Not touching binary callbacks, to avoid intercepting error handlers.
-  void zoneTest(
-    void Function() body, {
-    bool wrapLast = false,
-    void Function()? beforeCallback,
-    void Function()? beforeRun,
-    void Function(Object, StackTrace)? onError,
-  }) {
-    asyncStart();
-    // Keep this completer outside of all zone handling.
-    var testStack = StackTrace.current;
-    bool firstRun = true;
-    var zone = Zone.current.fork(
-      specification: ZoneSpecification(
-        registerCallback: <R>(self, parent, zone, f) {
-          R callback() {
-            beforeCallback?.call();
-            return f();
-          }
-
-          if (!wrapLast) {
-            // Parent registers wrapped function.
-            return parent.registerCallback(zone, callback);
-          } else {
-            // Wrapped function wraps parent registration.
-            f = parent.registerCallback(zone, f);
-            return callback;
-          }
-        },
-        registerUnaryCallback: <R, P>(self, parent, zone, f) {
-          R callback(P v) {
-            beforeCallback?.call();
-            return f(v);
-          }
-
-          if (!wrapLast) {
-            // Parent registers wrapped function.
-            return parent.registerUnaryCallback(zone, callback);
-          } else {
-            // Wrapped function wraps parent registration.
-            f = parent.registerUnaryCallback(zone, f);
-            return callback;
-          }
-        },
-        run: <R>(self, parent, zone, f) {
-          // Don't intercept the initial `zone.run` of `body`.
-          if (safeRun == 0) beforeRun?.call();
-          return parent.run(zone, f);
-        },
-        runUnary: <R, P>(self, parent, zone, f, v1) {
-          beforeRun?.call();
-          return parent.runUnary(zone, f, v1);
-        },
-        handleUncaughtError: (self, parent, zone, error, stack) {
-          stack = combineStacks(stack, testStack);
-          if (onError != null) {
-            onError(error, stack);
-          } else {
-            parent.handleUncaughtError(
-              zone,
-              error,
-              combineStacks(StackTrace.current, stack),
-            );
-          }
-        },
-      ),
-    );
-    safeRun++;
-    try {
-      zone.run(body);
-    } finally {
-      safeRun--;
-      asyncEnd();
-    }
-  }
-
-  // Reusable error.
-  Object error = AssertionError("RegisterThrows");
-
-  // Timers and microtasks work.
-  zoneTest(() {
-    asyncStart();
-    Timer(const Duration(milliseconds: 1), () {
-      asyncEnd();
-    });
-  });
-
-  zoneTest(() {
-    asyncStart();
-    Timer.periodic(const Duration(milliseconds: 1), (t) {
-      if (t.tick > 1) {
-        t.cancel();
-        asyncEnd();
-      }
-    });
-  });
-
-  zoneTest(() {
-    asyncStart();
-    scheduleMicrotask(() {
-      asyncEnd();
-    });
-  });
-
-  // If the callback throws, it's caught in the zone.
-  void Function(Object e, StackTrace s) expectError(Object expected) => (
-    Object e,
-    StackTrace s,
-  ) {
-    if (identical(expected, e)) {
-      asyncEnd();
-    } else {
-      Error.throwWithStackTrace(e, combineStacks(StackTrace.current, s));
-    }
-  };
-
-  zoneTest(() {
-    asyncStart();
-    Timer(const Duration(milliseconds: 1), () {
-      throw error;
-    });
-  }, onError: expectError(error));
-
-  zoneTest(() {
-    asyncStart();
-    Timer.periodic(const Duration(milliseconds: 1), (t) {
-      if (t.tick > 1) {
-        t.cancel();
-        throw error;
-      }
-    });
-  }, onError: expectError(error));
-
-  zoneTest(() {
-    asyncStart();
-    scheduleMicrotask(() {
-      throw error;
-    });
-  }, onError: expectError(error));
-
-  void throwError() {
-    throw error;
-  }
-
-  // If the registered function replacement throws,
-  // the error is also caught in the zone's error handler.
-  zoneTest(
-    () {
-      asyncStart();
-      Timer(const Duration(milliseconds: 1), () {
-        Expect.fail("Unreachable");
-      });
-    },
-    beforeCallback: throwError,
-    onError: expectError(error),
-  );
-
-  Timer? periodicTimer;
-  zoneTest(
-    () {
-      asyncStart();
-      periodicTimer = Timer.periodic(const Duration(milliseconds: 1), (t) {
-        Expect.fail("Unreachable");
-      });
-    },
-    beforeCallback: throwError,
-    onError: (e, s) {
-      periodicTimer!.cancel(); // Cancel the timer, otherwise it'll keep firing.
-      expectError(error)(e, s);
-    },
-  );
-
-  zoneTest(
-    () {
-      asyncStart();
-      scheduleMicrotask(() {
-        Expect.fail("Unreachable");
-      });
-    },
-    beforeCallback: throwError,
-    onError: expectError(error),
-  );
-
-  // Calling the zone functions directly does not register the function.
-  zoneTest(() {
-    asyncStart();
-    Zone.current.createTimer(const Duration(milliseconds: 1), () {
-      asyncEnd();
-    });
-  }, beforeCallback: throwError);
-
-  zoneTest(() {
-    asyncStart();
-    Zone.current.createPeriodicTimer(const Duration(milliseconds: 1), (t) {
-      if (t.tick > 1) {
-        t.cancel();
-        asyncEnd();
-      }
-    });
-  }, beforeCallback: throwError);
-
-  zoneTest(() {
-    asyncStart();
-    Zone.current.scheduleMicrotask(() {
-      asyncEnd();
-    });
-  }, beforeCallback: throwError);
-
-  // Nested zones work "as expected".
-
-  var error2 = StateError("Outer zone error");
-
-  // Outer `register*Callback` runs last, wraps (and here throws first).
-  zoneTest(
-    () {
-      asyncStart(4);
-      zoneTest(
-        () {
-          Timer(const Duration(milliseconds: 1), () {
-            Expect.fail("Unreachable");
-          });
-        },
-        beforeCallback: throwError,
-        onError: expectError(error2),
-      );
-      Timer? periodicTimer;
-      zoneTest(
-        () {
-          periodicTimer = Timer.periodic(const Duration(milliseconds: 1), (t) {
-            Expect.fail("Unreachable");
-          });
-        },
-        beforeCallback: throwError,
-        onError: (e, s) {
-          periodicTimer!.cancel();
-          expectError(error2)(e, s);
-        },
-      );
-      zoneTest(
-        () {
-          scheduleMicrotask(() {
-            Expect.fail("Unreachable");
-          });
-        },
-        beforeCallback: throwError,
-        onError: expectError(error2),
-      );
-      asyncEnd();
-    },
-    beforeCallback: () {
-      throw error2;
-    },
-  );
-
-  // Inner `register*Callback` wrapped function runs first if it wants to.
-
-  zoneTest(
-    () {
-      asyncStart(4);
-      zoneTest(
-        wrapLast: true,
-        () {
-          Timer(const Duration(milliseconds: 1), () {
-            Expect.fail("Unreachable");
-          });
-        },
-        beforeCallback: throwError,
-        onError: expectError(error),
-      );
-      Timer? periodicTimer;
-      zoneTest(
-        wrapLast: true,
-        () {
-          periodicTimer = Timer.periodic(const Duration(milliseconds: 1), (t) {
-            Expect.fail("Unreachable");
-          });
-        },
-        beforeCallback: throwError,
-        onError: (e, s) {
-          periodicTimer!.cancel();
-          expectError(error)(e, s);
-        },
-      );
-      zoneTest(
-        wrapLast: true,
-        () {
-          scheduleMicrotask(() {
-            Expect.fail("Unreachable");
-          });
-        },
-        beforeCallback: throwError,
-        onError: expectError(error),
-      );
-      asyncEnd();
-    },
-    beforeCallback: () {
-      throw error2;
-    },
-  );
-
-  // Inner `run*Callback* runs first, and all `run*s` run before the
-  // registered callback.
-  zoneTest(
-    () {
-      asyncStart();
-      zoneTest(
-        () {
-          asyncStart();
-          Timer(const Duration(milliseconds: 1), () {
-            Expect.fail("Unreachable");
-          });
-        },
-        beforeRun: throwError,
-        onError: expectError(error),
-      );
-      Timer? periodicTimer;
-      zoneTest(
-        () {
-          asyncStart();
-          periodicTimer = Timer.periodic(const Duration(milliseconds: 1), (t) {
-            Expect.fail("Unreachable");
-          });
-        },
-        beforeRun: throwError,
-        onError: (e, s) {
-          periodicTimer!.cancel();
-          expectError(error)(e, s);
-        },
-      );
-      zoneTest(
-        () {
-          asyncStart();
-          scheduleMicrotask(() {
-            Expect.fail("Unreachable");
-          });
-        },
-        beforeRun: throwError,
-        onError: expectError(error),
-      );
-      asyncEnd();
-    },
-    beforeCallback: () {
-      throw error2;
-    },
-    beforeRun: () {
-      throw error2;
-    },
-  );
-
-  asyncEnd();
-}
-
-StackTrace combineStacks(StackTrace s1, StackTrace s2) =>
-    StackTrace.fromString("${s1}caused by:\n$s2");