Version 2.15.0-295.0.dev

Merge commit 'dab9d70e97a64b1ad326db40e08b5c24a2e33737' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81152a0..4a37ecd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -309,6 +309,9 @@
   ready to be removed.
   Code catching the class should move to catching `Error` instead
   (or, for integers, check first for whether it's dividing by zero).
+- Add `Error.throwWithStackTrace` which can `throw` an
+  error with an existing stack trace, instead of creating
+  a new stack trace.
 
 #### `dart:io`
 
diff --git a/pkg/analyzer_utilities/lib/check/check.dart b/pkg/analyzer_utilities/lib/check/check.dart
index 8d3cc4d..b3147bb 100644
--- a/pkg/analyzer_utilities/lib/check/check.dart
+++ b/pkg/analyzer_utilities/lib/check/check.dart
@@ -5,6 +5,7 @@
 import 'package:analyzer_utilities/check/check_target.dart';
 import 'package:meta/meta.dart';
 
+export 'package:analyzer_utilities/check/bool.dart';
 export 'package:analyzer_utilities/check/check_target.dart';
 export 'package:analyzer_utilities/check/equality.dart';
 export 'package:analyzer_utilities/check/int.dart';
diff --git a/pkg/analyzer_utilities/lib/check/iterable.dart b/pkg/analyzer_utilities/lib/check/iterable.dart
index fbadfd9..0e3e0d3 100644
--- a/pkg/analyzer_utilities/lib/check/iterable.dart
+++ b/pkg/analyzer_utilities/lib/check/iterable.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer_utilities/check/check.dart';
+import 'package:meta/meta.dart';
 
 extension IterableExtension<T> on CheckTarget<Iterable<T>> {
   void get isEmpty {
@@ -10,4 +11,21 @@
       fail('is not empty');
     }
   }
+
+  void get isNotEmpty {
+    if (value.isEmpty) {
+      fail('is empty');
+    }
+  }
+
+  @UseResult.unless(parameterDefined: 'expected')
+  CheckTarget<int> hasLength([int? expected]) {
+    var actual = value.length;
+
+    if (expected != null && actual != expected) {
+      fail('does not have length ${valueStr(expected)}');
+    }
+
+    return nest(actual, (length) => 'has length $length');
+  }
 }
diff --git a/pkg/analyzer_utilities/lib/check/string.dart b/pkg/analyzer_utilities/lib/check/string.dart
index c4dfdf7..3d80003 100644
--- a/pkg/analyzer_utilities/lib/check/string.dart
+++ b/pkg/analyzer_utilities/lib/check/string.dart
@@ -18,11 +18,14 @@
     }
   }
 
-  @useResult
-  CheckTarget<int> hasLength() {
-    return nest(
-      value.length,
-      (length) => 'has length $length',
-    );
+  @UseResult.unless(parameterDefined: 'expected')
+  CheckTarget<int> hasLength([int? expected]) {
+    var actual = value.length;
+
+    if (expected != null && actual != expected) {
+      fail('does not have length ${valueStr(expected)}');
+    }
+
+    return nest(actual, (length) => 'has length $length');
   }
 }
diff --git a/pkg/analyzer_utilities/test/check/check_test.dart b/pkg/analyzer_utilities/test/check/check_test.dart
new file mode 100644
index 0000000..532eea7
--- /dev/null
+++ b/pkg/analyzer_utilities/test/check/check_test.dart
@@ -0,0 +1,166 @@
+// 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:analyzer_utilities/check/check.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('type', () {
+    group('bool', () {
+      test('isEqualTo', () {
+        check(true).isEqualTo(true);
+        check(false).isEqualTo(false);
+        _fails(() => check(true).isEqualTo(false));
+        _fails(() => check(false).isEqualTo(true));
+      });
+      test('isFalse', () {
+        check(false).isFalse;
+        _fails(() => check(true).isFalse);
+      });
+      test('isNotEqualTo', () {
+        check(true).isNotEqualTo(false);
+        check(false).isNotEqualTo(true);
+        _fails(() => check(true).isNotEqualTo(true));
+        _fails(() => check(false).isNotEqualTo(false));
+      });
+      test('isTrue', () {
+        check(true).isTrue;
+        _fails(() => check(false).isTrue);
+      });
+    });
+    group('int', () {
+      test('isEqualTo', () {
+        check(0).isEqualTo(0);
+        check(1).isEqualTo(1);
+        check(2).isEqualTo(2);
+        _fails(() => check(0).isEqualTo(1));
+        _fails(() => check(1).isEqualTo(0));
+      });
+      test('isGreaterThan', () {
+        check(2).isGreaterThan(1);
+        check(1).isGreaterThan(0);
+        check(-1).isGreaterThan(-2);
+        _fails(() => check(0).isGreaterThan(0));
+        _fails(() => check(0).isGreaterThan(1));
+        _fails(() => check(1).isGreaterThan(2));
+        _fails(() => check(-2).isGreaterThan(-1));
+      });
+      test('isNotEqualTo', () {
+        check(0).isNotEqualTo(1);
+        check(1).isNotEqualTo(0);
+        check(1).isNotEqualTo(2);
+        check(2).isNotEqualTo(1);
+        _fails(() => check(0).isNotEqualTo(0));
+        _fails(() => check(1).isNotEqualTo(1));
+        _fails(() => check(2).isNotEqualTo(2));
+      });
+      test('isZero', () {
+        check(0).isZero;
+        _fails(() => check(1).isZero);
+        _fails(() => check(-1).isZero);
+      });
+    });
+    group('Iterable', () {
+      test('hasLength', () {
+        check(<int>[]).hasLength().isZero;
+        check(<int>[0]).hasLength().isEqualTo(1);
+        check(<int>[0]).hasLength(1);
+        check(<int>[0, 1]).hasLength().isEqualTo(2);
+        check(<int>[0, 1]).hasLength(2);
+        check(<int>{}).hasLength().isZero;
+        check(<int>{0}).hasLength().isEqualTo(1);
+        check(<int>{0}).hasLength(1);
+        check(<int>{0, 1}).hasLength().isEqualTo(2);
+        check(<int>{0, 1}).hasLength(2);
+        _fails(() => check(<int>[]).hasLength(1));
+        _fails(() => check(<int>[]).hasLength(2));
+        _fails(() => check(<int>{}).hasLength(1));
+        _fails(() => check(<int>{}).hasLength(2));
+        _fails(() => check(<int>[]).hasLength().isEqualTo(1));
+        _fails(() => check(<int>[0]).hasLength().isEqualTo(0));
+      });
+      test('isEmpty', () {
+        check(<int>[]).isEmpty;
+        check(<int>{}).isEmpty;
+        _fails(() => check([0]).isEmpty);
+        _fails(() => check([0, 1]).isEmpty);
+        _fails(() => check({0}).isEmpty);
+        _fails(() => check({0, 1}).isEmpty);
+      });
+      test('isNotEmpty', () {
+        check([0]).isNotEmpty;
+        check([0, 1]).isNotEmpty;
+        check({0}).isNotEmpty;
+        check({0, 1}).isNotEmpty;
+        _fails(() => check(<int>[]).isNotEmpty);
+        _fails(() => check(<int>{}).isNotEmpty);
+      });
+    });
+    group('String', () {
+      test('contains', () {
+        check('abc').contains('a');
+        check('abc').contains('b');
+        check('abc').contains('c');
+        check('abc').contains('ab');
+        check('abc').contains('bc');
+        check('abc').contains(RegExp('a'));
+        check('abc').contains(RegExp('a.'));
+        check('abc').contains(RegExp('a.c'));
+        check('abc').contains(RegExp('.b.'));
+        _fails(() => check('abc').contains('x'));
+        _fails(() => check('abc').contains('ac'));
+        _fails(() => check('abc').contains(RegExp('ac.')));
+      });
+      test('hasLength', () {
+        check('').hasLength().isZero;
+        check('').hasLength(0);
+        check('a').hasLength().isEqualTo(1);
+        check('a').hasLength(1);
+        check('abc').hasLength().isEqualTo(3);
+        check('abc').hasLength(3);
+        _fails(() => check('abc').hasLength(0));
+        _fails(() => check('abc').hasLength(1));
+        _fails(() => check('abc').hasLength(2));
+      });
+      test('isEqualTo', () {
+        check('').isEqualTo('');
+        check('abc').isEqualTo('abc');
+        check('foobar').isEqualTo('foobar');
+        _fails(() => check('abc').isEqualTo('ab'));
+        _fails(() => check('abc').isEqualTo('xyz'));
+      });
+      test('isNotEqualTo', () {
+        check('abc').isNotEqualTo('ab');
+        check('abc').isNotEqualTo('xyz');
+        _fails(() => check('abc').isNotEqualTo('abc'));
+        _fails(() => check('foobar').isNotEqualTo('foobar'));
+      });
+      test('startsWith', () {
+        check('abc').startsWith('a');
+        check('abc').startsWith('ab');
+        check('abc').startsWith('abc');
+        check('abc').startsWith(RegExp('..c'));
+        check('abc').startsWith(RegExp('.*c'));
+        _fails(() => check('abc').startsWith('b'));
+        _fails(() => check('abc').startsWith('x'));
+        _fails(() => check('abc').startsWith(RegExp('.c')));
+      });
+    });
+    group('type', () {
+      test('isA', () {
+        check(0).isA<int>();
+        _fails(() => check(0.0 as dynamic).isA<int>());
+      });
+    });
+  });
+}
+
+void _fails(void Function() f) {
+  try {
+    f();
+  } on TestFailure {
+    return;
+  }
+  fail('expected to fail');
+}
diff --git a/runtime/lib/errors.cc b/runtime/lib/errors.cc
index 42c0acc..7a3415a 100644
--- a/runtime/lib/errors.cc
+++ b/runtime/lib/errors.cc
@@ -220,10 +220,10 @@
 }
 
 // Rethrow an error with a stacktrace.
-DEFINE_NATIVE_ENTRY(Async_rethrow, 0, 2) {
+DEFINE_NATIVE_ENTRY(Error_throwWithStackTrace, 0, 2) {
   GET_NON_NULL_NATIVE_ARGUMENT(Instance, error, arguments->NativeArgAt(0));
   GET_NON_NULL_NATIVE_ARGUMENT(Instance, stacktrace, arguments->NativeArgAt(1));
-  Exceptions::ReThrow(thread, error, stacktrace);
+  Exceptions::ThrowWithStackTrace(thread, error, stacktrace);
   return Object::null();
 }
 
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index 6f6d67d..46b44a3 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -160,7 +160,7 @@
   V(DateTime_localTimeZoneAdjustmentInSeconds, 0)                              \
   V(AssertionError_throwNew, 3)                                                \
   V(AssertionError_throwNewSource, 4)                                          \
-  V(Async_rethrow, 2)                                                          \
+  V(Error_throwWithStackTrace, 2)                                              \
   V(StackTrace_current, 0)                                                     \
   V(TypeError_throwNew, 4)                                                     \
   V(FallThroughError_throwNew, 1)                                              \
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index 2ea7550..95787d7 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -696,8 +696,9 @@
   if (exception.IsNull()) {
     exception ^=
         Exceptions::Create(Exceptions::kNullThrown, Object::empty_array());
-  } else if (exception.ptr() == object_store->out_of_memory() ||
-             exception.ptr() == object_store->stack_overflow()) {
+  } else if (existing_stacktrace.IsNull() &&
+             (exception.ptr() == object_store->out_of_memory() ||
+              exception.ptr() == object_store->stack_overflow())) {
     use_preallocated_stacktrace = true;
   }
   // Find the exception handler and determine if the handler needs a
@@ -729,11 +730,17 @@
     }
   } else {
     if (!existing_stacktrace.IsNull()) {
-      // If we have an existing stack trace then this better be a rethrow. The
-      // reverse is not necessarily true (e.g. Dart_PropagateError can cause
-      // a rethrow being called without an existing stacktrace.)
-      ASSERT(is_rethrow);
       stacktrace = existing_stacktrace.ptr();
+      // If this is not a rethrow, it's a "throw with stacktrace".
+      // Set an Error object's stackTrace field if needed.
+      if (!is_rethrow) {
+        const Field& stacktrace_field =
+            Field::Handle(zone, LookupStackTraceField(exception));
+        if (!stacktrace_field.IsNull() &&
+            (exception.GetField(stacktrace_field) == Object::null())) {
+          exception.SetField(stacktrace_field, stacktrace);
+        }
+      }
     } else {
       // Get stacktrace field of class Error to determine whether we have a
       // subclass of Error which carries around its stack trace.
@@ -917,6 +924,13 @@
   ThrowExceptionHelper(thread, exception, stacktrace, true);
 }
 
+void Exceptions::ThrowWithStackTrace(Thread* thread,
+                                     const Instance& exception,
+                                     const Instance& stacktrace) {
+  // Null object is a valid exception object.
+  ThrowExceptionHelper(thread, exception, stacktrace, false);
+}
+
 void Exceptions::PropagateError(const Error& error) {
   ASSERT(!error.IsNull());
   Thread* thread = Thread::Current();
diff --git a/runtime/vm/exceptions.h b/runtime/vm/exceptions.h
index 090615c..89090a6 100644
--- a/runtime/vm/exceptions.h
+++ b/runtime/vm/exceptions.h
@@ -32,6 +32,9 @@
   DART_NORETURN static void ReThrow(Thread* thread,
                                     const Instance& exception,
                                     const Instance& stacktrace);
+  DART_NORETURN static void ThrowWithStackTrace(Thread* thread,
+                                                const Instance& exception,
+                                                const Instance& stacktrace);
   DART_NORETURN static void PropagateError(const Error& error);
 
   // Propagate an error to the entry frame, skipping over Dart frames.
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart
index 2a991c9..b9cf805 100644
--- a/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart
+++ b/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart
@@ -195,11 +195,6 @@
   }
 }
 
-@patch
-void _rethrow(Object error, StackTrace stackTrace) {
-  JS('', 'throw #', dart.createErrorWithStack(error, stackTrace));
-}
-
 /// Used by the compiler to implement `async*` functions.
 ///
 /// This is inspired by _AsyncStarStreamController in dart-lang/sdk's
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart
index e07844b..73637d7 100644
--- a/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart
+++ b/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart
@@ -282,6 +282,12 @@
 
   @patch
   StackTrace? get stackTrace => dart.stackTraceForError(this);
+
+  @patch
+  static Never _throw(Object error, StackTrace stackTrace) {
+    JS("", "throw #", dart.createErrorWithStack(error, stackTrace));
+    throw "unreachable";
+  }
 }
 
 @patch
@@ -592,9 +598,7 @@
   }
 
   static String _stringFromJSArray(
-      /*=JSArray<int>*/ list,
-      int start,
-      int? endOrNull) {
+      /*=JSArray<int>*/ list, int start, int? endOrNull) {
     int len = list.length;
     int end = RangeError.checkValidRange(start, endOrNull, len);
     if (start > 0 || end < len) {
diff --git a/sdk/lib/_internal/js_runtime/lib/async_patch.dart b/sdk/lib/_internal/js_runtime/lib/async_patch.dart
index 293a271..4888baf 100644
--- a/sdk/lib/_internal/js_runtime/lib/async_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/async_patch.dart
@@ -704,10 +704,3 @@
   Iterator<T> get iterator =>
       new _SyncStarIterator<T>(JS('', '#()', _outerHelper));
 }
-
-@patch
-void _rethrow(Object error, StackTrace stackTrace) {
-  error = wrapException(error);
-  JS('void', '#.stack = #', error, stackTrace.toString());
-  JS('void', 'throw #', error);
-}
diff --git a/sdk/lib/_internal/js_runtime/lib/core_patch.dart b/sdk/lib/_internal/js_runtime/lib/core_patch.dart
index 5726f96..57cdac3 100644
--- a/sdk/lib/_internal/js_runtime/lib/core_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/core_patch.dart
@@ -22,7 +22,8 @@
         Primitives,
         quoteStringForRegExp,
         getTraceFromException,
-        RuntimeError;
+        RuntimeError,
+        wrapException;
 
 import 'dart:_foreign_helper' show JS;
 import 'dart:_native_typed_data' show NativeUint8List;
@@ -187,6 +188,14 @@
 
   @patch
   StackTrace? get stackTrace => Primitives.extractStackTrace(this);
+
+  @patch
+  static Never _throw(Object error, StackTrace stackTrace) {
+    error = wrapException(error);
+    JS('void', '#.stack = #', error, stackTrace.toString());
+    JS('', 'throw #', error);
+    throw "unreachable";
+  }
 }
 
 @patch
diff --git a/sdk/lib/_internal/vm/lib/async_patch.dart b/sdk/lib/_internal/vm/lib/async_patch.dart
index 4e56a79..363ae71 100644
--- a/sdk/lib/_internal/vm/lib/async_patch.dart
+++ b/sdk/lib/_internal/vm/lib/async_patch.dart
@@ -245,10 +245,6 @@
 }
 
 @patch
-@pragma("vm:external-name", "Async_rethrow")
-external void _rethrow(Object error, StackTrace stackTrace);
-
-@patch
 class _StreamImpl<T> {
   /// The closure implementing the async-generator body that is creating events
   /// for this stream.
diff --git a/sdk/lib/_internal/vm/lib/errors_patch.dart b/sdk/lib/_internal/vm/lib/errors_patch.dart
index 0e3a676..aaaf11f 100644
--- a/sdk/lib/_internal/vm/lib/errors_patch.dart
+++ b/sdk/lib/_internal/vm/lib/errors_patch.dart
@@ -21,6 +21,10 @@
 
   @pragma("vm:entry-point")
   StackTrace? _stackTrace;
+
+  @patch
+  @pragma("vm:external-name", "Error_throwWithStackTrace")
+  external static Never _throw(Object error, StackTrace stackTrace);
 }
 
 class _AssertionError extends Error implements AssertionError {
diff --git a/sdk/lib/async/zone.dart b/sdk/lib/async/zone.dart
index e206152..cb63080 100644
--- a/sdk/lib/async/zone.dart
+++ b/sdk/lib/async/zone.dart
@@ -1410,12 +1410,10 @@
 
 void _rootHandleError(Object error, StackTrace stackTrace) {
   _schedulePriorityAsyncCallback(() {
-    _rethrow(error, stackTrace);
+    Error.throwWithStackTrace(error, stackTrace);
   });
 }
 
-external void _rethrow(Object error, StackTrace stackTrace);
-
 R _rootRun<R>(Zone? self, ZoneDelegate? parent, Zone zone, R f()) {
   if (identical(Zone._current, zone)) return f();
 
@@ -1669,7 +1667,7 @@
   // Methods that can be customized by the zone specification.
 
   void handleUncaughtError(Object error, StackTrace stackTrace) {
-    _rootHandleUncaughtError(null, null, this, error, stackTrace);
+    _rootHandleError(error, stackTrace);
   }
 
   Zone fork(
diff --git a/sdk/lib/core/errors.dart b/sdk/lib/core/errors.dart
index 3770a2b..57b3afe 100644
--- a/sdk/lib/core/errors.dart
+++ b/sdk/lib/core/errors.dart
@@ -92,6 +92,23 @@
   /// trace filled in the first time they are thrown by a `throw`
   /// expression.
   external StackTrace? get stackTrace;
+
+  /// Throws [error] with associated stack trace [stackTrace].
+  ///
+  /// If [error] extends [Error] and has not yet been thrown,
+  /// its [stackTrace] is set as well, just as if it was thrown by a `throw`.
+  /// The actual stack trace captured along with the [error],
+  /// or set on [error] if it is an [Error],
+  /// may not be the [stackTrace] object itself,
+  /// but will be a stack trace with the same content.
+  @Since("2.15")
+  static Never throwWithStackTrace(Object error, StackTrace stackTrace) {
+    checkNotNullable(error, "error");
+    checkNotNullable(stackTrace, "stackTrace");
+    _throw(error, stackTrace);
+  }
+
+  external static Never _throw(Object error, StackTrace stackTrace);
 }
 
 /// Error thrown by the runtime system when an assert statement fails.
diff --git a/tests/corelib/error_throw_with_stacktrace_test.dart b/tests/corelib/error_throw_with_stacktrace_test.dart
new file mode 100644
index 0000000..026df50
--- /dev/null
+++ b/tests/corelib/error_throw_with_stacktrace_test.dart
@@ -0,0 +1,210 @@
+// 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 "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+// Test of Error.throwWithStackTrace.
+
+main() {
+  // Ensure `systemStack` is different from any other stack tracek.
+  var systemStack = (() => StackTrace.current)();
+
+  // Test that an error can be thrown with a system stack trace..
+  {
+    var error = ArgumentError("e1");
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+      Expect.fail("Didn't throw: e1.1");
+    } on Error catch (e, s) {
+      Expect.identical(error, e, "e1.2");
+      // No not expect *identical* stack trace objects.
+      Expect.equals("$systemStack", "$s", "e1.3");
+      Expect.isNotNull(error.stackTrace, "e1.4");
+      Expect.equals("$systemStack", "${error.stackTrace}", "e1.5");
+    }
+  }
+
+  // Test that an error can be thrown with a user-created stack trace..
+  {
+    var stringStack = StackTrace.fromString("Nonce");
+    var error = ArgumentError("e2");
+    try {
+      Error.throwWithStackTrace(error, stringStack);
+      Expect.fail("Didn't throw: e2.1");
+    } on Error catch (e, s) {
+      Expect.identical(error, e, "e2.2");
+      // No not expect *identical* stack trace objects.
+      Expect.equals("$stringStack", "$s", "e2.3");
+      Expect.isNotNull(error.stackTrace, "e2.4");
+      Expect.equals("$stringStack", "${error.stackTrace}", "e2.5");
+    }
+  }
+
+  // Test that a non-error object can be thrown too.
+  {
+    var exception = FormatException("e3");
+    try {
+      Error.throwWithStackTrace(exception, systemStack);
+      Expect.fail("Didn't throw: e3.1");
+    } on Exception catch (e, s) {
+      Expect.identical(exception, e, "e3.2");
+      // No not expect *identical* stack trace objects.
+      Expect.equals("$systemStack", "$s", "e3.3");
+    }
+  }
+
+  // Test that an [Error] not extending {Error} can be thrown,
+  // but doesn't (and cannot) set the stack trace.
+  {
+    var error = CustomError("e4");
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+      Expect.fail("Didn't throw: e4.1");
+    } on Error catch (e, s) {
+      Expect.identical(error, e, "e4.2");
+      // No not expect *identical* stack trace objects.
+      Expect.equals("$systemStack", "$s", "e4.3");
+      Expect.isNull(error.stackTrace, "e4.4");
+    }
+  }
+
+  // Test that an already set stack trace isn't changed.
+  {
+    var error = ArgumentError("e5");
+    StackTrace? originalStack;
+    try {
+      throw error;
+    } on Error catch (e) {
+      originalStack = e.stackTrace;
+    }
+    Expect.isNotNull(originalStack);
+    Expect.notIdentical(originalStack, systemStack);
+    Expect.notEquals("$originalStack", "");
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+      Expect.fail("Didn't throw: e5.1");
+    } on Error catch (e, s) {
+      Expect.identical(error, e, "e5.2");
+      // No not expect *identical* stack trace objects.
+      Expect.equals("$systemStack", "$s", "e5.3");
+      // Expect the already-set stack trace to stay.
+      Expect.isNotNull(error.stackTrace, "e5.4");
+      Expect.equals("$originalStack", "${error.stackTrace}", "e5.5");
+    }
+  }
+
+  // Works with OutOfMemoryError.
+  {
+    var error = const OutOfMemoryError();
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+    } on Error catch (e, s) {
+      Expect.identical(error, e);
+      Expect.equals("$systemStack", "$s");
+    }
+  }
+
+  // Works with StackOverflowError.
+  {
+    var error = const StackOverflowError();
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+    } on Error catch (e, s) {
+      Expect.identical(error, e);
+      Expect.equals("$systemStack", "$s");
+    }
+  }
+
+  // Also for live, captured, StackOverflowError.
+  {
+    Object error;
+    Never foo() => foo() + 1;
+    try {
+      foo(); // Force stack overflow.
+    } catch (e, s) {
+      error = e;
+    }
+    // Some platforms might use another error than StackOverflowError.
+    // Should work with whichever object gets here.
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+    } on Error catch (e, s) {
+      Expect.identical(error, e);
+      Expect.equals("$systemStack", "$s");
+    }
+  }
+
+  asyncTest(() async {
+    var theFuture = Future.value(null);
+
+    // Test that throwing inside an asynchronous context can be caught.
+    {
+      var error = ArgumentError("e6");
+      try {
+        await theFuture;
+        Error.throwWithStackTrace(error, systemStack);
+        Expect.fail("Didn't throw: e6.1");
+        await theFuture;
+      } on Error catch (e, s) {
+        Expect.identical(error, e, "e6.2");
+        // No not expect *identical* stack trace objects.
+        Expect.equals("$systemStack", "$s", "e6.3");
+        Expect.isNotNull(error.stackTrace, "e6.4");
+        Expect.equals("$systemStack", "${error.stackTrace}", "e6.5");
+      }
+    }
+
+    // Test that throwing in asynchronous context can be locally uncaught.
+    {
+      asyncStart();
+      var error = ArgumentError("e7");
+      var future = () async {
+        await theFuture;
+        Error.throwWithStackTrace(error, systemStack);
+        Expect.fail("Didn't throw: e7.1");
+        await theFuture;
+        return null; // Force future type to Future<dynamic>
+      }();
+      future.catchError((e, s) {
+        Expect.identical(error, e, "e7.2");
+        // No not expect *identical* stack trace objects.
+        Expect.equals("$systemStack", "$s", "e7.3");
+        Expect.isNotNull(error.stackTrace, "e7.4");
+        Expect.equals("$systemStack", "${error.stackTrace}", "e7.5");
+        asyncEnd();
+      });
+    }
+
+    // Test throwing an uncaught async error caught by the Zone.
+    {
+      asyncStart();
+      var error = ArgumentError("e8");
+      await runZonedGuarded(() {
+        // Make an uncaught asynchronous error.
+        (() async {
+          await theFuture;
+          Error.throwWithStackTrace(error, systemStack);
+          Expect.fail("Didn't throw: e8.1");
+          await theFuture;
+        }());
+      }, (e, s) {
+        Expect.identical(error, e, "e8.2");
+        // No not expect *identical* stack trace objects.
+        Expect.equals("$systemStack", "$s", "e8.3");
+        Expect.isNotNull(error.stackTrace, "e8.4");
+        Expect.equals("$systemStack", "${error.stackTrace}", "e8.5");
+        asyncEnd();
+      });
+    }
+  });
+}
+
+class CustomError implements Error {
+  final String message;
+  CustomError(this.message);
+  StackTrace? get stackTrace => null;
+  String toString() => "CustomError: $message";
+}
diff --git a/tests/corelib_2/error_throw_with_stacktrace_test.dart b/tests/corelib_2/error_throw_with_stacktrace_test.dart
new file mode 100644
index 0000000..303b06c
--- /dev/null
+++ b/tests/corelib_2/error_throw_with_stacktrace_test.dart
@@ -0,0 +1,211 @@
+// 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.
+
+// @dart = 2.9
+
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+// Test of Error.throwWithStackTrace.
+main() {
+  // Ensure `systemStack` is different from any other stack tracek.
+  var systemStack = (() => StackTrace.current)();
+
+  // Test that an error can be thrown with a system stack trace..
+  {
+    var error = ArgumentError("e1");
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+      Expect.fail("Didn't throw: e1.1");
+    } on Error catch (e, s) {
+      Expect.identical(error, e, "e1.2");
+      // No not expect *identical* stack trace objects.
+      Expect.equals("$systemStack", "$s", "e1.3");
+      Expect.isNotNull(error.stackTrace, "e1.4");
+      Expect.equals("$systemStack", "${error.stackTrace}", "e1.5");
+    }
+  }
+
+  // Test that an error can be thrown with a user-created stack trace..
+  {
+    var stringStack = StackTrace.fromString("Nonce");
+    var error = ArgumentError("e2");
+    try {
+      Error.throwWithStackTrace(error, stringStack);
+      Expect.fail("Didn't throw: e2.1");
+    } on Error catch (e, s) {
+      Expect.identical(error, e, "e2.2");
+      // No not expect *identical* stack trace objects.
+      Expect.equals("$stringStack", "$s", "e2.3");
+      Expect.isNotNull(error.stackTrace, "e2.4");
+      Expect.equals("$stringStack", "${error.stackTrace}", "e2.5");
+    }
+  }
+
+  // Test that a non-error object can be thrown too.
+  {
+    var exception = FormatException("e3");
+    try {
+      Error.throwWithStackTrace(exception, systemStack);
+      Expect.fail("Didn't throw: e3.1");
+    } on Exception catch (e, s) {
+      Expect.identical(exception, e, "e3.2");
+      // No not expect *identical* stack trace objects.
+      Expect.equals("$systemStack", "$s", "e3.3");
+    }
+  }
+
+  // Test that an [Error] not extending {Error} can be thrown,
+  // but doesn't (and cannot) set the stack trace.
+  {
+    var error = CustomError("e4");
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+      Expect.fail("Didn't throw: e4.1");
+    } on Error catch (e, s) {
+      Expect.identical(error, e, "e4.2");
+      // No not expect *identical* stack trace objects.
+      Expect.equals("$systemStack", "$s", "e4.3");
+      Expect.isNull(error.stackTrace, "e4.4");
+    }
+  }
+
+  // Test that an already set stack trace isn't changed.
+  {
+    var error = ArgumentError("e5");
+    StackTrace originalStack;
+    try {
+      throw error;
+    } on Error catch (e) {
+      originalStack = e.stackTrace;
+    }
+    Expect.isNotNull(originalStack);
+    Expect.notIdentical(originalStack, systemStack);
+    Expect.notEquals("$originalStack", "");
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+      Expect.fail("Didn't throw: e5.1");
+    } on Error catch (e, s) {
+      Expect.identical(error, e, "e5.2");
+      // No not expect *identical* stack trace objects.
+      Expect.equals("$systemStack", "$s", "e5.3");
+      // Expect the already-set stack trace to stay.
+      Expect.isNotNull(error.stackTrace, "e5.4");
+      Expect.equals("$originalStack", "${error.stackTrace}", "e5.5");
+    }
+  }
+
+  // Works with OutOfMemoryError.
+  {
+    var error = const OutOfMemoryError();
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+    } on Error catch (e, s) {
+      Expect.identical(error, e);
+      Expect.equals("$systemStack", "$s");
+    }
+  }
+
+  // Works with StackOverflowError.
+  {
+    var error = const StackOverflowError();
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+    } on Error catch (e, s) {
+      Expect.identical(error, e);
+      Expect.equals("$systemStack", "$s");
+    }
+  }
+
+  // Also for live, captured, StackOverflowError.
+  {
+    Object error;
+    int foo() => foo() + 1;
+    try {
+      foo(); // Force stack overflow.
+    } catch (e, s) {
+      error = e;
+    }
+    // Some platforms might use another error than StackOverflowError.
+    // Should work with whichever object gets here.
+    try {
+      Error.throwWithStackTrace(error, systemStack);
+    } on Error catch (e, s) {
+      Expect.identical(error, e);
+      Expect.equals("$systemStack", "$s");
+    }
+  }
+
+  asyncTest(() async {
+    var theFuture = Future.value(null);
+
+    // Test that throwing inside an asynchronous context can be caught.
+    {
+      var error = ArgumentError("e6");
+      try {
+        await theFuture;
+        Error.throwWithStackTrace(error, systemStack);
+        Expect.fail("Didn't throw: e6.1");
+        await theFuture;
+      } on Error catch (e, s) {
+        Expect.identical(error, e, "e6.2");
+        // No not expect *identical* stack trace objects.
+        Expect.equals("$systemStack", "$s", "e6.3");
+        Expect.isNotNull(error.stackTrace, "e6.4");
+        Expect.equals("$systemStack", "${error.stackTrace}", "e6.5");
+      }
+    }
+
+    // Test that throwing in asynchronous context can be locally uncaught.
+    {
+      asyncStart();
+      var error = ArgumentError("e7");
+      var future = () async {
+        await theFuture;
+        Error.throwWithStackTrace(error, systemStack);
+        Expect.fail("Didn't throw: e7.1");
+        await theFuture;
+        return null; // Force future type to Future<dynamic>
+      }();
+      future.catchError((e, s) {
+        Expect.identical(error, e, "e7.2");
+        // No not expect *identical* stack trace objects.
+        Expect.equals("$systemStack", "$s", "e7.3");
+        Expect.isNotNull(error.stackTrace, "e7.4");
+        Expect.equals("$systemStack", "${error.stackTrace}", "e7.5");
+        asyncEnd();
+      });
+    }
+
+    // Test throwing an uncaught async error caught by the Zone.
+    {
+      asyncStart();
+      var error = ArgumentError("e8");
+      await runZonedGuarded(() {
+        // Make an uncaught asynchronous error.
+        (() async {
+          await theFuture;
+          Error.throwWithStackTrace(error, systemStack);
+          Expect.fail("Didn't throw: e8.1");
+          await theFuture;
+        }());
+      }, (e, s) {
+        Expect.identical(error, e, "e8.2");
+        // No not expect *identical* stack trace objects.
+        Expect.equals("$systemStack", "$s", "e8.3");
+        Expect.isNotNull(error.stackTrace, "e8.4");
+        Expect.equals("$systemStack", "${error.stackTrace}", "e8.5");
+        asyncEnd();
+      });
+    }
+  });
+}
+
+class CustomError implements Error {
+  final String message;
+  CustomError(this.message);
+  StackTrace get stackTrace => null;
+  String toString() => "CustomError: $message";
+}
diff --git a/tools/VERSION b/tools/VERSION
index 80c6a13..a3fcafc 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 294
+PRERELEASE 295
 PRERELEASE_PATCH 0
\ No newline at end of file