Version 2.14.0-33.0.dev

Merge commit '5776d576a094191a120839718952ad91d236d424' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a45fa7a..7bf5a3a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
 
 ### Core libraries
 
+#### `dart:async`
+
+* The uncaught error handlers of `Zone`s are now run in the parent zone
+  of the zone where they were declared. This prevents a throwing handler
+  from causing an infinite loop by repeatedly triggering itself.
+
 #### `dart:core`
 
 *   The native `DateTime` class now better handles local time around
diff --git a/sdk/lib/async/zone.dart b/sdk/lib/async/zone.dart
index 9301ce3..fe6dcf0 100644
--- a/sdk/lib/async/zone.dart
+++ b/sdk/lib/async/zone.dart
@@ -17,6 +17,15 @@
 ///
 /// The [error] and [stackTrace] are the error and stack trace that
 /// was uncaught in [zone].
+///
+/// The function must only access zone-related functionality through
+/// [self], [parent] or [zone].
+/// It should not depend on the current zone ([Zone.current]).
+///
+/// If the uncaught error handler throws, the error will be passed
+/// to `parent.handleUncaughtError`. If the thrown object is [error],
+/// the throw is considered a re-throw and the original [stackTrace]
+/// is retained. This allows an asynchronous error to leave the error zone.
 typedef HandleUncaughtErrorHandler = void Function(Zone self,
     ZoneDelegate parent, Zone zone, Object error, StackTrace stackTrace);
 
@@ -34,6 +43,10 @@
 /// to call [f] in the current zone, [zone].
 /// A custom handler can do things before, after or instead of
 /// 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() f);
 
@@ -51,6 +64,10 @@
 /// to call [f] with argument [arg] in the current zone, [zone].
 /// A custom handler can do things before, after or instead of
 /// 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 RunUnaryHandler = R Function<R, T>(
     Zone self, ZoneDelegate parent, Zone zone, R Function(T arg) f, T arg);
 
@@ -68,6 +85,10 @@
 /// to call [f] with arguments [arg1] and [arg2] in the current zone, [zone].
 /// A custom handler can do things before, after or instead of
 /// calling [f].
+///
+/// The function must only access zone-related functionality through
+/// [self], [parent] or [zone].
+/// It should not depend on the current zone ([Zone.current]).
 typedef RunBinaryHandler = R Function<R, T1, T2>(Zone self, ZoneDelegate parent,
     Zone zone, R Function(T1 arg1, T2 arg2) f, T1 arg1, T2 arg2);
 
@@ -85,6 +106,10 @@
 /// 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 = ZoneCallback<R> Function<R>(
     Zone self, ZoneDelegate parent, Zone zone, R Function() f);
 
@@ -102,6 +127,10 @@
 /// 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 = ZoneUnaryCallback<R, T> Function<R, T>(
     Zone self, ZoneDelegate parent, Zone zone, R Function(T arg) f);
 
@@ -137,6 +166,12 @@
 /// to replace the original error and stack trace,
 /// or an [AsyncError] containing a replacement error and stack trace
 /// which will be used to replace the originals.
+///
+/// The error callback handler must not throw.
+///
+/// The function must only access zone-related functionality through
+/// [self], [parent] or [zone].
+/// It should not depend on the current zone ([Zone.current]).
 typedef AsyncError? ErrorCallbackHandler(Zone self, ZoneDelegate parent,
     Zone zone, Object error, StackTrace? stackTrace);
 
@@ -155,6 +190,10 @@
 /// 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.
+///
+/// The function must only access zone-related functionality through
+/// [self], [parent] or [zone].
+/// It should not depend on the current zone ([Zone.current]).
 typedef void ScheduleMicrotaskHandler(
     Zone self, ZoneDelegate parent, Zone zone, void f());
 
@@ -177,6 +216,10 @@
 ///
 /// The function should return a [Timer] object which can be used
 /// to inspect and control the scheduled timer callback.
+///
+/// The function must only access zone-related functionality through
+/// [self], [parent] or [zone].
+/// It should not depend on the current zone ([Zone.current]).
 typedef Timer CreateTimerHandler(
     Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f());
 
@@ -199,6 +242,10 @@
 ///
 /// The function should return a [Timer] object which can be used
 /// to inspect and control the scheduled timer callbacks.
+///
+/// The function must only access zone-related functionality through
+/// [self], [parent] or [zone].
+/// It should not depend on the current zone ([Zone.current]).
 typedef Timer CreatePeriodicTimerHandler(Zone self, ZoneDelegate parent,
     Zone zone, Duration period, void f(Timer timer));
 
@@ -214,6 +261,10 @@
 ///
 /// The custom handler can intercept print operations and
 /// redirect them to other targets than the console.
+///
+/// The function must only access zone-related functionality through
+/// [self], [parent] or [zone].
+/// It should not depend on the current zone ([Zone.current]).
 typedef void PrintHandler(
     Zone self, ZoneDelegate parent, Zone zone, String line);
 
@@ -235,6 +286,10 @@
 /// values before calling `parent.fork(zone, specification, zoneValues)`,
 /// but it has to call the [parent]'s [ZoneDelegate.fork] in order
 /// to create a valid [Zone] object.
+///
+/// The function must only access zone-related functionality through
+/// [self], [parent] or [zone].
+/// It should not depend on the current zone ([Zone.current]).
 typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
     ZoneSpecification? specification, Map<Object?, Object?>? zoneValues);
 
@@ -915,10 +970,7 @@
   _ZoneDelegate(this._delegationTarget);
 
   void handleUncaughtError(Zone zone, Object error, StackTrace stackTrace) {
-    var implementation = _delegationTarget._handleUncaughtError;
-    _Zone implZone = implementation.zone;
-    HandleUncaughtErrorHandler handler = implementation.function;
-    return handler(implZone, implZone._parentDelegate, zone, error, stackTrace);
+    _delegationTarget._processUncaughtError(zone, error, stackTrace);
   }
 
   R run<R>(Zone zone, R f()) {
@@ -1040,6 +1092,28 @@
     return identical(this, otherZone) ||
         identical(errorZone, otherZone.errorZone);
   }
+
+  void _processUncaughtError(Zone zone, Object error, StackTrace stackTrace) {
+    var implementation = _handleUncaughtError;
+    _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;
+    try {
+      Zone._current = parentZone;
+      handler(implZone, parentDelegate, zone, error, stackTrace);
+      Zone._current = currentZone;
+    } catch (e, s) {
+      Zone._current = currentZone;
+      parentZone._processUncaughtError(
+          implZone, e, identical(error, e) ? stackTrace : s);
+    }
+  }
 }
 
 class _CustomZone extends _Zone {
@@ -1235,11 +1309,7 @@
   // Methods that can be customized by the zone specification.
 
   void handleUncaughtError(Object error, StackTrace stackTrace) {
-    var implementation = this._handleUncaughtError;
-    ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
-    HandleUncaughtErrorHandler handler = implementation.function;
-    return handler(
-        implementation.zone, parentDelegate, this, error, stackTrace);
+    _processUncaughtError(this, error, stackTrace);
   }
 
   Zone fork(
@@ -1335,6 +1405,10 @@
 
 void _rootHandleUncaughtError(Zone? self, ZoneDelegate? parent, Zone zone,
     Object error, StackTrace stackTrace) {
+  _rootHandleError(error, stackTrace);
+}
+
+void _rootHandleError(Object error, StackTrace stackTrace) {
   _schedulePriorityAsyncCallback(() {
     _rethrow(error, stackTrace);
   });
diff --git a/tests/lib/async/uncaught_error_handler_throws_test.dart b/tests/lib/async/uncaught_error_handler_throws_test.dart
new file mode 100644
index 0000000..f70f098
--- /dev/null
+++ b/tests/lib/async/uncaught_error_handler_throws_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2021, 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/expect.dart';
+import 'package:async_helper/async_helper.dart';
+import 'dart:async';
+
+void main() async {
+  asyncStart();
+  await testThrowSame();
+  await testThrowOther();
+  asyncEnd();
+}
+
+Future<void> testThrowSame() async {
+  asyncStart();
+  var object1 = Object();
+  var stack1 = StackTrace.current;
+  var outerZone = Zone.current;
+  var firstZone = Zone.current.fork(specification: onError((error, stack) {
+    // Uncaught error handlers run in the parent zone.
+    Expect.identical(outerZone, Zone.current);
+    Expect.identical(object1, error);
+    Expect.identical(stack1, stack); // Get same stack trace.
+    asyncEnd();
+  }));
+  firstZone.run(() async {
+    Expect.identical(firstZone, Zone.current);
+    var secondZone = Zone.current.fork(specification: onError((error, stack) {
+      // Uncaught error handlers run in the parent zone.
+      Expect.identical(firstZone, Zone.current);
+      Expect.identical(object1, error);
+      Expect.identical(stack1, stack);
+      throw error; // Throw same object
+    }));
+    secondZone.run(() async {
+      Expect.identical(secondZone, Zone.current);
+      Future.error(object1, stack1); // Unhandled async error.
+      await Future(() {});
+    });
+  });
+}
+
+Future<void> testThrowOther() async {
+  asyncStart();
+  var object1 = Object();
+  var object2 = Object();
+  var stack1 = StackTrace.current;
+  var outerZone = Zone.current;
+  var firstZone = Zone.current.fork(specification: onError((error, stack) {
+    Expect.identical(outerZone, Zone.current);
+    Expect.identical(object2, error);
+    Expect.notIdentical(stack1, stack); // Get different stack trace.
+    asyncEnd();
+  }));
+  firstZone.run(() async {
+    Expect.identical(firstZone, Zone.current);
+    var secondZone = Zone.current.fork(specification: onError((error, stack) {
+      Expect.identical(firstZone, Zone.current);
+      Expect.identical(object1, error);
+      Expect.identical(stack1, stack);
+      throw object2; // Throw different object
+    }));
+    secondZone.run(() async {
+      Expect.identical(secondZone, Zone.current);
+      Future.error(object1, stack1); // Unhandled async error.
+      await Future(() {});
+    });
+  });
+}
+
+ZoneSpecification onError(void Function(Object, StackTrace) handler) {
+  return ZoneSpecification(
+      handleUncaughtError: (s, p, z, e, st) => handler(e, st));
+}
diff --git a/tests/lib_2/async/slow_consumer2_test.dart b/tests/lib_2/async/slow_consumer2_test.dart
index de37668..23a001a 100644
--- a/tests/lib_2/async/slow_consumer2_test.dart
+++ b/tests/lib_2/async/slow_consumer2_test.dart
@@ -88,7 +88,7 @@
       listSize -= sentCount - targetCount;
       sentCount = targetCount;
     }
-    controller.add(new List(listSize));
+    controller.add(new List<int>(listSize));
     int ms = listSize * 1000 ~/ bytesPerSecond;
     Duration duration = new Duration(milliseconds: ms);
     if (!controller.isPaused) new Timer(duration, send);
diff --git a/tests/lib_2/async/uncaught_error_handler_throws_test.dart b/tests/lib_2/async/uncaught_error_handler_throws_test.dart
new file mode 100644
index 0000000..f70f098
--- /dev/null
+++ b/tests/lib_2/async/uncaught_error_handler_throws_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2021, 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/expect.dart';
+import 'package:async_helper/async_helper.dart';
+import 'dart:async';
+
+void main() async {
+  asyncStart();
+  await testThrowSame();
+  await testThrowOther();
+  asyncEnd();
+}
+
+Future<void> testThrowSame() async {
+  asyncStart();
+  var object1 = Object();
+  var stack1 = StackTrace.current;
+  var outerZone = Zone.current;
+  var firstZone = Zone.current.fork(specification: onError((error, stack) {
+    // Uncaught error handlers run in the parent zone.
+    Expect.identical(outerZone, Zone.current);
+    Expect.identical(object1, error);
+    Expect.identical(stack1, stack); // Get same stack trace.
+    asyncEnd();
+  }));
+  firstZone.run(() async {
+    Expect.identical(firstZone, Zone.current);
+    var secondZone = Zone.current.fork(specification: onError((error, stack) {
+      // Uncaught error handlers run in the parent zone.
+      Expect.identical(firstZone, Zone.current);
+      Expect.identical(object1, error);
+      Expect.identical(stack1, stack);
+      throw error; // Throw same object
+    }));
+    secondZone.run(() async {
+      Expect.identical(secondZone, Zone.current);
+      Future.error(object1, stack1); // Unhandled async error.
+      await Future(() {});
+    });
+  });
+}
+
+Future<void> testThrowOther() async {
+  asyncStart();
+  var object1 = Object();
+  var object2 = Object();
+  var stack1 = StackTrace.current;
+  var outerZone = Zone.current;
+  var firstZone = Zone.current.fork(specification: onError((error, stack) {
+    Expect.identical(outerZone, Zone.current);
+    Expect.identical(object2, error);
+    Expect.notIdentical(stack1, stack); // Get different stack trace.
+    asyncEnd();
+  }));
+  firstZone.run(() async {
+    Expect.identical(firstZone, Zone.current);
+    var secondZone = Zone.current.fork(specification: onError((error, stack) {
+      Expect.identical(firstZone, Zone.current);
+      Expect.identical(object1, error);
+      Expect.identical(stack1, stack);
+      throw object2; // Throw different object
+    }));
+    secondZone.run(() async {
+      Expect.identical(secondZone, Zone.current);
+      Future.error(object1, stack1); // Unhandled async error.
+      await Future(() {});
+    });
+  });
+}
+
+ZoneSpecification onError(void Function(Object, StackTrace) handler) {
+  return ZoneSpecification(
+      handleUncaughtError: (s, p, z, e, st) => handler(e, st));
+}
diff --git a/tools/VERSION b/tools/VERSION
index e2b1bd9..1a63460 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 32
+PRERELEASE 33
 PRERELEASE_PATCH 0
\ No newline at end of file